Skip to content

Commit 4994cb3

Browse files
committed
Add Scalar::halve()
1 parent 1ad4603 commit 4994cb3

File tree

5 files changed

+57
-1
lines changed

5 files changed

+57
-1
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ pub(crate) const RR: Scalar29 = Scalar29([
113113
0x0005046d,
114114
]);
115115

116+
/// `TWO_INVERT` = 1/2
117+
pub(crate) const TWO_INVERT: Scalar29 = Scalar29([
118+
0x0e7ae9f7, 0x00498c69, 0x1ef39acb, 0x1ef9dea2, 0x000000a6, 0x00000000, 0x00000000, 0x00000000,
119+
0x00080000,
120+
]);
121+
116122
/// The Ed25519 basepoint, as an `EdwardsPoint`.
117123
///
118124
/// This is called `_POINT` to distinguish it from

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,15 @@ pub(crate) const RR: Scalar52 = Scalar52([
153153
0x000009411b7c309a,
154154
]);
155155

156+
/// `TWO_INVERT` = 1/2
157+
pub(crate) const TWO_INVERT: Scalar52 = Scalar52([
158+
0x0009318d2e7ae9f7,
159+
0x000ef517bce6b2c0,
160+
0x00000000000a6f7c,
161+
0x0000000000000000,
162+
0x0000080000000000,
163+
]);
164+
156165
/// The Ed25519 basepoint, as an `EdwardsPoint`.
157166
///
158167
/// This is called `_POINT` to distinguish it from

curve25519-dalek/src/constants.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ pub static RISTRETTO_BASEPOINT_TABLE: &RistrettoBasepointTable = unsafe {
9090

9191
#[cfg(test)]
9292
mod test {
93-
use crate::constants;
9493
use crate::field::FieldElement;
9594
use crate::traits::{IsIdentity, ValidityCheck};
95+
use crate::{Scalar, constants};
9696

9797
#[test]
9898
fn test_eight_torsion() {
@@ -182,4 +182,11 @@ mod test {
182182

183183
assert_eq!(constants::ED25519_SQRTAM2.square(), a_minus_two)
184184
}
185+
186+
#[test]
187+
fn test_two_invert() {
188+
let two_invert = Scalar::from(2_u8).invert().unpack();
189+
dbg!(format!("{:08x?}", two_invert.0));
190+
assert_eq!(constants::TWO_INVERT.0, two_invert.0);
191+
}
185192
}

curve25519-dalek/src/ristretto.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,6 +1867,35 @@ mod test {
18671867
}
18681868
}
18691869

1870+
#[test]
1871+
#[cfg(all(feature = "alloc", feature = "rand_core", feature = "group"))]
1872+
fn multiply_double_and_compress_1024_random_points() {
1873+
use ff::Field;
1874+
use group::Group;
1875+
let mut rng = OsRng;
1876+
1877+
let mut scalars: Vec<Scalar> = (0..1024)
1878+
.map(|_| Scalar::try_from_rng(&mut rng).unwrap())
1879+
.collect();
1880+
scalars[500] = Scalar::ZERO;
1881+
1882+
let mut points: Vec<RistrettoPoint> = (0..1024)
1883+
.map(|_| RistrettoPoint::try_from_rng(&mut rng).unwrap())
1884+
.collect();
1885+
points[500] = <RistrettoPoint as Group>::identity();
1886+
1887+
let multiplied_points: Vec<RistrettoPoint> = scalars
1888+
.iter()
1889+
.zip(&points)
1890+
.map(|(scalar, point)| scalar.halve() * point)
1891+
.collect();
1892+
let compressed = RistrettoPoint::double_and_compress_batch(&multiplied_points);
1893+
1894+
for ((s, P), P2_compressed) in scalars.iter().zip(points).zip(compressed) {
1895+
assert_eq!(P2_compressed, (s * P).compress());
1896+
}
1897+
}
1898+
18701899
#[test]
18711900
#[cfg(feature = "alloc")]
18721901
fn vartime_precomputed_vs_nonprecomputed_multiscalar() {

curve25519-dalek/src/scalar.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,11 @@ impl Scalar {
11291129
fn is_canonical(&self) -> Choice {
11301130
self.ct_eq(&self.reduce())
11311131
}
1132+
1133+
/// Returns half the `Scalar`
1134+
pub fn halve(&self) -> Scalar {
1135+
UnpackedScalar::mul(&self.unpack(), &constants::TWO_INVERT).pack()
1136+
}
11321137
}
11331138

11341139
impl UnpackedScalar {

0 commit comments

Comments
 (0)