Repository navigation
Conversation
EdDSAEngine verifies Ed25519 keys with the same computation as before (ref10's double scalar multiplication, sliding windows and tables, then the encoding of R' compared with R), on int[10] field elements updated in place instead of a new object per field operation. multiply, square, squareAndDouble, invert and pow22523 share in-place forms with the same arithmetic. Verification outcomes and signatures are unchanged: the tests replay 2,600 verification outcomes and 4,000 signatures made by 0.3.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJrQU97MHKeQpEH7XcJT9A
The in-place path now runs only for the standard spec instance (EdDSANamedCurveTable.ED_25519_CURVE_SPEC, whose base point carries the precomputed tables) and for key points that Ed25519VerifyEquation.supports: Ed25519FieldElement coordinates on the standard curve, Z != 0, TZ = XY and the curve equation. A key point built through GroupElement.p3 with another T, or off the curve, and a spec equal to the standard one without precomputed tables keep the 0.3.1 computation, with its outcomes and exceptions. Ed25519VerifyEquation.holds returns false for S >= L and for a k or signature of the wrong length, and its class documents that it is internal to EdDSAEngine. The version is 0.3.2-SNAPSHOT. Tests: a key point with an inconsistent T, points off the curve or with Z = 0, an equal spec without precomputed tables (NullPointerException, as in 0.3.1), streaming input, and the preconditions of holds. Keys deserialized from this version's EdDSAPublicKey no longer carry a computed Aneg: the in-place path does not use it, and getNegativeA computes it on demand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJrQU97MHKeQpEH7XcJT9A
…coordinates Ed25519VerifyEquation.supports now requires, before any arithmetic, that the point is on the standard curve instance and that every coordinate is an Ed25519FieldElement of the standard field instance with limbs within 1.1 * 2^25 (even) and 1.1 * 2^24 (odd), the input bounds of the field arithmetic. A point built through GroupElement.p3 with an out-of-bound limb or with a coordinate of another Field (for which 0.3.1 accepts the identity signature, or throws) keeps the 0.3.1 computation; supports neither throws nor overflows. Tests: both hand-built points, against 0.3.1's outcomes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJrQU97MHKeQpEH7XcJT9A
Instead of a separate equation chosen by guards, the in-place code is now the loop of GroupElement.doubleScalarMultiplyVariableTime itself: the same steps (dbl, toP3, madd, msub, toP2), the same operand order and the same field kernels, over the same inputs 0.3.1 reads: the cached negated key's dblPrecmp table, the base point's dblPrecmp table and curve.getZero(P2). It runs when every element it reads is exactly an Ed25519FieldElement (not a subclass) in a full PRECOMP table, which holds for every table the library builds; otherwise the 0.3.1 loop runs. EdDSAEngine is again 0.3.1's, so the key's cached getNegativeA() snapshot, the spec's base point and every exception are 0.3.1's, and a caller changing their own limb arrays or subclassing a field element sees 0.3.1's outcome. Ed25519VerifyEquation now only holds that loop (public because GroupElement is in another package; documented as internal). The guard methods supports and holds are gone. Tests: a key point whose caller-owned limbs change after the negation was cached, and a field-element subclass whose isNonZero throws, both with 0.3.1's outcomes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJrQU97MHKeQpEH7XcJT9A
- GroupElement.doubleScalarMultiplyVariableTime takes the in-place loop only when this, A, the starting zero point r and every table entry are exactly GroupElement; a subclass (for example a zero point whose dbl() is overridden) keeps the 0.3.1 loop, which calls its methods. The result carries the curve of r, as the 0.3.1 loop's result does. - Ed25519FieldElement.invert and pow22523 use the in-place chains only for exactly Ed25519FieldElement; a subclass gets 0.3.1's chain of square() and multiply() calls, so its overrides run as before. Tests: a curve whose P2 zero point overrides dbl(), and subclasses overriding square() or multiply() (invert, pow22523, encoding a P3 point). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJrQU97MHKeQpEH7XcJT9A
0.3.1 did not declare one, so the new package-private kernels changed the computed value (8932514744815372482 -> 2707052793834647740) and elements serialized by 0.3.1 failed to deserialize. Declare the 0.3.1 value and test it, plus a round trip on a serializable field. Checked across versions against the Central 0.3.1 jar in both directions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJrQU97MHKeQpEH7XcJT9A
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This makes Ed25519 verification faster and cuts its allocation. Behaviour is unchanged: every verdict, every exception and serialization all match 0.3.1. There is no API change and no new checks; libsodium-style input checks stay in callers such as cardano-client-lib (#718, and bloxbean/cardano-client-lib#751). Version 0.3.2-SNAPSHOT. Nothing is released.
How it works
EdDSAEngineis byte-identical to v0.3.1, so the S check, the cachedgetNegativeA(), the base point and every exception path are all 0.3.1's.GroupElement.doubleScalarMultiplyVariableTime:slideandgetZero(P2), it runs an in-place replica of the same loop. It uses the same steps (dbl, toP3, madd/msub, toP2), the same operand order and the same field kernels, so the limbs are bit-identical.GroupElement/Ed25519FieldElement(getClass() ==), the field matches and the precomputed tables are well-formed. Anything else runs 0.3.1's loop unchanged.Ed25519FieldElement:multiply,squareandsquareAndDoublewere moved verbatim into package-private static kernels.invertandpow22523use in-place chains only for the exact class. Subclasses get the verbatim 0.3.1 chains, which callsquare()andmultiply()virtually.serialVersionUIDis pinned to 0.3.1's computed value.Ed25519VerifyEquationis a new internal helper: not a stable API, and public only because of package layout.Performance (JDK 25, 20k verifies):
A fresh key still builds 0.3.1's
getNegativeA()precomputation, with one inversion per entry. That is the cost of reading exactly 0.3.1's snapshot.Review history
Five Codex adversarial rounds and an independent Claude review. Each round's counterexample is now a regression test:
equals;Fieldencoding;getNegativeA()is cached, and field-element subclasses;GroupElementsubclass overridingdbl(), and virtual dispatch ininvert/pow22523;serialVersionUIDchanging. It is now pinned and checked across versions against the Central 0.3.1 jar in both directions.Rounds 1–2 added guards that later rounds replaced with the snapshot and exact-class design, so the intermediate commit messages describe superseded approaches. Squash merge is suggested.
Testing
Tests: 147/147 on JDK 11, including the 2,600 verify and 4,000 signature replays.
Differentials against the Central 0.3.1 jar:
🤖 Generated with Claude Code
https://claude.ai/code/session_01MJrQU97MHKeQpEH7XcJT9A