Skip to main content

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