Skip to content

Commit

Permalink
Implement elligator2 tranform from point to representative, compatible
Browse files Browse the repository at this point in the history
with agl/ed25519/extra25519, the kleshni C implementation, and rfc9380.
  • Loading branch information
jmwample committed Feb 13, 2024
1 parent 4ac84dd commit a972ecf
Show file tree
Hide file tree
Showing 14 changed files with 1,370 additions and 46 deletions.
5 changes: 4 additions & 1 deletion curve25519-dalek/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ rustdoc-args = [
"--html-in-header", "docs/assets/rustdoc-include-katex-header.html",
"--cfg", "docsrs",
]
features = ["serde", "rand_core", "digest", "legacy_compatibility", "group-bits"]
features = ["serde", "rand_core", "elligator2", "digest", "legacy_compatibility", "group-bits"]

[dev-dependencies]
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"] }

Expand Down Expand Up @@ -69,6 +70,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" }
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
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)
}
}
62 changes: 58 additions & 4 deletions curve25519-dalek/src/edwards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ use core::ops::{Mul, MulAssign};

use cfg_if::cfg_if;

#[cfg(feature = "digest")]
use crate::elligator2::map_to_point;
#[cfg(feature = "digest")]
use digest::{generic_array::typenum::U64, Digest};

Expand Down Expand Up @@ -595,15 +597,67 @@ 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);
let fe1 = map_to_point(&res);
let E1_opt = fe1.to_edwards(sign_bit);

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

#[cfg(elligator2)]
/// Build an [`EdwardsPoint`] using the birational mapping from (the
/// extended `(u, v)` form of) a montgomery point.
pub fn from_uv(u: &[u8; 32], v: &[u8; 32]) -> EdwardsPoint {
let u_fe = FieldElement::from_bytes(u);
let v_fe = FieldElement::from_bytes(v);
let (x, y) = Self::new_edwards_point(&u_fe, &v_fe);
Self::from_xy(x, y)
}

#[cfg(elligator2)]
fn new_edwards_point(u: &FieldElement, v: &FieldElement) -> (FieldElement, FieldElement) {
// Per RFC 7748: (x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))

let two = &FieldElement::ONE + &FieldElement::ONE;
let (_, sqrt_neg_a_plus_two) =
FieldElement::sqrt_ratio_i(&(&MONTGOMERY_A_NEG + &two), &FieldElement::ONE);

let mut x = &(u * &v.invert()) * &sqrt_neg_a_plus_two;

let u_plus_one = u + &FieldElement::ONE;
let u_minus_one = u - &FieldElement::ONE;

let mut y = &u_minus_one * &u_plus_one.invert();

// This mapping is undefined when t == 0 or s == -1, i.e., when the
// denominator of either of the above rational functions is zero.
// Implementations MUST detect exceptional cases and return the value
// (v, w) = (0, 1), which is the identity point on all twisted Edwards
// curves.
let result_undefined = v.is_zero() | u_plus_one.is_zero();
x.conditional_assign(&FieldElement::ZERO, result_undefined);
y.conditional_assign(&FieldElement::ONE, result_undefined);

// Convert from Edwards (x, y) to extended (x, y, z, t) coordinates.
// new_edwards_from_xy(x, y)

(x, y)
}

#[cfg(elligator2)]
fn from_xy(x: &FieldElement, y: &FieldElement) -> EdwardsPoint {
// Yeah yeah yeah, no where better to put this. :(
let z = FieldElement::ONE;
let t = x * y;

EdwardsPoint {
X: *x,
Y: *y,
Z: z,
T: t,
}
}
}

// ------------------------------------------------------------------------
Expand Down
Loading

0 comments on commit a972ecf

Please sign in to comment.