Skip to main content

ed448_goldilocks/sign/
signing_key.rs

1//! Much of this code is borrowed from Thomas Pornin's [CRRL Project](https://github.com/pornin/crrl/blob/main/src/ed448.rs)
2//! and adapted to mirror `ed25519-dalek`'s API.
3
4use crate::{sign::expanded::ExpandedSecretKey, *};
5use core::fmt::{self, Debug, Formatter};
6use elliptic_curve::{
7    common::Generate,
8    rand_core::TryCryptoRng,
9    zeroize::{Zeroize, ZeroizeOnDrop},
10};
11use shake::digest::{
12    Digest, ExtendableOutput, FixedOutput, FixedOutputReset, HashMarker, Update, XofReader,
13    common::BlockSizeUser, consts::U64, typenum::IsEqual,
14};
15use signature::Error;
16use subtle::{Choice, ConstantTimeEq};
17
18#[cfg(feature = "pkcs8")]
19use ::ed448::pkcs8::{KeypairBytes, PublicKeyBytes};
20
21/// Ed448 secret key as defined in [RFC8032 § 5.2.5]
22///
23/// The private key is 57 octets (448 bits, 56 bytes) long.
24pub type SecretKey = EdwardsScalarBytes;
25
26/// Signing hash trait for Ed448ph
27pub trait PreHash {
28    /// Fill the given `out` buffer with the hash bytes
29    fn fill_bytes(&mut self, out: &mut [u8]);
30}
31
32/// Signing pre-hasher for Ed448ph with a fixed output size
33#[derive(Debug)]
34pub struct PreHasherXmd<HashT>
35where
36    HashT: BlockSizeUser + Default + FixedOutput + FixedOutputReset + Update + HashMarker,
37    HashT::OutputSize: IsEqual<U64>,
38{
39    hasher: HashT,
40}
41
42impl<HashT> From<HashT> for PreHasherXmd<HashT>
43where
44    HashT: BlockSizeUser + Default + FixedOutput + FixedOutputReset + Update + HashMarker,
45    HashT::OutputSize: IsEqual<U64>,
46{
47    fn from(hasher: HashT) -> Self {
48        Self::new(hasher)
49    }
50}
51
52impl<HashT> PreHasherXmd<HashT>
53where
54    HashT: BlockSizeUser + Default + FixedOutput + FixedOutputReset + Update + HashMarker,
55    HashT::OutputSize: IsEqual<U64>,
56{
57    /// Create a new [`PreHasherXmd`] from a `HashT`
58    pub fn new(hasher: HashT) -> Self {
59        Self { hasher }
60    }
61}
62
63impl<HashT> PreHash for PreHasherXmd<HashT>
64where
65    HashT: BlockSizeUser + Default + FixedOutput + FixedOutputReset + Update + HashMarker,
66    HashT::OutputSize: IsEqual<U64>,
67{
68    fn fill_bytes(&mut self, out: &mut [u8]) {
69        out.copy_from_slice(self.hasher.finalize_reset().as_slice());
70    }
71}
72
73/// Signing pre-hasher for Ed448ph with a xof output
74pub struct PreHasherXof<HashT>
75where
76    HashT: Default + ExtendableOutput + Update,
77{
78    reader: <HashT as ExtendableOutput>::Reader,
79}
80
81impl<HashT> Debug for PreHasherXof<HashT>
82where
83    HashT: Default + ExtendableOutput + Update,
84{
85    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
86        f.debug_struct("SigningPreHasherXof")
87            .finish_non_exhaustive()
88    }
89}
90
91impl<HashT> PreHash for PreHasherXof<HashT>
92where
93    HashT: Default + ExtendableOutput + Update,
94{
95    fn fill_bytes(&mut self, out: &mut [u8]) {
96        self.reader.read(out);
97    }
98}
99
100impl<HashT> From<HashT> for PreHasherXof<HashT>
101where
102    HashT: Default + ExtendableOutput + Update,
103{
104    fn from(hasher: HashT) -> Self {
105        Self::new(hasher)
106    }
107}
108
109impl<HashT> PreHasherXof<HashT>
110where
111    HashT: Default + ExtendableOutput + Update,
112{
113    /// Create a new [`PreHasherXof`] from a `HashT`
114    pub fn new(hasher: HashT) -> Self {
115        Self {
116            reader: hasher.finalize_xof(),
117        }
118    }
119}
120
121/// Signing key for Ed448
122#[derive(Clone)]
123pub struct SigningKey {
124    pub(crate) secret: ExpandedSecretKey,
125}
126
127impl Debug for SigningKey {
128    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
129        f.debug_struct("SigningKey")
130            .field("verifying_key", &self.secret.public_key)
131            .finish_non_exhaustive()
132    }
133}
134
135impl Generate for SigningKey {
136    fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
137        let mut secret_scalar = SecretKey::default();
138        rng.try_fill_bytes(secret_scalar.as_mut())?;
139        assert!(!secret_scalar.iter().all(|&v| v == 0));
140        Ok(Self {
141            secret: ExpandedSecretKey::from(&secret_scalar),
142        })
143    }
144}
145
146impl Zeroize for SigningKey {
147    fn zeroize(&mut self) {
148        self.secret.zeroize();
149    }
150}
151
152impl Drop for SigningKey {
153    fn drop(&mut self) {
154        self.secret.zeroize();
155    }
156}
157
158impl ZeroizeOnDrop for SigningKey {}
159
160impl ConstantTimeEq for SigningKey {
161    fn ct_eq(&self, other: &Self) -> Choice {
162        self.secret.seed.ct_eq(&other.secret.seed)
163    }
164}
165
166impl Eq for SigningKey {}
167
168impl PartialEq for SigningKey {
169    fn eq(&self, other: &Self) -> bool {
170        self.ct_eq(other).into()
171    }
172}
173
174impl From<SecretKey> for SigningKey {
175    fn from(secret_scalar: SecretKey) -> Self {
176        Self::from(&secret_scalar)
177    }
178}
179
180impl From<&SecretKey> for SigningKey {
181    fn from(secret_scalar: &SecretKey) -> Self {
182        Self {
183            secret: ExpandedSecretKey::from(secret_scalar),
184        }
185    }
186}
187
188#[cfg(feature = "alloc")]
189impl TryFrom<Vec<u8>> for SigningKey {
190    type Error = &'static str;
191
192    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
193        Self::try_from(value.as_slice())
194    }
195}
196
197#[cfg(feature = "alloc")]
198impl TryFrom<&Vec<u8>> for SigningKey {
199    type Error = &'static str;
200
201    fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
202        Self::try_from(value.as_slice())
203    }
204}
205
206impl TryFrom<&[u8]> for SigningKey {
207    type Error = &'static str;
208
209    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
210        if value.len() != SECRET_KEY_LENGTH {
211            return Err("Invalid length for a signing key");
212        }
213        Ok(Self::from(
214            EdwardsScalarBytes::try_from(value).expect("Invalid length"),
215        ))
216    }
217}
218
219#[cfg(feature = "alloc")]
220impl TryFrom<Box<[u8]>> for SigningKey {
221    type Error = &'static str;
222
223    fn try_from(value: Box<[u8]>) -> Result<Self, Self::Error> {
224        Self::try_from(value.as_ref())
225    }
226}
227
228impl<D> signature::DigestSigner<D, Signature> for SigningKey
229where
230    D: Default + FixedOutput + HashMarker + Update,
231{
232    fn try_sign_digest<F: Fn(&mut D) -> Result<(), Error>>(
233        &self,
234        f: F,
235    ) -> Result<Signature, Error> {
236        let mut digest = D::new();
237        f(&mut digest)?;
238        let sig = self.secret.sign_prehashed(&[], &digest.finalize())?;
239        Ok(sig.into())
240    }
241}
242
243impl signature::hazmat::PrehashSigner<Signature> for SigningKey {
244    fn sign_prehash(&self, prehash: &[u8]) -> Result<Signature, Error> {
245        let sig = self.secret.sign_prehashed(&[], prehash)?;
246        Ok(sig.into())
247    }
248}
249
250impl signature::Signer<Signature> for SigningKey {
251    fn try_sign(&self, msg: &[u8]) -> Result<Signature, Error> {
252        let sig = self.secret.sign_raw(msg)?;
253        Ok(sig.into())
254    }
255}
256
257impl<D> signature::DigestSigner<D, Signature> for Context<'_, '_, SigningKey>
258where
259    D: Default + FixedOutput + HashMarker + Update,
260{
261    fn try_sign_digest<F: Fn(&mut D) -> Result<(), Error>>(
262        &self,
263        f: F,
264    ) -> Result<Signature, Error> {
265        let mut digest = D::new();
266        f(&mut digest)?;
267        let sig = self
268            .key
269            .secret
270            .sign_prehashed(self.value, &digest.finalize())?;
271        Ok(sig.into())
272    }
273}
274
275impl signature::hazmat::PrehashSigner<Signature> for Context<'_, '_, SigningKey> {
276    fn sign_prehash(&self, prehash: &[u8]) -> Result<Signature, Error> {
277        let sig = self.key.secret.sign_prehashed(self.value, prehash)?;
278        Ok(sig.into())
279    }
280}
281
282impl signature::Signer<Signature> for Context<'_, '_, SigningKey> {
283    fn try_sign(&self, msg: &[u8]) -> Result<Signature, Error> {
284        let sig = self.key.secret.sign_ctx(self.value, msg)?;
285        Ok(sig.into())
286    }
287}
288
289impl<D> signature::DigestVerifier<D, Signature> for SigningKey
290where
291    D: Default + FixedOutput + HashMarker + Update,
292{
293    fn verify_digest<F: Fn(&mut D) -> Result<(), Error>>(
294        &self,
295        f: F,
296        signature: &Signature,
297    ) -> Result<(), Error> {
298        <VerifyingKey as signature::DigestVerifier<D, Signature>>::verify_digest(
299            &self.secret.public_key,
300            f,
301            signature,
302        )
303    }
304}
305
306impl signature::Verifier<Signature> for SigningKey {
307    fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {
308        self.secret.public_key.verify_raw(signature, msg)
309    }
310}
311
312#[cfg(all(feature = "alloc", feature = "pkcs8"))]
313impl pkcs8::EncodePrivateKey for SigningKey {
314    fn to_pkcs8_der(&self) -> pkcs8::Result<pkcs8::SecretDocument> {
315        KeypairBytes::from(self).to_pkcs8_der()
316    }
317}
318
319#[cfg(all(feature = "alloc", feature = "pkcs8"))]
320impl pkcs8::spki::DynSignatureAlgorithmIdentifier for SigningKey {
321    fn signature_algorithm_identifier(
322        &self,
323    ) -> pkcs8::spki::Result<pkcs8::spki::AlgorithmIdentifierOwned> {
324        // See https://datatracker.ietf.org/doc/html/rfc8410 for id-Ed448
325        Ok(pkcs8::spki::AlgorithmIdentifier {
326            oid: super::ALGORITHM_OID,
327            parameters: None,
328        })
329    }
330}
331
332#[cfg(feature = "pkcs8")]
333impl TryFrom<KeypairBytes> for SigningKey {
334    type Error = pkcs8::Error;
335
336    fn try_from(value: KeypairBytes) -> Result<Self, Self::Error> {
337        Self::try_from(&value)
338    }
339}
340
341#[cfg(feature = "pkcs8")]
342impl TryFrom<&KeypairBytes> for SigningKey {
343    type Error = pkcs8::Error;
344
345    fn try_from(value: &KeypairBytes) -> Result<Self, Self::Error> {
346        let signing_key =
347            SigningKey::from(SecretKey::try_from(&value.secret_key[..]).expect("invalid length"));
348
349        if let Some(public_bytes) = &value.public_key {
350            let verifying_key = VerifyingKey::from_bytes(public_bytes.as_ref())
351                .map_err(|_| pkcs8::KeyError::Invalid)?;
352            if signing_key.verifying_key() != verifying_key {
353                return Err(pkcs8::KeyError::Invalid.into());
354            }
355        }
356        Ok(signing_key)
357    }
358}
359
360#[cfg(feature = "pkcs8")]
361impl From<&SigningKey> for KeypairBytes {
362    fn from(signing_key: &SigningKey) -> Self {
363        KeypairBytes {
364            secret_key: signing_key.to_bytes().into(),
365            public_key: Some(PublicKeyBytes(signing_key.verifying_key().to_bytes())),
366        }
367    }
368}
369
370#[cfg(feature = "pkcs8")]
371impl TryFrom<pkcs8::PrivateKeyInfoRef<'_>> for SigningKey {
372    type Error = pkcs8::Error;
373
374    fn try_from(value: pkcs8::PrivateKeyInfoRef<'_>) -> Result<Self, Self::Error> {
375        KeypairBytes::try_from(value)?.try_into()
376    }
377}
378
379#[cfg(feature = "serde")]
380impl serdect::serde::Serialize for SigningKey {
381    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
382    where
383        S: serdect::serde::Serializer,
384    {
385        serdect::array::serialize_hex_lower_or_bin(&self.secret.seed, s)
386    }
387}
388
389#[cfg(feature = "serde")]
390impl<'de> serdect::serde::Deserialize<'de> for SigningKey {
391    fn deserialize<D>(d: D) -> Result<Self, D::Error>
392    where
393        D: serdect::serde::Deserializer<'de>,
394    {
395        let mut bytes = SecretKey::default();
396        serdect::array::deserialize_hex_or_bin(&mut bytes, d)?;
397        Ok(SigningKey::from(bytes))
398    }
399}
400
401impl SigningKey {
402    /// Serialize this [`SigningKey`] as bytes.
403    pub fn to_bytes(&self) -> SecretKey {
404        self.secret.seed
405    }
406
407    /// Serialize this [`SigningKey`] as a byte reference.
408    pub fn as_bytes(&self) -> &SecretKey {
409        &self.secret.seed
410    }
411
412    /// Return the clamped [`EdwardsScalar`] for this [`SigningKey`].
413    ///
414    /// This is the scalar that is actually used for signing.
415    /// Be warned, this is secret material that should be handled with care.
416    pub fn to_scalar(&self) -> EdwardsScalar {
417        self.secret.scalar
418    }
419
420    /// Get the [`VerifyingKey`] for this [`SigningKey`].
421    pub fn verifying_key(&self) -> VerifyingKey {
422        self.secret.public_key
423    }
424
425    /// Create a signing context that can be used for Ed448ph with
426    /// [`signature::DigestSigner`]
427    pub fn with_context<'k, 'v>(&'k self, context: &'v [u8]) -> Context<'k, 'v, Self> {
428        Context {
429            key: self,
430            value: context,
431        }
432    }
433
434    /// Sign a `message` with this [`SigningKey`] using the Ed448 algorithm
435    /// defined in [RFC8032 §5.2](https://datatracker.ietf.org/doc/html/rfc8032#section-5.2).
436    pub fn sign_raw(&self, message: &[u8]) -> Signature {
437        let sig = self
438            .secret
439            .sign_raw(message)
440            .expect("to succeed since no context is provided");
441        sig.into()
442    }
443
444    /// Sign a `message` in the given `context` with this [`SigningKey`] using the Ed448ph algorithm
445    /// defined in [RFC8032 §5.2](https://datatracker.ietf.org/doc/html/rfc8032#section-5.2).
446    pub fn sign_ctx(&self, context: &[u8], message: &[u8]) -> Result<Signature, Error> {
447        let sig = self.secret.sign_ctx(context, message)?;
448        Ok(sig.into())
449    }
450
451    /// Sign a `prehashed_message` with this [`SigningKey`] using the
452    /// Ed448ph algorithm defined in [RFC8032 §5.2](https://datatracker.ietf.org/doc/html/rfc8032#section-5.2).
453    pub fn sign_prehashed<D>(
454        &self,
455        context: Option<&[u8]>,
456        mut prehashed_message: D,
457    ) -> Result<Signature, Error>
458    where
459        D: PreHash,
460    {
461        let mut m = [0u8; 64];
462        prehashed_message.fill_bytes(&mut m);
463        let sig = self
464            .secret
465            .sign_prehashed(context.unwrap_or_default(), &m)?;
466        Ok(sig.into())
467    }
468}
469
470#[cfg(all(feature = "getrandom", feature = "serde"))]
471#[test]
472fn serialization() {
473    let signing_key = SigningKey::generate();
474
475    let bytes = serde_bare::to_vec(&signing_key).unwrap();
476    let signing_key2: SigningKey = serde_bare::from_slice(&bytes).unwrap();
477    assert_eq!(signing_key, signing_key2);
478
479    let string = serde_json::to_string(&signing_key).unwrap();
480    let signing_key3: SigningKey = serde_json::from_str(&string).unwrap();
481    assert_eq!(signing_key, signing_key3);
482}