Skip to content

test: cover the rejection of small-order Ed25519 points in verify - #47652

Open
robobun wants to merge 4 commits into
mainfrom
robobun/9bce678e/ed25519-small-order-tests
Open

robobun wants to merge 4 commits into
mainfrom
robobun/9bce678e/ed25519-small-order-tests

Conversation

@robobun

@robobun robobun commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Related to #46252

Problem

Fix

  • Add the fixture test/js/web/crypto/ed25519-small-order.ts: 14 encodings of small-order points. Each forgery derives a message that an unfiltered verifier accepts.
  • Add six cases to test/js/web/crypto/web-crypto.test.ts and six to test/js/node/crypto/crypto.key-objects.test.ts. One case in each suite forges a signature with an honest R, so only the check on the public key can reject it.
  • Update test/js/node/test/parallel/test-webcrypto-sign-verify-eddsa.js to the Node v26.12.1 copy (byte-identical). It has Node's small-order vector.
  • Verified: the 12 cases and the Node file pass on main (3f3a9ff, release and debug builds). On canary da867c6, the last build before node:crypto, webcrypto, tls: more Node compatibility fixes #47567, 10 of the 12 cases and the Node file fail.

Background

  • Ed25519 verify checks [S]B = R + [h]A. A is the public key and R is one half of the signature.
  • A small-order point is one of 8 curve points whose multiples repeat after at most 8 steps. When A is such a point and h is a multiple of 8, [h]A vanishes. Then R = [S]B verifies with no private key.
  • Node rejects these points in verify (crypto: reject small-order EdDSA points during verify nodejs/node#64026). The fixture has the 8 canonical encodings and 6 non-canonical encodings that BoringSSL decodes to the same points.

Behaviour change: none


no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/node/crypto/crypto.key-objects.test.ts

crypto.verify() and crypto.subtle.verify() return false when the Ed25519
public key or the R of the signature is a small-order point (89fbb78).
No test covered that.

Add the cases and the fixture of the superseded pull request 46509: five
cases for node:crypto, five for WebCrypto, over fourteen encodings that
each have a witness message. Update test-webcrypto-sign-verify-eddsa.js to
the Node v26.12.1 copy, which has Node's small-order vector.
@github-actions github-actions Bot added the claude label Nov 1, 2026
@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 3e1469b and 004d23f.

Files selected for processing: 3
  • test/js/node/crypto/crypto.key-objects.test.ts
  • test/js/web/crypto/ed25519-small-order.ts
  • test/js/web/crypto/web-crypto.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

Walkthrough

The PR adds Ed25519 small-order point fixtures and signature helpers for Node crypto and Web Crypto verification tests. The tests cover small-order keys and signature points, identity cases, and multiple key input forms. The PR also updates RSA-PSS test key sizes under FIPS and Ed448 oversized-context assertions.

Changes

EdDSA Verification Tests

Layer / File(s) Summary
Small-order vectors and signature helpers
test/js/web/crypto/ed25519-small-order.ts
Adds Ed25519 small-order point fixtures and helpers for constructing signatures used in verification tests.
Node crypto verification coverage
test/js/node/crypto/crypto.key-objects.test.ts
Adds verification tests for small-order public keys and signature points, identity cases, and multiple key input forms. Tests also check valid and tampered signatures and digest-based verification errors.
Web Crypto verification and EdDSA assertions
test/js/web/crypto/web-crypto.test.ts, test/js/node/test/parallel/test-webcrypto-sign-verify-eddsa.js
Adds Web Crypto verification tests for small-order vectors. Updates FIPS RSA-PSS key sizes and Ed448 oversized-context error assertions.

Possibly related issues

  • oven-sh/bun#46252 reports forged Ed25519 signatures accepted by crypto.subtle.verify and node:crypto.verify under small-order public keys. This PR adds regression tests for rejecting small-order public keys and signature R points.

Possibly related PRs

  • oven-sh/bun#47567 adds production checks for small-order Ed25519 and Ed448 points during signature verification. This PR adds tests for related Ed25519 verification cases.

Suggested reviewers: dylan-conway

Merge Risk: Minimal · up to 004d2

The changes add verification regression tests without an established runtime behavior change. No actionable merge-blocking issue remains; merge after normal checks pass.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: tests for rejecting small-order Ed25519 points during verification.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description explains what the PR changes and how the author verified it. It does not use the template’s exact headings, but it provides the required information in the Problem, Fix, and Verified s…
Full details: Description check

Explanation

The description explains what the PR changes and how the author verified it. It does not use the template’s exact headings, but it provides the required information in the Problem, Fix, and Verified sections.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@robobun

robobun commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 1:09 PM PT - Nov 1st, 2026

❌ @robobun, your commit 004d23f has 40 failures in Build #136523 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 47652

That installs a local version of the PR into your bun-47652 executable, so you can run:

bun-47652 --bun

@robobun

robobun commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: ready for review. This PR adds tests only.

How the tests were checked

  • On main (3f3a9ff, release build and debug build): the 12 new cases pass, and test/js/node/test/parallel/test-webcrypto-sign-verify-eddsa.js exits 0.
  • On canary da867c6, the last build before node:crypto, webcrypto, tls: more Node compatibility fixes #47567: 10 of the 12 cases fail, and the Node file fails. The forged signatures with an honest R verify as true there under all 14 small-order key encodings.
bun bd test test/js/web/crypto/web-crypto.test.ts -t "small-order"
bun bd test test/js/node/crypto/crypto.key-objects.test.ts -t "small-order"
bun bd test/js/node/test/parallel/test-webcrypto-sign-verify-eddsa.js

CI: build 136523 finished red on test/aot/* and test/bundler/compile-aot-size.test.ts only, plus tests that passed on a retry. This PR does not touch those tests, and most of them fail on main too. None of the files of this PR is in the failure set.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Beyond the inline findings, I also checked the edits to the ported Node file test/js/node/test/parallel/test-webcrypto-sign-verify-eddsa.js: the rewritten Ed448 context-length assertions and the Ed448 small-order vector are unreachable on Bun (test/js/node/test/fixtures/crypto/eddsa.js only pushes Ed448 when !process.features.openssl_is_boringssl, and isBoringSSL is exported from common/crypto.js), so they cannot fail here; whether the file now matches upstream v26.12.1 byte-for-byte is a claim I could not verify offline and is worth a human check given the directory's verbatim-upstream rule. The 14 encodings in ed25519-small-order.ts match the kEd25519SmallOrderPoints table in src/jsc/bindings/ncrypto.cpp one-for-one, and the helper module has no .test. suffix so it is not collected as a test on its own.

Extended reasoning...

Test-only change (+352/-16) adding Ed25519 small-order verify coverage to web-crypto.test.ts and crypto.key-objects.test.ts via a new shared fixture, plus edits to a ported Node test; it exercises the crypto verify path but touches no source. Two inline findings were posted (public-key branch of hasSmallOrderEdDsaPoint never reached because every key case also uses a small-order R; bare toThrow). The ported-file edits are dead on BoringSSL builds and the upstream-match claim is unverifiable offline, so a human look is still worthwhile.

Findings marked 🟡 are optional suggestions and need no follow-up push.

Comment thread test/js/web/crypto/web-crypto.test.ts Resolved
Every case for a small-order public key used a signature whose R is the
identity point, so the check on R rejected it before the key was read.

Add a forged signature with an honest R for each of the 14 encodings, in
both suites: R is the public key of the RFC 8032 TEST 1 seed, S is the
scalar of that seed, and the message makes h a multiple of 8. Before
89fbb78 all 14 verify as true.

Assert the error code of a digest on an Ed25519 key in place of a bare
toThrow().
@robobun

robobun commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator Author

On the vendored file: test/js/node/test/parallel/test-webcrypto-sign-verify-eddsa.js is byte-identical to the Node v26.12.1 file. Both commands print d6fc7dd538246ab073a10793e596cfcb6c6289862f134d4dfc4a75865b82cb0b:

curl -sL https://raw.githubusercontent.com/nodejs/node/v26.12.1/test/parallel/test-webcrypto-sign-verify-eddsa.js | sha256sum
sha256sum test/js/node/test/parallel/test-webcrypto-sign-verify-eddsa.js

The Ed448 parts of that file do not run on BoringSSL, as the review says. The two inline findings are addressed in 3e1469b.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Both points from my earlier review are addressed by the second commit (forgery cases with an honest R now reach the public-key branch in both suites; the digest assertion checks a specific error code), and only a nit remains inline.

Beyond that, I also checked: the forgeryWithHonestR construction (S = a, R = [a]B, message chosen so h is a multiple of 8) correctly makes the unfiltered equation hold for every point of order dividing 8; isBoringSSL is exported from test/js/node/test/common/crypto.js; and the rewritten Ed448 context assertions in the ported file are unreachable under Bun because the eddsa fixture only adds Ed448 when !process.features.openssl_is_boringssl. I could not verify offline that test-webcrypto-sign-verify-eddsa.js is a verbatim Node v26.12.1 copy, so a human diff against upstream is worth doing before merge.

Extended reasoning...

Test-only change (+421/-16, no src/ edits) adding Ed25519 small-order rejection coverage to node:crypto and WebCrypto suites via a new shared fixture with bigint scalar arithmetic, plus an update of a ported Node test file. It exercises crypto verification paths but changes no runtime code. The second commit addressed both prior inline findings; the one remaining finding is a tautological assertion nit. Not approved outright because the ported-file "verbatim upstream copy" claim cannot be verified offline and the hand-rolled fixture math warrants a human glance.

The assertion on the first 32 bytes of the forged signature compared a
constant with itself. Compare the R of every forged signature with the
public key that node:crypto derives from the seed.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Beyond the inline finding, I checked the current diff against the earlier threads: the honest-R forgery now reaches the public-key branch for all 14 encodings, the digest assertion pins ERR_OSSL_EVP_COMMAND_NOT_SUPPORTED, and the R check in crypto.key-objects.test.ts now compares against the key derived from the seed rather than the fixture constant. I also confirmed isBoringSSL is exported by test/js/node/test/common/crypto.js, so the Ed448 vector gate in the updated Node file resolves; I could not verify the claim that the Node file is byte-identical to upstream v26.12.1, which a human may want to spot-check.

Extended reasoning...

Test-only change across four files: a new Ed25519 small-order fixture module, parallel suites in web-crypto.test.ts and crypto.key-objects.test.ts, and an update to the vendored Node test test-webcrypto-sign-verify-eddsa.js. It touches no production code and only exercises the existing verify rejection path. Not approving because a verified finding is being withheld from the posted set and the vendored-file upstream-identity claim cannot be checked offline.

The fixture listed a message for each point as a constant that nothing
checked. An entry with a wrong message would verify as false on every
build, and the suites could not tell.

Every forgery now searches for its own message, as the one with an
honest R does. The constants were correct: before 89fbb78 all 14
entries verify as true as a key, and all 8 canonical ones as R, with
the old messages and with the derived ones.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the follow-up commits — every point from the earlier rounds is now addressed in the code, and this automated review found no bugs in the latest version. A human look is still worthwhile for one thing: the edit to test/js/node/test/parallel/test-webcrypto-sign-verify-eddsa.js is described as a byte-identical sync to Node v26.12.1, which cannot be verified from this checkout, and that directory is meant to hold unmodified upstream tests.

What was reviewed:

  • The fixture math in test/js/web/crypto/ed25519-small-order.ts: each forgery satisfies [S]B = R + [h]A with h reduced mod L before the mod-8 test, matching BoringSSL's verifier; the 14-entry table matches kEd25519SmallOrderPoints in src/jsc/bindings/ncrypto.cpp row for row.
  • forgeryWithHonestR cases now reach the public-key branch at ncrypto.cpp:1996-2004 with a prime-order R, and the node:crypto suite checks that R against the seed's exported public key.
  • The ported Node file: isBoringSSL is exported by test/js/node/test/common/crypto.js, crypto.getFips exists in Bun, and the Ed448 branches are unreachable under BoringSSL because fixtures/crypto/eddsa.js skips Ed448 — so the changed context-message assertions cannot affect Bun's run.
Extended reasoning...

Test-only change (+405/-16) adding Ed25519 small-order rejection coverage to web-crypto.test.ts and crypto.key-objects.test.ts via a new shared helper, plus an edit to a ported Node test in the parallel directory; no src/ is touched and the filter it exercises (hasSmallOrderEdDsaPoint) already exists on the base. It touches no security-sensitive runtime surface itself, only tests for a crypto verification path. All four inline findings from prior runs are addressed in the current code and no new issues were found. Defer rather than approve because the PR's claims that the parallel-directory file is byte-identical to upstream Node and that the cases fail on the pre-fix build cannot be verified here (no network or build tools), and that directory's convention is to not carry local edits.

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant