ed448_goldilocks_plus/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::curve::edwards::extended::PointBytes;
5use crate::sign::expanded::ExpandedSecretKey;
6use crate::{
7    Context, Scalar, ScalarBytes, Signature, VerifyingKey, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH,
8};
9use core::fmt::{self, Debug, Formatter};
10use crypto_signature::Error;
11use sha3::{
12    digest::{
13        consts::U64, crypto_common::BlockSizeUser, typenum::IsEqual, ExtendableOutput, FixedOutput,
14        FixedOutputReset, HashMarker, Update, XofReader,
15    },
16    Digest,
17};
18use subtle::{Choice, ConstantTimeEq};
19use zeroize::{Zeroize, ZeroizeOnDrop};
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 = ScalarBytes;
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 Zeroize for SigningKey {
136    fn zeroize(&mut self) {
137        self.secret.zeroize();
138    }
139}
140
141impl Drop for SigningKey {
142    fn drop(&mut self) {
143        self.secret.zeroize();
144    }
145}
146
147impl ZeroizeOnDrop for SigningKey {}
148
149impl ConstantTimeEq for SigningKey {
150    fn ct_eq(&self, other: &Self) -> Choice {
151        self.secret.seed.ct_eq(&other.secret.seed)
152    }
153}
154
155impl Eq for SigningKey {}
156
157impl PartialEq for SigningKey {
158    fn eq(&self, other: &Self) -> bool {
159        self.ct_eq(other).into()
160    }
161}
162
163impl From<SecretKey> for SigningKey {
164    fn from(secret_scalar: SecretKey) -> Self {
165        Self::from(&secret_scalar)
166    }
167}
168
169impl From<&SecretKey> for SigningKey {
170    fn from(secret_scalar: &SecretKey) -> Self {
171        Self {
172            secret: ExpandedSecretKey::from(secret_scalar),
173        }
174    }
175}
176
177#[cfg(any(feature = "alloc", feature = "std"))]
178impl TryFrom<Vec<u8>> for SigningKey {
179    type Error = &'static str;
180
181    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
182        Self::try_from(value.as_slice())
183    }
184}
185
186#[cfg(any(feature = "alloc", feature = "std"))]
187impl TryFrom<&Vec<u8>> for SigningKey {
188    type Error = &'static str;
189
190    fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
191        Self::try_from(value.as_slice())
192    }
193}
194
195impl TryFrom<&[u8]> for SigningKey {
196    type Error = &'static str;
197
198    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
199        if value.len() != SECRET_KEY_LENGTH {
200            return Err("Invalid length for a signing key");
201        }
202        Ok(Self::from(ScalarBytes::from_slice(value)))
203    }
204}
205
206#[cfg(any(feature = "alloc", feature = "std"))]
207impl TryFrom<Box<[u8]>> for SigningKey {
208    type Error = &'static str;
209
210    fn try_from(value: Box<[u8]>) -> Result<Self, Self::Error> {
211        Self::try_from(value.as_ref())
212    }
213}
214
215impl<D> crypto_signature::DigestSigner<D, Signature> for SigningKey
216where
217    D: Digest,
218{
219    fn try_sign_digest(&self, digest: D) -> Result<Signature, Error> {
220        let mut prehashed_message = [0u8; 64];
221        prehashed_message.copy_from_slice(digest.finalize().as_slice());
222        let sig = self.secret.sign_prehashed(&[], &prehashed_message)?;
223        Ok(sig.into())
224    }
225}
226
227impl crypto_signature::hazmat::PrehashSigner<Signature> for SigningKey {
228    fn sign_prehash(&self, prehash: &[u8]) -> Result<Signature, Error> {
229        let sig = self.secret.sign_prehashed(&[], prehash)?;
230        Ok(sig.into())
231    }
232}
233
234impl crypto_signature::Signer<Signature> for SigningKey {
235    fn try_sign(&self, msg: &[u8]) -> Result<Signature, Error> {
236        let sig = self.secret.sign_raw(msg)?;
237        Ok(sig.into())
238    }
239}
240
241impl<D> crypto_signature::DigestSigner<D, Signature> for Context<'_, '_, SigningKey>
242where
243    D: Digest,
244{
245    fn try_sign_digest(&self, digest: D) -> Result<Signature, Error> {
246        let mut prehashed_message = [0u8; 64];
247        prehashed_message.copy_from_slice(digest.finalize().as_slice());
248        let sig = self
249            .key
250            .secret
251            .sign_prehashed(self.value, &prehashed_message)?;
252        Ok(sig.into())
253    }
254}
255
256impl crypto_signature::hazmat::PrehashSigner<Signature> for Context<'_, '_, SigningKey> {
257    fn sign_prehash(&self, prehash: &[u8]) -> Result<Signature, Error> {
258        let sig = self.key.secret.sign_prehashed(self.value, prehash)?;
259        Ok(sig.into())
260    }
261}
262
263impl crypto_signature::Signer<Signature> for Context<'_, '_, SigningKey> {
264    fn try_sign(&self, msg: &[u8]) -> Result<Signature, Error> {
265        let sig = self.key.secret.sign_ctx(self.value, msg)?;
266        Ok(sig.into())
267    }
268}
269
270impl<D> crypto_signature::DigestVerifier<D, Signature> for SigningKey
271where
272    D: Digest,
273{
274    fn verify_digest(&self, msg: D, signature: &Signature) -> Result<(), Error> {
275        <VerifyingKey as crypto_signature::DigestVerifier<D, Signature>>::verify_digest(
276            &self.secret.public_key,
277            msg,
278            signature,
279        )
280    }
281}
282
283impl crypto_signature::Verifier<Signature> for SigningKey {
284    fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {
285        self.secret.public_key.verify_raw(signature, msg)
286    }
287}
288
289#[cfg(all(any(feature = "alloc", feature = "std"), feature = "pkcs8"))]
290impl pkcs8::EncodePrivateKey for SigningKey {
291    fn to_pkcs8_der(&self) -> pkcs8::Result<pkcs8::SecretDocument> {
292        KeypairBytes::from(self).to_pkcs8_der()
293    }
294}
295
296#[cfg(all(any(feature = "alloc", feature = "std"), feature = "pkcs8"))]
297impl pkcs8::spki::DynSignatureAlgorithmIdentifier for SigningKey {
298    fn signature_algorithm_identifier(
299        &self,
300    ) -> pkcs8::spki::Result<pkcs8::spki::AlgorithmIdentifierOwned> {
301        // See https://datatracker.ietf.org/doc/html/rfc8410 for id-Ed448
302        Ok(pkcs8::spki::AlgorithmIdentifier {
303            oid: super::ALGORITHM_OID,
304            parameters: None,
305        })
306    }
307}
308
309#[cfg(feature = "pkcs8")]
310/// Keypair bytes for Ed448
311#[derive(Clone, Copy, Debug, Eq, PartialEq)]
312pub struct KeypairBytes {
313    /// The secret key bytes
314    pub secret_key: PointBytes,
315    /// The public key bytes if included
316    pub verifying_key: Option<PointBytes>,
317}
318
319#[cfg(all(any(feature = "alloc", feature = "std"), feature = "pkcs8"))]
320impl pkcs8::EncodePrivateKey for KeypairBytes {
321    fn to_pkcs8_der(&self) -> pkcs8::Result<pkcs8::SecretDocument> {
322        let mut private_key = [0u8; 2 + SECRET_KEY_LENGTH];
323        private_key[0] = 0x04;
324        private_key[1] = SECRET_KEY_LENGTH as u8;
325        private_key[2..].copy_from_slice(self.secret_key.as_ref());
326
327        let private_key_info = pkcs8::PrivateKeyInfo {
328            algorithm: super::ALGORITHM_ID,
329            private_key: &private_key,
330            public_key: self.verifying_key.as_ref().map(|v| v.as_ref()),
331        };
332        let result = pkcs8::SecretDocument::encode_msg(&private_key_info)?;
333
334        #[cfg(feature = "zeroize")]
335        private_key.zeroize();
336
337        Ok(result)
338    }
339}
340
341#[cfg(feature = "pkcs8")]
342impl TryFrom<pkcs8::PrivateKeyInfo<'_>> for KeypairBytes {
343    type Error = pkcs8::Error;
344
345    fn try_from(value: pkcs8::PrivateKeyInfo<'_>) -> Result<Self, Self::Error> {
346        if value.algorithm.oid != super::ALGORITHM_OID {
347            return Err(pkcs8::Error::KeyMalformed);
348        }
349        if value.private_key.len() != SECRET_KEY_LENGTH {
350            return Err(pkcs8::Error::KeyMalformed);
351        }
352        let mut secret_key = [0u8; SECRET_KEY_LENGTH];
353        secret_key.copy_from_slice(value.private_key);
354        let verifying_key = if let Some(public_key) = value.public_key {
355            if public_key.len() != PUBLIC_KEY_LENGTH {
356                return Err(pkcs8::Error::KeyMalformed);
357            }
358            let mut bytes = [0u8; PUBLIC_KEY_LENGTH];
359            bytes.copy_from_slice(public_key);
360            Some(bytes)
361        } else {
362            None
363        };
364        Ok(KeypairBytes {
365            secret_key,
366            verifying_key,
367        })
368    }
369}
370
371#[cfg(feature = "pkcs8")]
372impl TryFrom<KeypairBytes> for SigningKey {
373    type Error = pkcs8::Error;
374
375    fn try_from(value: KeypairBytes) -> Result<Self, Self::Error> {
376        Self::try_from(&value)
377    }
378}
379
380#[cfg(feature = "pkcs8")]
381impl TryFrom<&KeypairBytes> for SigningKey {
382    type Error = pkcs8::Error;
383
384    fn try_from(value: &KeypairBytes) -> Result<Self, Self::Error> {
385        let signing_key = SigningKey::from(SecretKey::from_slice(value.secret_key.as_ref()));
386
387        if let Some(public_bytes) = value.verifying_key {
388            let verifying_key =
389                VerifyingKey::from_bytes(&public_bytes).map_err(|_| pkcs8::Error::KeyMalformed)?;
390            if signing_key.verifying_key() != verifying_key {
391                return Err(pkcs8::Error::KeyMalformed);
392            }
393        }
394        Ok(signing_key)
395    }
396}
397
398#[cfg(feature = "pkcs8")]
399impl From<&SigningKey> for KeypairBytes {
400    fn from(signing_key: &SigningKey) -> Self {
401        KeypairBytes {
402            secret_key: PointBytes::from(signing_key.to_bytes()),
403            verifying_key: Some(PointBytes::from(signing_key.verifying_key().to_bytes())),
404        }
405    }
406}
407
408#[cfg(feature = "pkcs8")]
409impl TryFrom<pkcs8::PrivateKeyInfo<'_>> for SigningKey {
410    type Error = pkcs8::Error;
411
412    fn try_from(value: pkcs8::PrivateKeyInfo<'_>) -> Result<Self, Self::Error> {
413        KeypairBytes::try_from(value)?.try_into()
414    }
415}
416
417#[cfg(feature = "serde")]
418impl serdect::serde::Serialize for SigningKey {
419    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
420    where
421        S: serdect::serde::Serializer,
422    {
423        serdect::array::serialize_hex_lower_or_bin(&self.secret.seed, s)
424    }
425}
426
427#[cfg(feature = "serde")]
428impl<'de> serdect::serde::Deserialize<'de> for SigningKey {
429    fn deserialize<D>(d: D) -> Result<Self, D::Error>
430    where
431        D: serdect::serde::Deserializer<'de>,
432    {
433        let mut bytes = SecretKey::default();
434        serdect::array::deserialize_hex_or_bin(&mut bytes, d)?;
435        Ok(SigningKey::from(bytes))
436    }
437}
438
439impl SigningKey {
440    /// Generate a cryptographically random [`SigningKey`].
441    pub fn generate(mut rng: impl rand_core::CryptoRngCore) -> Self {
442        let mut secret_scalar = SecretKey::default();
443        rng.fill_bytes(secret_scalar.as_mut());
444        assert!(!secret_scalar.iter().all(|&v| v == 0));
445        Self {
446            secret: ExpandedSecretKey::from(&secret_scalar),
447        }
448    }
449
450    /// Serialize this [`SigningKey`] as bytes.
451    pub fn to_bytes(&self) -> SecretKey {
452        self.secret.seed
453    }
454
455    /// Serialize this [`SigningKey`] as a byte reference.
456    pub fn as_bytes(&self) -> &SecretKey {
457        &self.secret.seed
458    }
459
460    /// Return the clamped [`Scalar`] for this [`SigningKey`].
461    ///
462    /// This is the scalar that is actually used for signing.
463    /// Be warned, this is secret material that should be handled with care.
464    pub fn to_scalar(&self) -> Scalar {
465        self.secret.scalar
466    }
467
468    /// Get the [`VerifyingKey`] for this [`SigningKey`].
469    pub fn verifying_key(&self) -> VerifyingKey {
470        self.secret.public_key
471    }
472
473    /// Create a signing context that can be used for Ed448ph with
474    /// [`DigestSigner`]
475    pub fn with_context<'k, 'v>(&'k self, context: &'v [u8]) -> Context<'k, 'v, Self> {
476        Context {
477            key: self,
478            value: context,
479        }
480    }
481
482    /// Sign a `message` with this [`SigningKey`] using the Ed448 algorithm
483    /// defined in [RFC8032 §5.2](https://datatracker.ietf.org/doc/html/rfc8032#section-5.2).
484    pub fn sign_raw(&self, message: &[u8]) -> Signature {
485        let sig = self
486            .secret
487            .sign_raw(message)
488            .expect("to succeed since no context is provided");
489        sig.into()
490    }
491
492    /// Sign a `message` in the given `context` with this [`SigningKey`] using the Ed448ph algorithm
493    /// defined in [RFC8032 §5.2](https://datatracker.ietf.org/doc/html/rfc8032#section-5.2).
494    pub fn sign_ctx(&self, context: &[u8], message: &[u8]) -> Result<Signature, Error> {
495        let sig = self.secret.sign_ctx(context, message)?;
496        Ok(sig.into())
497    }
498
499    /// Sign a `prehashed_message` with this [`SigningKey`] using the
500    /// Ed448ph algorithm defined in [RFC8032 §5.2](https://datatracker.ietf.org/doc/html/rfc8032#section-5.2).
501    pub fn sign_prehashed<D>(
502        &self,
503        context: Option<&[u8]>,
504        mut prehashed_message: D,
505    ) -> Result<Signature, Error>
506    where
507        D: PreHash,
508    {
509        let mut m = [0u8; 64];
510        prehashed_message.fill_bytes(&mut m);
511        let sig = self
512            .secret
513            .sign_prehashed(context.unwrap_or_default(), &m)?;
514        Ok(sig.into())
515    }
516}
517#[cfg(feature = "serde")]
518#[test]
519fn serialization() {
520    use rand_chacha::ChaCha8Rng;
521    use rand_core::SeedableRng;
522
523    let mut rng = ChaCha8Rng::from_seed([0u8; 32]);
524    let signing_key = SigningKey::generate(&mut rng);
525
526    let bytes = serde_bare::to_vec(&signing_key).unwrap();
527    let signing_key2: SigningKey = serde_bare::from_slice(&bytes).unwrap();
528    assert_eq!(signing_key, signing_key2);
529
530    let string = serde_json::to_string(&signing_key).unwrap();
531    let signing_key3: SigningKey = serde_json::from_str(&string).unwrap();
532    assert_eq!(signing_key, signing_key3);
533}