Skip to content

Commit 84dc009

Browse files
committed
Implement elligator2 tranform from point to representative, compatible
with agl/ed25519/extra25519, the kleshni C implementation, and rfc9380.
1 parent 5b7082b commit 84dc009

File tree

14 files changed

+1370
-46
lines changed

14 files changed

+1370
-46
lines changed

curve25519-dalek/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@ rustdoc-args = [
2727
"--html-in-header", "docs/assets/rustdoc-include-katex-header.html",
2828
"--cfg", "docsrs",
2929
]
30-
features = ["serde", "rand_core", "digest", "legacy_compatibility", "group-bits"]
30+
features = ["serde", "rand_core", "elligator2", "digest", "legacy_compatibility", "group-bits"]
3131

3232
[dev-dependencies]
3333
sha2 = { version = "0.10", default-features = false }
3434
bincode = "1"
3535
criterion = { version = "0.5", features = ["html_reports"] }
3636
hex = "0.4.2"
37+
json = "0.12.4"
3738
rand = "0.8"
3839
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
3940

@@ -68,6 +69,8 @@ precomputed-tables = []
6869
legacy_compatibility = []
6970
group = ["dep:group", "rand_core"]
7071
group-bits = ["group", "ff/bits"]
72+
elligator2 = []
73+
digest = ["dep:digest", "elligator2"]
7174

7275
[target.'cfg(all(not(curve25519_dalek_backend = "fiat"), not(curve25519_dalek_backend = "serial"), target_arch = "x86_64"))'.dependencies]
7376
curve25519-dalek-derive = { version = "0.1", path = "../curve25519-dalek-derive" }

curve25519-dalek/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ curve25519-dalek = ">= 4.0, < 4.2"
5656
| `serde` | | Enables `serde` serialization/deserialization for all the point and scalar types. |
5757
| `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. |
5858
| `group` | | Enables external `group` and `ff` crate traits |
59+
| `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. |
5960

6061
To disable the default features when using `curve25519-dalek` as a dependency,
6162
add `default-features = false` to the dependency in your `Cargo.toml`. To

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,14 @@ pub(crate) const SQRT_M1: FieldElement2625 = FieldElement2625::from_limbs([
7171
pub(crate) const APLUS2_OVER_FOUR: FieldElement2625 =
7272
FieldElement2625::from_limbs([121666, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
7373

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

81+
#[cfg(feature = "elligator2")]
8082
/// `MONTGOMERY_A_NEG` is equal to -486662. (This is used internally within the
8183
/// Elligator map.)
8284
pub(crate) const MONTGOMERY_A_NEG: FieldElement2625 = FieldElement2625::from_limbs([

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,4 +601,24 @@ impl FieldElement2625 {
601601
}
602602
FieldElement2625::reduce(coeffs)
603603
}
604+
605+
/// Returns 1 if self is greater than the other and 0 otherwise
606+
// implementation based on C libgmp -> mpn_sub_n
607+
pub(crate) fn gt(&self, other: &Self) -> Choice {
608+
let mut _ul = 0_u32;
609+
let mut _vl = 0_u32;
610+
let mut _rl = 0_u32;
611+
612+
let mut cy = 0_u32;
613+
for i in 0..10 {
614+
_ul = self.0[i];
615+
_vl = other.0[i];
616+
617+
let (_sl, _cy1) = _ul.overflowing_sub(_vl);
618+
let (_rl, _cy2) = _sl.overflowing_sub(cy);
619+
cy = _cy1 as u32 | _cy2 as u32;
620+
}
621+
622+
Choice::from((cy != 0_u32) as u8)
623+
}
604624
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,13 @@ pub(crate) const SQRT_M1: FieldElement51 = FieldElement51::from_limbs([
9898
pub(crate) const APLUS2_OVER_FOUR: FieldElement51 =
9999
FieldElement51::from_limbs([121666, 0, 0, 0, 0]);
100100

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

107+
#[cfg(feature = "elligator2")]
106108
/// `MONTGOMERY_A_NEG` is equal to -486662. (This is used internally within the
107109
/// Elligator map.)
108110
pub(crate) const MONTGOMERY_A_NEG: FieldElement51 = FieldElement51::from_limbs([

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,4 +572,24 @@ impl FieldElement51 {
572572

573573
square
574574
}
575+
576+
/// Returns 1 if self is greater than the other and 0 otherwise
577+
// implementation based on C libgmp -> mpn_sub_n
578+
pub(crate) fn gt(&self, other: &Self) -> Choice {
579+
let mut _ul = 0_u64;
580+
let mut _vl = 0_u64;
581+
let mut _rl = 0_u64;
582+
583+
let mut cy = 0_u64;
584+
for i in 0..5 {
585+
_ul = self.0[i];
586+
_vl = other.0[i];
587+
588+
let (_sl, _cy1) = _ul.overflowing_sub(_vl);
589+
let (_rl, _cy2) = _sl.overflowing_sub(cy);
590+
cy = _cy1 as u64 | _cy2 as u64;
591+
}
592+
593+
Choice::from((cy != 0_u64) as u8)
594+
}
575595
}

curve25519-dalek/src/edwards.rs

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ use core::ops::{Mul, MulAssign};
103103

104104
use cfg_if::cfg_if;
105105

106+
#[cfg(feature = "digest")]
107+
use crate::elligator2::map_to_point;
106108
#[cfg(feature = "digest")]
107109
use digest::{generic_array::typenum::U64, Digest};
108110

@@ -596,15 +598,67 @@ impl EdwardsPoint {
596598

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

599-
let fe = FieldElement::from_bytes(&res);
600-
601-
let M1 = crate::montgomery::elligator_encode(&fe);
602-
let E1_opt = M1.to_edwards(sign_bit);
601+
let fe1 = map_to_point(&res);
602+
let E1_opt = fe1.to_edwards(sign_bit);
603603

604604
E1_opt
605605
.expect("Montgomery conversion to Edwards point in Elligator failed")
606606
.mul_by_cofactor()
607607
}
608+
609+
#[cfg(elligator2)]
610+
/// Build an [`EdwardsPoint`] using the birational mapping from (the
611+
/// extended `(u, v)` form of) a montgomery point.
612+
pub fn from_uv(u: &[u8; 32], v: &[u8; 32]) -> EdwardsPoint {
613+
let u_fe = FieldElement::from_bytes(u);
614+
let v_fe = FieldElement::from_bytes(v);
615+
let (x, y) = Self::new_edwards_point(&u_fe, &v_fe);
616+
Self::from_xy(x, y)
617+
}
618+
619+
#[cfg(elligator2)]
620+
fn new_edwards_point(u: &FieldElement, v: &FieldElement) -> (FieldElement, FieldElement) {
621+
// Per RFC 7748: (x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))
622+
623+
let two = &FieldElement::ONE + &FieldElement::ONE;
624+
let (_, sqrt_neg_a_plus_two) =
625+
FieldElement::sqrt_ratio_i(&(&MONTGOMERY_A_NEG + &two), &FieldElement::ONE);
626+
627+
let mut x = &(u * &v.invert()) * &sqrt_neg_a_plus_two;
628+
629+
let u_plus_one = u + &FieldElement::ONE;
630+
let u_minus_one = u - &FieldElement::ONE;
631+
632+
let mut y = &u_minus_one * &u_plus_one.invert();
633+
634+
// This mapping is undefined when t == 0 or s == -1, i.e., when the
635+
// denominator of either of the above rational functions is zero.
636+
// Implementations MUST detect exceptional cases and return the value
637+
// (v, w) = (0, 1), which is the identity point on all twisted Edwards
638+
// curves.
639+
let result_undefined = v.is_zero() | u_plus_one.is_zero();
640+
x.conditional_assign(&FieldElement::ZERO, result_undefined);
641+
y.conditional_assign(&FieldElement::ONE, result_undefined);
642+
643+
// Convert from Edwards (x, y) to extended (x, y, z, t) coordinates.
644+
// new_edwards_from_xy(x, y)
645+
646+
(x, y)
647+
}
648+
649+
#[cfg(elligator2)]
650+
fn from_xy(x: &FieldElement, y: &FieldElement) -> EdwardsPoint {
651+
// Yeah yeah yeah, no where better to put this. :(
652+
let z = FieldElement::ONE;
653+
let t = x * y;
654+
655+
EdwardsPoint {
656+
X: *x,
657+
Y: *y,
658+
Z: z,
659+
T: t,
660+
}
661+
}
608662
}
609663

610664
// ------------------------------------------------------------------------

0 commit comments

Comments
 (0)