curve25519-dalek: add batch montgomery conversion (#722)

This commit is contained in:
Andrew Poelstra 2025-06-02 22:02:43 +00:00 committed by GitHub
parent dd5bd108d6
commit dcd39743ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -563,6 +563,28 @@ impl EdwardsPoint {
MontgomeryPoint(u.as_bytes())
}
/// Converts a large batch of points to Edwards at once. This has the same
/// behavior on identity elements as [`Self::to_montgomery`].
#[cfg(feature = "alloc")]
pub fn to_montgomery_batch(eds: &[Self]) -> Vec<MontgomeryPoint> {
// Do the same thing as the above function. u = (1+y)/(1-y) = (Z+Y)/(Z-Y).
// We will do this in a batch, ie compute (Z-Y) for all the input
// points, then invert them all at once
// Compute the denominators in a batch
let mut denominators = eds.iter().map(|p| &p.Z - &p.Y).collect::<Vec<_>>();
FieldElement::batch_invert(&mut denominators);
// Now compute the Montgomery u coordinate for every point
let mut ret = Vec::with_capacity(eds.len());
for (ed, d) in eds.iter().zip(denominators.iter()) {
let u = &(&ed.Z + &ed.Y) * d;
ret.push(MontgomeryPoint(u.as_bytes()));
}
ret
}
/// Compress this point to `CompressedEdwardsY` format.
pub fn compress(&self) -> CompressedEdwardsY {
let recip = self.Z.invert();
@ -2189,6 +2211,31 @@ mod test {
}
}
#[test]
#[cfg(feature = "alloc")]
fn batch_to_montgomery() {
let mut rng = rand::thread_rng();
let scalars = (0..128)
.map(|_| Scalar::random(&mut rng))
.collect::<Vec<_>>();
let points = scalars
.iter()
.map(EdwardsPoint::mul_base)
.collect::<Vec<_>>();
let single_monts = points
.iter()
.map(EdwardsPoint::to_montgomery)
.collect::<Vec<_>>();
for i in [0, 1, 2, 3, 10, 50, 128] {
let invs = EdwardsPoint::to_montgomery_batch(&points[..i]);
assert_eq!(&invs, &single_monts[..i]);
}
}
#[test]
#[cfg(feature = "alloc")]
fn vartime_precomputed_vs_nonprecomputed_multiscalar() {