x509_certificate/
signing.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use {
6    crate::{
7        rfc3447::RsaPrivateKey, rfc5958::OneAsymmetricKey, EcdsaCurve, KeyAlgorithm,
8        SignatureAlgorithm, X509CertificateError as Error,
9    },
10    bcder::decode::Constructed,
11    bytes::Bytes,
12    der::SecretDocument,
13    ring::{
14        rand::SystemRandom,
15        signature::{self as ringsig, KeyPair},
16    },
17    signature::{SignatureEncoding as SignatureTrait, Signer},
18    zeroize::Zeroizing,
19};
20
21/// Signifies that an entity is capable of producing cryptographic signatures.
22pub trait Sign {
23    /// Create a cyrptographic signature over a message.
24    ///
25    /// Takes the message to be signed, which will be digested by the implementation.
26    ///
27    /// Returns the raw bytes constituting the signature and which signature algorithm
28    /// was used. The returned [SignatureAlgorithm] can be serialized into an
29    /// ASN.1 `AlgorithmIdentifier` via `.into()`.
30    #[deprecated(since = "0.13.0", note = "use the signature::Signer trait instead")]
31    fn sign(&self, message: &[u8]) -> Result<(Vec<u8>, SignatureAlgorithm), Error>;
32
33    /// Obtain the algorithm of the private key.
34    ///
35    /// If we can't coerce the key algorithm to [KeyAlgorithm], None is returned.
36    fn key_algorithm(&self) -> Option<KeyAlgorithm>;
37
38    /// Obtain the raw bytes constituting the public key of the signing certificate.
39    ///
40    /// This will be `.tbs_certificate.subject_public_key_info.subject_public_key` of a parsed
41    /// X.509 public certificate.
42    fn public_key_data(&self) -> Bytes;
43
44    /// Obtain the [SignatureAlgorithm] that this signer will use.
45    ///
46    /// Instances can be coerced into the ASN.1 `AlgorithmIdentifier` via `.into()`
47    /// for easy inclusion in ASN.1 structures.
48    fn signature_algorithm(&self) -> Result<SignatureAlgorithm, Error>;
49
50    /// Obtain the raw private key data.
51    fn private_key_data(&self) -> Option<Zeroizing<Vec<u8>>>;
52
53    /// Obtain RSA key primes p and q, if available.
54    fn rsa_primes(&self) -> Result<Option<(Zeroizing<Vec<u8>>, Zeroizing<Vec<u8>>)>, Error>;
55}
56
57/// A superset of [Signer] and [Sign].
58pub trait KeyInfoSigner: Signer<Signature> + Sign {}
59
60#[derive(Clone, Debug)]
61pub struct Signature(Vec<u8>);
62
63impl From<Vec<u8>> for Signature {
64    fn from(v: Vec<u8>) -> Self {
65        Self(v)
66    }
67}
68
69impl From<Signature> for Vec<u8> {
70    fn from(v: Signature) -> Vec<u8> {
71        v.0
72    }
73}
74
75impl From<Signature> for Bytes {
76    fn from(v: Signature) -> Self {
77        Self::copy_from_slice(&v.0)
78    }
79}
80
81impl AsRef<[u8]> for Signature {
82    fn as_ref(&self) -> &[u8] {
83        &self.0
84    }
85}
86
87impl SignatureTrait for Signature {
88    type Repr = Vec<u8>;
89}
90
91impl TryFrom<&[u8]> for Signature {
92    type Error = ();
93
94    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
95        Ok(Self(value.to_vec()))
96    }
97}
98
99/// An ECDSA key pair.
100#[derive(Debug)]
101pub struct EcdsaKeyPair {
102    pkcs8_der: SecretDocument,
103    ring_pair: ringsig::EcdsaKeyPair,
104    curve: EcdsaCurve,
105    private_key: Zeroizing<Vec<u8>>,
106}
107
108/// An ED25519 key pair.
109#[derive(Debug)]
110pub struct Ed25519KeyPair {
111    pkcs8_der: SecretDocument,
112    ring_pair: ringsig::Ed25519KeyPair,
113}
114
115/// An RSA key pair.
116#[derive(Debug)]
117pub struct RsaKeyPair {
118    pkcs8_der: SecretDocument,
119    ring_pair: ringsig::RsaKeyPair,
120    private_key: Zeroizing<Vec<u8>>,
121}
122
123/// Represents a key pair that exists in memory and can be used to create cryptographic signatures.
124///
125/// This is a wrapper around ring's various key pair types. It provides
126/// abstractions tailored for X.509 certificates.
127#[derive(Debug)]
128pub enum InMemorySigningKeyPair {
129    /// ECDSA key pair.
130    Ecdsa(Box<EcdsaKeyPair>),
131
132    /// ED25519 key pair.
133    Ed25519(Box<Ed25519KeyPair>),
134
135    /// RSA key pair.
136    Rsa(Box<RsaKeyPair>),
137}
138
139impl Signer<Signature> for InMemorySigningKeyPair {
140    fn try_sign(&self, msg: &[u8]) -> Result<Signature, signature::Error> {
141        match self {
142            Self::Rsa(kp) => {
143                let mut signature = vec![0; kp.ring_pair.public().modulus_len()];
144
145                kp.ring_pair
146                    .sign(
147                        &ringsig::RSA_PKCS1_SHA256,
148                        &ring::rand::SystemRandom::new(),
149                        msg,
150                        &mut signature,
151                    )
152                    .map_err(|_| signature::Error::new())?;
153
154                Ok(signature.into())
155            }
156            Self::Ecdsa(kp) => {
157                let signature = kp
158                    .ring_pair
159                    .sign(&ring::rand::SystemRandom::new(), msg)
160                    .map_err(|_| signature::Error::new())?;
161
162                Ok(Signature::from(signature.as_ref().to_vec()))
163            }
164            Self::Ed25519(kp) => {
165                let signature = kp.ring_pair.sign(msg);
166
167                Ok(Signature::from(signature.as_ref().to_vec()))
168            }
169        }
170    }
171}
172
173impl Sign for InMemorySigningKeyPair {
174    /// This will use a new instance of ring's SystemRandom. The RSA
175    /// padding algorithm is hard-coded to RSA_PCS1_SHA256.
176    ///
177    /// If you want total control over signing parameters, obtain the
178    /// underlying ring keypair and call its `.sign()`.
179    fn sign(&self, message: &[u8]) -> Result<(Vec<u8>, SignatureAlgorithm), Error> {
180        let algorithm = self.signature_algorithm()?;
181
182        Ok((self.try_sign(message)?.into(), algorithm))
183    }
184
185    fn key_algorithm(&self) -> Option<KeyAlgorithm> {
186        Some(match self {
187            Self::Rsa(_) => KeyAlgorithm::Rsa,
188            Self::Ed25519(_) => KeyAlgorithm::Ed25519,
189            Self::Ecdsa(kp) => KeyAlgorithm::Ecdsa(kp.curve),
190        })
191    }
192
193    fn public_key_data(&self) -> Bytes {
194        match self {
195            Self::Rsa(kp) => Bytes::copy_from_slice(kp.ring_pair.public_key().as_ref()),
196            Self::Ecdsa(kp) => Bytes::copy_from_slice(kp.ring_pair.public_key().as_ref()),
197            Self::Ed25519(kp) => Bytes::copy_from_slice(kp.ring_pair.public_key().as_ref()),
198        }
199    }
200
201    fn signature_algorithm(&self) -> Result<SignatureAlgorithm, Error> {
202        Ok(match self {
203            Self::Rsa(_) => SignatureAlgorithm::RsaSha256,
204            Self::Ecdsa(kp) => {
205                // ring refuses to mix and match the bitness of curves and signature
206                // algorithms. e.g. it can't pair secp256r1 with SHA-384. It chooses
207                // signatures on its own. We reimplement that logic here.
208                match kp.curve {
209                    EcdsaCurve::Secp256r1 => SignatureAlgorithm::EcdsaSha256,
210                    EcdsaCurve::Secp384r1 => SignatureAlgorithm::EcdsaSha384,
211                }
212            }
213            Self::Ed25519(_) => SignatureAlgorithm::Ed25519,
214        })
215    }
216
217    fn private_key_data(&self) -> Option<Zeroizing<Vec<u8>>> {
218        match self {
219            Self::Rsa(kp) => Some(kp.private_key.clone()),
220            Self::Ecdsa(kp) => Some(kp.private_key.clone()),
221            Self::Ed25519(_) => None,
222        }
223    }
224
225    fn rsa_primes(&self) -> Result<Option<(Zeroizing<Vec<u8>>, Zeroizing<Vec<u8>>)>, Error> {
226        match self {
227            Self::Rsa(kp) => {
228                let key = Constructed::decode(kp.private_key.as_ref(), bcder::Mode::Der, |cons| {
229                    RsaPrivateKey::take_from(cons)
230                })?;
231
232                Ok(Some((
233                    Zeroizing::new(key.p.as_slice().to_vec()),
234                    Zeroizing::new(key.q.as_slice().to_vec()),
235                )))
236            }
237            Self::Ecdsa(_) => Ok(None),
238            Self::Ed25519(_) => Ok(None),
239        }
240    }
241}
242
243impl KeyInfoSigner for InMemorySigningKeyPair {}
244
245impl InMemorySigningKeyPair {
246    /// Attempt to instantiate an instance from PKCS#8 DER data.
247    ///
248    /// The DER data should be a [OneAsymmetricKey] ASN.1 structure.
249    pub fn from_pkcs8_der(data: impl AsRef<[u8]>) -> Result<Self, Error> {
250        let pkcs8_der = SecretDocument::try_from(data.as_ref())?;
251
252        // We need to parse the PKCS#8 to know what kind of key we're dealing with.
253        let key = Constructed::decode(data.as_ref(), bcder::Mode::Der, |cons| {
254            OneAsymmetricKey::take_from(cons)
255        })?;
256
257        let algorithm = KeyAlgorithm::try_from(&key.private_key_algorithm)?;
258
259        // self.key_algorithm() assumes a 1:1 mapping between KeyAlgorithm and our enum
260        // variants. If you change this, change that function as well.
261        match algorithm {
262            KeyAlgorithm::Rsa => {
263                let pair = ringsig::RsaKeyPair::from_pkcs8(data.as_ref())?;
264
265                Ok(Self::Rsa(Box::new(RsaKeyPair {
266                    pkcs8_der,
267                    ring_pair: pair,
268                    private_key: Zeroizing::new(key.private_key.into_bytes().to_vec()),
269                })))
270            }
271            KeyAlgorithm::Ecdsa(curve) => {
272                let pair = ringsig::EcdsaKeyPair::from_pkcs8(
273                    curve.into(),
274                    data.as_ref(),
275                    &SystemRandom::new(),
276                )?;
277
278                Ok(Self::Ecdsa(Box::new(EcdsaKeyPair {
279                    pkcs8_der,
280                    ring_pair: pair,
281                    curve,
282                    private_key: Zeroizing::new(data.as_ref().to_vec()),
283                })))
284            }
285            KeyAlgorithm::Ed25519 => Ok(Self::Ed25519(Box::new(Ed25519KeyPair {
286                pkcs8_der,
287                ring_pair: ringsig::Ed25519KeyPair::from_pkcs8(data.as_ref())?,
288            }))),
289        }
290    }
291
292    /// Attempt to instantiate an instance from PEM encoded PKCS#8.
293    ///
294    /// This is just a wrapper for [Self::from_pkcs8_der] that does the PEM
295    /// decoding for you.
296    pub fn from_pkcs8_pem(data: impl AsRef<[u8]>) -> Result<Self, Error> {
297        let der = pem::parse(data.as_ref()).map_err(Error::PemDecode)?;
298
299        Self::from_pkcs8_der(der.contents())
300    }
301
302    /// Generate a random key pair given a key algorithm and optional ECDSA signing algorithm.
303    ///
304    /// The raw PKCS#8 document is returned to facilitate access to the private key.
305    ///
306    /// Not attempt is made to protect the private key in memory.
307    pub fn generate_random(key_algorithm: KeyAlgorithm) -> Result<Self, Error> {
308        let rng = SystemRandom::new();
309
310        let document = match key_algorithm {
311            KeyAlgorithm::Ed25519 => ringsig::Ed25519KeyPair::generate_pkcs8(&rng)
312                .map_err(|_| Error::KeyPairGenerationError),
313            KeyAlgorithm::Ecdsa(curve) => ringsig::EcdsaKeyPair::generate_pkcs8(curve.into(), &rng)
314                .map_err(|_| Error::KeyPairGenerationError),
315            KeyAlgorithm::Rsa => Err(Error::RsaKeyGenerationNotSupported),
316        }?;
317
318        Self::from_pkcs8_der(document.as_ref())
319    }
320
321    /// Attempt to resolve a verification algorithm for this key pair.
322    ///
323    /// This is a wrapper around [SignatureAlgorithm::resolve_verification_algorithm()]
324    /// with our bound [KeyAlgorithm]. However, since there are no parameters
325    /// that can result in wrong choices, this is guaranteed to always work
326    /// and doesn't require `Result`.
327    pub fn verification_algorithm(
328        &self,
329    ) -> Result<&'static dyn ringsig::VerificationAlgorithm, Error> {
330        Ok(self.signature_algorithm()?
331            .resolve_verification_algorithm(self.key_algorithm().expect("key algorithm should be known for InMemorySigningKeyPair")).expect(
332            "illegal combination of key algorithm in signature algorithm: this should not occur"
333        ))
334    }
335
336    /// Serialize this instance to a PKCS#8 [OneAsymmetricKey] ASN.1 structure.
337    pub fn to_pkcs8_one_asymmetric_key_der(&self) -> Zeroizing<Vec<u8>> {
338        match self {
339            Self::Ecdsa(kp) => kp.pkcs8_der.to_bytes(),
340            Self::Ed25519(kp) => kp.pkcs8_der.to_bytes(),
341            Self::Rsa(kp) => kp.pkcs8_der.to_bytes(),
342        }
343    }
344}
345
346impl From<&InMemorySigningKeyPair> for KeyAlgorithm {
347    fn from(key: &InMemorySigningKeyPair) -> Self {
348        match key {
349            InMemorySigningKeyPair::Rsa(_) => KeyAlgorithm::Rsa,
350            InMemorySigningKeyPair::Ecdsa(kp) => KeyAlgorithm::Ecdsa(kp.curve),
351            InMemorySigningKeyPair::Ed25519(_) => KeyAlgorithm::Ed25519,
352        }
353    }
354}
355
356#[cfg(test)]
357mod test {
358    use {super::*, crate::rfc5280, crate::testutil::*, ringsig::UnparsedPublicKey};
359
360    #[test]
361    fn generate_random_ecdsa() {
362        for curve in EcdsaCurve::all() {
363            InMemorySigningKeyPair::generate_random(KeyAlgorithm::Ecdsa(*curve)).unwrap();
364        }
365    }
366
367    #[test]
368    fn generate_random_ed25519() {
369        InMemorySigningKeyPair::generate_random(KeyAlgorithm::Ed25519).unwrap();
370    }
371
372    #[test]
373    fn generate_random_rsa() {
374        assert!(InMemorySigningKeyPair::generate_random(KeyAlgorithm::Rsa).is_err());
375    }
376
377    #[test]
378    fn signing_key_from_ecdsa_pkcs8() {
379        let rng = ring::rand::SystemRandom::new();
380
381        for alg in &[
382            &ringsig::ECDSA_P256_SHA256_ASN1_SIGNING,
383            &ringsig::ECDSA_P384_SHA384_ASN1_SIGNING,
384        ] {
385            let doc = ringsig::EcdsaKeyPair::generate_pkcs8(alg, &rng).unwrap();
386
387            let signing_key = InMemorySigningKeyPair::from_pkcs8_der(doc.as_ref()).unwrap();
388            assert!(matches!(signing_key, InMemorySigningKeyPair::Ecdsa(_,)));
389
390            let pem_data = pem::Pem::new("PRIVATE KEY", doc.as_ref()).to_string();
391
392            let signing_key = InMemorySigningKeyPair::from_pkcs8_pem(pem_data.as_bytes()).unwrap();
393            assert!(matches!(signing_key, InMemorySigningKeyPair::Ecdsa(_)));
394
395            let key_pair_asn1 = Constructed::decode(doc.as_ref(), bcder::Mode::Der, |cons| {
396                OneAsymmetricKey::take_from(cons)
397            })
398            .unwrap();
399            assert_eq!(
400                key_pair_asn1.private_key_algorithm.algorithm,
401                // Inner value doesn't matter here.
402                KeyAlgorithm::Ecdsa(EcdsaCurve::Secp256r1).into()
403            );
404
405            let expected = if *alg == &ringsig::ECDSA_P256_SHA256_ASN1_SIGNING {
406                EcdsaCurve::Secp256r1
407            } else if *alg == &ringsig::ECDSA_P384_SHA384_ASN1_SIGNING {
408                EcdsaCurve::Secp384r1
409            } else {
410                panic!("unhandled test case");
411            };
412
413            assert!(key_pair_asn1.private_key_algorithm.parameters.is_some());
414            let oid = key_pair_asn1
415                .private_key_algorithm
416                .parameters
417                .unwrap()
418                .decode_oid()
419                .unwrap();
420
421            assert_eq!(EcdsaCurve::try_from(&oid).unwrap(), expected);
422        }
423    }
424
425    #[test]
426    fn signing_key_from_ed25519_pkcs8() {
427        let rng = ring::rand::SystemRandom::new();
428
429        let doc = ringsig::Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
430
431        let signing_key = InMemorySigningKeyPair::from_pkcs8_der(doc.as_ref()).unwrap();
432        assert!(matches!(signing_key, InMemorySigningKeyPair::Ed25519(_)));
433
434        let pem_data = pem::Pem::new("PRIVATE KEY", doc.as_ref()).to_string();
435
436        let signing_key = InMemorySigningKeyPair::from_pkcs8_pem(pem_data.as_bytes()).unwrap();
437        assert!(matches!(signing_key, InMemorySigningKeyPair::Ed25519(_)));
438
439        let key_pair_asn1 = Constructed::decode(doc.as_ref(), bcder::Mode::Der, |cons| {
440            OneAsymmetricKey::take_from(cons)
441        })
442        .unwrap();
443        assert_eq!(
444            key_pair_asn1.private_key_algorithm.algorithm,
445            SignatureAlgorithm::Ed25519.into()
446        );
447        assert!(key_pair_asn1.private_key_algorithm.parameters.is_none());
448    }
449
450    #[test]
451    fn ecdsa_self_signed_certificate_verification() {
452        for curve in EcdsaCurve::all() {
453            let (cert, _) = self_signed_ecdsa_key_pair(Some(*curve));
454            cert.verify_signed_by_certificate(&cert).unwrap();
455
456            let raw: &rfc5280::Certificate = cert.as_ref();
457
458            let tbs_signature_algorithm =
459                SignatureAlgorithm::try_from(&raw.tbs_certificate.signature).unwrap();
460            let expected = match curve {
461                EcdsaCurve::Secp256r1 => SignatureAlgorithm::EcdsaSha256,
462                EcdsaCurve::Secp384r1 => SignatureAlgorithm::EcdsaSha384,
463            };
464            assert_eq!(tbs_signature_algorithm, expected);
465
466            let spki = &raw.tbs_certificate.subject_public_key_info;
467
468            // The algorithm in the SPKI should be constant.
469            assert_eq!(
470                spki.algorithm.algorithm,
471                crate::algorithm::OID_EC_PUBLIC_KEY
472            );
473            // But the parameters depend on the curve in use.
474            let expected = match curve {
475                EcdsaCurve::Secp256r1 => crate::algorithm::OID_EC_SECP256R1,
476                EcdsaCurve::Secp384r1 => crate::algorithm::OID_EC_SECP384R1,
477            };
478            assert!(spki.algorithm.parameters.is_some());
479            assert_eq!(
480                spki.algorithm
481                    .parameters
482                    .as_ref()
483                    .unwrap()
484                    .decode_oid()
485                    .unwrap(),
486                expected
487            );
488
489            // This should match the tbs signature algorithm.
490            let cert_algorithm = SignatureAlgorithm::try_from(&raw.signature_algorithm).unwrap();
491            assert_eq!(cert_algorithm, tbs_signature_algorithm);
492        }
493    }
494
495    #[test]
496    fn ed25519_self_signed_certificate_verification() {
497        let (cert, _) = self_signed_ed25519_key_pair();
498        cert.verify_signed_by_certificate(&cert).unwrap();
499    }
500
501    #[test]
502    fn rsa_signing_roundtrip() {
503        let key = rsa_private_key();
504        let cert = rsa_cert();
505        let message = b"hello, world";
506
507        let signature = Signer::try_sign(&key, message).unwrap();
508
509        let public_key = UnparsedPublicKey::new(
510            key.verification_algorithm().unwrap(),
511            cert.public_key_data(),
512        );
513
514        public_key.verify(message, signature.as_ref()).unwrap();
515    }
516}