Skip to content

Commit e9cc0b1

Browse files
committed
curve25519-dalek: add batch montgomery conversion
1 parent 43a16f0 commit e9cc0b1

File tree

1 file changed

+60
-0
lines changed

1 file changed

+60
-0
lines changed

curve25519-dalek/src/edwards.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,41 @@ impl EdwardsPoint {
562562
MontgomeryPoint(u.as_bytes())
563563
}
564564

565+
/// Converts a large batch of points to Edwards at once.
566+
#[cfg(feature = "alloc")]
567+
pub fn batch_to_montgomery(eds: &[Self]) -> alloc::vec::Vec<MontgomeryPoint> {
568+
// "Montgomery's trick" borrowed from https://github.com/bitcoin-core/secp256k1/pull/16
569+
if eds.is_empty() {
570+
return vec![];
571+
}
572+
573+
let mut ret = alloc::vec::Vec::with_capacity(eds.len());
574+
let mut w_invs = alloc::vec::Vec::with_capacity(eds.len());
575+
576+
// First build a list of W1, W1W2, W1W2W3, ...
577+
w_invs.push(&eds[0].Z - &eds[0].Y);
578+
for i in 1..eds.len() {
579+
let last = w_invs[i - 1];
580+
w_invs.push(&last * &(&eds[i].Z - &eds[i].Y));
581+
}
582+
// Then invert the final one to get a product of all inverses.
583+
let mut w_inv = w_invs[eds.len() - 1].invert();
584+
// Then, going backward, compute all the individual inverses.
585+
for i in (0..eds.len() - 1).rev() {
586+
w_invs[i + 1] = &w_inv * &w_invs[i];
587+
w_inv *= &(&eds[i + 1].Z - &eds[i + 1].Y);
588+
}
589+
w_invs[0] = w_inv;
590+
591+
// Then complete the points
592+
for (ed, w_inv) in eds.iter().zip(w_invs.iter()) {
593+
let u = &(&ed.Z + &ed.Y) * w_inv;
594+
ret.push(MontgomeryPoint(u.as_bytes()));
595+
}
596+
597+
ret
598+
}
599+
565600
/// Compress this point to `CompressedEdwardsY` format.
566601
pub fn compress(&self) -> CompressedEdwardsY {
567602
let recip = self.Z.invert();
@@ -2106,6 +2141,31 @@ mod test {
21062141
}
21072142
}
21082143

2144+
#[test]
2145+
#[cfg(feature = "alloc")]
2146+
fn batch_to_montgomery() {
2147+
let mut rng = rand::thread_rng();
2148+
2149+
let scalars = (0..128)
2150+
.map(|_| Scalar::random(&mut rng))
2151+
.collect::<Vec<_>>();
2152+
2153+
let points = scalars
2154+
.iter()
2155+
.map(EdwardsPoint::mul_base)
2156+
.collect::<Vec<_>>();
2157+
2158+
let single_monts = points
2159+
.iter()
2160+
.map(EdwardsPoint::to_montgomery)
2161+
.collect::<Vec<_>>();
2162+
2163+
for i in [0, 1, 2, 3, 10, 50, 128] {
2164+
let invs = EdwardsPoint::batch_to_montgomery(&points[..i]);
2165+
assert_eq!(&invs, &single_monts[..i]);
2166+
}
2167+
}
2168+
21092169
#[test]
21102170
#[cfg(feature = "alloc")]
21112171
fn vartime_precomputed_vs_nonprecomputed_multiscalar() {

0 commit comments

Comments
 (0)