Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elligator2 Forward and Reverse Mappings #612

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
5 changes: 5 additions & 0 deletions curve25519-dalek/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ sha2 = { version = "0.10", default-features = false }
bincode = "1"
criterion = { version = "0.5", features = ["html_reports"] }
hex = "0.4.2"
json = "0.12.4"
rand = "0.8"
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
rand_distr = "0.4.3"
kolmogorov_smirnov = "1.1.0"

[build-dependencies]
rustc_version = "0.4.0"
Expand Down Expand Up @@ -68,6 +71,8 @@ precomputed-tables = []
legacy_compatibility = []
group = ["dep:group", "rand_core"]
group-bits = ["group", "ff/bits"]
elligator2 = []
digest = ["dep:digest", "elligator2"]

[target.'cfg(all(not(curve25519_dalek_backend = "fiat"), not(curve25519_dalek_backend = "serial"), target_arch = "x86_64"))'.dependencies]
curve25519-dalek-derive = { version = "0.1", path = "../curve25519-dalek-derive" }
Expand Down
1 change: 1 addition & 0 deletions curve25519-dalek/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ curve25519-dalek = ">= 4.0, < 4.2"
| `serde` | | Enables `serde` serialization/deserialization for all the point and scalar types. |
| `legacy_compatibility`| | Enables `Scalar::from_bits`, which allows the user to build unreduced scalars whose arithmetic is broken. Do not use this unless you know what you're doing. |
| `group` | | Enables external `group` and `ff` crate traits |
| `elligator2` | | Enables elligator2 functionality for supported types. This allows curve points to be encoded to uniform random representatives, and 32 byte values to be mapped (back) to curve points. |

To disable the default features when using `curve25519-dalek` as a dependency,
add `default-features = false` to the dependency in your `Cargo.toml`. To
Expand Down
20 changes: 20 additions & 0 deletions curve25519-dalek/src/backend/serial/fiat_u32/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,24 @@ impl FieldElement2625 {
fiat_25519_carry(&mut output.0, &output_loose);
output
}

/// Returns 1 if self is greater than the other and 0 otherwise
// implementation based on C libgmp -> mpn_sub_n
pub(crate) fn gt(&self, other: &Self) -> Choice {
let mut _ul = 0_u32;
let mut _vl = 0_u32;
let mut _rl = 0_u32;

let mut cy = 0_u32;
for i in 0..10 {
_ul = self.0[i];
_vl = other.0[i];

let (_sl, _cy1) = _ul.overflowing_sub(_vl);
let (_rl, _cy2) = _sl.overflowing_sub(cy);
cy = _cy1 as u32 | _cy2 as u32;
}

Choice::from((cy != 0_u32) as u8)
}
}
20 changes: 20 additions & 0 deletions curve25519-dalek/src/backend/serial/fiat_u64/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,24 @@ impl FieldElement51 {
fiat_25519_carry(&mut output.0, &output_loose);
output
}

/// Returns 1 if self is greater than the other and 0 otherwise
// implementation based on C libgmp -> mpn_sub_n
pub(crate) fn gt(&self, other: &Self) -> Choice {
let mut _ul = 0_u64;
let mut _vl = 0_u64;
let mut _rl = 0_u64;

let mut cy = 0_u64;
for i in 0..5 {
_ul = self.0[i];
_vl = other.0[i];

let (_sl, _cy1) = _ul.overflowing_sub(_vl);
let (_rl, _cy2) = _sl.overflowing_sub(cy);
cy = _cy1 as u64 | _cy2 as u64;
}

Choice::from((cy != 0_u64) as u8)
}
}
2 changes: 2 additions & 0 deletions curve25519-dalek/src/backend/serial/u32/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ pub(crate) const SQRT_M1: FieldElement2625 = FieldElement2625::from_limbs([
pub(crate) const APLUS2_OVER_FOUR: FieldElement2625 =
FieldElement2625::from_limbs([121666, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

#[cfg(feature = "elligator2")]
/// `MONTGOMERY_A` is equal to 486662, which is a constant of the curve equation
/// for Curve25519 in its Montgomery form. (This is used internally within the
/// Elligator map.)
pub(crate) const MONTGOMERY_A: FieldElement2625 =
FieldElement2625::from_limbs([486662, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

#[cfg(feature = "elligator2")]
/// `MONTGOMERY_A_NEG` is equal to -486662. (This is used internally within the
/// Elligator map.)
pub(crate) const MONTGOMERY_A_NEG: FieldElement2625 = FieldElement2625::from_limbs([
Expand Down
20 changes: 20 additions & 0 deletions curve25519-dalek/src/backend/serial/u32/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,4 +601,24 @@ impl FieldElement2625 {
}
FieldElement2625::reduce(coeffs)
}

/// Returns 1 if self is greater than the other and 0 otherwise
// implementation based on C libgmp -> mpn_sub_n
pub(crate) fn gt(&self, other: &Self) -> Choice {
let mut _ul = 0_u32;
let mut _vl = 0_u32;
let mut _rl = 0_u32;

let mut cy = 0_u32;
for i in 0..10 {
_ul = self.0[i];
_vl = other.0[i];

let (_sl, _cy1) = _ul.overflowing_sub(_vl);
let (_rl, _cy2) = _sl.overflowing_sub(cy);
cy = _cy1 as u32 | _cy2 as u32;
}

Choice::from((cy != 0_u32) as u8)
}
}
2 changes: 2 additions & 0 deletions curve25519-dalek/src/backend/serial/u64/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,13 @@ pub(crate) const SQRT_M1: FieldElement51 = FieldElement51::from_limbs([
pub(crate) const APLUS2_OVER_FOUR: FieldElement51 =
FieldElement51::from_limbs([121666, 0, 0, 0, 0]);

#[cfg(feature = "elligator2")]
/// `MONTGOMERY_A` is equal to 486662, which is a constant of the curve equation
/// for Curve25519 in its Montgomery form. (This is used internally within the
/// Elligator map.)
pub(crate) const MONTGOMERY_A: FieldElement51 = FieldElement51::from_limbs([486662, 0, 0, 0, 0]);

#[cfg(feature = "elligator2")]
/// `MONTGOMERY_A_NEG` is equal to -486662. (This is used internally within the
/// Elligator map.)
pub(crate) const MONTGOMERY_A_NEG: FieldElement51 = FieldElement51::from_limbs([
Expand Down
20 changes: 20 additions & 0 deletions curve25519-dalek/src/backend/serial/u64/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -572,4 +572,24 @@ impl FieldElement51 {

square
}

/// Returns 1 if self is greater than the other and 0 otherwise
// implementation based on C libgmp -> mpn_sub_n
pub(crate) fn gt(&self, other: &Self) -> Choice {
let mut _ul = 0_u64;
let mut _vl = 0_u64;
let mut _rl = 0_u64;

let mut cy = 0_u64;
for i in 0..5 {
_ul = self.0[i];
_vl = other.0[i];

let (_sl, _cy1) = _ul.overflowing_sub(_vl);
let (_rl, _cy2) = _sl.overflowing_sub(cy);
cy = _cy1 as u64 | _cy2 as u64;
}

Choice::from((cy != 0_u64) as u8)
}
}
48 changes: 28 additions & 20 deletions curve25519-dalek/src/edwards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,8 @@ impl EdwardsPoint {
where
D: Digest<OutputSize = U64> + Default,
{
use crate::elligator2::Legacy;

let mut hash = D::new();
hash.update(bytes);
let h = hash.finalize();
Expand All @@ -596,10 +598,11 @@ impl EdwardsPoint {

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);
// rfc9380 should always result in a valid point since no field elements
// are invalid. so unwrap should be safe.
#[allow(clippy::unwrap_used)]
let fe1 = MontgomeryPoint::from_representative::<Legacy>(&res).unwrap();
let E1_opt = fe1.to_edwards(sign_bit);

E1_opt
.expect("Montgomery conversion to Edwards point in Elligator failed")
Expand Down Expand Up @@ -2221,10 +2224,6 @@ mod test {
"2eb10d432702ea7f79207da95d206f82d5a3b374f5f89f17a199531f78d3bea6",
"d8f8b508edffbb8b6dab0f602f86a9dd759f800fe18f782fdcac47c234883e7f",
],
vec![
"84cbe9accdd32b46f4a8ef51c85fd39d028711f77fb00e204a613fc235fd68b9",
"93c73e0289afd1d1fc9e4e78a505d5d1b2642fbdf91a1eff7d281930654b1453",
],
vec![
"c85165952490dc1839cb69012a3d9f2cc4b02343613263ab93a26dc89fd58267",
"43cbe8685fd3c90665b91835debb89ff1477f906f5170f38a192f6a199556537",
Expand All @@ -2237,35 +2236,44 @@ mod test {
"1618c08ef0233f94f0f163f9435ec7457cd7a8cd4bb6b160315d15818c30f7a2",
"da0b703593b29dbcd28ebd6e7baea17b6f61971f3641cae774f6a5137a12294c",
],
vec![
"48b73039db6fcdcb6030c4a38e8be80b6390d8ae46890e77e623f87254ef149c",
"ca11b25acbc80566603eabeb9364ebd50e0306424c61049e1ce9385d9f349966",
],
vec![
"a744d582b3a34d14d311b7629da06d003045ae77cebceeb4e0e72734d63bd07d",
"fad25a5ea15d4541258af8785acaf697a886c1b872c793790e60a6837b1adbc0",
],
vec![
"80a6ff33494c471c5eff7efb9febfbcf30a946fe6535b3451cda79f2154a7095",
"57ac03913309b3f8cd3c3d4c49d878bb21f4d97dc74a1eaccbe5c601f7f06f47",
],
vec![
"f06fc939bc10551a0fd415aebf107ef0b9c4ee1ef9a164157bdd089127782617",
"785b2a6a00a5579cc9da1ff997ce8339b6f9fb46c6f10cf7a12ff2986341a6e0",
],
// Non Least-Square-Root representative values. (i.e. representative > 2^254-10 )
vec![
"84cbe9accdd32b46f4a8ef51c85fd39d028711f77fb00e204a613fc235fd68b9",
"93c73e0289afd1d1fc9e4e78a505d5d1b2642fbdf91a1eff7d281930654b1453",
],
vec![
"48b73039db6fcdcb6030c4a38e8be80b6390d8ae46890e77e623f87254ef149c",
"ca11b25acbc80566603eabeb9364ebd50e0306424c61049e1ce9385d9f349966",
],
vec![
"80a6ff33494c471c5eff7efb9febfbcf30a946fe6535b3451cda79f2154a7095",
"57ac03913309b3f8cd3c3d4c49d878bb21f4d97dc74a1eaccbe5c601f7f06f47",
],
]
}

#[test]
#[allow(deprecated)]
#[cfg(all(feature = "alloc", feature = "digest"))]
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();
for (n, vector) in test_vectors().iter().enumerate() {
let input = hex::decode(vector[0]).expect("failed to decode hex input");
let output = hex::decode(vector[1]).expect("failed to decode hex output");

let point = EdwardsPoint::nonspec_map_to_curve::<sha2::Sha512>(&input);
assert_eq!(point.compress().to_bytes(), output[..]);
assert_eq!(
hex::encode(point.compress().to_bytes()),
hex::encode(&output[..]),
"signal map_to_curve failed for test {n}"
);
}
}
}
Loading