xkeys Seal encryption used fixed key for all encryption
Description
NATS.io is a high performance open source pub-sub distributed communication technology, built for the cloud, on-premise, IoT, and edge computing. The cryptographic key handling library, nkeys, recently gained support for encryption, not just for signing/authentication. This is used in nats-server 2.10 (Sep 2023) and newer for authentication callouts. In nkeys versions 0.4.0 through 0.4.5, corresponding with NATS server versions 2.10.0 through 2.10.3, the nkeys library's xkeys encryption handling logic mistakenly passed an array by value into an internal function, where the function mutated that buffer to populate the encryption key to use. As a result, all encryption was actually to an all-zeros key. This affects encryption only, not signing. FIXME: FILL IN IMPACT ON NATS-SERVER AUTH CALLOUT SECURITY. nkeys Go library 0.4.6, corresponding with NATS Server 2.10.4, has a patch for this issue. No known workarounds are available. For any application handling auth callouts in Go, if using the nkeys library, update the dependency, recompile and deploy that in lockstep.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
NATS nkeys library 0.4.0-0.4.5 used an all-zeros encryption key due to a Go slice pass-by-value bug, breaking auth callout security in nats-server 2.10.0-2.10.3.
Vulnerability
Description
The nkeys cryptographic key handling library, used by NATS server for authentication callouts, introduced encryption support in version 0.4.0. A logic error in the xkeys encryption handling caused the encryption key to be derived from an all-zeros buffer instead of the intended recipient's public key. The root cause was that the decodePubCurveKey function received a fixed-size array by value, and any mutations to that array were lost upon return, leaving the encryption key uninitialized [1][2]. This bug affected nkeys versions 0.4.0 through 0.4.5, corresponding to NATS server versions 2.10.0 through 2.10.3 [1].
Exploitation
The vulnerability is triggered during any encryption operation using the Seal or SealWithRand methods of an xkeys key pair. An attacker does not need special privileges to exploit the flaw; any party that can observe or participate in encrypted communication (e.g., an on-path network attacker) can read the ciphertext because it is encrypted with a known, fixed key of all zeros [2]. The bug affects only encryption, not signing, so authentication signatures remain secure [1].
Impact
Since the encryption key is all zeros, the confidentiality of any data encrypted with the affected nkeys library is completely compromised. In the context of NATS server authentication callouts, an attacker could decrypt and potentially modify the callout payload, undermining the security of the authentication process [2]. This could lead to unauthorized access or impersonation within the NATS cluster.
Mitigation
The issue is fixed in nkeys Go library version 0.4.6 and NATS server version 2.10.4 [1][2]. The fix ensures that the recipient's public key is properly copied into a mutable byte slice before being passed to the decoding function [4]. No workarounds are available; users must update the dependency, recompile, and deploy in lockstep [1].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/nats-io/nkeysGo | >= 0.4.0, < 0.4.6 | 0.4.6 |
github.com/nats-io/nats-server/v2Go | >= 2.10.0, < 2.10.4 | 2.10.4 |
Affected products
30- osv-coords29 versionspkg:apk/chainguard/k3spkg:apk/chainguard/k3s-embeddedpkg:apk/chainguard/k3s-imagespkg:apk/chainguard/k3s-multicallpkg:apk/chainguard/k3s-staticpkg:apk/chainguard/miniopkg:apk/chainguard/minio-bitnami-2024-compatpkg:apk/chainguard/minio-bitnami-2025-compatpkg:apk/chainguard/minio-iamguarded-2025-compatpkg:apk/chainguard/natspkg:apk/chainguard/nats-serverpkg:apk/chainguard/nats-server-compatpkg:apk/chainguard/telegraf-1.26pkg:apk/wolfi/k3spkg:apk/wolfi/k3s-embeddedpkg:apk/wolfi/k3s-imagespkg:apk/wolfi/k3s-multicallpkg:apk/wolfi/k3s-staticpkg:apk/wolfi/miniopkg:apk/wolfi/minio-bitnami-2024-compatpkg:apk/wolfi/minio-bitnami-2025-compatpkg:apk/wolfi/minio-iamguarded-2025-compatpkg:apk/wolfi/natspkg:apk/wolfi/nats-serverpkg:apk/wolfi/nats-server-compatpkg:apk/wolfi/telegraf-1.26pkg:bitnami/natspkg:golang/github.com/nats-io/nats-server/v2pkg:golang/github.com/nats-io/nkeys
< 1.28.3-r1+ 28 more
- (no CPE)range: < 1.28.3-r1
- (no CPE)range: < 1.28.3-r1
- (no CPE)range: < 1.28.3-r1
- (no CPE)range: < 1.28.3-r1
- (no CPE)range: < 1.28.3-r1
- (no CPE)range: < 0.20231101.183725-r0
- (no CPE)range: < 0.20231101.183725-r0
- (no CPE)range: < 0.20231101.183725-r0
- (no CPE)range: < 0.20231101.183725-r0
- (no CPE)range: < 0.1.1-r5
- (no CPE)range: < 2.10.4-r0
- (no CPE)range: < 2.10.4-r0
- (no CPE)range: < 1.26.3-r6
- (no CPE)range: < 1.28.3-r1
- (no CPE)range: < 1.28.3-r1
- (no CPE)range: < 1.28.3-r1
- (no CPE)range: < 1.28.3-r1
- (no CPE)range: < 1.28.3-r1
- (no CPE)range: < 0.20231101.183725-r0
- (no CPE)range: < 0.20231101.183725-r0
- (no CPE)range: < 0.20231101.183725-r0
- (no CPE)range: < 0.20231101.183725-r0
- (no CPE)range: < 0.1.1-r5
- (no CPE)range: < 2.10.4-r0
- (no CPE)range: < 2.10.4-r0
- (no CPE)range: < 1.26.3-r6
- (no CPE)range: >= 2.10.0, < 2.10.4
- (no CPE)range: >= 2.10.0, < 2.10.4
- (no CPE)range: >= 0.4.0, < 0.4.6
- nats-io/nkeysv5Range: >= 2.10.0, < 2.10.4
Patches
158fb9d69f42eMake sure to use byte slice to receive proper copy, otherwise empty public key is being used.
2 files changed · +38 −8
xkeys.go+8 −7 modified@@ -1,4 +1,4 @@ -// Copyright 2022 The NATS Authors +// Copyright 2022-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -38,12 +38,13 @@ type ckp struct { seed [curveKeyLen]byte // Private raw key. } -// CreateUser will create a User typed KeyPair. +// CreateCurveKeys will create a Curve typed KeyPair. func CreateCurveKeys() (KeyPair, error) { return CreateCurveKeysWithRand(rand.Reader) } -// CreateUser will create a User typed KeyPair with specified rand source. +// CreateCurveKeysWithRand will create a Curve typed KeyPair +// with specified rand source. func CreateCurveKeysWithRand(rr io.Reader) (KeyPair, error) { var kp ckp _, err := io.ReadFull(rr, kp.seed[:]) @@ -85,7 +86,7 @@ func (pair *ckp) PrivateKey() ([]byte, error) { return Encode(PrefixBytePrivate, pair.seed[:]) } -func decodePubCurveKey(src string, dest [curveKeyLen]byte) error { +func decodePubCurveKey(src string, dest []byte) error { var raw [curveDecodeLen]byte // should always be 35 n, err := b32Enc.Decode(raw[:], []byte(src)) if err != nil { @@ -112,7 +113,7 @@ func decodePubCurveKey(src string, dest [curveKeyLen]byte) error { } // Copy over, ignore prefix byte. - copy(dest[:], raw[1:end]) + copy(dest, raw[1:end]) return nil } @@ -134,7 +135,7 @@ func (pair *ckp) SealWithRand(input []byte, recipient string, rr io.Reader) ([]b err error ) - if err = decodePubCurveKey(recipient, rpub); err != nil { + if err = decodePubCurveKey(recipient, rpub[:]); err != nil { return nil, ErrInvalidRecipient } if _, err := io.ReadFull(rr, nonce[:]); err != nil { @@ -159,7 +160,7 @@ func (pair *ckp) Open(input []byte, sender string) ([]byte, error) { } copy(nonce[:], input[vlen:vlen+curveNonceLen]) - if err = decodePubCurveKey(sender, spub); err != nil { + if err = decodePubCurveKey(sender, spub[:]); err != nil { return nil, ErrInvalidSender }
xkeys_test.go+30 −1 modified@@ -1,4 +1,4 @@ -// Copyright 2022 The NATS Authors +// Copyright 2022-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -154,3 +154,32 @@ func TestCurvePublic(t *testing.T) { t.Fatalf("Expected %v but got %v", ErrCannotSeal, err) } } + +func TestCurvePublicEmptyBug(t *testing.T) { + kp, _ := CreateCurveKeys() + pub, _ := kp.PublicKey() + + rkp, _ := CreateCurveKeys() + rpub, _ := rkp.PublicKey() + + msg := []byte("Empty public better not work!") + encrypted, err := kp.Seal(msg, rpub) + if err != nil { + t.Fatalf("Received an error on Seal: %v", err) + } + decrypted, err := rkp.Open(encrypted, pub) + if err != nil { + t.Fatalf("Received an error on Open: %v", err) + } + if !bytes.Equal(decrypted, msg) { + t.Fatalf("Expected %q to be %q", decrypted, msg) + } + // Check an empty pub key. + var empty [curveKeyLen]byte + epub, _ := Encode(PrefixByteCurve, empty[:]) + + _, err = rkp.Open(encrypted, string(epub)) + if err == nil { + t.Fatalf("Expected a failure with empty pub key") + } +}
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-mr45-rx8q-wcm9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-46129ghsaADVISORY
- www.openwall.com/lists/oss-security/2023/10/31/1ghsaWEB
- github.com/nats-io/nkeys/commit/58fb9d69f42ea73fffad1d14e5914dc666f3daa1ghsaWEB
- github.com/nats-io/nkeys/security/advisories/GHSA-mr45-rx8q-wcm9ghsax_refsource_CONFIRMWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/R3UETKPUB3V5JS5TLZOF3SMTGT5K5APSghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/ULQQONMSCQSH5Z5OWFFQHCGEZ3NL4DRJghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/R3UETKPUB3V5JS5TLZOF3SMTGT5K5APS/mitre
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/ULQQONMSCQSH5Z5OWFFQHCGEZ3NL4DRJ/mitre
News mentions
0No linked articles in our index yet.