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    aws_lc_rs::{
11        rand::SystemRandom,
12        signature::{self as ringsig, KeyPair},
13    },
14    bcder::decode::Constructed,
15    bytes::Bytes,
16    der::SecretDocument,
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 {}
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_key().modulus_len()];
145
146                kp.ring_pair
147                    .sign(
148                        &ringsig::RSA_PKCS1_SHA256,
149                        &aws_lc_rs::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(&aws_lc_rs::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(curve.into(), data.as_ref())?;
274
275                Ok(Self::Ecdsa(Box::new(EcdsaKeyPair {
276                    pkcs8_der,
277                    ring_pair: pair,
278                    curve,
279                    private_key: Zeroizing::new(data.as_ref().to_vec()),
280                })))
281            }
282            KeyAlgorithm::Ed25519 => Ok(Self::Ed25519(Box::new(Ed25519KeyPair {
283                pkcs8_der,
284                ring_pair: ringsig::Ed25519KeyPair::from_pkcs8(data.as_ref())?,
285            }))),
286        }
287    }
288
289    /// Attempt to instantiate an instance from PEM encoded PKCS#8.
290    ///
291    /// This is just a wrapper for [Self::from_pkcs8_der] that does the PEM
292    /// decoding for you.
293    pub fn from_pkcs8_pem(data: impl AsRef<[u8]>) -> Result<Self, Error> {
294        let der = pem::parse(data.as_ref()).map_err(Error::PemDecode)?;
295
296        Self::from_pkcs8_der(der.contents())
297    }
298
299    /// Generate a random key pair given a key algorithm and optional ECDSA signing algorithm.
300    ///
301    /// The raw PKCS#8 document is returned to facilitate access to the private key.
302    ///
303    /// Not attempt is made to protect the private key in memory.
304    pub fn generate_random(key_algorithm: KeyAlgorithm) -> Result<Self, Error> {
305        let rng = SystemRandom::new();
306
307        let document = match key_algorithm {
308            KeyAlgorithm::Ed25519 => ringsig::Ed25519KeyPair::generate_pkcs8(&rng)
309                .map_err(|_| Error::KeyPairGenerationError),
310            KeyAlgorithm::Ecdsa(curve) => ringsig::EcdsaKeyPair::generate_pkcs8(curve.into(), &rng)
311                .map_err(|_| Error::KeyPairGenerationError),
312            KeyAlgorithm::Rsa => Err(Error::RsaKeyGenerationNotSupported),
313        }?;
314
315        Self::from_pkcs8_der(document.as_ref())
316    }
317
318    /// Attempt to resolve a verification algorithm for this key pair.
319    ///
320    /// This is a wrapper around [SignatureAlgorithm::resolve_verification_algorithm()]
321    /// with our bound [KeyAlgorithm]. However, since there are no parameters
322    /// that can result in wrong choices, this is guaranteed to always work
323    /// and doesn't require `Result`.
324    pub fn verification_algorithm(
325        &self,
326    ) -> Result<&'static dyn ringsig::VerificationAlgorithm, Error> {
327        Ok(self.signature_algorithm()?
328            .resolve_verification_algorithm(self.key_algorithm().expect("key algorithm should be known for InMemorySigningKeyPair")).expect(
329            "illegal combination of key algorithm in signature algorithm: this should not occur"
330        ))
331    }
332
333    /// Serialize this instance to a PKCS#8 [OneAsymmetricKey] ASN.1 structure.
334    pub fn to_pkcs8_one_asymmetric_key_der(&self) -> Zeroizing<Vec<u8>> {
335        match self {
336            Self::Ecdsa(kp) => kp.pkcs8_der.to_bytes(),
337            Self::Ed25519(kp) => kp.pkcs8_der.to_bytes(),
338            Self::Rsa(kp) => kp.pkcs8_der.to_bytes(),
339        }
340    }
341}
342
343impl From<&InMemorySigningKeyPair> for KeyAlgorithm {
344    fn from(key: &InMemorySigningKeyPair) -> Self {
345        match key {
346            InMemorySigningKeyPair::Rsa(_) => KeyAlgorithm::Rsa,
347            InMemorySigningKeyPair::Ecdsa(kp) => KeyAlgorithm::Ecdsa(kp.curve),
348            InMemorySigningKeyPair::Ed25519(_) => KeyAlgorithm::Ed25519,
349        }
350    }
351}
352
353#[cfg(test)]
354mod test {
355    use {super::*, crate::rfc5280, crate::testutil::*, ringsig::UnparsedPublicKey};
356
357    #[test]
358    fn generate_random_ecdsa() {
359        for curve in EcdsaCurve::all() {
360            InMemorySigningKeyPair::generate_random(KeyAlgorithm::Ecdsa(*curve)).unwrap();
361        }
362    }
363
364    #[test]
365    fn generate_random_ed25519() {
366        InMemorySigningKeyPair::generate_random(KeyAlgorithm::Ed25519).unwrap();
367    }
368
369    #[test]
370    fn generate_random_rsa() {
371        assert!(InMemorySigningKeyPair::generate_random(KeyAlgorithm::Rsa).is_err());
372    }
373
374    #[test]
375    fn signing_key_from_ecdsa_pkcs8() {
376        let rng = aws_lc_rs::rand::SystemRandom::new();
377
378        for alg in &[
379            &ringsig::ECDSA_P256_SHA256_ASN1_SIGNING,
380            &ringsig::ECDSA_P384_SHA384_ASN1_SIGNING,
381        ] {
382            let doc = ringsig::EcdsaKeyPair::generate_pkcs8(alg, &rng).unwrap();
383
384            let signing_key = InMemorySigningKeyPair::from_pkcs8_der(doc.as_ref()).unwrap();
385            assert!(matches!(signing_key, InMemorySigningKeyPair::Ecdsa(_,)));
386
387            let pem_data = pem::Pem::new("PRIVATE KEY", doc.as_ref()).to_string();
388
389            let signing_key = InMemorySigningKeyPair::from_pkcs8_pem(pem_data.as_bytes()).unwrap();
390            assert!(matches!(signing_key, InMemorySigningKeyPair::Ecdsa(_)));
391
392            let key_pair_asn1 = Constructed::decode(doc.as_ref(), bcder::Mode::Der, |cons| {
393                OneAsymmetricKey::take_from(cons)
394            })
395            .unwrap();
396            assert_eq!(
397                key_pair_asn1.private_key_algorithm.algorithm,
398                // Inner value doesn't matter here.
399                KeyAlgorithm::Ecdsa(EcdsaCurve::Secp256r1).into()
400            );
401
402            let expected = if *alg == &ringsig::ECDSA_P256_SHA256_ASN1_SIGNING {
403                EcdsaCurve::Secp256r1
404            } else if *alg == &ringsig::ECDSA_P384_SHA384_ASN1_SIGNING {
405                EcdsaCurve::Secp384r1
406            } else {
407                panic!("unhandled test case");
408            };
409
410            assert!(key_pair_asn1.private_key_algorithm.parameters.is_some());
411            let oid = key_pair_asn1
412                .private_key_algorithm
413                .parameters
414                .unwrap()
415                .decode_oid()
416                .unwrap();
417
418            assert_eq!(EcdsaCurve::try_from(&oid).unwrap(), expected);
419        }
420    }
421
422    #[test]
423    fn signing_key_from_ed25519_pkcs8() {
424        let rng = aws_lc_rs::rand::SystemRandom::new();
425
426        let doc = ringsig::Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
427
428        let signing_key = InMemorySigningKeyPair::from_pkcs8_der(doc.as_ref()).unwrap();
429        assert!(matches!(signing_key, InMemorySigningKeyPair::Ed25519(_)));
430
431        let pem_data = pem::Pem::new("PRIVATE KEY", doc.as_ref()).to_string();
432
433        let signing_key = InMemorySigningKeyPair::from_pkcs8_pem(pem_data.as_bytes()).unwrap();
434        assert!(matches!(signing_key, InMemorySigningKeyPair::Ed25519(_)));
435
436        let key_pair_asn1 = Constructed::decode(doc.as_ref(), bcder::Mode::Der, |cons| {
437            OneAsymmetricKey::take_from(cons)
438        })
439        .unwrap();
440        assert_eq!(
441            key_pair_asn1.private_key_algorithm.algorithm,
442            SignatureAlgorithm::Ed25519.into()
443        );
444        assert!(key_pair_asn1.private_key_algorithm.parameters.is_none());
445    }
446
447    #[test]
448    fn ecdsa_self_signed_certificate_verification() {
449        for curve in EcdsaCurve::all() {
450            let (cert, _) = self_signed_ecdsa_key_pair(Some(*curve));
451            cert.verify_signed_by_certificate(&cert).unwrap();
452
453            let raw: &rfc5280::Certificate = cert.as_ref();
454
455            let tbs_signature_algorithm =
456                SignatureAlgorithm::try_from(&raw.tbs_certificate.signature).unwrap();
457            let expected = match curve {
458                EcdsaCurve::Secp256r1 => SignatureAlgorithm::EcdsaSha256,
459                EcdsaCurve::Secp384r1 => SignatureAlgorithm::EcdsaSha384,
460            };
461            assert_eq!(tbs_signature_algorithm, expected);
462
463            let spki = &raw.tbs_certificate.subject_public_key_info;
464
465            // The algorithm in the SPKI should be constant.
466            assert_eq!(
467                spki.algorithm.algorithm,
468                crate::algorithm::OID_EC_PUBLIC_KEY
469            );
470            // But the parameters depend on the curve in use.
471            let expected = match curve {
472                EcdsaCurve::Secp256r1 => crate::algorithm::OID_EC_SECP256R1,
473                EcdsaCurve::Secp384r1 => crate::algorithm::OID_EC_SECP384R1,
474            };
475            assert!(spki.algorithm.parameters.is_some());
476            assert_eq!(
477                spki.algorithm
478                    .parameters
479                    .as_ref()
480                    .unwrap()
481                    .decode_oid()
482                    .unwrap(),
483                expected
484            );
485
486            // This should match the tbs signature algorithm.
487            let cert_algorithm = SignatureAlgorithm::try_from(&raw.signature_algorithm).unwrap();
488            assert_eq!(cert_algorithm, tbs_signature_algorithm);
489        }
490    }
491
492    #[test]
493    fn ed25519_self_signed_certificate_verification() {
494        let (cert, _) = self_signed_ed25519_key_pair();
495        cert.verify_signed_by_certificate(&cert).unwrap();
496    }
497
498    #[test]
499    fn rsa_signing_roundtrip() {
500        let key = rsa_private_key();
501        let cert = rsa_cert();
502        let message = b"hello, world";
503
504        let signature = Signer::try_sign(&key, message).unwrap();
505
506        let public_key = UnparsedPublicKey::new(
507            key.verification_algorithm().unwrap(),
508            cert.public_key_data(),
509        );
510
511        public_key.verify(message, signature.as_ref()).unwrap();
512    }
513}