1#![allow(unused_qualifications)]
27
28use cfg_if::cfg_if;
29
30use subtle::Choice;
31use subtle::ConditionallyNegatable;
32use subtle::ConditionallySelectable;
33use subtle::ConstantTimeEq;
34
35use crate::backend;
36use crate::constants;
37
38#[cfg(feature = "digest")]
39use digest::{
40 Digest, FixedOutput, HashMarker,
41 array::{Array, typenum::U64},
42 block_api::BlockSizeUser,
43 typenum::{IsGreater, True},
44};
45
46cfg_if! {
47 if #[cfg(curve25519_dalek_backend = "fiat")] {
48 #[cfg(curve25519_dalek_bits = "32")]
56 pub(crate) type FieldElement = backend::serial::fiat_u32::field::FieldElement2625;
57
58 #[cfg(curve25519_dalek_bits = "64")]
66 pub(crate) type FieldElement = backend::serial::fiat_u64::field::FieldElement51;
67 } else if #[cfg(curve25519_dalek_bits = "64")] {
68 pub(crate) type FieldElement = backend::serial::u64::field::FieldElement51;
74 } else {
75 pub(crate) type FieldElement = backend::serial::u32::field::FieldElement2625;
81 }
82}
83
84impl Eq for FieldElement {}
85
86impl PartialEq for FieldElement {
87 fn eq(&self, other: &FieldElement) -> bool {
88 self.ct_eq(other).into()
89 }
90}
91
92impl ConstantTimeEq for FieldElement {
93 fn ct_eq(&self, other: &FieldElement) -> Choice {
97 self.to_bytes().ct_eq(&other.to_bytes())
98 }
99}
100
101impl Default for FieldElement {
102 fn default() -> Self {
103 FieldElement::ZERO
104 }
105}
106
107impl FieldElement {
108 #[cfg(feature = "digest")]
110 pub(crate) fn from_bytes_wide(bytes: &[u8; 64]) -> Self {
111 let mut fl = [0u8; 32];
112 let mut gl = [0u8; 32];
113 fl.copy_from_slice(&bytes[..32]);
114 gl.copy_from_slice(&bytes[32..]);
115 let fl_top_bit = (fl[31] >> 7) as u16;
118 let gl_top_bit = (gl[31] >> 7) as u16;
119 fl[31] &= 0x7f;
120 gl[31] &= 0x7f;
121
122 let mut fe_f = Self::from_bytes(&fl);
124 let fe_g = Self::from_bytes(&gl);
125
126 let top_bits_sum = {
131 let addend: u16 = fl_top_bit * 19 + gl_top_bit * 722;
133 let mut addend_bytes = [0u8; 32];
134 addend_bytes[..2].copy_from_slice(&addend.to_le_bytes());
135 Self::from_bytes(&addend_bytes)
136 };
137 fe_f += &top_bits_sum;
138
139 const THIRTY_EIGHT: FieldElement = FieldElement::from_bytes(&[
141 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142 0, 0, 0,
143 ]);
144 fe_f += &(&THIRTY_EIGHT * &fe_g);
145
146 fe_f
147 }
148
149 pub(crate) fn is_negative(&self) -> Choice {
157 let bytes = self.to_bytes();
158 (bytes[0] & 1).into()
159 }
160
161 pub(crate) fn is_zero(&self) -> Choice {
167 let zero = [0u8; 32];
168 let bytes = self.to_bytes();
169
170 bytes.ct_eq(&zero)
171 }
172
173 #[rustfmt::skip] fn pow22501(&self) -> (FieldElement, FieldElement) {
177 let t0 = self.square(); let t1 = t0.square().square(); let t2 = self * &t1; let t3 = &t0 * &t2; let t4 = t3.square(); let t5 = &t2 * &t4; let t6 = t5.pow2k(5); let t7 = &t6 * &t5; let t8 = t7.pow2k(10); let t9 = &t8 * &t7; let t10 = t9.pow2k(20); let t11 = &t10 * &t9; let t12 = t11.pow2k(10); let t13 = &t12 * &t7; let t14 = t13.pow2k(50); let t15 = &t14 * &t13; let t16 = t15.pow2k(100); let t17 = &t16 * &t15; let t18 = t17.pow2k(50); let t19 = &t18 * &t13; (t19, t3)
210 }
211
212 pub(crate) fn invert_batch<const N: usize>(inputs: &mut [FieldElement; N]) {
216 let mut scratch = [FieldElement::ONE; N];
217
218 Self::internal_invert_batch(inputs, &mut scratch);
219 }
220
221 #[cfg(feature = "alloc")]
225 pub(crate) fn invert_batch_alloc(inputs: &mut [FieldElement]) {
226 let n = inputs.len();
227 let mut scratch = vec![FieldElement::ONE; n];
228
229 Self::internal_invert_batch(inputs, &mut scratch);
230 }
231
232 fn internal_invert_batch(inputs: &mut [FieldElement], scratch: &mut [FieldElement]) {
240 debug_assert_eq!(inputs.len(), scratch.len());
245
246 let mut acc = FieldElement::ONE;
248
249 for (input, scratch) in inputs.iter().zip(scratch.iter_mut()) {
252 *scratch = acc;
253 acc.conditional_assign(&(&acc * input), !input.is_zero());
255 }
256
257 assert!(bool::from(!acc.is_zero()));
259
260 acc = acc.invert();
262
263 for (input, scratch) in inputs.iter_mut().rev().zip(scratch.iter_mut().rev()) {
266 let tmp = &acc * input;
267 let nz = !input.is_zero();
270 input.conditional_assign(&(&acc * scratch), nz);
271 acc.conditional_assign(&tmp, nz);
272 }
273 }
274
275 #[rustfmt::skip] #[allow(clippy::let_and_return)]
283 pub(crate) fn invert(&self) -> FieldElement {
284 let (t19, t3) = self.pow22501(); let t20 = t19.pow2k(5); let t21 = &t20 * &t3; t21
292 }
293
294 #[rustfmt::skip] #[allow(clippy::let_and_return)]
297 pub(crate) fn pow_p58(&self) -> FieldElement {
298 let (t19, _) = self.pow22501(); let t20 = t19.pow2k(2); let t21 = self * &t20; t21
306 }
307
308 pub(crate) fn sqrt_ratio_i(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) {
321 let v3 = &v.square() * v;
346 let v7 = &v3.square() * v;
347 let mut r = &(u * &v3) * &(u * &v7).pow_p58();
348 let check = v * &r.square();
349
350 let i = &constants::SQRT_M1;
351
352 let correct_sign_sqrt = check.ct_eq(u);
353 let flipped_sign_sqrt = check.ct_eq(&(-u));
354 let flipped_sign_sqrt_i = check.ct_eq(&(&(-u) * i));
355
356 let r_prime = &constants::SQRT_M1 * &r;
357 r.conditional_assign(&r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i);
358
359 let r_is_negative = r.is_negative();
361 r.conditional_negate(r_is_negative);
362
363 let was_nonzero_square = correct_sign_sqrt | flipped_sign_sqrt;
364
365 (was_nonzero_square, r)
366 }
367
368 pub(crate) fn invsqrt(&self) -> (Choice, FieldElement) {
381 FieldElement::sqrt_ratio_i(&FieldElement::ONE, self)
382 }
383
384 #[cfg(feature = "digest")]
385 pub fn hash_to_field<D, const COUNT: usize>(
398 msg: &[&[u8]],
399 domain_sep: &[&[u8]],
400 ) -> [Self; COUNT]
401 where
402 D: BlockSizeUser + Default + FixedOutput<OutputSize = U64> + HashMarker,
403 D::BlockSize: IsGreater<D::OutputSize, Output = True>,
404 {
405 assert!(COUNT == 1 || COUNT == 2);
407
408 let len_in_bytes = COUNT * 48;
410 let mut buf = [0u8; 128];
413 let uniform_bytes = expand_msg_xmd::<D>(msg, domain_sep, &mut buf, len_in_bytes);
417
418 let mut result = [FieldElement::ONE; COUNT];
419 for i in 0..COUNT {
420 let mut bytes_wide = [0u8; 64];
421 bytes_wide[..48].copy_from_slice(&uniform_bytes[48 * i..48 * (i + 1)]);
422 bytes_wide[..48].reverse();
423
424 result[i] = FieldElement::from_bytes_wide(&bytes_wide);
425 }
426
427 result
428 }
429}
430
431#[cfg(feature = "digest")]
440fn expand_msg_xmd<'a, D>(
441 msg: &[&[u8]],
442 domain_sep: &[&[u8]],
443 buf: &'a mut [u8],
444 outlen: usize,
445) -> &'a [u8]
446where
447 D: BlockSizeUser + Default + FixedOutput + HashMarker,
448{
449 use core::iter::once;
450
451 let len_in_bytes = u16::try_from(outlen).expect("outlen must not exceed 65535");
453
454 assert!(buf.len() >= outlen);
455 let ell = u8::try_from((len_in_bytes as usize).div_ceil(D::output_size()))
456 .expect("output length cannot exceed 255 times digest size");
457 let domain_sep_len = u8::try_from(domain_sep.iter().map(|c| c.len()).sum::<usize>())
458 .expect("unexpected overflow from domain separator's size.");
459 assert_ne!(
460 domain_sep_len, 0,
461 "domain separator MUST have nonzero length."
462 );
463
464 let domain_sep_len_slice = &[domain_sep_len][..];
465 let dst_prime = domain_sep.iter().copied().chain(once(domain_sep_len_slice));
466 let z_pad = Array::<u8, D::BlockSize>::default();
467 let l_i_b_str = len_in_bytes.to_be_bytes();
468
469 let msg_prime = once(z_pad.as_slice())
471 .chain(msg.iter().copied())
472 .chain(once(l_i_b_str.as_slice()))
473 .chain(once(&[0u8][..]))
474 .chain(dst_prime.clone());
475 let b_0 = msg_prime
477 .fold(D::new(), |h, slice| h.chain_update(slice))
478 .finalize();
479
480 let b_1_input = once(b_0.as_slice())
482 .chain(once(&[1u8][..]))
483 .chain(dst_prime.clone());
484 let b_1 = b_1_input
485 .fold(D::new(), |h, slice| h.chain_update(slice))
486 .finalize();
487 buf[..D::output_size()].copy_from_slice(&b_1);
489
490 for i in 2..=ell {
491 let i_ = i as usize;
493 let last_b = &buf[(i_ - 2) * D::output_size()..(i_ - 1) * D::output_size()];
494 let mut xor_bs = b_0.clone();
496 xor_bs
497 .as_mut_slice()
498 .iter_mut()
499 .zip(last_b)
500 .for_each(|(l, r)| *l ^= *r);
501
502 let i_slice = &[i][..];
504 let b_i_input = once(xor_bs.as_slice())
505 .chain(once(i_slice))
506 .chain(dst_prime.clone());
507 let b_i = b_i_input
508 .fold(D::new(), |h, slice| h.chain_update(slice))
509 .finalize();
510
511 buf[(i_ - 1) * D::output_size()..i_ * D::output_size()].copy_from_slice(&b_i);
513 }
514
515 &buf[..outlen]
516}
517
518#[cfg(test)]
519mod test {
520 use crate::field::*;
521
522 static A_BYTES: [u8; 32] = [
526 0x04, 0xfe, 0xdf, 0x98, 0xa7, 0xfa, 0x0a, 0x68, 0x84, 0x92, 0xbd, 0x59, 0x08, 0x07, 0xa7,
527 0x03, 0x9e, 0xd1, 0xf6, 0xf2, 0xe1, 0xd9, 0xe2, 0xa4, 0xa4, 0x51, 0x47, 0x36, 0xf3, 0xc3,
528 0xa9, 0x17,
529 ];
530
531 static ASQ_BYTES: [u8; 32] = [
533 0x75, 0x97, 0x24, 0x9e, 0xe6, 0x06, 0xfe, 0xab, 0x24, 0x04, 0x56, 0x68, 0x07, 0x91, 0x2d,
534 0x5d, 0x0b, 0x0f, 0x3f, 0x1c, 0xb2, 0x6e, 0xf2, 0xe2, 0x63, 0x9c, 0x12, 0xba, 0x73, 0x0b,
535 0xe3, 0x62,
536 ];
537
538 static AINV_BYTES: [u8; 32] = [
540 0x96, 0x1b, 0xcd, 0x8d, 0x4d, 0x5e, 0xa2, 0x3a, 0xe9, 0x36, 0x37, 0x93, 0xdb, 0x7b, 0x4d,
541 0x70, 0xb8, 0x0d, 0xc0, 0x55, 0xd0, 0x4c, 0x1d, 0x7b, 0x90, 0x71, 0xd8, 0xe9, 0xb6, 0x18,
542 0xe6, 0x30,
543 ];
544
545 static AP58_BYTES: [u8; 32] = [
547 0x6a, 0x4f, 0x24, 0x89, 0x1f, 0x57, 0x60, 0x36, 0xd0, 0xbe, 0x12, 0x3c, 0x8f, 0xf5, 0xb1,
548 0x59, 0xe0, 0xf0, 0xb8, 0x1b, 0x20, 0xd2, 0xb5, 0x1f, 0x15, 0x21, 0xf9, 0xe3, 0xe1, 0x61,
549 0x21, 0x55,
550 ];
551
552 #[test]
553 fn a_mul_a_vs_a_squared_constant() {
554 let a = FieldElement::from_bytes(&A_BYTES);
555 let asq = FieldElement::from_bytes(&ASQ_BYTES);
556 assert_eq!(asq, &a * &a);
557 }
558
559 #[test]
560 fn a_square_vs_a_squared_constant() {
561 let a = FieldElement::from_bytes(&A_BYTES);
562 let asq = FieldElement::from_bytes(&ASQ_BYTES);
563 assert_eq!(asq, a.square());
564 }
565
566 #[test]
567 fn a_square2_vs_a_squared_constant() {
568 let a = FieldElement::from_bytes(&A_BYTES);
569 let asq = FieldElement::from_bytes(&ASQ_BYTES);
570 assert_eq!(a.square2(), &asq + &asq);
571 }
572
573 #[test]
574 fn a_invert_vs_inverse_of_a_constant() {
575 let a = FieldElement::from_bytes(&A_BYTES);
576 let ainv = FieldElement::from_bytes(&AINV_BYTES);
577 let should_be_inverse = a.invert();
578 assert_eq!(ainv, should_be_inverse);
579 assert_eq!(FieldElement::ONE, &a * &should_be_inverse);
580 }
581
582 #[test]
583 #[cfg(feature = "alloc")]
584 fn invert_batch_a_matches_nonbatched() {
585 let a = FieldElement::from_bytes(&A_BYTES);
586 let ap58 = FieldElement::from_bytes(&AP58_BYTES);
587 let asq = FieldElement::from_bytes(&ASQ_BYTES);
588 let ainv = FieldElement::from_bytes(&AINV_BYTES);
589 let a0 = &a - &a;
590 let a2 = &a + &a;
591 let a_list = vec![a, ap58, asq, ainv, a0, a2];
592 let mut ainv_list = a_list.clone();
593 FieldElement::invert_batch_alloc(&mut ainv_list[..]);
594 for i in 0..6 {
595 assert_eq!(a_list[i].invert(), ainv_list[i]);
596 }
597 }
598
599 #[test]
600 fn sqrt_ratio_behavior() {
601 let zero = FieldElement::ZERO;
602 let one = FieldElement::ONE;
603 let i = constants::SQRT_M1;
604 let two = &one + &one; let four = &two + &two; let (choice, sqrt) = FieldElement::sqrt_ratio_i(&zero, &zero);
609 assert!(bool::from(choice));
610 assert_eq!(sqrt, zero);
611 assert!(bool::from(!sqrt.is_negative()));
612
613 let (choice, sqrt) = FieldElement::sqrt_ratio_i(&one, &zero);
615 assert!(bool::from(!choice));
616 assert_eq!(sqrt, zero);
617 assert!(bool::from(!sqrt.is_negative()));
618
619 let (choice, sqrt) = FieldElement::sqrt_ratio_i(&two, &one);
621 assert!(bool::from(!choice));
622 assert_eq!(sqrt.square(), &two * &i);
623 assert!(bool::from(!sqrt.is_negative()));
624
625 let (choice, sqrt) = FieldElement::sqrt_ratio_i(&four, &one);
627 assert!(bool::from(choice));
628 assert_eq!(sqrt.square(), four);
629 assert!(bool::from(!sqrt.is_negative()));
630
631 let (choice, sqrt) = FieldElement::sqrt_ratio_i(&one, &four);
633 assert!(bool::from(choice));
634 assert_eq!(&sqrt.square() * &four, one);
635 assert!(bool::from(!sqrt.is_negative()));
636 }
637
638 #[test]
639 fn a_p58_vs_ap58_constant() {
640 let a = FieldElement::from_bytes(&A_BYTES);
641 let ap58 = FieldElement::from_bytes(&AP58_BYTES);
642 assert_eq!(ap58, a.pow_p58());
643 }
644
645 #[test]
646 fn equality() {
647 let a = FieldElement::from_bytes(&A_BYTES);
648 let ainv = FieldElement::from_bytes(&AINV_BYTES);
649 assert!(a == a);
650 assert!(a != ainv);
651 }
652
653 static B_BYTES: [u8; 32] = [
656 113, 191, 169, 143, 91, 234, 121, 15, 241, 131, 217, 36, 230, 101, 92, 234, 8, 208, 170,
657 251, 97, 127, 70, 210, 58, 23, 166, 87, 240, 169, 184, 178,
658 ];
659
660 #[test]
661 fn from_bytes_highbit_is_ignored() {
662 let mut cleared_bytes = B_BYTES;
663 cleared_bytes[31] &= 127u8;
664 let with_highbit_set = FieldElement::from_bytes(&B_BYTES);
665 let without_highbit_set = FieldElement::from_bytes(&cleared_bytes);
666 assert_eq!(without_highbit_set, with_highbit_set);
667 }
668
669 #[test]
670 fn conditional_negate() {
671 let one = FieldElement::ONE;
672 let minus_one = FieldElement::MINUS_ONE;
673 let mut x = one;
674 x.conditional_negate(Choice::from(1));
675 assert_eq!(x, minus_one);
676 x.conditional_negate(Choice::from(0));
677 assert_eq!(x, minus_one);
678 x.conditional_negate(Choice::from(1));
679 assert_eq!(x, one);
680 }
681
682 #[test]
683 fn encoding_is_canonical() {
684 let one_encoded_wrongly_bytes: [u8; 32] = [
686 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
687 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
688 0xff, 0xff, 0xff, 0x7f,
689 ];
690 let one = FieldElement::from_bytes(&one_encoded_wrongly_bytes);
692 let one_bytes = one.to_bytes();
694 assert_eq!(one_bytes[0], 1);
695 for byte in &one_bytes[1..] {
696 assert_eq!(*byte, 0);
697 }
698 }
699
700 #[test]
701 #[cfg(feature = "alloc")]
702 fn invert_batch_empty() {
703 FieldElement::invert_batch_alloc(&mut []);
704 }
705
706 #[cfg(feature = "digest")]
740 const FROM_BYTES_WIDE_KAT_BIG: &[(&str, &str)] = &[
741 (
742 "77b663085cac0e916f40dbeea5116f201816406e68ccf01b32a97162ae1d5bf95d0d01c2c72fbeeb27a63\
743 5b85b715d5ce6f74118a60a7aec53c798ad648a482f",
744 "62b38bd402c4498f5cead14643e54dd649e20a0810610e36a73f1f27a0a81f7e",
745 ),
746 (
747 "d437c75ec79886650243a79c62933bb307eb12ff16d05db4a6a8a877f4a91abb6eeb64d2e20519c021799\
748 3a1dc5639283a06639985a2c892208171503335afb5",
749 "3d2ec29972783de9043e8b982278beaba9d7c5c3ebef257e7cd38168928f1c33",
750 ),
751 (
752 "6daa9e1abe6c604fb6e841c04bf90a6ef88aef6b1eab17dd44f7207ef472cd2d54bac849f703e64f36e56\
753 77e7e86b82be7d26aa220daf1f208bb36dcc1a12338",
754 "28546a0e7303852bc6eead8312f06eeb48d9ca87f60bfeec98ba402ebb751703",
755 ),
756 (
757 "c3920e326dbf806a50105be78263c1dc9390fb4741587b250cd758c2bfa3ed70faedbbc5f9b1d024e00fe\
758 7d7daf796866853f42e72d638e6533c5eb5b7caf3c6",
759 "40eaf38b802a7be1956ba7f3fe2d2ad717f23f40342deb5180cb55ae04bb1d79",
760 ),
761 (
762 "23f143c72ead6c0f336b4e746a06921f0eb180002e8ce916d196de16216788617c6aeb90a074a85196f03\
763 81375011248927c1215e9ec65b382a6ec556fb3f504",
764 "b1bf354a04fd6d2e8321c24ecb3d3ed2c42e3f21c7b60ab8374effd7a709011e",
765 ),
766 ];
767
768 #[cfg(feature = "digest")]
771 const FROM_BYTES_WIDE_KAT_MEDIUM: &[(&str, &str)] = &[
772 (
773 "82e9cbe4928e3d0bbf1f91824a91acfb30d929f7a2fa5cbcc967c63ea0f3357c29c19f1bc9dcad69d85c1\
774 c6265970685",
775 "989582fe6c540cbbdee7c612570aa7ba44d929f7a2fa5cbcc967c63ea0f3357c",
776 ),
777 (
778 "5480494df4fb3a3b19da17e1c8b9192ccb09ec76720321977079300c42c17b9e95b01eb37ffe7048fcd1c\
779 9e6094da6c4",
780 "85b6d7e3e8c200fc8b050d234129c95ce809ec76720321977079300c42c17b1e",
781 ),
782 (
783 "93ec8a480dde098f74bcd341ef4f248f6440cc6e631d7000784f66975a4fd628438bb1350ba4c1421fec3\
784 670decced06",
785 "8598e540b737c87718c9fae9f3b870966540cc6e631d7000784f66975a4fd628",
786 ),
787 (
788 "fd0154ff9a5c4c9ee4e8183c23db97018e0e6201a812f6d4faedda50652d51f65c110b9a1a100a3fc3ff1\
789 c4ea3cf22e4",
790 "b895f8dc8dc0caf9dfdf66d460adc2deaf0e6201a812f6d4faedda50652d5176",
791 ),
792 (
793 "0e829dc955e0a1e0dbda9849cb2022b295275782348bd6308b3d0c5836f3ca0130911a17fd54054c3a0f8\
794 b2486f8ce85",
795 "2e0f8f37e77d6c29831d3db6b404db8ea9275782348bd6308b3d0c5836f3ca01",
796 ),
797 ];
798
799 #[cfg(feature = "digest")]
800 #[test]
801 fn from_bytes_wide() {
802 for (input_bytes, expected_reduced) in FROM_BYTES_WIDE_KAT_BIG {
804 let reduce_fe = FieldElement::from_bytes_wide(
805 &hex::decode(input_bytes)
806 .unwrap()
807 .as_slice()
808 .try_into()
809 .unwrap(),
810 );
811 assert_eq!(
812 &reduce_fe.to_bytes(),
813 hex::decode(expected_reduced).unwrap().as_slice()
814 );
815 }
816
817 for (input_bytes, expected_reduced) in FROM_BYTES_WIDE_KAT_MEDIUM {
819 let mut padded_input_bytes = [0u8; 64];
820 padded_input_bytes[..48].copy_from_slice(&hex::decode(input_bytes).unwrap());
821 let reduce_fe = FieldElement::from_bytes_wide(&padded_input_bytes);
822 assert_eq!(
823 &reduce_fe.to_bytes(),
824 hex::decode(expected_reduced).unwrap().as_slice()
825 );
826 }
827 }
828
829 #[cfg(feature = "digest")]
830 fn fe_from_test_vector(expected_hex: &str) -> FieldElement {
831 let mut expected_hash = hex::decode(expected_hex).unwrap();
832 expected_hash.reverse();
833 FieldElement::from_bytes(&expected_hash.try_into().unwrap())
834 }
835
836 #[cfg(feature = "digest")]
840 const RFC_HASH_TO_FIELD_KAT: &[(&[u8], &str)] = &[
841 (
842 b"",
843 "7f3e7fb9428103ad7f52db32f9df32505d7b427d894c5093f7a0f0374a30641d"
844 ),
845 (
846 b"abc",
847 "09cfa30ad79bd59456594a0f5d3a76f6b71c6787b04de98be5cd201a556e253b"
848 ),
849 (
850 b"abcdef0123456789",
851 "475ccff99225ef90d78cc9338e9f6a6bb7b17607c0c4428937de75d33edba941",
852 ),
853 (
854 b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
855 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
856 "049a1c8bd51bcb2aec339f387d1ff51428b88d0763a91bcdf6929814ac95d03d"
857 ),
858 (
859 b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
860 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
861 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
862 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
863 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
864 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
865 "3cb0178a8137cefa5b79a3a57c858d7eeeaa787b2781be4a362a2f0750d24fa0"
866 )
867 ];
868
869 #[test]
870 #[cfg(feature = "digest")]
871 fn hash_to_field() {
872 use sha2::Sha512;
873 let dst = "QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_NU_";
874
875 for (msg, expected_hash_hex) in RFC_HASH_TO_FIELD_KAT {
876 let fe = FieldElement::hash_to_field::<Sha512, 1>(&[msg], &[dst.as_bytes()])[0];
877 let expected_fe = fe_from_test_vector(expected_hash_hex);
878
879 assert_eq!(fe, expected_fe);
880 }
881 }
882
883 #[cfg(feature = "digest")]
887 const RFC_HASH_TO_FIELD_KAT_2: &[(&[u8], &str, &str)] = &[
888 (
889 b"",
890 "03fef4813c8cb5f98c6eef88fae174e6e7d5380de2b007799ac7ee712d203f3a",
891 "780bdddd137290c8f589dc687795aafae35f6b674668d92bf92ae793e6a60c75"
892 ),
893 (
894 b"abc",
895 "5081955c4141e4e7d02ec0e36becffaa1934df4d7a270f70679c78f9bd57c227",
896 "005bdc17a9b378b6272573a31b04361f21c371b256252ae5463119aa0b925b76"
897 ),
898 (
899 b"abcdef0123456789",
900 "285ebaa3be701b79871bcb6e225ecc9b0b32dff2d60424b4c50642636a78d5b3",
901 "2e253e6a0ef658fedb8e4bd6a62d1544fd6547922acb3598ec6b369760b81b31"
902 ),
903 (
904 b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
905 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
906 "4fedd25431c41f2a606952e2945ef5e3ac905a42cf64b8b4d4a83c533bf321af",
907 "02f20716a5801b843987097a8276b6d869295b2e11253751ca72c109d37485a9"
908 ),
909 (
910 b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
911 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
912 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
913 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
914 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
915 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
916 "6e34e04a5106e9bd59f64aba49601bf09d23b27f7b594e56d5de06df4a4ea33b",
917 "1c1c2cb59fc053f44b86c5d5eb8c1954b64976d0302d3729ff66e84068f5fd96"
918 )
919 ];
920
921 #[test]
922 #[cfg(feature = "digest")]
923 fn hash_to_field_2() {
924 use sha2::Sha512;
925 let dst = "QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_";
926
927 for (msg, expected_hash_hex_1, expected_hash_hex_2) in
928 crate::field::test::RFC_HASH_TO_FIELD_KAT_2
929 {
930 let fe = FieldElement::hash_to_field::<Sha512, 2>(&[msg], &[dst.as_bytes()]);
931
932 let expected_fe_1 = fe_from_test_vector(expected_hash_hex_1);
933 assert_eq!(fe[0], expected_fe_1);
934
935 let expected_fe_2 = fe_from_test_vector(expected_hash_hex_2);
936 assert_eq!(fe[1], expected_fe_2);
937 }
938 }
939}