Skip to content

Migrate jose from go-jose v3 to go-jose v4 - #1157

Open
maraino wants to merge 2 commits into
masterfrom
mariano/go-jose-v4
Open

maraino wants to merge 2 commits into
masterfrom
mariano/go-jose-v4

Conversation

@maraino

@maraino maraino commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Upgrades github.com/go-jose/go-jose from v3.0.5 to v4.1.4. The jose package keeps its function signatures, but v4 changes some behavior that callers depend on, and two aliased types change their API.

Parsing

  • ParseSigned, ParseJWS, and ParseEncrypted pass v4 an allowlist containing every algorithm this package supports, so they accept the same algorithms as before.
  • v4's jwt.ParseSigned only accepts compact tokens and no longer strips whitespace. ParseSigned keeps the v3 behavior: it strips whitespace and converts a JSON-serialized token with a single signature to compact form. It copies the original base64url segments, because re-encoding the header could change the signed bytes.
  • v4 rejects unsupported algorithms when parsing a JWE. Decrypt and guessKeyType now return an error for input that looks like a JWE but fails to parse; before, it was treated as plain data.

Key sizes

  • v4 rejects HMAC keys shorter than the hash output, on both sign and verify, and picks the AES variant from the key length.
  • GenerateJWK now defaults oct keys to the size the algorithm needs: 48 and 64 bytes for HS384 and HS512, and 16 and 24 bytes for the A128 and A192 key wrap algorithms. It rejects explicit sizes that won't work.
  • ValidateJWK applies the same checks to loaded keys, and ErrInvalidKeySize is exported.

Breaking changes

  • Builder.CompactSerialize() and FullSerialize() are replaced by Serialize().
  • Expected.Audience is replaced by AnyAudience, which matches if any audience is present. v3 required all of them, so a plain rename weakens the check.
  • HMAC keys shorter than the hash output no longer sign or verify. That includes HS384 and HS512 keys created with the old 32-byte default.
  • ParseSigned rejects JSON-serialized tokens that have an unprotected header or more than one signature.
  • Unknown algorithms now fail in ParseSigned and ParseEncrypted instead of in Verify and Decrypt, and some error messages changed. For example, an unsupported crit header now returns ErrUnsupportedCriticalHeader instead of ErrCryptoFailure.

Updates needed downstream:

  • certificates/acme/challenge.go:683 and :728 use Expected{Audience: ...}.
  • certificates/cas/stepcas/jwk_issuer.go:98 uses CompactSerialize().

Testing

  • go test ./jose/... ./kms/azurekms/... passes.

  • Ran the original v3 test suite against the new code. The only failures were the expected ones: JSON-serialized JWTs (now handled) and a 7-byte HMAC key.

  • Built a probe twice, once against v3 and once against v4, and checked artifacts in both directions:

    • JWTs and JWSs for every signature algorithm, in compact and JSON form.
    • Every JWE combination: 17 key algorithms × 6 content encryptions, in both serializations.
    • Password-encrypted JWKs.

    Everything interoperates apart from the breaking changes listed above, and key thumbprints match.

  • Diffed about 90 edge cases between the two versions. The PBES2 iteration limit, the decompression limit, whitespace handling, and x5cInsecure parsing behave the same.

🤖 Generated with Claude Code

ParseSigned, ParseJWS, and ParseEncrypted keep their signatures and
accept every algorithm this package supports. ParseSigned still strips
whitespace and accepts JSON-serialized tokens with a single signature.

Decrypt now returns an error for a JWE it cannot parse instead of
returning it as plain data.

GenerateJWK and ValidateJWK now enforce the key sizes v4 requires for
HMAC and AES key wrap algorithms, and ErrInvalidKeySize is exported.

Breaking changes:
- Builder.CompactSerialize and FullSerialize are replaced by Serialize.
- Expected.Audience is replaced by AnyAudience, which matches any
  audience instead of all of them.
- HMAC keys shorter than the hash output no longer sign or verify.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
markusthoemmes added a commit to chainguard-dev/apko that referenced this pull request Oct 30, 2026
DiscoverKeys was the only user of go.step.sm/crypto, which re-exports
go-jose v3's JSONWebKeySet. go-jose v3 is affected by CVE-2026-116587
(DoS when parsing JWKs containing RSA keys), CVE-2026-116585 and
CVE-2026-116586, and has no patched release.

Importing go-jose v4 directly drops go.step.sm/crypto and go-jose v3
from the module graph and pins v4 at v4.1.6, which also moves the v4
copy already pulled in via other dependencies past the fixed v4.1.5.
smallstep/crypto#1157 migrates upstream to v4 but targets v4.1.4, which
is still in the affected range.

v4.1.6 rejects RSA moduli larger than 4096 bits. The keys served by
apk.cgr.dev are 4096-bit and still discover correctly.
@hslatman hslatman linked an issue Oct 30, 2026 that may be closed by this pull request

@hslatman hslatman left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good. A couple remarks:

  • The breaking changes are only called out in the PR (currently). We'll need to ensure that usages get updated appropriately, and we don't control all of them. I believe the Audience -> AnyAudience is the riskiest one to get right for callers.
  • It might be nice to pin allowed algorithms as a caller, instead of relying on the hardcoded ones. That would need a new function.
  • The README mentions v3. Should become v4, but not a blocker.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upgrade to Go JOSE v4

2 participants