You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
github.com/lestrrat-go/jwx/v4 is now released. After the preview period (#1673) and a final round of fixes, v4.0.0 is tagged and ready to use.
v4 is a major release that rethinks how jwx is structured, with three goals: make it faster, make it lighter, and make it ready for post-quantum cryptography. The core JOSE suite is fully covered — RFC 7515 (JWS), RFC 7516 (JWE), RFC 7517 (JWK), RFC 7518 (JWA), RFC 7519 (JWT), and OpenID Connect Core 1.0 — and v4 additionally ships the cryptographic algorithms needed to carry JOSE into the post-quantum era.
Quick links:
Changes-v4.md — full list of incompatible changes from v3 → v4.
MIGRATION.md — step-by-step migration guide with before/after code.
jwx-go/jwxmigrate — machine-readable rewrite rules and an automated migration checker.
It covers JWT/JWS/JWE/JWK basics plus every post-quantum and HPKE feature described below — ML-KEM, ML-DSA, composite signatures, X448 HPKE, and the hybrid PQ HPKE draft. If you're looking for "how do I do X with v4?", start there.
git clone https://github.com/jwx-go/examples
cd examples
GOEXPERIMENT=jsonv2 go test ./...
⚠️ Heads up: GOEXPERIMENT=jsonv2 is required
v4 will not build without it. jwx/v4 uses encoding/json/v2, which is still gated behind Go's experiment flag. Every go build, go test, or go run against v4 must be invoked with:
GOEXPERIMENT=jsonv2 go build ./...
GOEXPERIMENT=jsonv2 go test ./...
Without the flag you will get build constraints exclude all Go files errors. The flag can be dropped from jwx/v4 once encoding/json/v2 graduates out of experimental status in the Go toolchain itself — a future patch release will make that switch.
Faster: Generics Eliminate Reflection Overhead
v3 relied heavily on any/interface{} and runtime type assertions throughout the codebase — in key registration, field accessors, JSON decoding, and more. v4 replaces these with Go generics wherever possible.
jwx/v4 vs golang-jwt (JWT only): Essentially tied on speed across all algorithms. Both are dramatically faster than v3 for HMAC operations.
jwx/v4 vs go-jose: jwx/v4 wins decisively on JWK parsing (2-10x faster on RSA private). go-jose wins on some JWE encrypt paths (DIRECT, A256KW). JWS verify goes to v4 across the board.
RSA decrypt is crypto-bound -- all libraries converge around 1.1ms, confirming the bottleneck is crypto/rsa.
Example of Generics Usage
Type-safe accessors are available across all packages: jwt.Get[T], jwk.Get[T], jws.Get[T], jwe.Get[T].
v4 also adopts Go's encoding/json/v2, replacing the goccy/go-json backend and the build-tag abstraction that came with it.
Lighter: Extension Module Architecture
v3's main module pulled in 11 direct dependencies including secp256k1, assembly-optimized base64, and HTTP caching — whether you used them or not. v4 cuts this to 5:
v3 (11 deps)
v4 (5 deps)
decred/secp256k1
lestrrat-go/dsig
goccy/go-json
lestrrat-go/option
segmentio/asm
stretchr/testify
lestrrat-go/blackmagic
valyala/fastjson
lestrrat-go/dsig-secp256k1
x/crypto
lestrrat-go/httprc
...
Everything else moves to opt-in extension modules under github.com/jwx-go/*. If you don't use ES256K, you don't pay for secp256k1. We don't particularly believe in religiously following "only core modules" principle, but we tried to make this module as lean as we could without sacrificing key features. The full catalog and how they register themselves is documented in docs/10-extensions.md.
Companion Modules at a Glance
Beyond the PQ and HPKE extensions detailed in the sections below, the following companion modules are available:
jwx-go/ed448 — EdDSA with the Ed448 curve (per RFC 9864).
jwx-go/asmbase64 — assembly-optimized base64 backend for environments that want the extra throughput.
jwx-go/jwkfetch — HTTP JWKS fetching and background-refreshing cache (the httprc-backed JWK cache that used to live in core jwx).
jwx-go/jwxmigrate — automated v3→v4 migration tool with machine-readable rewrite rules.
Post-Quantum Ready: Because "Harvest Now, Decrypt Later" Is Already Happening
Adversaries are already collecting encrypted traffic today, stockpiling it for the day quantum computers can break current key exchange and signature algorithms. If your JWTs carry sensitive claims or your JWE payloads have long-term value, the window to act is now — not when NIST finalizes every last spec.
Both ML-KEM (FIPS 203) and ML-DSA (FIPS 204) are supported via opt-in companion modules under github.com/jwx-go, each tracking its respective JOSE binding draft. A hybrid composite signature scheme (ML-DSA + classical) is also available for users who want PQ assurance without fully dropping traditional algorithms.
ML-DSA currently lives in an extension module because the Go stdlib doesn't have it yet. There is an active proposal to add crypto/mldsa to the standard library. Once that lands, we can move ML-DSA support into the core jwx module — no extension import needed, just jws.WithKey(jwa.MLDSA65(), key) out of the box.
Hybrid Composite Signatures (ML-DSA + Classical)
For users who want PQ assurance but aren't ready to fully drop traditional algorithms — for compliance, interop, or hedging reasons — github.com/jwx-go/compsig ships composite signatures per draft-ietf-jose-pq-composite-sigs. Each algorithm pairs ML-DSA with a traditional scheme, and a JWS verifies only when both components verify.
Algorithm
PQ component
Classical component
MLDSA44ES256
ML-DSA-44
ECDSA P-256 / SHA-256
MLDSA65ES256
ML-DSA-65
ECDSA P-256 / SHA-256
MLDSA87ES384
ML-DSA-87
ECDSA P-384 / SHA-384
MLDSA44Ed25519
ML-DSA-44
Ed25519
MLDSA65Ed25519
ML-DSA-65
Ed25519
MLDSA87Ed448
ML-DSA-87
Ed448
Both components sign the same domain-separated message, and the wire signature is their concatenation. The motivation is hybrid security: an attacker would need to break BOTH the post-quantum and the traditional family to forge a signature.
Post-quantum key encapsulation for JWE via github.com/jwx-go/mlkem, supporting ML-KEM-768, ML-KEM-1024, and key-wrap variants (ML-KEM-768+A192KW, ML-KEM-1024+A256KW). Uses Go's crypto/mlkem stdlib package with KMAC256-based key derivation (per NIST SP 800-185), as specified in draft-ietf-jose-pqc-kem.
ML-KEM lives in a companion module so the core jwx module stays free of draft-spec algorithms. Once draft-ietf-jose-pqc-kem is published as an RFC, ML-KEM may move directly into core jwx.
HPKE (Hybrid Public Key Encryption, RFC 9180) is implemented, following draft-ietf-jose-hpke-encrypt. Please note that because the JOSE binding is still in draft stage, the API/support may change in the future. The underlying primitives — HKDF (RFC 5869), AES-GCM (RFC 5116), ChaCha20-Poly1305 (RFC 8439), X25519 (RFC 7748) — are standards-track.
Six algorithm suites are supported out of the box using Go's crypto/hpke stdlib package:
Algorithm
KEM
KDF
AEAD
HPKE-0-KE
DHKEM(P-256)
HKDF-SHA256
AES-128-GCM
HPKE-1-KE
DHKEM(P-384)
HKDF-SHA384
AES-256-GCM
HPKE-2-KE
DHKEM(P-521)
HKDF-SHA512
AES-256-GCM
HPKE-3-KE
DHKEM(X25519)
HKDF-SHA256
AES-128-GCM
HPKE-4-KE
DHKEM(X25519)
HKDF-SHA256
ChaCha20-Poly1305
HPKE-7-KE
DHKEM(P-256)
HKDF-SHA256
AES-256-GCM
X448-based HPKE (DHKEM(X448, HKDF-SHA512)) is available as an extension module at github.com/jwx-go/x448, with a standalone DHKEM and HPKE implementation for the curves that Go's stdlib doesn't cover yet.
HPKE is a natural complement to the PQ story — it provides a path toward hybrid encryption schemes that combine traditional and post-quantum key encapsulation as the ecosystem matures.
Hybrid PQ HPKE (very early draft).github.qkg1.top/jwx-go/reddy-pqchpke ships HPKE-10-KE and HPKE-11-KE per draft-reddy-cose-jose-pqc-hybrid-hpke, pairing X25519 with ML-KEM-768 via the X-Wing KEM, with SHAKE256 as the HPKE KDF and AES-256-GCM (HPKE-10) or ChaCha20-Poly1305 (HPKE-11) as the AEAD. Heads up: the underlying spec is an individual submission, not yet WG-adopted — algorithm identifiers and wire formats may still change. Treat this as the most experimental piece of the v4 PQ story.
Other Improvements
Iterators — Native Go 1.23+ range-over-func support:
Ed25519 and Ed448 per RFC 9864 — v4 supports the updated EdDSA algorithm identifiers from RFC 9864, which supersedes the provisional "EdDSA" identifier of the original RFC 8037. Ed25519 is built into core; Ed448 is available via github.com/jwx-go/ed448.
ParseFS across all packages — A new ParseFS(fs.FS, path, ...) entry point is available alongside the existing ReadFile, so callers can read from embed.FS, testing/fstest, or a CWD-sandboxed os.DirFS without touching the real filesystem.
Consolidated code generation — Six separate generator binaries unified into a single tool with a shared library. Object definitions now live next to the packages they generate, not in a separate tools/ tree.
Try It
import"github.com/lestrrat-go/jwx/v4/jwt"
GOEXPERIMENT=jsonv2 go build ./...
And once again — for runnable usage examples covering everything above, the companion module is github.com/jwx-go/examples.
Further Reading
Per-package guides in the repo:
docs/01-jwt.md — working with JWT (parsing, signing, validation, custom claims).
docs/02-jws.md — working with JWS (compact, JSON serialization, multi-sig, detached).
docs/03-jwe.md — working with JWE (key management, content encryption, HPKE).
docs/04-jwk.md — working with JWK (key types, sets, import/export, thumbprints).
Cross-cutting topics:
docs/10-extensions.md — complete list of extension modules and how registration works.
docs/13-input-size.md — how v4 bounds resource use during parse/decrypt (amplification caps, streaming).
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
github.com/lestrrat-go/jwx/v4is now released. After the preview period (#1673) and a final round of fixes, v4.0.0 is tagged and ready to use.v4 is a major release that rethinks how jwx is structured, with three goals: make it faster, make it lighter, and make it ready for post-quantum cryptography. The core JOSE suite is fully covered — RFC 7515 (JWS), RFC 7516 (JWE), RFC 7517 (JWK), RFC 7518 (JWA), RFC 7519 (JWT), and OpenID Connect Core 1.0 — and v4 additionally ships the cryptographic algorithms needed to carry JOSE into the post-quantum era.
Quick links:
jwx-go/jwxmigrate— machine-readable rewrite rules and an automated migration checker.Faster: Generics Eliminate Reflection Overhead
v3 relied heavily on
any/interface{}and runtime type assertions throughout the codebase — in key registration, field accessors, JSON decoding, and more. v4 replaces these with Go generics wherever possible.Check out the full benchmark comparing jwx/v4 against jwx/v3, golang-jwt, and go-jose.
Overall (geomean, v4 = baseline)
Key Takeaways
crypto/rsa.Example of Generics Usage
Type-safe accessors are available across all packages:
jwt.Get[T],jwk.Get[T],jws.Get[T],jwe.Get[T].Key registration becomes simpler as well — no more zero-value discriminators or internal type switches:
v4 also adopts Go's
encoding/json/v2, replacing thegoccy/go-jsonbackend and the build-tag abstraction that came with it.Lighter: Extension Module Architecture
v3's main module pulled in 11 direct dependencies including secp256k1, assembly-optimized base64, and HTTP caching — whether you used them or not. v4 cuts this to 5:
decred/secp256k1lestrrat-go/dsiggoccy/go-jsonlestrrat-go/optionsegmentio/asmstretchr/testifylestrrat-go/blackmagicvalyala/fastjsonlestrrat-go/dsig-secp256k1x/cryptolestrrat-go/httprcEverything else moves to opt-in extension modules under
github.com/jwx-go/*. If you don't use ES256K, you don't pay for secp256k1. We don't particularly believe in religiously following "only core modules" principle, but we tried to make this module as lean as we could without sacrificing key features. The full catalog and how they register themselves is documented indocs/10-extensions.md.Companion Modules at a Glance
Beyond the PQ and HPKE extensions detailed in the sections below, the following companion modules are available:
jwx-go/es256k— secp256k1/ES256K signatures (Bitcoin/Ethereum curve).jwx-go/ed448— EdDSA with the Ed448 curve (per RFC 9864).jwx-go/asmbase64— assembly-optimized base64 backend for environments that want the extra throughput.jwx-go/jwkfetch— HTTP JWKS fetching and background-refreshing cache (thehttprc-backed JWK cache that used to live in core jwx).jwx-go/jwxmigrate— automated v3→v4 migration tool with machine-readable rewrite rules.Post-Quantum Ready: Because "Harvest Now, Decrypt Later" Is Already Happening
Adversaries are already collecting encrypted traffic today, stockpiling it for the day quantum computers can break current key exchange and signature algorithms. If your JWTs carry sensitive claims or your JWE payloads have long-term value, the window to act is now — not when NIST finalizes every last spec.
Both ML-KEM (FIPS 203) and ML-DSA (FIPS 204) are supported via opt-in companion modules under
github.com/jwx-go, each tracking its respective JOSE binding draft. A hybrid composite signature scheme (ML-DSA + classical) is also available for users who want PQ assurance without fully dropping traditional algorithms.ML-DSA (FIPS 204)
Post-quantum signatures via
github.com/jwx-go/mldsa, supporting all three parameter sets (ML-DSA-44/65/87). Fully integrated with JWS and JWK using the"AKP"key type from draft-ietf-jose-pqc-dsa:ML-DSA currently lives in an extension module because the Go stdlib doesn't have it yet. There is an active proposal to add
crypto/mldsato the standard library. Once that lands, we can move ML-DSA support into the core jwx module — no extension import needed, justjws.WithKey(jwa.MLDSA65(), key)out of the box.Hybrid Composite Signatures (ML-DSA + Classical)
For users who want PQ assurance but aren't ready to fully drop traditional algorithms — for compliance, interop, or hedging reasons —
github.com/jwx-go/compsigships composite signatures perdraft-ietf-jose-pq-composite-sigs. Each algorithm pairs ML-DSA with a traditional scheme, and a JWS verifies only when both components verify.MLDSA44ES256MLDSA65ES256MLDSA87ES384MLDSA44Ed25519MLDSA65Ed25519MLDSA87Ed448Both components sign the same domain-separated message, and the wire signature is their concatenation. The motivation is hybrid security: an attacker would need to break BOTH the post-quantum and the traditional family to forge a signature.
ML-KEM (FIPS 203)
Post-quantum key encapsulation for JWE via
github.com/jwx-go/mlkem, supportingML-KEM-768,ML-KEM-1024, and key-wrap variants (ML-KEM-768+A192KW,ML-KEM-1024+A256KW). Uses Go'scrypto/mlkemstdlib package with KMAC256-based key derivation (per NIST SP 800-185), as specified indraft-ietf-jose-pqc-kem.ML-KEM lives in a companion module so the core jwx module stays free of draft-spec algorithms. Once
draft-ietf-jose-pqc-kemis published as an RFC, ML-KEM may move directly into core jwx.HPKE (RFC 9180) for JWE
HPKE (Hybrid Public Key Encryption, RFC 9180) is implemented, following draft-ietf-jose-hpke-encrypt. Please note that because the JOSE binding is still in draft stage, the API/support may change in the future. The underlying primitives — HKDF (RFC 5869), AES-GCM (RFC 5116), ChaCha20-Poly1305 (RFC 8439), X25519 (RFC 7748) — are standards-track.
Six algorithm suites are supported out of the box using Go's
crypto/hpkestdlib package:X448-based HPKE (DHKEM(X448, HKDF-SHA512)) is available as an extension module at
github.com/jwx-go/x448, with a standalone DHKEM and HPKE implementation for the curves that Go's stdlib doesn't cover yet.HPKE is a natural complement to the PQ story — it provides a path toward hybrid encryption schemes that combine traditional and post-quantum key encapsulation as the ecosystem matures.
Hybrid PQ HPKE (very early draft).
github.com/jwx-go/reddy-pqchpkeships HPKE-10-KE and HPKE-11-KE perdraft-reddy-cose-jose-pqc-hybrid-hpke, pairing X25519 with ML-KEM-768 via the X-Wing KEM, with SHAKE256 as the HPKE KDF and AES-256-GCM (HPKE-10) or ChaCha20-Poly1305 (HPKE-11) as the AEAD. Heads up: the underlying spec is an individual submission, not yet WG-adopted — algorithm identifiers and wire formats may still change. Treat this as the most experimental piece of the v4 PQ story.Other Improvements
Iterators — Native Go 1.23+ range-over-func support:
Ed25519 and Ed448 per RFC 9864 — v4 supports the updated EdDSA algorithm identifiers from RFC 9864, which supersedes the provisional
"EdDSA"identifier of the original RFC 8037. Ed25519 is built into core; Ed448 is available viagithub.qkg1.top/jwx-go/ed448.ParseFSacross all packages — A newParseFS(fs.FS, path, ...)entry point is available alongside the existingReadFile, so callers can read fromembed.FS,testing/fstest, or a CWD-sandboxedos.DirFSwithout touching the real filesystem.Consolidated code generation — Six separate generator binaries unified into a single tool with a shared library. Object definitions now live next to the packages they generate, not in a separate
tools/tree.Try It
And once again — for runnable usage examples covering everything above, the companion module is
github.com/jwx-go/examples.Further Reading
Per-package guides in the repo:
docs/01-jwt.md— working with JWT (parsing, signing, validation, custom claims).docs/02-jws.md— working with JWS (compact, JSON serialization, multi-sig, detached).docs/03-jwe.md— working with JWE (key management, content encryption, HPKE).docs/04-jwk.md— working with JWK (key types, sets, import/export, thumbprints).Cross-cutting topics:
docs/10-extensions.md— complete list of extension modules and how registration works.docs/13-input-size.md— how v4 bounds resource use during parse/decrypt (amplification caps, streaming).docs/20-global-settings.md— global switches such asjson.Numberdecoding.docs/21-frameworks.md— integrating withnet/http, Echo, and other web frameworks.SECURITY.md— supported versions and how to report a vulnerability.And of course, the README is the top-level entry point.
Specifications implemented by v4 (at a glance):
b64)es256kcompanion)jwt/openid)Draft specifications tracked by v4 (API may change as drafts evolve):
mlkemcompanion)mldsacompanion)compsigcompanion)reddy-pqchpkecompanion, very early)Beta Was this translation helpful? Give feedback.
All reactions