Skip to content

Commit e60caa7

Browse files
committed
add hash_to_curve method for edwards curves
Signed-off-by: Michael Lodder <redmike7@gmail.com>
1 parent 89aabac commit e60caa7

File tree

6 files changed

+146
-2
lines changed

6 files changed

+146
-2
lines changed

curve25519-dalek/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ required-features = ["alloc", "rand_core"]
4848

4949
[dependencies]
5050
cfg-if = "1"
51+
elliptic-curve = { version = "0.13.5", features = ["hash2curve"], optional = true }
5152
ff = { version = "0.13", default-features = false, optional = true }
5253
group = { version = "0.13", default-features = false, optional = true }
5354
rand_core = { version = "0.6.4", default-features = false, optional = true }
@@ -63,11 +64,11 @@ cpufeatures = "0.2.6"
6364
fiat-crypto = { version = "0.2.1", default-features = false }
6465

6566
[features]
66-
default = ["alloc", "precomputed-tables", "zeroize"]
67+
default = ["alloc", "group", "precomputed-tables", "zeroize"]
6768
alloc = ["zeroize?/alloc"]
6869
precomputed-tables = []
6970
legacy_compatibility = []
70-
group = ["dep:group", "rand_core"]
71+
group = ["dep:group", "dep:elliptic-curve", "rand_core"]
7172
group-bits = ["group", "ff/bits"]
7273

7374
[target.'cfg(all(not(curve25519_dalek_backend = "fiat"), not(curve25519_dalek_backend = "serial"), target_arch = "x86_64"))'.dependencies]

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,14 @@ impl FieldElement2625 {
193193
FieldElement2625(fiat_25519_tight_field_element(limbs))
194194
}
195195

196+
/// Elligator2 constant for Edwards Curve
197+
pub const EDWARDS_ELL_A: FieldElement2625 =
198+
FieldElement2625::from_limbs([486662, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
199+
/// Elligator2 constant for Edwards Curve
200+
pub const EDWARDS_MINUS_ELL_A: FieldElement2625 = FieldElement2625::from_limbs([
201+
0xfff892e7, 0x7ffff, 0xffffffff, 0x7ffff, 0xffffffff, 0x7ffff, 0xffffffff, 0x7ffff,
202+
0xffffffff, 0x7ffff,
203+
]);
196204
/// The scalar \\( 0 \\).
197205
pub const ZERO: FieldElement2625 = FieldElement2625::from_limbs([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
198206
/// The scalar \\( 1 \\).

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,16 @@ impl FieldElement51 {
172172
FieldElement51(fiat_25519_tight_field_element(limbs))
173173
}
174174

175+
/// Elligator2 constant for Edwards Curve
176+
pub const EDWARDS_ELL_A: FieldElement51 = FieldElement51::from_limbs([486662, 0, 0, 0, 0]);
177+
/// Elligator2 constant for Edwards Curve
178+
pub const EDWARDS_MINUS_ELL_A: FieldElement51 = FieldElement51::from_limbs([
179+
0x7fffffff892e7,
180+
0x7ffffffffffff,
181+
0x7ffffffffffff,
182+
0x7ffffffffffff,
183+
0x7ffffffffffff,
184+
]);
175185
/// The scalar \\( 0 \\).
176186
pub const ZERO: FieldElement51 = FieldElement51::from_limbs([0, 0, 0, 0, 0]);
177187
/// The scalar \\( 1 \\).

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,14 @@ impl FieldElement2625 {
288288
FieldElement2625(limbs)
289289
}
290290

291+
/// Elligator2 constant for Edwards Curve
292+
pub const EDWARDS_ELL_A: FieldElement2625 =
293+
FieldElement2625::from_limbs([486662, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
294+
/// Elligator2 constant for Edwards Curve
295+
pub const EDWARDS_MINUS_ELL_A: FieldElement2625 = FieldElement2625::from_limbs([
296+
0xfff892e7, 0x7ffff, 0xffffffff, 0x7ffff, 0xffffffff, 0x7ffff, 0xffffffff, 0x7ffff,
297+
0xffffffff, 0x7ffff,
298+
]);
291299
/// The scalar \\( 0 \\).
292300
pub const ZERO: FieldElement2625 = FieldElement2625::from_limbs([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
293301
/// The scalar \\( 1 \\).

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,16 @@ impl FieldElement51 {
259259
FieldElement51(limbs)
260260
}
261261

262+
/// Elligator2 constant for Edwards Curve
263+
pub const EDWARDS_ELL_A: FieldElement51 = FieldElement51::from_limbs([486662, 0, 0, 0, 0]);
264+
/// Elligator2 constant for Edwards Curve
265+
pub const EDWARDS_MINUS_ELL_A: FieldElement51 = FieldElement51::from_limbs([
266+
0x7fffffff892e7,
267+
0x7ffffffffffff,
268+
0x7ffffffffffff,
269+
0x7ffffffffffff,
270+
0x7ffffffffffff,
271+
]);
262272
/// The scalar \\( 0 \\).
263273
pub const ZERO: FieldElement51 = FieldElement51::from_limbs([0, 0, 0, 0, 0]);
264274
/// The scalar \\( 1 \\).

curve25519-dalek/src/edwards.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,92 @@ impl EdwardsPoint {
604604
.expect("Montgomery conversion to Edwards point in Elligator failed")
605605
.mul_by_cofactor()
606606
}
607+
608+
#[cfg(feature = "group")]
609+
/// Maps the input bytes to the curve. This implements the spec for
610+
/// [`hash_to_curve`](https://datatracker.ietf.org/doc/rfc9380/) according to sections
611+
/// 8.5 and J.5
612+
pub fn hash_to_curve<X>(msg: &[u8], dst: &[u8]) -> Self
613+
where
614+
X: for<'a> elliptic_curve::hash2curve::ExpandMsg<'a>,
615+
{
616+
use elliptic_curve::{
617+
bigint::{ArrayEncoding, Encoding, NonZero, U384},
618+
hash2curve::Expander,
619+
};
620+
621+
let dst = [dst];
622+
let mut random_bytes = [0u8; 96];
623+
let mut expander =
624+
X::expand_message(&[msg], &dst, random_bytes.len()).expect("expand_message failed");
625+
expander.fill_bytes(&mut random_bytes);
626+
627+
let p = NonZero::new(U384::from_be_hex("000000000000000000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed")).expect("NonZero::new failed");
628+
let u0 = U384::from_be_bytes(
629+
<[u8; 48]>::try_from(&random_bytes[..48]).expect("try_from failed"),
630+
) % p;
631+
let u1 = U384::from_be_bytes(
632+
<[u8; 48]>::try_from(&random_bytes[48..]).expect("try_from failed"),
633+
) % p;
634+
635+
let mut arr = [0u8; 32];
636+
arr.copy_from_slice(&u0.to_le_byte_array()[..32]);
637+
let u0 = FieldElement::from_bytes(&arr);
638+
arr.copy_from_slice(&u1.to_le_byte_array()[..32]);
639+
let u1 = FieldElement::from_bytes(&arr);
640+
641+
let q0 = map_to_edwards(u0);
642+
let q1 = map_to_edwards(u1);
643+
let p = q0 + q1;
644+
p.mul_by_cofactor()
645+
}
646+
}
647+
648+
fn map_to_edwards(e: FieldElement) -> EdwardsPoint {
649+
let (u, v) = elligator_encode(e);
650+
let (x, y) = montgomery_to_edwards(u, v);
651+
affine_to_edwards(x, y)
652+
}
653+
654+
fn elligator_encode(e: FieldElement) -> (FieldElement, FieldElement) {
655+
let mut t1 = &(&FieldElement::ONE + &FieldElement::ONE) * &e.square(); // 2u^2
656+
let e1 = t1.ct_eq(&FieldElement::MINUS_ONE);
657+
t1.conditional_assign(&FieldElement::ZERO, e1); // if 2u^2 == -1, t1 = 0
658+
let x1 = &(&t1 + &FieldElement::ONE).invert() * &FieldElement::EDWARDS_MINUS_ELL_A; // -A / t1 + 1
659+
let min_x1 = -(&x1);
660+
661+
let gx1 = &(&(&(&x1 + &FieldElement::EDWARDS_ELL_A) * &x1) + &FieldElement::ONE) * &x1; // x1 * (x1 * (x1 + A) + 1)
662+
let x2 = &min_x1 - &FieldElement::EDWARDS_ELL_A; // -x1 - A
663+
let gx2 = &t1 * &gx1;
664+
let (is_square, root1) = FieldElement::sqrt_ratio_i(&gx1, &FieldElement::ONE);
665+
let neg_root1 = -(&root1);
666+
let (_, root2) = FieldElement::sqrt_ratio_i(&gx2, &FieldElement::ONE);
667+
668+
let x = FieldElement::conditional_select(&x2, &x1, is_square);
669+
let y = FieldElement::conditional_select(&root2, &neg_root1, is_square);
670+
(x, y)
671+
}
672+
673+
fn montgomery_to_edwards(u: FieldElement, v: FieldElement) -> (FieldElement, FieldElement) {
674+
let inv_sqr_d = FieldElement::from_bytes(&[
675+
6, 126, 69, 255, 170, 4, 110, 204, 130, 26, 125, 75, 209, 211, 161, 197, 126, 79, 252, 3,
676+
220, 8, 123, 210, 187, 6, 160, 96, 244, 237, 38, 15,
677+
]);
678+
let x = &(&v.invert() * &u) * &inv_sqr_d;
679+
let u1 = &u - &FieldElement::ONE;
680+
let u2 = &u + &FieldElement::ONE;
681+
let y = &u1 * &u2.invert();
682+
(x, y)
683+
}
684+
685+
fn affine_to_edwards(x: FieldElement, y: FieldElement) -> EdwardsPoint {
686+
let t = &x * &y;
687+
EdwardsPoint {
688+
X: x,
689+
Y: y,
690+
Z: FieldElement::ONE,
691+
T: t,
692+
}
607693
}
608694

609695
// ------------------------------------------------------------------------
@@ -2267,4 +2353,25 @@ mod test {
22672353
assert_eq!(point.compress().to_bytes(), output[..]);
22682354
}
22692355
}
2356+
2357+
#[cfg(feature = "group")]
2358+
#[test]
2359+
fn hash_to_curve() {
2360+
const DST: &[u8] = b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_";
2361+
let msgs: [(&[u8], &str); 5] = [
2362+
(b"", "09a6c8561a0b22bef63124c588ce4c62ea83a3c899763af26d795302e115dc21"),
2363+
(b"abc", "9a8395b88338f22e435bbd301183e7f20a5f9de643f11882fb237f88268a5531"),
2364+
(b"abcdef0123456789", "53060a3d140e7fbcda641ed3cf42c88a75411e648a1add71217f70ea8ec561a6"),
2365+
(b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "2eca15e355fcfa39d2982f67ddb0eea138e2994f5956ed37b7f72eea5e89d2f7"),
2366+
(b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "6dc2fc04f266c5c27f236a80b14f92ccd051ef1ff027f26a07f8c0f327d8f995"),
2367+
];
2368+
for (input, expected_hex) in msgs {
2369+
let pt = EdwardsPoint::hash_to_curve::<
2370+
elliptic_curve::hash2curve::ExpandMsgXmd<sha2::Sha512>,
2371+
>(input, DST);
2372+
let mut expected_bytes = hex::decode(expected_hex).unwrap();
2373+
expected_bytes.reverse();
2374+
assert_eq!(expected_bytes, pt.to_bytes());
2375+
}
2376+
}
22702377
}

0 commit comments

Comments
 (0)