Skip to content

fix: correctly handle computation of k with leading zeros (RFC 6979) — CVE-2025-14505 - #351

Open
zuzukobaladze wants to merge 1 commit into
indutny:masterfrom
zuzukobaladze:cve-2025-14505
Open

fix: correctly handle computation of k with leading zeros (RFC 6979) — CVE-2025-14505#351
zuzukobaladze wants to merge 1 commit into
indutny:masterfrom
zuzukobaladze:cve-2025-14505

Conversation

@zuzukobaladze

Copy link
Copy Markdown

Summary

ECDSA's per-iteration nonce k is generated as a fixed-width buffer of this.n.byteLength() bytes, but is then truncated with this._truncateToN(k, true), which derives the bit length from BN#byteLength(). For values whose fixed-width representation has leading zero byte(s), those bytes are dropped, giving the wrong RFC 6979 §2.3.2 bits2int shift and an incorrect k.

This produces faulty signatures for the affected k values; combined with a correct signature over the same input, it can leak the private key (CVE-2025-14505 / GHSA-848j-6mx2-7j84).

The fix passes the full generated bit length explicitly, mirroring the existing message path (options.msgBitLength):

k = this._truncateToN(k, true, this.n.byteLength() * 8);

_truncateToN itself is unchanged.

Test

Adds a p521 regression test that pins r to the value derived from the correctly full-width-truncated k. A plain sign/verify round-trip cannot detect this bug, since a single signature is always self-consistent for whatever k was actually used. The test fails on master and passes with the fix.

Note

This is an already-public advisory, so disclosure here is appropriate. Consumers who produced signatures with a vulnerable build should rotate affected keys.

Made with Cursor

The per-iteration ECDSA nonce k is generated as a fixed-width buffer of
this.n.byteLength() bytes, but was then truncated using BN#byteLength(),
which drops leading zero bytes. That yields the wrong RFC 6979 (sec.
2.3.2) bits2int shift and produces incorrect signatures for the affected
k values; given a faulty and a correct signature over the same input the
private key can be recovered.

Pass the full generated bit length explicitly, mirroring the existing
message-truncation path (msgBitLength). _truncateToN is unchanged.

Adds a regression test on p521 that pins r to the value derived from the
correctly full-width-truncated k (a plain sign/verify round-trip cannot
detect this, since a single signature is self-consistent for whatever k
was used).

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant