Skip to content

Commit

Permalink
Merge pull request #336 from huitseeker/elligator2
Browse files Browse the repository at this point in the history
Elligator2 mapping for curve25519
  • Loading branch information
isislovecruft committed Mar 25, 2021
2 parents d0014f6 + e2651cc commit b79a276
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 4 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ travis-ci = { repository = "dalek-cryptography/curve25519-dalek", branch = "mast
sha2 = { version = "0.9", default-features = false }
bincode = "1"
criterion = "0.3.0"
hex = "0.4.2"
rand = "0.7"

[[bench]]
Expand Down
4 changes: 4 additions & 0 deletions src/backend/serial/u32/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ pub(crate) const SQRT_M1: FieldElement2625 = FieldElement2625([
pub(crate) const APLUS2_OVER_FOUR: FieldElement2625 =
FieldElement2625([121666, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

/// `MONT_A` is a constant of Curve25519. (This is used internally within the Elligator map.)
pub(crate) const MONT_A: FieldElement2625 =
FieldElement2625([486662, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

/// `L` is the order of base point, i.e. 2^252 +
/// 27742317777372353535851937790883648493
pub(crate) const L: Scalar29 = Scalar29([
Expand Down
3 changes: 3 additions & 0 deletions src/backend/serial/u64/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ pub(crate) const SQRT_M1: FieldElement51 = FieldElement51([
/// `APLUS2_OVER_FOUR` is (A+2)/4. (This is used internally within the Montgomery ladder.)
pub(crate) const APLUS2_OVER_FOUR: FieldElement51 = FieldElement51([121666, 0, 0, 0, 0]);

/// `MONT_A` is a constant of Curve25519. (This is used internally within the Elligator map.)
pub(crate) const MONT_A: FieldElement51 = FieldElement51([486662, 0, 0, 0, 0]);

/// `L` is the order of base point, i.e. 2^252 + 27742317777372353535851937790883648493
pub(crate) const L: Scalar52 = Scalar52([
0x0002631a5cf5d3ed,
Expand Down
87 changes: 87 additions & 0 deletions src/edwards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ use core::ops::{Add, Neg, Sub};
use core::ops::{AddAssign, SubAssign};
use core::ops::{Mul, MulAssign};

use digest::{generic_array::typenum::U64, Digest};
use subtle::Choice;
use subtle::ConditionallyNegatable;
use subtle::ConditionallySelectable;
Expand Down Expand Up @@ -518,6 +519,31 @@ impl EdwardsPoint {
s[31] ^= x.is_negative().unwrap_u8() << 7;
CompressedEdwardsY(s)
}

/// Perform hashing to the group using the Elligator2 map
///
/// See https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-10#section-6.7.1
pub fn hash_from_bytes<D>(bytes: &[u8]) -> EdwardsPoint
where
D: Digest<OutputSize = U64> + Default,
{
let mut hash = D::new();
hash.update(bytes);
let h = hash.finalize();
let mut res = [0u8; 32];
res.copy_from_slice(&h[..32]);

let sign_bit = (res[31] & 0x80) >> 7;

let fe = FieldElement::from_bytes(&res);

let M1 = crate::montgomery::elligator_encode(&fe);
let E1_opt = M1.to_edwards(sign_bit);

E1_opt
.expect("Montgomery conversion to Edwards point in Elligator failed")
.mul_by_cofactor()
}
}

// ------------------------------------------------------------------------
Expand Down Expand Up @@ -1457,4 +1483,65 @@ mod test {
let bp: EdwardsPoint = bincode::deserialize(raw_bytes).unwrap();
assert_eq!(bp, constants::ED25519_BASEPOINT_POINT);
}

////////////////////////////////////////////////////////////
// Signal tests from //
// https://github.com/signalapp/libsignal-protocol-c/ //
////////////////////////////////////////////////////////////

fn test_vectors() -> Vec<Vec<&'static str>> {
vec![
vec![
"214f306e1576f5a7577636fe303ca2c625b533319f52442b22a9fa3b7ede809f",
"c95becf0f93595174633b9d4d6bbbeb88e16fa257176f877ce426e1424626052",
],
vec![
"2eb10d432702ea7f79207da95d206f82d5a3b374f5f89f17a199531f78d3bea6",
"d8f8b508edffbb8b6dab0f602f86a9dd759f800fe18f782fdcac47c234883e7f",
],
vec![
"84cbe9accdd32b46f4a8ef51c85fd39d028711f77fb00e204a613fc235fd68b9",
"93c73e0289afd1d1fc9e4e78a505d5d1b2642fbdf91a1eff7d281930654b1453",
],
vec![
"c85165952490dc1839cb69012a3d9f2cc4b02343613263ab93a26dc89fd58267",
"43cbe8685fd3c90665b91835debb89ff1477f906f5170f38a192f6a199556537",
],
vec![
"26e7fc4a78d863b1a4ccb2ce0951fbcd021e106350730ee4157bacb4502e1b76",
"b6fc3d738c2c40719479b2f23818180cdafa72a14254d4016bbed8f0b788a835",
],
vec![
"1618c08ef0233f94f0f163f9435ec7457cd7a8cd4bb6b160315d15818c30f7a2",
"da0b703593b29dbcd28ebd6e7baea17b6f61971f3641cae774f6a5137a12294c",
],
vec![
"48b73039db6fcdcb6030c4a38e8be80b6390d8ae46890e77e623f87254ef149c",
"ca11b25acbc80566603eabeb9364ebd50e0306424c61049e1ce9385d9f349966",
],
vec![
"a744d582b3a34d14d311b7629da06d003045ae77cebceeb4e0e72734d63bd07d",
"fad25a5ea15d4541258af8785acaf697a886c1b872c793790e60a6837b1adbc0",
],
vec![
"80a6ff33494c471c5eff7efb9febfbcf30a946fe6535b3451cda79f2154a7095",
"57ac03913309b3f8cd3c3d4c49d878bb21f4d97dc74a1eaccbe5c601f7f06f47",
],
vec![
"f06fc939bc10551a0fd415aebf107ef0b9c4ee1ef9a164157bdd089127782617",
"785b2a6a00a5579cc9da1ff997ce8339b6f9fb46c6f10cf7a12ff2986341a6e0",
],
]
}

#[test]
fn elligator_signal_test_vectors() {
for vector in test_vectors().iter() {
let input = hex::decode(vector[0]).unwrap();
let output = hex::decode(vector[1]).unwrap();

let point = EdwardsPoint::hash_from_bytes::<sha2::Sha512>(&input);
assert_eq!(point.compress().to_bytes(), output[..]);
}
}
}
61 changes: 57 additions & 4 deletions src/montgomery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@

use core::ops::{Mul, MulAssign};

use constants::APLUS2_OVER_FOUR;
use constants::{APLUS2_OVER_FOUR, MONT_A};
use edwards::{CompressedEdwardsY, EdwardsPoint};
use field::FieldElement;
use scalar::Scalar;

use traits::Identity;

use subtle::Choice;
use subtle::ConditionallySelectable;
use subtle::ConstantTimeEq;
use subtle::{ConditionallyNegatable, ConditionallySelectable};

use zeroize::Zeroize;

Expand Down Expand Up @@ -157,6 +157,33 @@ impl MontgomeryPoint {
}
}

/// Perform the Elligator2 mapping to a Montgomery point
///
/// See
/// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-10#section-6.7.1
pub(crate) fn elligator_encode(r_0: &FieldElement) -> MontgomeryPoint {
let minus_a = -&MONT_A; /* A = 486662 */
let one = FieldElement::one();
let d_1 = &one + &r_0.square2(); /* 2r^2 */

let d = &minus_a * &(d_1.invert()); /* A/(1+2r^2) */

let d_sq = &d.square();
let au = &MONT_A * &d;

let inner = &(d_sq + &au) + &one;
let eps = &d * &inner; /* eps = d^3 + Ad^2 + d */

let (eps_is_sq, _eps) = FieldElement::sqrt_ratio_i(&eps, &one);

let zero = FieldElement::zero();
let Atemp = FieldElement::conditional_select(&MONT_A, &zero, eps_is_sq); /* 0, or A if nonsquare*/
let mut u = &d + &Atemp; /* d, or d+A if nonsquare */
u.conditional_negate(!eps_is_sq); /* d, or -d-A if nonsquare */

MontgomeryPoint(u.to_bytes())
}

/// A `ProjectivePoint` holds a point on the projective line
/// \\( \mathbb P(\mathbb F\_p) \\), which we identify with the Kummer
/// line of the Montgomery curve.
Expand Down Expand Up @@ -316,8 +343,9 @@ impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Scalar {

#[cfg(test)]
mod test {
use constants;
use super::*;
use constants;
use core::convert::TryInto;

use rand_core::OsRng;

Expand Down Expand Up @@ -397,8 +425,33 @@ mod test {
let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery();

let expected = s * p_edwards;
let result = s * p_montgomery;
let result = s * p_montgomery;

assert_eq!(result, expected.to_montgomery())
}

const ELLIGATOR_CORRECT_OUTPUT: [u8; 32] = [
0x5f, 0x35, 0x20, 0x00, 0x1c, 0x6c, 0x99, 0x36, 0xa3, 0x12, 0x06, 0xaf, 0xe7, 0xc7, 0xac,
0x22, 0x4e, 0x88, 0x61, 0x61, 0x9b, 0xf9, 0x88, 0x72, 0x44, 0x49, 0x15, 0x89, 0x9d, 0x95,
0xf4, 0x6e,
];

#[test]
#[cfg(feature = "std")] // Vec
fn montgomery_elligator_correct() {
let bytes: std::vec::Vec<u8> = (0u8..32u8).collect();
let bits_in: [u8; 32] = (&bytes[..]).try_into().expect("Range invariant broken");

let fe = FieldElement::from_bytes(&bits_in);
let eg = elligator_encode(&fe);
assert_eq!(eg.to_bytes(), ELLIGATOR_CORRECT_OUTPUT);
}

#[test]
fn montgomery_elligator_zero_zero() {
let zero = [0u8; 32];
let fe = FieldElement::from_bytes(&zero);
let eg = elligator_encode(&fe);
assert_eq!(eg.to_bytes(), zero);
}
}

0 comments on commit b79a276

Please sign in to comment.