ciphersuite/
ed448.rs

1use zeroize::Zeroize;
2
3use digest::{
4  typenum::U114, core_api::BlockSizeUser, Update, Output, OutputSizeUser, FixedOutput,
5  ExtendableOutput, XofReader, HashMarker, Digest,
6};
7use sha3::Shake256;
8
9use group::Group;
10use minimal_ed448::{Scalar, Point};
11
12use crate::Ciphersuite;
13
14/// Shake256, fixed to a 114-byte output, as used by Ed448.
15#[derive(Clone, Default)]
16pub struct Shake256_114(Shake256);
17impl BlockSizeUser for Shake256_114 {
18  type BlockSize = <Shake256 as BlockSizeUser>::BlockSize;
19  fn block_size() -> usize {
20    Shake256::block_size()
21  }
22}
23impl OutputSizeUser for Shake256_114 {
24  type OutputSize = U114;
25  fn output_size() -> usize {
26    114
27  }
28}
29impl Update for Shake256_114 {
30  fn update(&mut self, data: &[u8]) {
31    self.0.update(data);
32  }
33  fn chain(mut self, data: impl AsRef<[u8]>) -> Self {
34    Update::update(&mut self, data.as_ref());
35    self
36  }
37}
38impl FixedOutput for Shake256_114 {
39  fn finalize_fixed(self) -> Output<Self> {
40    let mut res = Default::default();
41    FixedOutput::finalize_into(self, &mut res);
42    res
43  }
44  fn finalize_into(self, out: &mut Output<Self>) {
45    let mut reader = self.0.finalize_xof();
46    reader.read(out);
47  }
48}
49impl HashMarker for Shake256_114 {}
50
51/// Ciphersuite for Ed448, inspired by RFC-8032. This is not recommended for usage.
52///
53/// hash_to_F is implemented with a naive concatenation of the dst and data, allowing transposition
54/// between the two. This means `dst: b"abc", data: b"def"`, will produce the same scalar as
55/// `dst: "abcdef", data: b""`. Please use carefully, not letting dsts be substrings of each other.
56#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
57pub struct Ed448;
58impl Ciphersuite for Ed448 {
59  type F = Scalar;
60  type G = Point;
61  type H = Shake256_114;
62
63  const ID: &'static [u8] = b"ed448";
64
65  fn generator() -> Self::G {
66    Point::generator()
67  }
68
69  fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
70    Scalar::wide_reduce(Self::H::digest([dst, data].concat()).as_ref().try_into().unwrap())
71  }
72}
73
74#[test]
75fn test_ed448() {
76  use ff::PrimeField;
77
78  ff_group_tests::group::test_prime_group_bits::<_, Point>(&mut rand_core::OsRng);
79
80  // Ideally, a test vector from RFC-8032 (not FROST) would be here
81  // Unfortunately, the IETF draft doesn't provide any vectors for the derived challenges
82  assert_eq!(
83    Ed448::hash_to_F(
84      b"FROST-ED448-SHAKE256-v11nonce",
85      &hex::decode(
86        "\
8789bf16040081ff2990336b200613787937ebe1f024b8cdff90eb6f1c741d91c1\
884a2b2f5858a932ad3d3b18bd16e76ced3070d72fd79ae4402df201f5\
8925e754716a1bc1b87a502297f2a99d89ea054e0018eb55d39562fd01\
9000"
91      )
92      .unwrap()
93    )
94    .to_repr()
95    .to_vec(),
96    hex::decode(
97      "\
9867a6f023e77361707c6e894c625e809e80f33fdb310810053ae29e28\
99e7011f3193b9020e73c183a98cc3a519160ed759376dd92c94831622\
10000"
101    )
102    .unwrap()
103  );
104}