Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion curve25519-dalek/benches/dalek_benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mod edwards_benches {
let mut rng = OsRng.unwrap_err();
let points: Vec<EdwardsPoint> =
(0..size).map(|_| EdwardsPoint::random(&mut rng)).collect();
b.iter(|| EdwardsPoint::compress_batch(&points));
b.iter(|| EdwardsPoint::compress_batch_alloc(&points));
},
);
}
Expand Down
22 changes: 17 additions & 5 deletions curve25519-dalek/src/edwards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ impl EdwardsPoint {

// Compute the denominators in a batch
let mut denominators = eds.iter().map(|p| &p.Z - &p.Y).collect::<Vec<_>>();
FieldElement::batch_invert(&mut denominators);
FieldElement::invert_batch_alloc(&mut denominators);

// Now compute the Montgomery u coordinate for every point
let mut ret = Vec::with_capacity(eds.len());
Expand All @@ -616,12 +616,24 @@ impl EdwardsPoint {
self.to_affine().compress()
}

/// Compress several `EdwardsPoint`s into `CompressedEdwardsY` format, using a batch inversion
/// for a significant speedup.
pub fn compress_batch<const N: usize>(inputs: &[EdwardsPoint; N]) -> [CompressedEdwardsY; N] {
let mut zs: [_; N] = core::array::from_fn(|i| inputs[i].Z);
FieldElement::invert_batch(&mut zs);

core::array::from_fn(|i| {
let x = &inputs[i].X * &zs[i];
let y = &inputs[i].Y * &zs[i];
AffinePoint { x, y }.compress()
})
}
/// Compress several `EdwardsPoint`s into `CompressedEdwardsY` format, using a batch inversion
/// for a significant speedup.
#[cfg(feature = "alloc")]
pub fn compress_batch(inputs: &[EdwardsPoint]) -> Vec<CompressedEdwardsY> {
pub fn compress_batch_alloc(inputs: &[EdwardsPoint]) -> Vec<CompressedEdwardsY> {
let mut zs = inputs.iter().map(|input| input.Z).collect::<Vec<_>>();
FieldElement::batch_invert(&mut zs);
FieldElement::invert_batch_alloc(&mut zs);

inputs
.iter()
Expand Down Expand Up @@ -2177,7 +2189,7 @@ mod test {

#[cfg(feature = "alloc")]
{
let compressed = EdwardsPoint::compress_batch(&[EdwardsPoint::identity()]);
let compressed = EdwardsPoint::compress_batch_alloc(&[EdwardsPoint::identity()]);
assert_eq!(&compressed, &[CompressedEdwardsY::identity()]);
}
}
Expand All @@ -2193,7 +2205,7 @@ mod test {
.map(|n| constants::ED25519_BASEPOINT_POINT * Scalar::from(n))
.collect::<Vec<_>>();
points.extend(core::iter::repeat_with(|| EdwardsPoint::random(&mut rng)).take(100));
let compressed = EdwardsPoint::compress_batch(&points);
let compressed = EdwardsPoint::compress_batch_alloc(&points);

// Check that the batch-compressed points match the individually compressed ones
for (point, compressed) in points.iter().zip(&compressed) {
Expand Down
33 changes: 24 additions & 9 deletions curve25519-dalek/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,17 +203,32 @@ impl FieldElement {
(t19, t3)
}

/// Given a slice of pub(crate)lic `FieldElements`, replace each with its inverse.
///
/// When an input `FieldElement` is zero, its value is unchanged.
pub(crate) fn invert_batch<const N: usize>(inputs: &mut [FieldElement; N]) {
let mut scratch = [FieldElement::ONE; N];

Self::internal_invert_batch(inputs, &mut scratch);
}

/// Given a slice of pub(crate)lic `FieldElements`, replace each with its inverse.
///
/// When an input `FieldElement` is zero, its value is unchanged.
#[cfg(feature = "alloc")]
pub(crate) fn batch_invert(inputs: &mut [FieldElement]) {
pub(crate) fn invert_batch_alloc(inputs: &mut [FieldElement]) {
let n = inputs.len();
let mut scratch = vec![FieldElement::ONE; n];

Self::internal_invert_batch(inputs, &mut scratch);
}

fn internal_invert_batch(inputs: &mut [FieldElement], scratch: &mut [FieldElement]) {
// Montgomery’s Trick and Fast Implementation of Masked AES
// Genelle, Prouff and Quisquater
// Section 3.2

let n = inputs.len();
let mut scratch = vec![FieldElement::ONE; n];
debug_assert_eq!(inputs.len(), scratch.len());

// Keep an accumulator of all of the previous products
let mut acc = FieldElement::ONE;
Expand All @@ -234,12 +249,12 @@ impl FieldElement {

// Pass through the vector backwards to compute the inverses
// in place
for (input, scratch) in inputs.iter_mut().rev().zip(scratch.into_iter().rev()) {
for (input, scratch) in inputs.iter_mut().rev().zip(scratch.iter_mut().rev()) {
let tmp = &acc * input;
// input <- acc * scratch, then acc <- tmp
// Again, we skip zeros in a constant-time way
let nz = !input.is_zero();
input.conditional_assign(&(&acc * &scratch), nz);
input.conditional_assign(&(&acc * scratch), nz);
acc.conditional_assign(&tmp, nz);
}
}
Expand Down Expand Up @@ -553,7 +568,7 @@ mod test {

#[test]
#[cfg(feature = "alloc")]
fn batch_invert_a_matches_nonbatched() {
fn invert_batch_a_matches_nonbatched() {
let a = FieldElement::from_bytes(&A_BYTES);
let ap58 = FieldElement::from_bytes(&AP58_BYTES);
let asq = FieldElement::from_bytes(&ASQ_BYTES);
Expand All @@ -562,7 +577,7 @@ mod test {
let a2 = &a + &a;
let a_list = vec![a, ap58, asq, ainv, a0, a2];
let mut ainv_list = a_list.clone();
FieldElement::batch_invert(&mut ainv_list[..]);
FieldElement::invert_batch_alloc(&mut ainv_list[..]);
for i in 0..6 {
assert_eq!(a_list[i].invert(), ainv_list[i]);
}
Expand Down Expand Up @@ -671,8 +686,8 @@ mod test {

#[test]
#[cfg(feature = "alloc")]
fn batch_invert_empty() {
FieldElement::batch_invert(&mut []);
fn invert_batch_empty() {
FieldElement::invert_batch_alloc(&mut []);
}

// The following two consts were generated with the following sage script:
Expand Down
2 changes: 1 addition & 1 deletion curve25519-dalek/src/ristretto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ impl RistrettoPoint {

let mut invs: Vec<FieldElement> = states.iter().map(|state| state.efgh()).collect();

FieldElement::batch_invert(&mut invs[..]);
FieldElement::invert_batch_alloc(&mut invs[..]);

states
.iter()
Expand Down