From de07b0a3890153e54d95612666865bdb6a391bfe Mon Sep 17 00:00:00 2001 From: Edvard Fagerholm Date: Mon, 8 Jun 2026 20:04:02 +0300 Subject: [PATCH] ed25519: precompute AVX2 basepoint-128 table (#25) Add a static AVX2 NafLookupTable5 for B * 2^128 and use it from the vector triple-base verifier path instead of rebuilding that table every verification. 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.382 us, with 95% CI 19.327..19.426 us. - master measured 20.051 us, with 95% CI 19.938..20.134 us, so this branch was about 3.34% faster in that run. --- .../src/backend/vector/avx2/constants.rs | 178 ++++++++++++++++++ .../vector/scalar_mul/vartime_triple_base.rs | 9 +- 2 files changed, 183 insertions(+), 4 deletions(-) diff --git a/curve25519/solana-ed25519/src/backend/vector/avx2/constants.rs b/curve25519/solana-ed25519/src/backend/vector/avx2/constants.rs index 25c7bde..f34e843 100644 --- a/curve25519/solana-ed25519/src/backend/vector/avx2/constants.rs +++ b/curve25519/solana-ed25519/src/backend/vector/avx2/constants.rs @@ -16,6 +16,7 @@ use crate::backend::vector::packed_simd::u32x8; use crate::backend::vector::avx2::edwards::{CachedPoint, ExtendedPoint}; use crate::backend::vector::avx2::field::FieldElement2625x4; +use crate::window::NafLookupTable5; #[cfg(feature = "precomputed-tables")] use crate::window::NafLookupTable8; @@ -37,6 +38,183 @@ pub(crate) static CACHEDPOINT_IDENTITY: CachedPoint = CachedPoint(FieldElement26 u32x8::new_const(67108863, 0, 33554431, 0, 0, 67108863, 0, 33554431), ])); +/// Odd multiples of `B * 2^128`, where `B` is the Ed25519 basepoint. +pub(crate) static BASEPOINT_128_ODD_LOOKUP_TABLE: NafLookupTable5 = NafLookupTable5([ + CachedPoint(FieldElement2625x4([ + u32x8::new_const( + 66534737, 44155216, 17527085, 25046477, 50454485, 25275908, 24427949, 19365218, + ), + u32x8::new_const( + 15981845, 36873404, 8477272, 11176021, 40772245, 51234208, 6101715, 20397724, + ), + u32x8::new_const( + 35665917, 53574296, 1951442, 2105772, 44081965, 9258257, 14333944, 20637141, + ), + u32x8::new_const( + 16279886, 64719067, 15989606, 21668724, 19140425, 62032528, 31894001, 14000444, + ), + u32x8::new_const( + 36500306, 17827321, 19052241, 838783, 31029387, 53863823, 8190087, 6168061, + ), + ])), + CachedPoint(FieldElement2625x4([ + u32x8::new_const( + 58297975, 1271791, 26327207, 12780927, 21326724, 55825376, 1881468, 19837539, + ), + u32x8::new_const( + 41889841, 51572094, 29732198, 3211604, 26179218, 66326610, 23747079, 9590379, + ), + u32x8::new_const( + 3826490, 25399835, 21583051, 18952539, 65295023, 42750425, 2246330, 32346254, + ), + u32x8::new_const( + 52190758, 32188098, 24570211, 20678866, 40932593, 8178429, 27967937, 28257661, + ), + u32x8::new_const( + 30236672, 9815342, 29650636, 24107060, 43660614, 37629440, 12674453, 2006553, + ), + ])), + CachedPoint(FieldElement2625x4([ + u32x8::new_const( + 61053967, 64436103, 447268, 25700182, 19255118, 14993526, 899478, 2434175, + ), + u32x8::new_const( + 35812815, 10060004, 31384605, 25670757, 9561857, 60752897, 21085685, 27549514, + ), + u32x8::new_const( + 18488525, 64357302, 10815830, 2935874, 29965756, 65776827, 18067405, 29757081, + ), + u32x8::new_const( + 9151450, 55058906, 9505908, 19318842, 16283246, 36644461, 27208985, 12764280, + ), + u32x8::new_const( + 935064, 62208992, 28617878, 9642406, 33296477, 20692366, 14092783, 29523038, + ), + ])), + CachedPoint(FieldElement2625x4([ + u32x8::new_const( + 43982257, 1870168, 4563592, 25651161, 16953299, 59521203, 32759146, 23740415, + ), + u32x8::new_const( + 58238384, 34563808, 32443148, 21494201, 4125650, 52067840, 14679425, 9739512, + ), + u32x8::new_const( + 22799713, 45658991, 31646134, 20985007, 25125757, 30914923, 23225832, 25549240, + ), + u32x8::new_const( + 57964769, 17027909, 11726313, 16601782, 55202345, 44469180, 19529404, 17872655, + ), + u32x8::new_const( + 29135281, 26779810, 23911498, 19293297, 15382461, 22625292, 27273113, 11918932, + ), + ])), + CachedPoint(FieldElement2625x4([ + u32x8::new_const( + 20356928, 44286797, 22416632, 26923729, 37256426, 26174151, 8631283, 13726744, + ), + u32x8::new_const( + 15050322, 50131342, 17556728, 23214692, 57865782, 26708371, 14597005, 31489486, + ), + u32x8::new_const( + 10930030, 52761167, 17112689, 22969526, 21350113, 19294179, 2468751, 28829631, + ), + u32x8::new_const( + 21428704, 20303938, 30589121, 12662814, 5328394, 19277415, 17102411, 16988972, + ), + u32x8::new_const( + 18480836, 42343373, 1078384, 27210912, 44126946, 66557826, 6700400, 7552628, + ), + ])), + CachedPoint(FieldElement2625x4([ + u32x8::new_const( + 26298451, 31358962, 2410750, 1222016, 6602808, 44151217, 19067066, 20265648, + ), + u32x8::new_const( + 55489773, 8500419, 18022394, 14157663, 64952743, 30690464, 4133387, 4111644, + ), + u32x8::new_const( + 62382671, 36329179, 8557628, 5794859, 23898950, 33060808, 8782720, 32408648, + ), + u32x8::new_const( + 42610331, 38124770, 7134858, 18736387, 7942085, 52164416, 31680501, 29556327, + ), + u32x8::new_const( + 5234530, 9444407, 25023918, 10390934, 8479340, 52612004, 32163536, 23949516, + ), + ])), + CachedPoint(FieldElement2625x4([ + u32x8::new_const( + 23655649, 20966851, 549747, 32424363, 65477828, 28514773, 964955, 24097969, + ), + u32x8::new_const( + 10190745, 12573645, 17574436, 8670785, 40257066, 24017385, 18903364, 2903730, + ), + u32x8::new_const( + 65255895, 6342458, 8253517, 21684439, 25311893, 11419775, 17555636, 8477305, + ), + u32x8::new_const( + 28276837, 53282575, 15032193, 7864586, 13987263, 58062910, 14566708, 20452982, + ), + u32x8::new_const( + 23280558, 4071800, 13973538, 14314990, 3800625, 49289684, 21965196, 25337015, + ), + ])), + CachedPoint(FieldElement2625x4([ + u32x8::new_const( + 12346933, 43640065, 11294461, 3235676, 17615927, 5260789, 4540788, 21895087, + ), + u32x8::new_const( + 24450060, 4710546, 4707754, 6116116, 59999924, 32574139, 18219955, 32529652, + ), + u32x8::new_const( + 48787857, 56833627, 595516, 15964297, 18845497, 56029604, 13864576, 18463356, + ), + u32x8::new_const( + 7335332, 5495540, 24018875, 1219221, 18695805, 51707846, 5336005, 18884320, + ), + u32x8::new_const( + 27774957, 50806778, 32219720, 17063358, 30418813, 19859422, 2807745, 31952068, + ), + ])), +]); + +#[cfg(all(test, target_feature = "avx2"))] +mod test { + use super::*; + use crate::constants as curve_constants; + + fn lanes(limb: u32x8) -> [u32; 8] { + [ + limb.extract::<0>(), + limb.extract::<1>(), + limb.extract::<2>(), + limb.extract::<3>(), + limb.extract::<4>(), + limb.extract::<5>(), + limb.extract::<6>(), + limb.extract::<7>(), + ] + } + + #[test] + fn basepoint_128_odd_lookup_table_matches_runtime_generation() { + let runtime = + NafLookupTable5::::from(&curve_constants::ED25519_BASEPOINT_128_POINT); + + for (static_point, runtime_point) in BASEPOINT_128_ODD_LOOKUP_TABLE + .0 + .iter() + .zip(runtime.0.iter()) + { + let static_field = &static_point.0; + let runtime_field = &runtime_point.0; + for (static_limb, runtime_limb) in static_field.0.iter().zip(runtime_field.0.iter()) { + assert_eq!(lanes(*static_limb), lanes(*runtime_limb)); + } + } + } +} + /// The low limbs of (2p, 2p, 2p, 2p), so that /// ```ascii,no_run /// (2p, 2p, 2p, 2p) = [P_TIMES_2_LO, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI] diff --git a/curve25519/solana-ed25519/src/backend/vector/scalar_mul/vartime_triple_base.rs b/curve25519/solana-ed25519/src/backend/vector/scalar_mul/vartime_triple_base.rs index 7dcc5d1..921b533 100644 --- a/curve25519/solana-ed25519/src/backend/vector/scalar_mul/vartime_triple_base.rs +++ b/curve25519/solana-ed25519/src/backend/vector/scalar_mul/vartime_triple_base.rs @@ -15,10 +15,13 @@ pub mod spec { #[for_target_feature("avx2")] use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; + #[for_target_feature("avx2")] + use crate::backend::vector::avx2::constants::BASEPOINT_128_ODD_LOOKUP_TABLE; #[cfg(feature = "precomputed-tables")] #[for_target_feature("avx2")] use crate::backend::vector::avx2::constants::BASEPOINT_ODD_LOOKUP_TABLE; + #[cfg(not(feature = "precomputed-tables"))] use crate::constants; use crate::edwards::EdwardsPoint; use crate::scalar::HEEA_MAX_INDEX; @@ -111,10 +114,8 @@ pub mod spec { #[cfg(not(feature = "precomputed-tables"))] let table_B = &NafLookupTable5::::from(&constants::ED25519_BASEPOINT_POINT); - // B' = B * 2^128 (precomputed constant point) - // TODO: For optimal performance, this should also use the wider lookup table when precomputed-tables is enabled - let table_B_128 = - &NafLookupTable5::::from(&constants::ED25519_BASEPOINT_128_POINT); + // B' = B * 2^128. + let table_B_128 = &BASEPOINT_128_ODD_LOOKUP_TABLE; let mut Q = ExtendedPoint::identity();