diff --git a/curve25519-dalek/src/edwards.rs b/curve25519-dalek/src/edwards.rs index d740d638f..9436c436d 100644 --- a/curve25519-dalek/src/edwards.rs +++ b/curve25519-dalek/src/edwards.rs @@ -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 { + // 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::>(); + 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::>(); + + let points = scalars + .iter() + .map(EdwardsPoint::mul_base) + .collect::>(); + + let single_monts = points + .iter() + .map(EdwardsPoint::to_montgomery) + .collect::>(); + + 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() {