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

add hash_to_curve method for Edwards curves #596

Open
wants to merge 10 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ build*.txt
*.bak

*.s
.idea/
17 changes: 9 additions & 8 deletions curve25519-dalek/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
[package]
name = "curve25519-dalek"
name = "curve25519-dalek-ml"
# Before incrementing:
# - update CHANGELOG
# - update README if required by semver
# - if README was updated, also update module documentation in src/lib.rs
version = "4.1.3"
version = "4.2.2"
edition = "2021"
rust-version = "1.60.0"
authors = ["Isis Lovecruft <[email protected]>",
"Henry de Valence <[email protected]>"]
"Henry de Valence <[email protected]>",
"Michael Lodder <[email protected]>"]
readme = "README.md"
license = "BSD-3-Clause"
repository = "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/curve25519-dalek"
repository = "https://github.com/mikelodder7/curve25519-dalek-ml/tree/main/curve25519-dalek"
homepage = "https://github.com/dalek-cryptography/curve25519-dalek"
documentation = "https://docs.rs/curve25519-dalek"
documentation = "https://docs.rs/curve25519-dalek-ml"
categories = ["cryptography", "no-std"]
keywords = ["cryptography", "crypto", "ristretto", "curve25519", "ristretto255"]
description = "A pure-Rust implementation of group operations on ristretto255 and Curve25519"
Expand Down Expand Up @@ -47,6 +47,7 @@ required-features = ["alloc", "rand_core"]

[dependencies]
cfg-if = "1"
elliptic-curve = { version = "0.13", features = ["hash2curve"], optional = true }
ff = { version = "0.13", default-features = false, optional = true }
group = { version = "0.13", default-features = false, optional = true }
rand_core = { version = "0.6.4", default-features = false, optional = true }
Expand All @@ -62,11 +63,11 @@ cpufeatures = "0.2.6"
fiat-crypto = { version = "0.2.1", default-features = false }

[features]
default = ["alloc", "precomputed-tables", "zeroize"]
default = ["alloc", "group", "precomputed-tables", "zeroize"]
alloc = ["zeroize?/alloc"]
precomputed-tables = []
legacy_compatibility = []
group = ["dep:group", "rand_core"]
group = ["dep:group", "dep:elliptic-curve", "rand_core"]
group-bits = ["group", "ff/bits"]

[target.'cfg(all(not(curve25519_dalek_backend = "fiat"), not(curve25519_dalek_backend = "serial"), target_arch = "x86_64"))'.dependencies]
Expand Down
3 changes: 3 additions & 0 deletions curve25519-dalek/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ prime-order group from a non-prime-order Edwards curve. This provides the
speed and safety benefits of Edwards curve arithmetic, without the pitfalls of
cofactor-related abstraction mismatches.

**NOTE** The main difference with this crate and [curve25519-dalek](https://docs.rs/curve25519-dalek) is this adds
hash_to_curve for Edwards curves.

# Use

## Stable
Expand Down
16 changes: 16 additions & 0 deletions curve25519-dalek/src/backend/serial/fiat_u32/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,22 @@ impl FieldElement2625 {
FieldElement2625(fiat_25519_tight_field_element(limbs))
}

/// Elligator2 constant for Edwards Curve
pub const EDWARDS_ELL_A: FieldElement2625 =
FieldElement2625::from_limbs([486662, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
/// Elligator2 constant for Edwards Curve
pub const EDWARDS_MINUS_ELL_A: FieldElement2625 = FieldElement2625::from_limbs([
0x03f892e7, 0x01ffffff, 0x03ffffff, 0x01ffffff, 0x03ffffff, 0x01ffffff, 0x03ffffff,
0x01ffffff, 0x03ffffff, 0x01ffffff,
]);
/// 1/sqrt(D) used for converting a montgomery point to edwards
pub const MONTGOMERY_TO_EDWARDS_INV_SQRT_D: FieldElement2625 = FieldElement2625::from_limbs([
0x03457e06, 0x01812abf, 0x0350598d, 0x08a5be8, 0x0316874f, 0x01fc4f7e, 0x01846e01,
0x00d77a4f, 0x03460a00, 0x003c9bb7,
]);
/// 2^192
pub const F_2_192: FieldElement2625 =
FieldElement2625::from_limbs([0, 0, 0, 0, 0, 0, 0, 0x2000, 0, 0]);
/// The scalar \\( 0 \\).
pub const ZERO: FieldElement2625 = FieldElement2625::from_limbs([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
/// The scalar \\( 1 \\).
Expand Down
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 @@ -172,6 +172,26 @@ impl FieldElement51 {
FieldElement51(fiat_25519_tight_field_element(limbs))
}

/// Elligator2 constant for Edwards Curve
pub const EDWARDS_ELL_A: FieldElement51 = FieldElement51::from_limbs([486662, 0, 0, 0, 0]);
/// Elligator2 constant for Edwards Curve
pub const EDWARDS_MINUS_ELL_A: FieldElement51 = FieldElement51::from_limbs([
0x7fffffff892e7,
0x7ffffffffffff,
0x7ffffffffffff,
0x7ffffffffffff,
0x7ffffffffffff,
]);
/// 1/sqrt(D) used for converting a montgomery point to edwards
pub const MONTGOMERY_TO_EDWARDS_INV_SQRT_D: FieldElement51 = FieldElement51::from_limbs([
0x604aaff457e06,
0x2296fa350598d,
0x7f13dfb16874f,
0x35de93d846e01,
0x0f26edf460a00,
]);
/// 2^192
pub const F_2_192: FieldElement51 = FieldElement51::from_limbs([0, 0, 0, 0x0008000000000, 0]);
/// The scalar \\( 0 \\).
pub const ZERO: FieldElement51 = FieldElement51::from_limbs([0, 0, 0, 0, 0]);
/// The scalar \\( 1 \\).
Expand Down
16 changes: 16 additions & 0 deletions curve25519-dalek/src/backend/serial/u32/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,22 @@ impl FieldElement2625 {
FieldElement2625(limbs)
}

/// Elligator2 constant for Edwards Curve
pub const EDWARDS_ELL_A: FieldElement2625 =
FieldElement2625::from_limbs([486662, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
/// Elligator2 constant for Edwards Curve
pub const EDWARDS_MINUS_ELL_A: FieldElement2625 = FieldElement2625::from_limbs([
0x03f892e7, 0x01ffffff, 0x03ffffff, 0x01ffffff, 0x03ffffff, 0x01ffffff, 0x03ffffff,
0x01ffffff, 0x03ffffff, 0x01ffffff,
]);
/// 1/sqrt(D) used for converting a montgomery point to edwards
pub const MONTGOMERY_TO_EDWARDS_INV_SQRT_D: FieldElement2625 = FieldElement2625::from_limbs([
0x03457e06, 0x01812abf, 0x0350598d, 0x08a5be8, 0x0316874f, 0x01fc4f7e, 0x01846e01,
0x00d77a4f, 0x03460a00, 0x003c9bb7,
]);
/// 2^192
pub const F_2_192: FieldElement2625 =
FieldElement2625::from_limbs([0, 0, 0, 0, 0, 0, 0, 0x2000, 0, 0]);
/// The scalar \\( 0 \\).
pub const ZERO: FieldElement2625 = FieldElement2625::from_limbs([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
/// The scalar \\( 1 \\).
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 @@ -259,6 +259,26 @@ impl FieldElement51 {
FieldElement51(limbs)
}

/// Elligator2 constant for Edwards Curve
pub const EDWARDS_ELL_A: FieldElement51 = FieldElement51::from_limbs([486662, 0, 0, 0, 0]);
/// Elligator2 constant for Edwards Curve
pub const EDWARDS_MINUS_ELL_A: FieldElement51 = FieldElement51::from_limbs([
0x7fffffff892e7,
0x7ffffffffffff,
0x7ffffffffffff,
0x7ffffffffffff,
0x7ffffffffffff,
]);
/// 1/sqrt(D) used for converting a montgomery point to edwards
pub const MONTGOMERY_TO_EDWARDS_INV_SQRT_D: FieldElement51 = FieldElement51::from_limbs([
0x604aaff457e06,
0x2296fa350598d,
0x7f13dfb16874f,
0x35de93d846e01,
0x0f26edf460a00,
]);
/// 2^192
pub const F_2_192: FieldElement51 = FieldElement51::from_limbs([0, 0, 0, 0x0008000000000, 0]);
/// The scalar \\( 0 \\).
pub const ZERO: FieldElement51 = FieldElement51::from_limbs([0, 0, 0, 0, 0]);
/// The scalar \\( 1 \\).
Expand Down
102 changes: 97 additions & 5 deletions curve25519-dalek/src/edwards.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// This file is part of curve25519-dalek_ml.
// Copyright (c) 2016-2021 isis lovecruft
// Copyright (c) 2016-2020 Henry de Valence
// See LICENSE for licensing information.
Expand Down Expand Up @@ -85,7 +85,7 @@
//! successful decompression of a compressed point, or else by
//! operations on other (valid) `EdwardsPoint`s.
//!
//! [curve_models]: https://docs.rs/curve25519-dalek/latest/curve25519-dalek/backend/serial/curve_models/index.html
//! [curve_models]: https://docs.rs/curve25519-dalek_ml/latest/curve25519-dalek/backend/serial/curve_models/index.html

// We allow non snake_case names because coordinates in projective space are
// traditionally denoted by the capitalisation of their respective
Expand Down Expand Up @@ -605,6 +605,77 @@ impl EdwardsPoint {
.expect("Montgomery conversion to Edwards point in Elligator failed")
.mul_by_cofactor()
}

#[cfg(feature = "group")]
/// Maps the input bytes to the curve. This implements the spec for
/// [`hash_to_curve`](https://datatracker.ietf.org/doc/rfc9380/) according to sections
/// 8.5 and J.5
pub fn hash_to_curve<X>(msg: &[u8], dst: &[u8]) -> Self
where
X: for<'a> elliptic_curve::hash2curve::ExpandMsg<'a>,
{
use elliptic_curve::hash2curve::Expander;

let dst = [dst];
let mut random_bytes = [0u8; 96];
let mut expander =
X::expand_message(&[msg], &dst, random_bytes.len()).expect("expand_message failed");
expander.fill_bytes(&mut random_bytes);

let u0 = FieldElement::from_xmd_bytes_mod_order(&random_bytes[..48]);
let u1 = FieldElement::from_xmd_bytes_mod_order(&random_bytes[48..]);

let q0 = map_to_edwards(u0);
let q1 = map_to_edwards(u1);
let p = q0 + q1;
p.mul_by_cofactor()
}
}

#[cfg(feature = "group")]
fn map_to_edwards(e: FieldElement) -> EdwardsPoint {
let (u, v) = elligator_encode(e);
let (x, y) = montgomery_to_edwards(u, v);
affine_to_edwards(x, y)
}

#[cfg(feature = "group")]
fn elligator_encode(e: FieldElement) -> (FieldElement, FieldElement) {
let mut t1 = &(&FieldElement::ONE + &FieldElement::ONE) * &e.square(); // 2u^2
let e1 = t1.ct_eq(&FieldElement::MINUS_ONE);
t1.conditional_assign(&FieldElement::ZERO, e1); // if 2u^2 == -1, t1 = 0
let x1 = &(&t1 + &FieldElement::ONE).invert() * &FieldElement::EDWARDS_MINUS_ELL_A; // -A / t1 + 1
let min_x1 = -(&x1);

let gx1 = &(&(&(&x1 + &FieldElement::EDWARDS_ELL_A) * &x1) + &FieldElement::ONE) * &x1; // x1 * (x1 * (x1 + A) + 1)
let x2 = &min_x1 - &FieldElement::EDWARDS_ELL_A; // -x1 - A
let gx2 = &t1 * &gx1;
let (is_square, root1) = FieldElement::sqrt_ratio_i(&gx1, &FieldElement::ONE);
let neg_root1 = -(&root1);
let (_, root2) = FieldElement::sqrt_ratio_i(&gx2, &FieldElement::ONE);

let x = FieldElement::conditional_select(&x2, &x1, is_square);
let y = FieldElement::conditional_select(&root2, &neg_root1, is_square);
(x, y)
}

#[cfg(feature = "group")]
fn montgomery_to_edwards(u: FieldElement, v: FieldElement) -> (FieldElement, FieldElement) {
let x = &(&v.invert() * &u) * &FieldElement::MONTGOMERY_TO_EDWARDS_INV_SQRT_D;
let u1 = &u - &FieldElement::ONE;
let u2 = &u + &FieldElement::ONE;
let y = &u1 * &u2.invert();
(x, y)
}

#[cfg(feature = "group")]
fn affine_to_edwards(x: FieldElement, y: FieldElement) -> EdwardsPoint {
EdwardsPoint {
X: x,
Y: y,
Z: FieldElement::ONE,
T: &x * &y,
}
}

// ------------------------------------------------------------------------
Expand Down Expand Up @@ -1211,7 +1282,7 @@ impl EdwardsPoint {
/// # Example
///
/// ```
/// use curve25519_dalek::constants;
/// use curve25519_dalek_ml::constants;
///
/// // Generator of the prime-order subgroup
/// let P = constants::ED25519_BASEPOINT_POINT;
Expand Down Expand Up @@ -1241,7 +1312,7 @@ impl EdwardsPoint {
/// # Example
///
/// ```
/// use curve25519_dalek::constants;
/// use curve25519_dalek_ml::constants;
///
/// // Generator of the prime-order subgroup
/// let P = constants::ED25519_BASEPOINT_POINT;
Expand Down Expand Up @@ -1335,7 +1406,7 @@ impl GroupEncoding for EdwardsPoint {
/// A `SubgroupPoint` represents a point on the Edwards form of Curve25519, that is
/// guaranteed to be in the prime-order subgroup.
#[cfg(feature = "group")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
pub struct SubgroupPoint(EdwardsPoint);

#[cfg(feature = "group")]
Expand Down Expand Up @@ -2268,4 +2339,25 @@ mod test {
assert_eq!(point.compress().to_bytes(), output[..]);
}
}

#[cfg(feature = "group")]
#[test]
fn hash_to_curve() {
const DST: &[u8] = b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_";
let msgs: [(&[u8], &str); 5] = [
(b"", "09a6c8561a0b22bef63124c588ce4c62ea83a3c899763af26d795302e115dc21"),
(b"abc", "9a8395b88338f22e435bbd301183e7f20a5f9de643f11882fb237f88268a5531"),
(b"abcdef0123456789", "53060a3d140e7fbcda641ed3cf42c88a75411e648a1add71217f70ea8ec561a6"),
(b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "2eca15e355fcfa39d2982f67ddb0eea138e2994f5956ed37b7f72eea5e89d2f7"),
(b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "6dc2fc04f266c5c27f236a80b14f92ccd051ef1ff027f26a07f8c0f327d8f995"),
];
for (input, expected_hex) in msgs {
let pt = EdwardsPoint::hash_to_curve::<
elliptic_curve::hash2curve::ExpandMsgXmd<sha2::Sha512>,
>(input, DST);
let mut expected_bytes = hex::decode(expected_hex).unwrap();
expected_bytes.reverse();
assert_eq!(expected_bytes, pt.to_bytes());
}
}
}
23 changes: 23 additions & 0 deletions curve25519-dalek/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,29 @@ impl FieldElement {
pub(crate) fn invsqrt(&self) -> (Choice, FieldElement) {
FieldElement::sqrt_ratio_i(&FieldElement::ONE, self)
}

#[cfg(feature = "group")]
/// Handle 48 bytes like a big integer and reduce mod order
/// i.e. big_int(48 bytes) % p
/// but without using any reduce methods
pub(crate) fn from_xmd_bytes_mod_order(bytes: &[u8]) -> FieldElement {
assert_eq!(bytes.len(), 48);
// XMD output is expected to be big endian but FieldElement51
// expects bytes to be little endian.
// Break the array in half with the 1st half as the hi value
// and the 2nd half as the lo value
let mut arr = [0u8; 32];
for i in 0..24 {
arr[i] = bytes[23 - i];
}
let mut hi = FieldElement::from_bytes(&arr);
for i in 0..24 {
arr[i] = bytes[47 - i];
}
let lo = FieldElement::from_bytes(&arr);
hi *= &FieldElement::F_2_192;
&hi + &lo
}
}

#[cfg(test)]
Expand Down
18 changes: 9 additions & 9 deletions curve25519-dalek/src/ristretto.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// This file is part of curve25519-dalek_ml.
// Copyright (c) 2016-2021 isis lovecruft
// Copyright (c) 2016-2020 Henry de Valence
// See LICENSE for licensing information.
Expand Down Expand Up @@ -56,7 +56,7 @@
//! [Why Ristretto?][why_ristretto] section of the Ristretto website.
//!
//! Ristretto
//! points are provided in `curve25519-dalek` by the `RistrettoPoint`
//! points are provided in `curve25519-dalek_ml` by the `RistrettoPoint`
//! struct.
//!
//! ## Encoding and Decoding
Expand Down Expand Up @@ -531,7 +531,7 @@ impl RistrettoPoint {
///
#[cfg_attr(feature = "rand_core", doc = "```")]
#[cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
/// # use curve25519_dalek::ristretto::RistrettoPoint;
/// # use curve25519_dalek_ml::ristretto::RistrettoPoint;
/// use rand_core::OsRng;
///
/// # // Need fn main() here in comment so the doctest compiles
Expand Down Expand Up @@ -735,7 +735,7 @@ impl RistrettoPoint {
///
#[cfg_attr(feature = "digest", doc = "```")]
#[cfg_attr(not(feature = "digest"), doc = "```ignore")]
/// # use curve25519_dalek::ristretto::RistrettoPoint;
/// # use curve25519_dalek_ml::ristretto::RistrettoPoint;
/// use sha2::Sha512;
///
/// # // Need fn main() here in comment so the doctest compiles
Expand Down Expand Up @@ -1070,8 +1070,8 @@ impl RistrettoPoint {
/// A precomputed table of multiples of the Ristretto basepoint is
/// available in the `constants` module:
/// ```
/// use curve25519_dalek::constants::RISTRETTO_BASEPOINT_TABLE;
/// use curve25519_dalek::scalar::Scalar;
/// use curve25519_dalek_ml::constants::RISTRETTO_BASEPOINT_TABLE;
/// use curve25519_dalek_ml::scalar::Scalar;
///
/// let a = Scalar::from(87329482u64);
/// let P = &a * RISTRETTO_BASEPOINT_TABLE;
Expand Down Expand Up @@ -1125,9 +1125,9 @@ impl ConditionallySelectable for RistrettoPoint {
/// use subtle::ConditionallySelectable;
/// use subtle::Choice;
/// #
/// # use curve25519_dalek::traits::Identity;
/// # use curve25519_dalek::ristretto::RistrettoPoint;
/// # use curve25519_dalek::constants;
/// # use curve25519_dalek_ml::traits::Identity;
/// # use curve25519_dalek_ml::ristretto::RistrettoPoint;
/// # use curve25519_dalek_ml::constants;
/// # fn main() {
///
/// let A = RistrettoPoint::identity();
Expand Down
Loading
Loading