Skip to main content

ed448_goldilocks/sign/
expanded.rs

1use crate::{
2    EdwardsPoint, EdwardsScalar, EdwardsScalarBytes, SECRET_KEY_LENGTH, SecretKey, SigningError,
3    VerifyingKey, WideEdwardsScalarBytes,
4    sign::{HASH_HEAD, InnerSignature},
5};
6use elliptic_curve::{
7    group::GroupEncoding,
8    zeroize::{Zeroize, ZeroizeOnDrop},
9};
10use shake::{
11    Shake256,
12    digest::{ExtendableOutput, ExtendableOutputReset, Update, XofReader},
13};
14
15#[derive(Clone)]
16pub struct ExpandedSecretKey {
17    pub(crate) seed: SecretKey,
18    pub(crate) scalar: EdwardsScalar,
19    pub(crate) public_key: VerifyingKey,
20    pub(crate) hash_prefix: EdwardsScalarBytes,
21}
22
23impl Zeroize for ExpandedSecretKey {
24    fn zeroize(&mut self) {
25        self.seed.zeroize();
26        self.scalar.zeroize();
27        self.hash_prefix.zeroize();
28    }
29}
30
31impl Drop for ExpandedSecretKey {
32    fn drop(&mut self) {
33        self.zeroize();
34    }
35}
36
37impl From<&SecretKey> for ExpandedSecretKey {
38    fn from(secret_key: &SecretKey) -> Self {
39        Self::from_seed(secret_key)
40    }
41}
42
43impl ZeroizeOnDrop for ExpandedSecretKey {}
44
45impl ExpandedSecretKey {
46    pub fn from_seed(seed: &SecretKey) -> Self {
47        let mut reader = Shake256::default().chain(seed).finalize_xof();
48        let mut bytes = WideEdwardsScalarBytes::default();
49        reader.read(&mut bytes);
50        let mut scalar_bytes = EdwardsScalarBytes::default();
51        scalar_bytes.copy_from_slice(&bytes[..SECRET_KEY_LENGTH]);
52
53        // The two least significant bits of the first byte are cleared
54        // All eight most significant bits of the last byte are cleared
55        // with the highest bit of the second byte set.
56        scalar_bytes[0] &= 0xFC;
57        scalar_bytes[56] = 0;
58        scalar_bytes[55] |= 0x80;
59
60        let scalar = EdwardsScalar::from_bytes_mod_order(&scalar_bytes);
61
62        let mut hash_prefix = EdwardsScalarBytes::default();
63        hash_prefix.copy_from_slice(&bytes[SECRET_KEY_LENGTH..]);
64
65        let point = EdwardsPoint::GENERATOR * scalar;
66        let public_key = VerifyingKey {
67            compressed: point.to_affine().compress(),
68            point,
69        };
70
71        Self {
72            seed: *seed,
73            scalar,
74            public_key,
75            hash_prefix,
76        }
77    }
78
79    /// Signs a message.
80    ///
81    /// This is the "Ed448" mode of RFC 8032 (no pre-hashing),
82    /// also known as "PureEdDSA on Curve448". No context is provided;
83    /// this is equivalent to `sign_ctx()` with an empty (zero-length)
84    /// context.
85    pub fn sign_raw(&self, m: &[u8]) -> Result<InnerSignature, SigningError> {
86        self.sign_inner(0, &[], m)
87    }
88
89    /// Signs a message (with context).
90    ///
91    /// This is the "Ed448" mode of RFC 8032 (no pre-hashing),
92    /// also known as "PureEdDSA on Curve448". A context string is also
93    /// provided; it MUST have length at most 255 bytes.
94    pub fn sign_ctx(&self, ctx: &[u8], m: &[u8]) -> Result<InnerSignature, SigningError> {
95        self.sign_inner(0, ctx, m)
96    }
97
98    /// Signs a pre-hashed message.
99    ///
100    /// This is the "Ed448ph" mode of RFC 8032 (message is pre-hashed),
101    /// also known as "HashEdDSA on Curve448". The hashed message `hm`
102    /// is provided (presumably, that hash value was obtained with
103    /// SHAKE256 and an output of 64 bytes; the caller does the hashing
104    /// itself). A context string is also provided; it MUST have length
105    /// at most 255 bytes.
106    pub fn sign_prehashed(&self, ctx: &[u8], m: &[u8]) -> Result<InnerSignature, SigningError> {
107        self.sign_inner(1, ctx, m)
108    }
109
110    fn sign_inner(&self, phflag: u8, ctx: &[u8], m: &[u8]) -> Result<InnerSignature, SigningError> {
111        if ctx.len() > 255 {
112            return Err(SigningError::PrehashedContextLength);
113        }
114        // SHAKE256(dom4(F, C) || prefix || PH(M), 114) -> scalar r
115        let ctx_len = ctx.len() as u8;
116        let mut reader = Shake256::default()
117            .chain(HASH_HEAD)
118            .chain([phflag])
119            .chain([ctx_len])
120            .chain(ctx)
121            .chain(self.hash_prefix)
122            .chain(m)
123            .finalize_xof_reset();
124        let mut bytes = WideEdwardsScalarBytes::default();
125        reader.read(&mut bytes);
126        let r = EdwardsScalar::from_bytes_mod_order_wide(&bytes);
127
128        // R = r*B
129        let big_r = EdwardsPoint::GENERATOR * r;
130        let compressed_r = big_r.to_bytes();
131
132        // SHAKE256(dom4(F, C) || R || A || PH(M), 114) -> scalar k
133        reader = Shake256::default()
134            .chain(HASH_HEAD)
135            .chain([phflag])
136            .chain([ctx_len])
137            .chain(ctx)
138            .chain(compressed_r)
139            .chain(self.public_key.compressed.as_bytes())
140            .chain(m)
141            .finalize_xof();
142        reader.read(&mut bytes);
143        let k = EdwardsScalar::from_bytes_mod_order_wide(&bytes);
144        Ok(InnerSignature {
145            r: big_r,
146            s: r + k * self.scalar,
147        })
148    }
149}