Skip to content

Commit 6da7e75

Browse files
committed
Implement hash2curve for Curve448
1 parent 89d7a75 commit 6da7e75

File tree

5 files changed

+89
-7
lines changed

5 files changed

+89
-7
lines changed

ed448-goldilocks/src/field/element.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
44

55
use super::{ConstMontyType, MODULUS};
66
use crate::{
7-
AffinePoint, Decaf448, DecafPoint, Ed448, EdwardsPoint, MontgomeryPoint,
8-
curve::twedwards::extended::ExtendedPoint as TwistedExtendedPoint,
7+
AffinePoint, Curve448, Decaf448, DecafPoint, Ed448, EdwardsPoint, MontgomeryPoint,
8+
ProjectiveMontgomeryPoint, curve::twedwards::extended::ExtendedPoint as TwistedExtendedPoint,
99
};
1010
use elliptic_curve::ops::Reduce;
1111
use elliptic_curve::{
@@ -272,6 +272,16 @@ impl Field for FieldElement {
272272
}
273273
}
274274

275+
impl MapToCurve for Curve448 {
276+
type SecurityLevel = U28;
277+
type FieldElement = FieldElement;
278+
type Length = U84;
279+
280+
fn map_to_curve(element: FieldElement) -> ProjectiveMontgomeryPoint {
281+
element.map_to_curve_elligator2_curve448().into()
282+
}
283+
}
284+
275285
impl FieldElement {
276286
pub const A_PLUS_TWO_OVER_FOUR: Self = Self(ConstMontyType::new(&U448::from_be_hex(
277287
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000098aa",

ed448-goldilocks/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,10 @@ impl CurveArithmetic for Curve448 {
222222
type ProjectivePoint = ProjectiveMontgomeryPoint;
223223
type Scalar = MontgomeryScalar;
224224
}
225+
226+
impl GroupDigest for Curve448 {
227+
const HASH_TO_CURVE_ID: &[u8] = b"curve448_XOF:SHAKE256_ELL2_RO_";
228+
const ENCODE_TO_CURVE_ID: &[u8] = b"curve448_XOF:SHAKE256_ELL2_NU_";
229+
230+
type ExpandMsg = ExpandMsgXof<Shake256>;
231+
}

ed448-goldilocks/src/montgomery/ops.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -308,17 +308,15 @@ where
308308

309309
#[cfg(test)]
310310
mod test {
311-
use elliptic_curve::Field;
311+
use elliptic_curve::Group;
312312
use rand_core::OsRng;
313313

314314
use super::*;
315315

316316
#[test]
317317
fn mixed_addition() {
318-
let p1 = ProjectiveMontgomeryPoint::GENERATOR
319-
* MontgomeryScalar::try_from_rng(&mut OsRng).unwrap();
320-
let p2 = ProjectiveMontgomeryPoint::GENERATOR
321-
* MontgomeryScalar::try_from_rng(&mut OsRng).unwrap();
318+
let p1 = ProjectiveMontgomeryPoint::try_from_rng(&mut OsRng).unwrap();
319+
let p2 = ProjectiveMontgomeryPoint::try_from_rng(&mut OsRng).unwrap();
322320
let p3 = p1 + p2;
323321

324322
assert_eq!(p3.to_affine(), (p1.to_affine() + p2).into());

ed448-goldilocks/src/montgomery/point.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,8 @@ mod tests {
482482
use crate::field::MODULUS;
483483
use elliptic_curve::bigint::modular::ConstMontyParams;
484484
use elliptic_curve::bigint::{ArrayEncoding, CheckedSub, Uint};
485+
use hash2curve::GroupDigest;
486+
use hex_literal::hex;
485487

486488
#[test]
487489
fn decode() {
@@ -574,4 +576,52 @@ mod tests {
574576

575577
assert_eq!(identity, x_identity);
576578
}
579+
580+
#[test]
581+
fn hash_with_test_vectors() {
582+
const DST: &[u8] = b"QUUX-V01-CS02-with-curve448_XOF:SHAKE256_ELL2_RO_";
583+
const MSGS: &[(&[u8], [u8; 56], [u8; 56])] = &[
584+
(b"", hex!("5ea5ff623d27c75e73717514134e73e419f831a875ca9e82915fdfc7069d0a9f8b532cfb32b1d8dd04ddeedbe3fa1d0d681c01e825d6a9ea"), hex!("afadd8de789f8f8e3516efbbe313a7eba364c939ecba00dabf4ced5c563b18e70a284c17d8f46b564c4e6ce11784a3825d941116622128c1")),
585+
(b"abc", hex!("9b2f7ce34878d7cebf34c582db14958308ea09366d1ec71f646411d3de0ae564d082b06f40cd30dfc08d9fb7cb21df390cf207806ad9d0e4"), hex!("138a0eef0a4993ea696152ed7db61f7ddb4e8100573591e7466d61c0c568ecaec939e36a84d276f34c402526d8989a96e99760c4869ed633")),
586+
(b"abcdef0123456789", hex!("f54ecd14b85a50eeeee0618452df3a75be7bfba11da5118774ae4ea55ac204e153f77285d780c4acee6c96abe3577a0c0b00be6e790cf194"), hex!("935247a64bf78c107069943c7e3ecc52acb27ce4a3230407c8357341685ea2152e8c3da93f8cd77da1bddb5bb759c6e7ae7d516dced42850")),
587+
(b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", hex!("5bd67c4f88adf6beb10f7e0d0054659776a55c97b809ec8b3101729e104fd0f684e103792f267fd87cc4afc25a073956ef4f268fb02824d5"), hex!("da1f5cb16a352719e4cb064cf47ba72aeba7752d03e8ca2c56229f419b4ef378785a5af1a53dd7ab4d467c1f92f7b139b3752faf29c96432")),
588+
(b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", hex!("ea441c10b3636ecedd5c0dfcae96384cc40de8390a0ab648765b4508da12c586d55dc981275776507ebca0e4d1bcaa302bb69dcfa31b3451"), hex!("fee0192d49bcc0c28d954763c2cbe739b9265c4bebe3883803c64971220cfda60b9ac99ad986cd908c0534b260b5cfca46f6c2b0f3f21bda")),
589+
];
590+
591+
for (msg, x, y) in MSGS {
592+
let p = Curve448::hash_from_bytes(msg, DST).unwrap().to_affine();
593+
let mut xx = [0u8; 56];
594+
xx.copy_from_slice(&x[..]);
595+
xx.reverse();
596+
let mut yy = [0u8; 56];
597+
yy.copy_from_slice(&y[..]);
598+
yy.reverse();
599+
assert_eq!(p.x(), xx);
600+
assert_eq!(p.y(), yy);
601+
}
602+
}
603+
604+
#[test]
605+
fn encode_with_test_vectors() {
606+
const DST: &[u8] = b"QUUX-V01-CS02-with-curve448_XOF:SHAKE256_ELL2_NU_";
607+
const MSGS: &[(&[u8], [u8; 56], [u8; 56])] = &[
608+
(b"", hex!("b65e8dbb279fd656f926f68d463b13ca7a982b32f5da9c7cc58afcf6199e4729863fb75ca9ae3c95c6887d95a5102637a1c5c40ff0aafadc"), hex!("ea1ea211cf29eca11c057fe8248181591a19f6ac51d45843a65d4bb8b71bc83a64c771ed7686218a278ef1c5d620f3d26b53162188645453")),
609+
(b"abc", hex!("51aceca4fa95854bbaba58d8a5e17a86c07acadef32e1188cafda26232131800002cc2f27c7aec454e5e0c615bddffb7df6a5f7f0f14793f"), hex!("c590c9246eb28b08dee816d608ef233ea5d76e305dc458774a1e1bd880387e6734219e2018e4aa50a49486dce0ba8740065da37e6cf5212c")),
610+
(b"abcdef0123456789", hex!("c6d65987f146b8d0cb5d2c44e1872ac3af1f458f6a8bd8c232ffe8b9d09496229a5a27f350eb7d97305bcc4e0f38328718352e8e3129ed71"), hex!("4d2f901bf333fdc4135b954f20d59207e9f6a4ecf88ce5af11c892b44f79766ec4ecc9f60d669b95ca8940f39b1b7044140ac2040c1bf659")),
611+
(b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", hex!("9b8d008863beb4a02fb9e4efefd2eba867307fb1c7ce01746115d32e1db551bb254e8e3e4532d5c74a83949a69a60519ecc9178083cbe943"), hex!("346a1fca454d1e67c628437c270ec0f0c4256bb774fe6c0e49de7004ff6d9199e2cd99d8f7575a96aafc4dc8db1811ba0a44317581f41371")),
612+
(b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", hex!("8746dc34799112d1f20acda9d7f722c9abb29b1fb6b7e9e566983843c20bd7c9bfad21b45c5166b808d2f5d44e188f1fdaf29cdee8a72e4c"), hex!("7c1293484c9287c298a1a0600c64347eee8530acf563cd8705e05728274d8cd8101835f8003b6f3b78b5beb28f5be188a3d7bce1ec5a36b1")),
613+
];
614+
615+
for (msg, x, y) in MSGS {
616+
let p = Curve448::encode_from_bytes(msg, DST).unwrap().to_affine();
617+
let mut xx = [0u8; 56];
618+
xx.copy_from_slice(&x[..]);
619+
xx.reverse();
620+
let mut yy = [0u8; 56];
621+
yy.copy_from_slice(&y[..]);
622+
yy.reverse();
623+
assert_eq!(p.x(), xx);
624+
assert_eq!(p.y(), yy);
625+
}
626+
}
577627
}

ed448-goldilocks/src/montgomery/scalar.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ impl Reduce<Array<u8, U84>> for MontgomeryScalar {
7575
#[cfg(test)]
7676
mod test {
7777
use super::*;
78+
use elliptic_curve::PrimeField;
79+
use hash2curve::ExpandMsgXof;
7880
use hex_literal::hex;
81+
use sha3::Shake256;
7982

8083
#[test]
8184
fn test_basic_add() {
@@ -266,4 +269,18 @@ mod test {
266269
assert!(res.is_ok());
267270
assert_eq!(res.unwrap(), MontgomeryScalar::TWO_INV);
268271
}
272+
273+
#[test]
274+
fn scalar_hash() {
275+
let msg = b"hello world";
276+
let res = hash2curve::hash_to_scalar::<Curve448, ExpandMsgXof<Shake256>, U84>(
277+
&[msg],
278+
&[b"test DST"],
279+
)
280+
.unwrap();
281+
let expected: [u8; 56] = hex_literal::hex!(
282+
"1db46e2f81d60ff23cc532d371e0c0aa3956746ca7d57c0089da8e313f5fdc770a846ea9932cc2f0a6aa59bfb94af97a402f0317add21c10"
283+
);
284+
assert_eq!(res.to_repr(), Array::from(expected));
285+
}
269286
}

0 commit comments

Comments
 (0)