|
1 | 1 | // use crate::constants::A_PLUS_TWO_OVER_FOUR;
|
2 |
| -use super::{MontgomeryPoint, MontgomeryScalar, ProjectiveMontgomeryPoint}; |
3 |
| -use crate::AffinePoint; |
4 |
| -use crate::field::{ConstMontyType, FieldElement}; |
| 2 | +use super::{ |
| 3 | + DEFAULT_ENCODE_TO_CURVE_SUITE, DEFAULT_HASH_TO_CURVE_SUITE, MontgomeryPoint, MontgomeryScalar, |
| 4 | + ProjectiveMontgomeryPoint, |
| 5 | +}; |
| 6 | +use crate::field::{ConstMontyType, FieldElement, FieldElementU84}; |
| 7 | +use crate::{AffinePoint, Curve448}; |
5 | 8 | use core::fmt;
|
6 | 9 | use core::ops::Mul;
|
| 10 | +use elliptic_curve::array::Array; |
7 | 11 | use elliptic_curve::bigint::U448;
|
| 12 | +use elliptic_curve::consts::{U28, U84}; |
| 13 | +use hash2curve::{ExpandMsg, ExpandMsgXof, Expander, FromOkm, MapToCurve}; |
| 14 | +use sha3::Shake256; |
8 | 15 | use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq};
|
9 | 16 |
|
10 | 17 | impl MontgomeryXpoint {
|
@@ -277,6 +284,67 @@ impl ProjectiveMontgomeryXpoint {
|
277 | 284 | Self { U, W }
|
278 | 285 | }
|
279 | 286 |
|
| 287 | + /// Hash a message to a point on the curve |
| 288 | + /// |
| 289 | + /// Hash using the default domain separation tag and hash function. |
| 290 | + /// For more control see [`Self::hash()`]. |
| 291 | + pub fn hash_with_defaults(msg: &[u8]) -> Self { |
| 292 | + Self::hash::<ExpandMsgXof<Shake256>>(&[msg], &[DEFAULT_HASH_TO_CURVE_SUITE]) |
| 293 | + } |
| 294 | + |
| 295 | + /// Hash a message to a point on the curve |
| 296 | + /// |
| 297 | + /// Implements hash to curve according |
| 298 | + /// see <https://datatracker.ietf.org/doc/rfc9380/> |
| 299 | + pub fn hash<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Self |
| 300 | + where |
| 301 | + X: ExpandMsg<U28>, |
| 302 | + { |
| 303 | + let mut expander = |
| 304 | + X::expand_message(msg, dst, (84 * 2).try_into().expect("should never fail")) |
| 305 | + .expect("should never fail with the given `ExpandMsg` and `dst`"); |
| 306 | + let mut data = Array::<u8, U84>::default(); |
| 307 | + expander.fill_bytes(&mut data); |
| 308 | + let u0 = FieldElementU84::from_okm(&data).0; |
| 309 | + expander.fill_bytes(&mut data); |
| 310 | + let u1 = FieldElementU84::from_okm(&data).0; |
| 311 | + |
| 312 | + let q0 = Curve448::map_to_curve(FieldElementU84(u0)); |
| 313 | + let q1 = Curve448::map_to_curve(FieldElementU84(u1)); |
| 314 | + |
| 315 | + (q0 + q1).to_projective_x().double().double() |
| 316 | + } |
| 317 | + |
| 318 | + /// Encode a message to a point on the curve |
| 319 | + /// |
| 320 | + /// Encode using the default domain separation tag and hash function. |
| 321 | + /// For more control see [`Self::encode()`]. |
| 322 | + pub fn encode_with_defaults(msg: &[u8]) -> Self { |
| 323 | + Self::encode::<ExpandMsgXof<Shake256>>(&[msg], &[DEFAULT_ENCODE_TO_CURVE_SUITE]) |
| 324 | + } |
| 325 | + |
| 326 | + /// Encode a message to a point on the curve |
| 327 | + /// |
| 328 | + /// Implements encode to curve according |
| 329 | + /// see <https://datatracker.ietf.org/doc/rfc9380/> |
| 330 | + pub fn encode<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Self |
| 331 | + where |
| 332 | + X: ExpandMsg<U28>, |
| 333 | + { |
| 334 | + let mut expander = X::expand_message(msg, dst, 84.try_into().expect("should never fail")) |
| 335 | + .expect("should never fail with the given `ExpandMsg` and `dst`"); |
| 336 | + let mut data = Array::<u8, U84>::default(); |
| 337 | + expander.fill_bytes(&mut data); |
| 338 | + let u = FieldElementU84::from_okm(&data).0; |
| 339 | + |
| 340 | + Self { |
| 341 | + U: u.map_to_curve_elligator2_curve448_x(), |
| 342 | + W: FieldElement::ONE, |
| 343 | + } |
| 344 | + .double() |
| 345 | + .double() |
| 346 | + } |
| 347 | + |
280 | 348 | /// Convert the point to affine form
|
281 | 349 | pub fn to_affine(&self) -> MontgomeryXpoint {
|
282 | 350 | let x = self.U * self.W.invert();
|
@@ -307,6 +375,8 @@ mod tests {
|
307 | 375 | use super::*;
|
308 | 376 | use crate::EdwardsPoint;
|
309 | 377 | use elliptic_curve::CurveGroup;
|
| 378 | + use hex_literal::hex; |
| 379 | + use sha3::Shake256; |
310 | 380 |
|
311 | 381 | #[test]
|
312 | 382 | fn test_montgomery_edwards() {
|
@@ -338,4 +408,54 @@ mod tests {
|
338 | 408 |
|
339 | 409 | assert_eq!(x_identity.to_extended(Choice::from(1)), identity);
|
340 | 410 | }
|
| 411 | + |
| 412 | + #[test] |
| 413 | + fn hash_with_test_vectors() { |
| 414 | + const DST: &[u8] = b"QUUX-V01-CS02-with-curve448_XOF:SHAKE256_ELL2_RO_"; |
| 415 | + const MSGS: &[(&[u8], [u8; 56], [u8; 56])] = &[ |
| 416 | + (b"", hex!("5ea5ff623d27c75e73717514134e73e419f831a875ca9e82915fdfc7069d0a9f8b532cfb32b1d8dd04ddeedbe3fa1d0d681c01e825d6a9ea"), hex!("afadd8de789f8f8e3516efbbe313a7eba364c939ecba00dabf4ced5c563b18e70a284c17d8f46b564c4e6ce11784a3825d941116622128c1")), |
| 417 | + (b"abc", hex!("9b2f7ce34878d7cebf34c582db14958308ea09366d1ec71f646411d3de0ae564d082b06f40cd30dfc08d9fb7cb21df390cf207806ad9d0e4"), hex!("138a0eef0a4993ea696152ed7db61f7ddb4e8100573591e7466d61c0c568ecaec939e36a84d276f34c402526d8989a96e99760c4869ed633")), |
| 418 | + (b"abcdef0123456789", hex!("f54ecd14b85a50eeeee0618452df3a75be7bfba11da5118774ae4ea55ac204e153f77285d780c4acee6c96abe3577a0c0b00be6e790cf194"), hex!("935247a64bf78c107069943c7e3ecc52acb27ce4a3230407c8357341685ea2152e8c3da93f8cd77da1bddb5bb759c6e7ae7d516dced42850")), |
| 419 | + (b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", hex!("5bd67c4f88adf6beb10f7e0d0054659776a55c97b809ec8b3101729e104fd0f684e103792f267fd87cc4afc25a073956ef4f268fb02824d5"), hex!("da1f5cb16a352719e4cb064cf47ba72aeba7752d03e8ca2c56229f419b4ef378785a5af1a53dd7ab4d467c1f92f7b139b3752faf29c96432")), |
| 420 | + (b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", hex!("ea441c10b3636ecedd5c0dfcae96384cc40de8390a0ab648765b4508da12c586d55dc981275776507ebca0e4d1bcaa302bb69dcfa31b3451"), hex!("fee0192d49bcc0c28d954763c2cbe739b9265c4bebe3883803c64971220cfda60b9ac99ad986cd908c0534b260b5cfca46f6c2b0f3f21bda")), |
| 421 | + ]; |
| 422 | + |
| 423 | + for (msg, x, y) in MSGS { |
| 424 | + let p = ProjectiveMontgomeryXpoint::hash::<ExpandMsgXof<Shake256>>(&[msg], &[DST]) |
| 425 | + .to_affine(); |
| 426 | + let mut xx = [0u8; 56]; |
| 427 | + xx.copy_from_slice(&x[..]); |
| 428 | + xx.reverse(); |
| 429 | + let mut yy = [0u8; 56]; |
| 430 | + yy.copy_from_slice(&y[..]); |
| 431 | + yy.reverse(); |
| 432 | + assert_eq!(p.0, xx); |
| 433 | + assert!(p.y(Choice::from(0)) == yy || p.y(Choice::from(1)) == yy); |
| 434 | + } |
| 435 | + } |
| 436 | + |
| 437 | + #[test] |
| 438 | + fn encode_with_test_vectors() { |
| 439 | + const DST: &[u8] = b"QUUX-V01-CS02-with-curve448_XOF:SHAKE256_ELL2_NU_"; |
| 440 | + const MSGS: &[(&[u8], [u8; 56], [u8; 56])] = &[ |
| 441 | + (b"", hex!("b65e8dbb279fd656f926f68d463b13ca7a982b32f5da9c7cc58afcf6199e4729863fb75ca9ae3c95c6887d95a5102637a1c5c40ff0aafadc"), hex!("ea1ea211cf29eca11c057fe8248181591a19f6ac51d45843a65d4bb8b71bc83a64c771ed7686218a278ef1c5d620f3d26b53162188645453")), |
| 442 | + (b"abc", hex!("51aceca4fa95854bbaba58d8a5e17a86c07acadef32e1188cafda26232131800002cc2f27c7aec454e5e0c615bddffb7df6a5f7f0f14793f"), hex!("c590c9246eb28b08dee816d608ef233ea5d76e305dc458774a1e1bd880387e6734219e2018e4aa50a49486dce0ba8740065da37e6cf5212c")), |
| 443 | + (b"abcdef0123456789", hex!("c6d65987f146b8d0cb5d2c44e1872ac3af1f458f6a8bd8c232ffe8b9d09496229a5a27f350eb7d97305bcc4e0f38328718352e8e3129ed71"), hex!("4d2f901bf333fdc4135b954f20d59207e9f6a4ecf88ce5af11c892b44f79766ec4ecc9f60d669b95ca8940f39b1b7044140ac2040c1bf659")), |
| 444 | + (b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", hex!("9b8d008863beb4a02fb9e4efefd2eba867307fb1c7ce01746115d32e1db551bb254e8e3e4532d5c74a83949a69a60519ecc9178083cbe943"), hex!("346a1fca454d1e67c628437c270ec0f0c4256bb774fe6c0e49de7004ff6d9199e2cd99d8f7575a96aafc4dc8db1811ba0a44317581f41371")), |
| 445 | + (b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", hex!("8746dc34799112d1f20acda9d7f722c9abb29b1fb6b7e9e566983843c20bd7c9bfad21b45c5166b808d2f5d44e188f1fdaf29cdee8a72e4c"), hex!("7c1293484c9287c298a1a0600c64347eee8530acf563cd8705e05728274d8cd8101835f8003b6f3b78b5beb28f5be188a3d7bce1ec5a36b1")), |
| 446 | + ]; |
| 447 | + |
| 448 | + for (msg, x, y) in MSGS { |
| 449 | + let p = ProjectiveMontgomeryXpoint::encode::<ExpandMsgXof<Shake256>>(&[msg], &[DST]) |
| 450 | + .to_affine(); |
| 451 | + let mut xx = [0u8; 56]; |
| 452 | + xx.copy_from_slice(&x[..]); |
| 453 | + xx.reverse(); |
| 454 | + let mut yy = [0u8; 56]; |
| 455 | + yy.copy_from_slice(&y[..]); |
| 456 | + yy.reverse(); |
| 457 | + assert_eq!(p.0, xx); |
| 458 | + assert!(p.y(Choice::from(0)) == yy || p.y(Choice::from(1)) == yy); |
| 459 | + } |
| 460 | + } |
341 | 461 | }
|
0 commit comments