Loading the parity record
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Parity / JOSE
Every number on this page was produced by running both implementations over the same cases: the real jose package pinned at 5.9.6 answers first, and its answer is the expectation the Go port is held to. Nothing is a hand-written expectation, so a new upstream release re-scores the port on its own. See JOSE for the port's own documentation. Source: github.com/malcolmston/jose.
Not a generic diagram: every node below names an artefact of this harness — the pinned package it installed, the runner files it started, the case files it streamed, and the counts it wrote out.
| Case group | Cases | Match | Mismatch | Group parity |
|---|---|---|---|---|
| reject | 40 | 0 | 0 | 0.0% |
| jwe-compact | 31 | 0 | 0 | 0.0% |
| jwk | 28 | 0 | 0 | 0.0% |
| jws-compact | 22 | 0 | 0 | 0.0% |
| jws-json | 14 | 0 | 0 | 0.0% |
| jwe-json | 11 | 0 | 0 | 0.0% |
| util | 11 | 0 | 0 | 0.0% |
Every exported symbol of the upstream package, and what the port offers for it. The upstream list is derived mechanically, never from a README: cd parity/jose/node
node --input-type=module -e "import * as m from 'jose'; console.log(Object.keys(m).sort().join('\n'))"
node --input-type=module -e "import * as m from 'jose'; console.log(Object.keys(m.errors).sort().join(','))"
node --input-type=module -e "import * as m from 'jose'; console.log(Object.keys(m.base64url).sort().join(','))". A symbol with no case is untested, never a match.
| Upstream symbol | Go symbol | Status | Cases | Note |
|---|---|---|---|---|
| jose.CompactSign | jose.Sign | differs | 13 pass, 3 fail (jws-hs256-sign … jws-hs256-sign-crit, rej-jws-jwk-*-sign) | byte-identical output for HS256/384/512, RS256/384/512, EdDSA, and for kid/typ/cty/crit headers. **Accepts JWK use, key_ops and alg misuse that upstream rejects** — see findings 1–3. |
| jose.compactVerify | jose.Verify, jose.VerifyWithOptions | differs | 20 pass, 3 fail | verifies every algorithm cross-language and rejects tampered signatures, tampered payloads, wrong keys, alg:none, unknown alg, missing alg, a disallowed alg, unknown/registered/empty/absent crit, a detached payload with nothing supplied, non-base64url segments and a two-segment token. Same JWK-misuse gap as CompactSign. |
| jose.FlattenedSign | — (jose.SignJSON emits the **general** form only) | differs | 5 pass (jws-flat-*, node → Go only) | the port can verify the flattened serialization but cannot produce it, so those cases run one-directionally. Also: with b64:false upstream always omits the payload member, the port emits the raw payload inline; both verify each other once the payload is supplied out of band. |
| jose.flattenedVerify | jose.VerifyJSON, jose.VerifyJSONWithOptions | match | 7 pass (jws-flat-*, jws-hs256-roundtrip-detached, rej-jws-json-no-protected-no-header) | also reached internally by compactVerify and generalVerify. Detached compact verification uses the documented split-and-flattenedVerify idiom. |
| jose.GeneralSign | jose.SignJSONMulti | match | 8 pass | single, double and triple signatures; per-signature unprotected headers; crit; detached; RFC 7797 b64:false. jws-gen-hs256-value and jws-gen-multi-hs-rs-value compare the whole JSON document byte for byte. |
| jose.generalVerify | jose.VerifyJSON, jose.VerifyJSONWithOptions | differs | 8 pass, 2 fail | selects the right signature by key in a multi-signature document. **Reads b64 from the merged header instead of the protected header** — findings 4 and 5. |
| jose.UnsecuredJWT | — | missing | — | the port refuses alg:none outright and offers no opt-in; rej-jws-alg-none confirms neither side will verify one. |
| jose.EmbeddedJWK | — | missing | — | the port never resolves a key from the jwk header parameter. |
| jose.CompactEncrypt | jose.Encrypt | differs | 27 pass, 1 fail | round-trips in **both** directions for RSA1_5, RSA-OAEP, RSA-OAEP-256, A128/192/256KW, A128/192/256GCMKW, dir, ECDH-ES (P-256/P-384/P-521/X25519), ECDH-ES+A128/192/256KW (incl. apu/apv) and PBES2-HS256/384/512+A128/192/256KW, against A128/192/256GCM and A128CBC-HS256/A192CBC-HS384/A256CBC-HS512, plus empty and multi-byte-UTF-8 plaintext. **zip:"DEF" is a divergence** — finding 8. |
| jose.compactDecrypt | jose.Decrypt, jose.DecryptWithOptions | differs | 33 pass, 2 fail | rejects tampered ciphertext/tag/IV/protected header, wrong symmetric and wrong RSA keys, unsupported alg, unsupported enc, disallowed alg, disallowed enc and a truncated token. Diverges on PBES2 defaults — findings 6 and 7. |
| jose.FlattenedEncrypt | — (jose.EncryptJSON emits the **general** form only) | differs | 4 pass (jwe-flat-*, node → Go only) | the port decrypts the flattened serialization but cannot produce it. aad and a shared unprotected header both survive the crossing. |
| jose.flattenedDecrypt | jose.DecryptJSON, jose.DecryptJSONWithOptions | match | 4 pass (jwe-flat-*) | also reached internally by compactDecrypt and generalDecrypt. |
| jose.GeneralEncrypt | jose.EncryptJSONMulti, jose.Recipient | match | 9 pass | one, two and three recipients over one CEK, mixing A128KW, A256KW, A256GCMKW, RSA-OAEP-256, PBES2-HS256+A128KW and ECDH-ES+A256KW; aad; shared unprotected header. |
| jose.generalDecrypt | jose.DecryptJSON, jose.DecryptJSONWithOptions | differs | 8 pass, 1 fail | picks the recipient the supplied key unlocks, whichever position it sits in. Diverges on a malformed mixed-form document — finding 9. |
| jose.EncryptJWT | — | missing | — | JWT claim layer; lives in the sibling github.com/malcolmston/jwt, not in this port. |
| jose.jwtDecrypt | — | missing | — | as above. |
| jose.importJWK | jose.ParseJWK, JWK.Key | match | 11 pass (jwk-rt-*) | RSA (with all CRT parameters), EC P-256/P-384/P-521, OKP Ed25519, OKP X25519, oct. |
| jose.exportJWK | jose.FromKey, JWK.Public | match | 15 pass (jwk-rt-*, jwk-public-*) | import-then-export reproduces every parameter, byte for byte, for every key type. |
| jose.calculateJwkThumbprint | JWK.Thumbprint | match | 12 pass (jwk-thumbprint-*, jwks-lookup-*) | RFC 7638 for RSA, EC, OKP and oct; unaffected by private parameters and by use/alg/kid. |
| jose.calculateJwkThumbprintUri | — | missing | — | the port has no urn:ietf:params:oauth:jwk-thumbprint: helper. |
| jose.createLocalJWKSet | jose.ParseJWKSet, JWKSet.LookupKeyID | match | 4 pass (jwks-lookup-*) | selection by kid; both sides fail on an unknown kid. Upstream additionally filters by alg/use and can raise JWKSMultipleMatchingKeys; the port matches on kid alone and returns the first hit. |
| jose.createRemoteJWKSet | — | missing | — | fetches over HTTP; the port has no network code. |
| jose.jwksCache | — | missing | — | belongs to the remote JWKS machinery. |
| jose.experimental_jwksCache | — | missing | — | as above. |
| jose.importPKCS8 | — | missing | — | the port has no PEM/DER import. keys/*-priv.pem is committed for reference and for the upstream runner, but the Go runner can only consume the JWK form. |
| jose.importSPKI | — | missing | — | as above. |
| jose.importX509 | — | missing | — | no certificate parsing. |
| jose.exportPKCS8 | — | missing | — | no PEM/DER export. |
| jose.exportSPKI | — | missing | — | as above. |
| jose.generateKeyPair | — | missing | — | no key generation in the port; harness keys are generated once, upstream, and committed. |
| jose.generateSecret | — | missing | — | as above. |
| jose.SignJWT | — | missing | — | the port is deliberately payload-agnostic: []byte in, []byte out, no claim model and no clock. Time-based claim checks (and therefore an explicit now) have no counterpart to compare against, so no case supplies one. |
| jose.jwtVerify | — | missing | — | as above. |
| jose.decodeJwt | — | missing | — | as above. |
| jose.decodeProtectedHeader | — | missing | — | the port only ever returns a header as part of Verify/Decrypt; there is no standalone decoder. |
| jose.base64url.encode | jose.EncodeSegment | match | 6 pass (b64-encode-*) | unpadded, URL-safe alphabet, all input lengths mod 3. |
| jose.base64url.decode | jose.DecodeSegment | differs | 4 pass, 1 fail (b64-decode-*) | agrees on every valid input; diverges on invalid input — finding 10. |
| jose.cryptoRuntime | — | missing | — | informational string ("node:crypto"); nothing to port. |
| jose.errors.JOSEError | — | untested | — | base class; the port uses plain sentinel error values, not a hierarchy. |
| jose.errors.JOSEAlgNotAllowed | jose.ErrUnsupportedAlgorithm / ErrSignatureInvalid | untested | — | raised by rej-jws-alg-not-allowed, rej-jwe-alg-not-allowed, rej-jwe-pbes2-without-opt-in. |
| jose.errors.JOSENotSupported | jose.ErrUnsupportedAlgorithm, ErrUnsupportedEncryption | untested | — | raised by rej-jws-alg-unsupported, rej-jwe-alg-unsupported, rej-jwe-enc-unsupported, rej-jwe-zip-def. |
| jose.errors.JWEDecryptionFailed | jose.ErrDecryptFailed | untested | — | raised by the rej-jwe-tampered-* and rej-jwe-wrong-* cases. |
| jose.errors.JWEInvalid | jose.ErrMalformed, ErrInvalidHeader | untested | — | raised by rej-jwe-truncated, rej-jwe-pbes2-p2c-below-minimum. |
| jose.errors.JWSInvalid | jose.ErrMalformed, ErrInvalidHeader, ErrInvalidCrit | untested | — | raised by rej-jws-alg-missing, rej-jws-crit-*, rej-jws-not-base64url, rej-jws-too-few-segments, rej-jws-json-no-protected-no-header. |
| jose.errors.JWSSignatureVerificationFailed | jose.ErrSignatureInvalid | untested | — | raised by rej-jws-tampered-signature, rej-jws-tampered-payload, rej-jws-wrong-key. |
| jose.errors.JWKInvalid | jose.ErrInvalidKey | untested | — | |
| jose.errors.JWKSInvalid | jose.ErrInvalidKey | untested | — | |
| jose.errors.JWKSNoMatchingKey | jose.ErrKeyNotFound | untested | — | raised by jwks-lookup-unknown-kid. |
| jose.errors.JWKSMultipleMatchingKeys | — | untested | — | the port has no ambiguity error: LookupKeyID returns the first kid match. |
| jose.errors.JWKSTimeout | — | untested | — | remote JWKS only. |
| jose.errors.JWTClaimValidationFailed | — | untested | — | no claim layer in the port. |
| jose.errors.JWTExpired | — | untested | — | as above. |
| jose.errors.JWTInvalid | — | untested | — | as above. |
| — | jose.AESKeyWrap, jose.AESKeyUnwrap | extra | — | RFC 3394, exported because golang.org/x/crypto is off-limits for this family. Exercised indirectly by every *KW case. |
| — | jose.PBKDF2 | extra | — | RFC 8018 §5.2; exercised indirectly by the PBES2 cases. |
| — | jose.ConcatKDF | extra | — | RFC 7518 §4.6.2; exercised indirectly by the ECDH-ES cases. |
| — | jose.SignatureAlgorithms, jose.KeyManagementAlgorithms, jose.ContentEncryptionAlgorithms, jose.ContentEncryptionKeySize | extra | — | registry accessors. |
| — | jose.Header + Algorithm/Encryption/KeyID/Type/ContentType/Critical/String | extra | — | typed header accessors over map[string]any. |
| — | jose.JWK.IsPrivate | extra | — | |
| — | jose.SignOptions, VerifyOptions, EncryptOptions, DecryptOptions, Signer, Recipient | extra | — | option/aggregate types; upstream uses builder classes instead. |
| — | jose.VerifyWithOptions, VerifyJSONWithOptions, DecryptWithOptions, DecryptJSONWithOptions | extra | — | the option-carrying forms; exercised by the allow-list, crit and detached-payload cases. |
| — | jose.MaxDecompressedSize, DefaultPBES2Count, MinPBES2Count, MaxPBES2Count, MaxPBES2SaltInput | extra | — | exported security bounds. MinPBES2Count is the cause of finding 7. |
| — | jose.Err* (15 sentinels) | extra | — | see the error table above. |
| — | jose.HS256 … jose.PBES2_HS512_A256KW (alg/enc constants) | extra | — | string constants for every alg/enc; their values are exercised by every case. |
Every case the harness streamed to both runners, with the exact upstream symbol and Go symbol it exercised. A deliberate, documented difference is a deviation and is counted apart from a mismatch.
| Case | Group | Upstream symbol | Go symbol | Status | Note |
|---|---|---|---|---|---|
| jwe-rsa1_5-a128cbc-hs256 | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | legacy RSAES-PKCS1-v1_5; deprecated upstream but still implemented on both sides |
| jwe-rsa-oaep-a128gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-rsa-oaep-256-a256gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-rsa-oaep-256-a128cbc-hs256 | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-a128kw-a128gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | AES Key Wrap, RFC 3394 |
| jwe-a192kw-a192gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-a256kw-a256cbc-hs512 | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-a128gcmkw-a128gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | AES-GCM key wrapping puts iv and tag in the protected header |
| jwe-a192gcmkw-a192cbc-hs384 | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-a256gcmkw-a256gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-dir-a128gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-dir-a192gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-dir-a256gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-dir-a128cbc-hs256 | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-dir-a192cbc-hs384 | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-dir-a256cbc-hs512 | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-ecdh-es-p256-a128gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | ECDH-ES direct key agreement; the agreed key is the CEK |
| jwe-ecdh-es-p384-a256cbc-hs512 | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-ecdh-es-p521-a256gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-ecdh-es-x25519-a128gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | RFC 8037 X25519 key agreement |
| jwe-ecdh-es-a128kw-a128gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-ecdh-es-a192kw-a192gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-ecdh-es-a256kw-a256gcm-apu-apv | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | Concat KDF PartyUInfo / PartyVInfo fed through the apu and apv headers |
| jwe-ecdh-es-x25519-a256kw-a256gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-pbes2-hs256-a128kw-a128gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | PBKDF2 salt is random, so only the round-trip can be compared Upstream requires PBES2 to be named in keyManagementAlgorithms before it will decrypt. |
| jwe-pbes2-hs384-a192kw-a192gcm | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | Upstream requires PBES2 to be named in keyManagementAlgorithms before it will decrypt. |
| jwe-pbes2-hs512-a256kw-a256cbc-hs512 | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | Upstream requires PBES2 to be named in keyManagementAlgorithms before it will decrypt. |
| jwe-rsa-oaep-256-a256gcm-kid-cty | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-a256kw-a256gcm-alg-allowlist | jwe-compact | jose.compactDecrypt | jose.DecryptWithOptions | match | alg and enc allow-lists accept the matching values |
| jwe-empty-plaintext | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-utf8-plaintext | jwe-compact | jose.CompactEncrypt | jose.Encrypt | match | |
| jwe-flat-a256kw | jwe-json | jose.FlattenedEncrypt | jose.DecryptJSON | match | RFC 7516 section 7.2.2; the port has no flattened producer, so only node -> Go runs |
| jwe-flat-a256kw-aad | jwe-json | jose.FlattenedEncrypt | jose.DecryptJSON | match | RFC 7520 section 5.10, the aad member |
| jwe-flat-shared-unprotected | jwe-json | jose.FlattenedEncrypt | jose.DecryptJSON | match | RFC 7520 section 5.11, parameters outside the authentication tag |
| jwe-flat-rsa-oaep-256 | jwe-json | jose.flattenedDecrypt | jose.DecryptJSON | match | |
| jwe-gen-single-a256kw | jwe-json | jose.GeneralEncrypt | jose.EncryptJSONMulti | match | |
| jwe-gen-single-aad | jwe-json | jose.GeneralEncrypt | jose.EncryptJSONMulti | match | |
| jwe-gen-single-shared-unprotected | jwe-json | jose.GeneralEncrypt | jose.EncryptJSONMulti | match | |
| jwe-gen-multi-a256kw-rsa | jwe-json | jose.GeneralEncrypt | jose.EncryptJSONMulti | match | RFC 7520 section 5.13, one content encryption delivered to several recipients |
| jwe-gen-multi-first-recipient | jwe-json | jose.generalDecrypt | jose.DecryptJSON | match | |
| jwe-gen-multi-three-recipients | jwe-json | jose.GeneralEncrypt | jose.EncryptJSONMulti | match | three key-management families over one CEK |
| jwe-gen-single-ecdh-es-a256kw | jwe-json | jose.GeneralEncrypt | jose.EncryptJSONMulti | match | |
| jwk-rt-rsa-priv | jwk | jose.importJWK / jose.exportJWK | jose.ParseJWK / JWK.Key / jose.FromKey | match | import then export must reproduce every RSA CRT parameter |
| jwk-rt-rsa-pub | jwk | jose.importJWK / jose.exportJWK | jose.ParseJWK / JWK.Key / jose.FromKey | match | |
| jwk-rt-ec-p256-priv | jwk | jose.importJWK / jose.exportJWK | jose.ParseJWK / JWK.Key / jose.FromKey | match | |
| jwk-rt-ec-p256-pub | jwk | jose.importJWK / jose.exportJWK | jose.ParseJWK / JWK.Key / jose.FromKey | match | |
| jwk-rt-ec-p384-priv | jwk | jose.importJWK / jose.exportJWK | jose.ParseJWK / JWK.Key / jose.FromKey | match | |
| jwk-rt-ec-p521-priv | jwk | jose.importJWK / jose.exportJWK | jose.ParseJWK / JWK.Key / jose.FromKey | match | |
| jwk-rt-ed25519-priv | jwk | jose.importJWK / jose.exportJWK | jose.ParseJWK / JWK.Key / jose.FromKey | match | |
| jwk-rt-ed25519-pub | jwk | jose.importJWK / jose.exportJWK | jose.ParseJWK / JWK.Key / jose.FromKey | match | |
| jwk-rt-x25519-priv | jwk | jose.importJWK / jose.exportJWK | jose.ParseJWK / JWK.Key / jose.FromKey | match | RFC 8037 OKP X25519 |
| jwk-rt-x25519-pub | jwk | jose.importJWK / jose.exportJWK | jose.ParseJWK / JWK.Key / jose.FromKey | match | |
| jwk-rt-oct | jwk | jose.importJWK / jose.exportJWK | jose.ParseJWK / JWK.Key / jose.FromKey | match | |
| jwk-thumbprint-rsa | jwk | jose.calculateJwkThumbprint | JWK.Thumbprint | match | RFC 7638 |
| jwk-thumbprint-rsa-priv | jwk | jose.calculateJwkThumbprint | JWK.Thumbprint | match | private parameters must not affect the thumbprint |
| jwk-thumbprint-ec-p256 | jwk | jose.calculateJwkThumbprint | JWK.Thumbprint | match | |
| jwk-thumbprint-ec-p384 | jwk | jose.calculateJwkThumbprint | JWK.Thumbprint | match | |
| jwk-thumbprint-ec-p521 | jwk | jose.calculateJwkThumbprint | JWK.Thumbprint | match | |
| jwk-thumbprint-ed25519 | jwk | jose.calculateJwkThumbprint | JWK.Thumbprint | match | |
| jwk-thumbprint-x25519 | jwk | jose.calculateJwkThumbprint | JWK.Thumbprint | match | |
| jwk-thumbprint-oct | jwk | jose.calculateJwkThumbprint | JWK.Thumbprint | match | |
| jwk-thumbprint-ignores-use-alg-kid | jwk | jose.calculateJwkThumbprint | JWK.Thumbprint | match | only the required members enter the digest |
| jwk-public-rsa | jwk | jose.exportJWK | JWK.Public | match | |
| jwk-public-ec-p256 | jwk | jose.exportJWK | JWK.Public | match | |
| jwk-public-ed25519 | jwk | jose.exportJWK | JWK.Public | match | |
| jwk-public-x25519 | jwk | jose.exportJWK | JWK.Public | match | |
| jwks-lookup-rsa-1 | jwk | jose.createLocalJWKSet | JWKSet.LookupKeyID | match | JWKS selection by kid, reported as the selected key's thumbprint |
| jwks-lookup-ec-1 | jwk | jose.createLocalJWKSet | JWKSet.LookupKeyID | match | |
| jwks-lookup-ed-1 | jwk | jose.createLocalJWKSet | JWKSet.LookupKeyID | match | |
| jwks-lookup-unknown-kid | jwk | jose.createLocalJWKSet | JWKSet.LookupKeyID | match | no key for the requested kid |
| jws-hs256-sign | jws-compact | jose.CompactSign | jose.Sign | match | HMAC is deterministic, so the compact serialization is compared byte for byte |
| jws-hs384-sign | jws-compact | jose.CompactSign | jose.Sign | match | |
| jws-hs512-sign | jws-compact | jose.CompactSign | jose.Sign | match | |
| jws-rs256-sign | jws-compact | jose.CompactSign | jose.Sign | match | RSASSA-PKCS1-v1_5 is deterministic |
| jws-rs384-sign | jws-compact | jose.CompactSign | jose.Sign | match | |
| jws-rs512-sign | jws-compact | jose.CompactSign | jose.Sign | match | |
| jws-eddsa-sign | jws-compact | jose.CompactSign | jose.Sign | match | Ed25519 is deterministic |
| jws-hs256-sign-kid | jws-compact | jose.CompactSign | jose.Sign | match | |
| jws-hs256-sign-typ-cty | jws-compact | jose.CompactSign | jose.Sign | match | |
| jws-hs256-sign-crit | jws-compact | jose.CompactSign | jose.Sign | match | RFC 7515 section 4.1.11 crit extension |
| jws-hs256-roundtrip | jws-compact | jose.compactVerify | jose.Verify | match | |
| jws-rs256-roundtrip | jws-compact | jose.compactVerify | jose.Verify | match | |
| jws-eddsa-roundtrip | jws-compact | jose.compactVerify | jose.Verify | match | |
| jws-es256-roundtrip | jws-compact | jose.compactVerify | jose.Verify | match | ECDSA has a random nonce, so only a cross round-trip can compare |
| jws-es384-roundtrip | jws-compact | jose.compactVerify | jose.Verify | match | |
| jws-es512-roundtrip | jws-compact | jose.compactVerify | jose.Verify | match | |
| jws-ps256-roundtrip | jws-compact | jose.compactVerify | jose.Verify | match | RSASSA-PSS has a random salt |
| jws-ps384-roundtrip | jws-compact | jose.compactVerify | jose.Verify | match | |
| jws-ps512-roundtrip | jws-compact | jose.compactVerify | jose.Verify | match | |
| jws-es256-roundtrip-alg-allowlist | jws-compact | jose.compactVerify | jose.VerifyWithOptions | match | algorithm allow-list accepts the matching alg |
| jws-hs256-roundtrip-crit | jws-compact | jose.compactVerify | jose.VerifyWithOptions | match | a crit extension the recipient declares as understood is accepted |
| jws-hs256-roundtrip-detached | jws-compact | jose.compactVerify | jose.VerifyWithOptions | match | RFC 7515 Appendix F detached payload; upstream's CompactSign has no detach option, so only the Go producer is exercised |
| jws-flat-hs256 | jws-json | jose.FlattenedSign | jose.VerifyJSON | match | the port has no flattened producer, so only node -> Go runs |
| jws-flat-hs256-unprotected-kid | jws-json | jose.FlattenedSign | jose.VerifyJSON | match | RFC 7515 section 7.2.2, unprotected header merged into the reported header |
| jws-flat-es256 | jws-json | jose.flattenedVerify | jose.VerifyJSON | match | |
| jws-flat-detached | jws-json | jose.flattenedVerify | jose.VerifyJSONWithOptions | match | detached payload supplied out of band |
| jws-flat-b64false | jws-json | jose.FlattenedSign | jose.VerifyJSONWithOptions | match | RFC 7797 unencoded payload; upstream always omits the payload member when b64 is false, the port emits it inline |
| jws-gen-hs256 | jws-json | jose.GeneralSign | jose.SignJSONMulti | match | |
| jws-gen-hs256-value | jws-json | jose.GeneralSign | jose.SignJSONMulti | match | single HMAC signature is deterministic, so the whole document is compared |
| jws-gen-multi-hs-rs | jws-json | jose.GeneralSign | jose.SignJSONMulti | match | RFC 7515 section 7.2.1 multiple signatures; the recipient key selects the second one |
A case can show the port is less safe than the library it ports. Those findings are written into security.json and filed as draft advisories, never as public issues with a working repro.
| Finding | Severity | Affected | Cases | Summary |
|---|---|---|---|---|
| GHSA-w4qr-w6rh-v2mx | medium | <= 0.1.0 | — | JWK "alg" pinning is not enforced, allowing silent algorithm downgrade |
| GHSA-89qh-5q5j-xrx9 | low | <= 0.1.0 | — | JWK "key_ops" is not enforced: an encrypt/decrypt-only key signs and verifies a JWS |
| GHSA-qvw9-rcpm-hhmw | low | <= 0.1.0 | — | JWK "use" is not enforced: an "enc" key is accepted for JWS signing and verification |
| GHSA-4689-9mp2-q5rm | high | <= 0.1.0 | — | crit can be satisfied by an attacker-supplied unprotected header, defeating RFC 7797 protection |
| GHSA-657x-hc2h-j7mj | medium | <= 0.1.0 | — | Unprotected {"b64":false} header allows keyless payload substitution in a verified JWS |