Skip to content

Commit 0eb1f8c

Browse files
committed
Merge branch 'zz/three_scalar_mul' into zz/impl_heea
2 parents 63e47ae + f40a373 commit 0eb1f8c

File tree

7 files changed

+148
-33
lines changed

7 files changed

+148
-33
lines changed

curve25519-dalek-derive/tests/tests.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,18 +104,35 @@ mod inner_spec {
104104
#[for_target_feature("avx2")]
105105
const IS_AVX2: bool = true;
106106

107+
#[for_target_feature("sse2")]
107108
#[test]
108109
fn test_specialized() {
109110
assert!(!IS_AVX2);
110111
}
111112

113+
#[for_target_feature("avx2")]
114+
#[test]
115+
fn test_specialized_avx2() {
116+
assert!(IS_AVX2);
117+
}
118+
112119
#[cfg(test)]
120+
#[for_target_feature("sse2")]
113121
mod tests {
114122
#[test]
115123
fn test_specialized_inner() {
116124
assert!(!super::IS_AVX2);
117125
}
118126
}
127+
128+
#[cfg(test)]
129+
#[for_target_feature("avx2")]
130+
mod tests_avx2 {
131+
#[test]
132+
fn test_specialized_inner_avx2() {
133+
assert!(super::IS_AVX2);
134+
}
135+
}
119136
}
120137

121138
#[unsafe_target_feature("sse2")]
@@ -127,9 +144,17 @@ fn test_sse2_only() {}
127144
// pretty esoteric feature. Looking at the table of supported avx512 features at
128145
// https://en.wikipedia.org/wiki/AVX-512#CPUs_with_AVX-512 it seems avx512vp2intersect is one of the
129146
// most unusual ones that has rustc knows about
147+
#[cfg(target_feature = "avx512vp2intersect")]
130148
#[unsafe_target_feature("avx512vp2intersect")]
131149
#[test]
132150
fn test_unset_target_feature() {
151+
assert!(std::arch::is_x86_feature_detected!("avx512vp2intersect"));
152+
}
153+
154+
#[cfg(not(target_feature = "avx512vp2intersect"))]
155+
#[unsafe_target_feature("avx512vp2intersect")]
156+
#[test]
157+
fn test_unset_target_feature_removed() {
133158
compile_error!("When an unknown target_feature is set on a test, unsafe_target_feature is expected remove the function");
134159
}
135160

curve25519-dalek/docs/parallel-formulas.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ element vectors, whose optimum choice is determined by the details of
219219
the instruction set. However, it's not possible to perfectly separate
220220
the implementation of the field element vectors from the
221221
implementation of the point operations. Instead, the [`avx2`] and
222-
[`ifma`] backends provide `ExtendedPoint` and `CachedPoint` types, and
222+
`ifma` backends provide `ExtendedPoint` and `CachedPoint` types, and
223223
the [`scalar_mul`] code uses one of the backend types by a type alias.
224224

225225
# Comparison to non-vectorized formulas

curve25519-dalek/src/backend.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ pub fn vartime_triple_base_mul_128_128_256(
292292
BackendKind::Avx2 => {
293293
vector::scalar_mul::vartime_triple_base::spec_avx2::mul_128_128_256(a1, A1, a2, A2, b)
294294
}
295-
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
295+
#[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
296296
BackendKind::Avx512 => {
297297
vector::scalar_mul::vartime_triple_base::spec_avx512ifma_avx512vl::mul_128_128_256(
298298
a1, A1, a2, A2, b,

curve25519-dalek/src/backend/serial/scalar_mul/vartime_triple_base.rs

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ pub fn mul_128_128_256(
7575

7676
// Find starting index - check all NAFs up to bit 127
7777
// (with potential carry to bit 128 or 129)
78-
let mut i = HEEA_MAX_INDEX;
79-
for j in (0..HEEA_MAX_INDEX).rev() {
78+
let mut i: usize = HEEA_MAX_INDEX;
79+
for j in (0..=HEEA_MAX_INDEX).rev() {
8080
i = j;
8181
if a1_naf[i] != 0 || a2_naf[i] != 0 || b_lo_naf[i] != 0 || b_hi_naf[i] != 0 {
8282
break;
@@ -147,17 +147,24 @@ pub fn mul_128_128_256(
147147
#[cfg(test)]
148148
mod test {
149149

150-
use rand::rng;
150+
use rand::{RngCore, rng};
151151

152152
use super::*;
153153
use crate::scalar::Scalar;
154154

155+
fn random_scalar() -> Scalar {
156+
let mut wide = [0u8; 64];
157+
let mut rng = rng();
158+
rng.fill_bytes(&mut wide);
159+
Scalar::from_bytes_mod_order_wide(&wide)
160+
}
161+
155162
#[test]
156163
fn test_triple_base_multiplication() {
157164
// Test vectors with random scalars
158165
let a1 = Scalar::from(12345u64);
159166
let a2 = Scalar::from(67890u64);
160-
let b = Scalar::random(&mut rng());
167+
let b = random_scalar();
161168

162169
// Random points (using scalar multiplication of basepoint)
163170
let A1 = &constants::ED25519_BASEPOINT_POINT * &Scalar::from(2u64);
@@ -191,7 +198,7 @@ mod test {
191198
let a2 = Scalar::from_bytes_mod_order(a2_bytes);
192199

193200
// Full 256-bit scalar for b
194-
let b = Scalar::random(&mut rng());
201+
let b = random_scalar();
195202

196203
// Test points
197204
let A1 = &constants::ED25519_BASEPOINT_POINT * &Scalar::from(5u64);
@@ -210,7 +217,7 @@ mod test {
210217
fn test_triple_base_with_zero_scalars() {
211218
let a1 = Scalar::ZERO;
212219
let a2 = Scalar::from(123u64);
213-
let b = Scalar::random(&mut rng());
220+
let b = random_scalar();
214221

215222
let A1 = &constants::ED25519_BASEPOINT_POINT * &Scalar::from(2u64);
216223
let A2 = &constants::ED25519_BASEPOINT_POINT * &Scalar::from(3u64);
@@ -225,7 +232,7 @@ mod test {
225232
fn test_triple_base_with_identity_points() {
226233
let a1 = Scalar::from(111u64);
227234
let a2 = Scalar::from(222u64);
228-
let b = Scalar::random(&mut rng());
235+
let b = random_scalar();
229236

230237
let A1 = EdwardsPoint::identity();
231238
let A2 = &constants::ED25519_BASEPOINT_POINT * &Scalar::from(3u64);
@@ -241,7 +248,7 @@ mod test {
241248
// Test that both functions give the same result for 128-bit inputs
242249
let a1 = Scalar::from(0x123456789ABCDEFu64);
243250
let a2 = Scalar::from(0xFEDCBA987654321u64);
244-
let b = Scalar::random(&mut rng());
251+
let b = random_scalar();
245252

246253
let A1 = &constants::ED25519_BASEPOINT_POINT * &Scalar::from(11u64);
247254
let A2 = &constants::ED25519_BASEPOINT_POINT * &Scalar::from(13u64);
@@ -268,7 +275,7 @@ mod test {
268275
}
269276
let a2 = Scalar::from_bytes_mod_order(a2_bytes);
270277

271-
let b = Scalar::random(&mut rng());
278+
let b = random_scalar();
272279

273280
let A1 = &constants::ED25519_BASEPOINT_POINT * &Scalar::from(17u64);
274281
let A2 = &constants::ED25519_BASEPOINT_POINT * &Scalar::from(19u64);
@@ -278,4 +285,42 @@ mod test {
278285

279286
assert_eq!(result, expected);
280287
}
288+
289+
// Proptest for vartime_triple_scalar_mul_basepoint equivalence
290+
proptest::proptest! {
291+
#[test]
292+
fn proptest_triple_scalar_mul_equivalence(
293+
a1_bytes_16 in proptest::array::uniform16(proptest::num::u8::ANY),
294+
a2_bytes_16 in proptest::array::uniform16(proptest::num::u8::ANY),
295+
b_bytes in proptest::array::uniform32(proptest::num::u8::ANY),
296+
A1_scalar_bytes in proptest::array::uniform32(proptest::num::u8::ANY),
297+
A2_scalar_bytes in proptest::array::uniform32(proptest::num::u8::ANY),
298+
) {
299+
// Construct 128-bit scalars a1 and a2 (upper 16 bytes are zero)
300+
let mut a1_bytes = [0u8; 32];
301+
let mut a2_bytes = [0u8; 32];
302+
a1_bytes[..16].copy_from_slice(&a1_bytes_16);
303+
a2_bytes[..16].copy_from_slice(&a2_bytes_16);
304+
305+
let a1 = Scalar::from_bytes_mod_order(a1_bytes);
306+
let a2 = Scalar::from_bytes_mod_order(a2_bytes);
307+
308+
// Construct full 256-bit scalar b
309+
let b = Scalar::from_bytes_mod_order(b_bytes);
310+
311+
// Generate random points A1 and A2 using scalar multiplication of basepoint
312+
let A1_scalar = Scalar::from_bytes_mod_order(A1_scalar_bytes);
313+
let A2_scalar = Scalar::from_bytes_mod_order(A2_scalar_bytes);
314+
let A1 = &constants::ED25519_BASEPOINT_POINT * &A1_scalar;
315+
let A2 = &constants::ED25519_BASEPOINT_POINT * &A2_scalar;
316+
317+
// Compute using the optimized triple-base function
318+
let result_optimized = mul_128_128_256(&a1, &A1, &a2, &A2, &b);
319+
320+
// Compute using raw operations: a1*A1 + a2*A2 + b*B
321+
let expected = &(&(&a1 * &A1) + &(&a2 * &A2)) + &(&b * &constants::ED25519_BASEPOINT_POINT);
322+
323+
proptest::prop_assert_eq!(result_optimized, expected, "Optimized triple scalar mul should equal raw operations");
324+
}
325+
}
281326
}

curve25519-dalek/src/backend/serial/u32/constants.rs

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,48 @@ pub const ED25519_BASEPOINT_POINT: EdwardsPoint = EdwardsPoint {
136136
]),
137137
};
138138

139+
/// The Ed25519 basepoint, mul by 2^128, as an `EdwardsPoint`.
140+
pub const ED25519_BASEPOINT_128_POINT: EdwardsPoint = EdwardsPoint {
141+
X: FieldElement2625::from_limbs([
142+
2664042, 23449881, 8588504, 31570262, 52025907, 14016958, 17934911, 10536770, 36081707,
143+
18715816,
144+
]),
145+
Y: FieldElement2625::from_limbs([
146+
53612635, 17322216, 64979144, 12220533, 27384794, 7796776, 63981171, 31808137, 3318544,
147+
10876052,
148+
]),
149+
Z: FieldElement2625::from_limbs([
150+
38318927, 6633020, 30360108, 27133620, 43190211, 599215, 50990868, 21586734, 34463843,
151+
14390137,
152+
]),
153+
T: FieldElement2625::from_limbs([
154+
46012201, 27645749, 48994527, 27092089, 44549182, 4023192, 8388284, 20428666, 53367776,
155+
2097936,
156+
]),
157+
};
158+
159+
#[cfg(all(test, feature = "precomputed-tables"))]
160+
mod tests {
161+
use super::*;
162+
use crate::window::NafLookupTable5;
163+
164+
#[test]
165+
fn basepoint_128_table_matches_generated() {
166+
let generated = NafLookupTable5::<ProjectiveNielsPoint>::from(&ED25519_BASEPOINT_128_POINT);
167+
168+
for (expected, actual) in AFFINE_ODD_MULTIPLES_OF_BASEPOINT_128
169+
.0
170+
.iter()
171+
.zip(generated.0.iter())
172+
{
173+
assert_eq!(expected.Y_plus_X, actual.Y_plus_X);
174+
assert_eq!(expected.Y_minus_X, actual.Y_minus_X);
175+
assert_eq!(expected.Z, actual.Z);
176+
assert_eq!(expected.T2d, actual.T2d);
177+
}
178+
}
179+
}
180+
139181
/// The 8-torsion subgroup \\(\mathcal E \[8\]\\).
140182
///
141183
/// In the case of Curve25519, it is cyclic; the \\(i\\)-th element of
@@ -4817,8 +4859,8 @@ pub(crate) const AFFINE_ODD_MULTIPLES_OF_BASEPOINT_128: NafLookupTable5<Projecti
48174859
NafLookupTable5([
48184860
ProjectiveNielsPoint {
48194861
Y_plus_X: FieldElement2625::from_limbs([
4820-
56276677, 7217665, 6458785, 10236364, 12301838, 21813735, 14807218, 8790476,
4821-
39400252, 29591868,
4862+
56276677, 40772097, 73567648, 43790795, 79410701, 21813734, 81916082, 42344907,
4863+
39400251, 29591868,
48224864
]),
48234865
Y_minus_X: FieldElement2625::from_limbs([
48244866
50948574, 27426767, 56390639, 14204703, 42467750, 27334249, 46046259, 21271367,
@@ -4835,8 +4877,8 @@ pub(crate) const AFFINE_ODD_MULTIPLES_OF_BASEPOINT_128: NafLookupTable5<Projecti
48354877
},
48364878
ProjectiveNielsPoint {
48374879
Y_plus_X: FieldElement2625::from_limbs([
4838-
61573526, 26839672, 66658950, 7860582, 5467228, 30187735, 9483513, 29613442,
4839-
34510433, 30513335,
4880+
61573526, 60394104, 66658949, 41415014, 72576091, 30187734, 76592377, 63167873,
4881+
101619296, 30513334,
48404882
]),
48414883
Y_minus_X: FieldElement2625::from_limbs([
48424884
51181788, 696007, 24915963, 12735707, 2911894, 7060820, 64624395, 1392014, 2117242,
@@ -4853,8 +4895,8 @@ pub(crate) const AFFINE_ODD_MULTIPLES_OF_BASEPOINT_128: NafLookupTable5<Projecti
48534895
},
48544896
ProjectiveNielsPoint {
48554897
Y_plus_X: FieldElement2625::from_limbs([
4856-
63997925, 22141397, 16344335, 19270123, 11338216, 16163263, 54904184, 24982069,
4857-
56606655, 24922282,
4898+
63997925, 22141397, 83453199, 52824554, 78447079, 49717694, 122013047, 24982068,
4899+
56606655, 58476714,
48584900
]),
48594901
Y_minus_X: FieldElement2625::from_limbs([
48604902
25892846, 11528509, 46114731, 26269695, 31949658, 18508240, 8742696, 14557236,
@@ -4871,8 +4913,8 @@ pub(crate) const AFFINE_ODD_MULTIPLES_OF_BASEPOINT_128: NafLookupTable5<Projecti
48714913
},
48724914
ProjectiveNielsPoint {
48734915
Y_plus_X: FieldElement2625::from_limbs([
4874-
18135939, 20390706, 771316, 3834009, 62046955, 32059486, 53528634, 28397810,
4875-
53903558, 2683232,
4916+
85244803, 20390705, 67880180, 3834008, 62046955, 32059486, 53528634, 61952242,
4917+
53903557, 36237664,
48764918
]),
48774919
Y_minus_X: FieldElement2625::from_limbs([
48784920
51282070, 16196724, 13662050, 32134248, 30369654, 19444710, 35256476, 33331300,
@@ -4889,8 +4931,8 @@ pub(crate) const AFFINE_ODD_MULTIPLES_OF_BASEPOINT_128: NafLookupTable5<Projecti
48894931
},
48904932
ProjectiveNielsPoint {
48914933
Y_plus_X: FieldElement2625::from_limbs([
4892-
43828549, 14757490, 56664015, 24665671, 50233976, 14590870, 65779056, 33374083,
4893-
47093275, 4915216,
4934+
110937413, 48311921, 56664014, 24665671, 117342840, 14590869, 65779056, 33374083,
4935+
47093275, 38469648,
48944936
]),
48954937
Y_minus_X: FieldElement2625::from_limbs([
48964938
11795097, 23045357, 37986619, 25517870, 61752555, 20274894, 5272019, 20059223,
@@ -4907,8 +4949,8 @@ pub(crate) const AFFINE_ODD_MULTIPLES_OF_BASEPOINT_128: NafLookupTable5<Projecti
49074949
},
49084950
ProjectiveNielsPoint {
49094951
Y_plus_X: FieldElement2625::from_limbs([
4910-
22997800, 33050677, 60441767, 7973230, 30621382, 4134210, 41797844, 1978192,
4911-
58504534, 317870,
4952+
22997800, 33050677, 60441767, 41527662, 30621381, 4134210, 108906708, 35532623,
4953+
58504533, 33872302,
49124954
]),
49134955
Y_minus_X: FieldElement2625::from_limbs([
49144956
6811554, 1638711, 35767789, 14166397, 19866339, 260838, 19580826, 7806685,
@@ -4925,8 +4967,8 @@ pub(crate) const AFFINE_ODD_MULTIPLES_OF_BASEPOINT_128: NafLookupTable5<Projecti
49254967
},
49264968
ProjectiveNielsPoint {
49274969
Y_plus_X: FieldElement2625::from_limbs([
4928-
21843428, 19355043, 6522572, 1255394, 64421578, 23324883, 31082733, 13182074,
4929-
56269698, 27274610,
4970+
88952292, 52909474, 73631435, 34809825, 64421577, 23324883, 98191597, 46736505,
4971+
56269697, 27274610,
49304972
]),
49314973
Y_minus_X: FieldElement2625::from_limbs([
49324974
36626131, 26445435, 43443322, 31269185, 4788786, 21966751, 10657839, 11622879,
@@ -4943,8 +4985,8 @@ pub(crate) const AFFINE_ODD_MULTIPLES_OF_BASEPOINT_128: NafLookupTable5<Projecti
49434985
},
49444986
ProjectiveNielsPoint {
49454987
Y_plus_X: FieldElement2625::from_limbs([
4946-
47704924, 31694873, 47305006, 31556775, 44753887, 19755612, 25884799, 6259103,
4947-
377598, 26990890,
4988+
47704924, 31694873, 47305006, 31556775, 111862751, 53310043, 92993662, 39813534,
4989+
67486461, 60545321,
49484990
]),
49494991
Y_minus_X: FieldElement2625::from_limbs([
49504992
4781635, 20898487, 30324746, 31566849, 66314586, 2020338, 46386772, 13303771,

curve25519-dalek/src/backend/serial/u64/constants.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,28 +187,28 @@ pub const ED25519_BASEPOINT_POINT: EdwardsPoint = EdwardsPoint {
187187

188188
/// The Ed25519 basepoint, mul by 2^128, as an `EdwardsPoint`.
189189
pub const ED25519_BASEPOINT_128_POINT: EdwardsPoint = EdwardsPoint {
190-
X: FieldElement51([
190+
X: FieldElement51::from_limbs([
191191
1573694877509226,
192192
2118644427590872,
193193
940662180141619,
194194
707110682864191,
195195
1255997186674731,
196196
]),
197-
Y: FieldElement51([
197+
Y: FieldElement51::from_limbs([
198198
1162474291335259,
199199
820106152083656,
200200
523232807607258,
201201
2134608004007539,
202202
729879497843472,
203203
]),
204-
Z: FieldElement51([
204+
Z: FieldElement51::from_limbs([
205205
445134475408207,
206206
1820906444767788,
207207
40212681131971,
208208
1448661247201044,
209209
965705781338211,
210210
]),
211-
T: FieldElement51([
211+
T: FieldElement51::from_limbs([
212212
1855274855831337,
213213
1818119365171423,
214214
269991889323070,

curve25519-dalek/src/backend/vector/scalar_mul/vartime_triple_base.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99

1010
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
1111
"avx2",
12-
conditional("avx512ifma,avx512vl", nightly)
12+
conditional(
13+
"avx512ifma,avx512vl",
14+
all(curve25519_dalek_backend = "unstable_avx512", nightly)
15+
)
1316
)]
1417
pub mod spec {
1518

@@ -25,14 +28,14 @@ pub mod spec {
2528
#[for_target_feature("avx2")]
2629
use crate::backend::vector::avx2::constants::BASEPOINT_ODD_LOOKUP_TABLE;
2730

28-
#[cfg(feature = "precomputed-tables")]
2931
#[for_target_feature("avx512ifma")]
3032
use crate::backend::vector::ifma::constants::BASEPOINT_ODD_LOOKUP_TABLE;
3133

3234
use crate::constants;
3335
use crate::edwards::EdwardsPoint;
3436
use crate::scalar::HEEA_MAX_INDEX;
3537
use crate::scalar::Scalar;
38+
#[allow(unused_imports)]
3639
use crate::traits::Identity;
3740
use crate::window::NafLookupTable5;
3841

0 commit comments

Comments
 (0)