minimal_ed448/
ciphersuite.rs

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