ed25519: avoid split-scalar canonical checks (#24)

The triple-base verifier splits b into zero-extended 128-bit halves, so b_lo and b_hi are already canonical. Add a crate-private unchecked constructor and use it for that internal AVX2 path.

Benchmark notes:

- Ran this repository's Criterion benchmark program, benches/bench.rs, filtering to Single Verification, pinned to CPU 4 with 1s warmup, 2s measurement, and sample size 10.

- local_verify_zebra estimate was 19.996 us, with 95% CI 19.862..20.077 us.

- master measured 20.051 us, with 95% CI 19.938..20.134 us, so this branch was about 0.27% faster in that run.
This commit is contained in:
Edvard Fagerholm 2026-06-08 20:02:22 +03:00 committed by GitHub
parent d5aa796046
commit 7d80488798
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 9 additions and 2 deletions

View file

@ -78,8 +78,8 @@ pub mod spec {
b_lo_bytes[..16].copy_from_slice(&b_bytes[..16]); b_lo_bytes[..16].copy_from_slice(&b_bytes[..16]);
b_hi_bytes[..16].copy_from_slice(&b_bytes[16..]); b_hi_bytes[..16].copy_from_slice(&b_bytes[16..]);
let b_lo = Scalar::from_canonical_bytes(b_lo_bytes).unwrap(); let b_lo = Scalar::from_canonical_bytes_unchecked(b_lo_bytes);
let b_hi = Scalar::from_canonical_bytes(b_hi_bytes).unwrap(); let b_hi = Scalar::from_canonical_bytes_unchecked(b_hi_bytes);
// Compute NAF representations (all scalars are now ~128 bits) // Compute NAF representations (all scalars are now ~128 bits)
let a1_naf = a1.non_adjacent_form(5); let a1_naf = a1.non_adjacent_form(5);

View file

@ -232,6 +232,13 @@ impl Scalar {
CtOption::new(candidate, high_bit_unset & candidate.is_canonical()) CtOption::new(candidate, high_bit_unset & candidate.is_canonical())
} }
/// Construct a `Scalar` from bytes that are known to be canonical.
#[inline]
#[cfg(target_arch = "x86_64")]
pub(crate) const fn from_canonical_bytes_unchecked(bytes: [u8; 32]) -> Scalar {
Scalar { bytes }
}
/// Construct a `Scalar` from the low 255 bits of a 256-bit integer. This breaks the invariant /// Construct a `Scalar` from the low 255 bits of a 256-bit integer. This breaks the invariant
/// that scalars are always reduced. Scalar-scalar arithmetic, i.e., addition, subtraction, /// that scalars are always reduced. Scalar-scalar arithmetic, i.e., addition, subtraction,
/// multiplication, **does not work** on scalars produced from this function. You may only use /// multiplication, **does not work** on scalars produced from this function. You may only use