Skip to main content

isideload_x509_certificate/
algorithm.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
5//! Cryptographic algorithms commonly encountered in X.509 certificates.
6
7use {
8    crate::{
9        X509CertificateError as Error,
10        rfc3447::DigestInfo,
11        rfc5280::{AlgorithmIdentifier, AlgorithmParameter},
12    },
13    aws_lc_rs::{digest, signature},
14    bcder::{ConstOid, OctetString, Oid, encode::Values},
15    spki::ObjectIdentifier,
16    std::fmt::{Display, Formatter},
17};
18
19/// SHA-1 digest algorithm.
20///
21/// 1.3.14.3.2.26
22const OID_SHA1: ConstOid = Oid(&[43, 14, 3, 2, 26]);
23
24/// SHA-256 digest algorithm.
25///
26/// 2.16.840.1.101.3.4.2.1
27const OID_SHA256: ConstOid = Oid(&[96, 134, 72, 1, 101, 3, 4, 2, 1]);
28
29/// SHA-512 digest algorithm.
30///
31/// 2.16.840.1.101.3.4.2.2
32const OID_SHA384: ConstOid = Oid(&[96, 134, 72, 1, 101, 3, 4, 2, 2]);
33
34/// SHA-512 digest algorithm.
35///
36/// 2.16.840.1.101.3.4.2.3
37const OID_SHA512: ConstOid = Oid(&[96, 134, 72, 1, 101, 3, 4, 2, 3]);
38
39/// RSA+SHA-1 encryption.
40///
41/// 1.2.840.113549.1.1.5
42const OID_SHA1_RSA: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 1, 5]);
43
44/// RSA+SHA-256 encryption.
45///
46/// 1.2.840.113549.1.1.11
47const OID_SHA256_RSA: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 1, 11]);
48
49/// RSA+SHA-384 encryption.
50///
51/// 1.2.840.113549.1.1.12
52const OID_SHA384_RSA: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 1, 12]);
53
54/// RSA+SHA-512 encryption.
55///
56/// 1.2.840.113549.1.1.13
57const OID_SHA512_RSA: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 1, 13]);
58
59/// RSA encryption.
60///
61/// 1.2.840.113549.1.1.1
62const OID_RSA: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 1, 1]);
63
64/// ECDSA with SHA-256.
65///
66/// 1.2.840.10045.4.3.2
67pub(crate) const OID_ECDSA_SHA256: ConstOid = Oid(&[42, 134, 72, 206, 61, 4, 3, 2]);
68
69/// ECDSA with SHA-384.
70///
71/// 1.2.840.10045.4.3.2
72pub(crate) const OID_ECDSA_SHA384: ConstOid = Oid(&[42, 134, 72, 206, 61, 4, 3, 3]);
73
74/// Elliptic curve public key cryptography.
75///
76/// 1.2.840.10045.2.1
77pub(crate) const OID_EC_PUBLIC_KEY: ConstOid = Oid(&[42, 134, 72, 206, 61, 2, 1]);
78
79/// ED25519 key agreement.
80///
81/// 1.3.101.110
82const OID_ED25519_KEY_AGREEMENT: ConstOid = Oid(&[43, 101, 110]);
83
84/// Edwards curve digital signature algorithm.
85///
86/// 1.3.101.112
87const OID_ED25519_SIGNATURE_ALGORITHM: ConstOid = Oid(&[43, 101, 112]);
88
89/// Elliptic curve identifier for secp256r1.
90///
91/// 1.2.840.10045.3.1.7
92pub(crate) const OID_EC_SECP256R1: ConstOid = Oid(&[42, 134, 72, 206, 61, 3, 1, 7]);
93
94/// Elliptic curve identifier for secp384r1.
95///
96/// 1.3.132.0.34
97pub(crate) const OID_EC_SECP384R1: ConstOid = Oid(&[43, 129, 4, 0, 34]);
98
99/// No signature identifier
100///
101/// 1.3.6.1.5.5.7.6.2
102pub(crate) const OID_NO_SIGNATURE_ALGORITHM: ConstOid = Oid(&[43, 6, 1, 5, 5, 7, 6, 2]);
103
104/// A hashing algorithm used for digesting data.
105///
106/// Instances can be converted to and from [Oid] via `From`/`Into`
107/// implementations.
108///
109/// They can also be converted to and from The ASN.1 [AlgorithmIdentifier],
110/// which is commonly used to represent them in X.509 certificates.
111///
112/// Instances can be converted into a [digest::Context] capable of computing
113/// digests via `From`/`Into`.
114#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
115pub enum DigestAlgorithm {
116    /// SHA-1.
117    ///
118    /// Corresponds to OID 1.3.14.3.2.26.
119    Sha1,
120
121    /// SHA-256.
122    ///
123    /// Corresponds to OID 2.16.840.1.101.3.4.2.1.
124    Sha256,
125
126    /// SHA-384.
127    ///
128    /// Corresponds to OID 2.16.840.1.101.3.4.2.2.
129    Sha384,
130
131    /// SHA-512.
132    ///
133    /// Corresponds to OID 2.16.840.1.101.3.4.2.3.
134    Sha512,
135}
136
137impl Display for DigestAlgorithm {
138    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
139        match self {
140            DigestAlgorithm::Sha1 => f.write_str("SHA-1"),
141            DigestAlgorithm::Sha256 => f.write_str("SHA-256"),
142            DigestAlgorithm::Sha384 => f.write_str("SHA-384"),
143            DigestAlgorithm::Sha512 => f.write_str("SHA-512"),
144        }
145    }
146}
147
148impl From<DigestAlgorithm> for Oid {
149    fn from(alg: DigestAlgorithm) -> Self {
150        Oid(match alg {
151            DigestAlgorithm::Sha1 => OID_SHA1.as_ref(),
152            DigestAlgorithm::Sha256 => OID_SHA256.as_ref(),
153            DigestAlgorithm::Sha384 => OID_SHA384.as_ref(),
154            DigestAlgorithm::Sha512 => OID_SHA512.as_ref(),
155        }
156        .into())
157    }
158}
159
160impl TryFrom<&Oid> for DigestAlgorithm {
161    type Error = Error;
162
163    fn try_from(v: &Oid) -> Result<Self, Self::Error> {
164        if v == &OID_SHA1 {
165            Ok(Self::Sha1)
166        } else if v == &OID_SHA256 {
167            Ok(Self::Sha256)
168        } else if v == &OID_SHA384 {
169            Ok(Self::Sha384)
170        } else if v == &OID_SHA512 {
171            Ok(Self::Sha512)
172        } else {
173            Err(Error::UnknownDigestAlgorithm(format!("{}", v)))
174        }
175    }
176}
177
178impl TryFrom<&AlgorithmIdentifier> for DigestAlgorithm {
179    type Error = Error;
180
181    fn try_from(v: &AlgorithmIdentifier) -> Result<Self, Self::Error> {
182        Self::try_from(&v.algorithm)
183    }
184}
185
186impl From<DigestAlgorithm> for AlgorithmIdentifier {
187    fn from(alg: DigestAlgorithm) -> Self {
188        Self {
189            algorithm: alg.into(),
190            parameters: None,
191        }
192    }
193}
194
195impl From<DigestAlgorithm> for digest::Context {
196    fn from(alg: DigestAlgorithm) -> Self {
197        digest::Context::new(match alg {
198            DigestAlgorithm::Sha1 => &digest::SHA1_FOR_LEGACY_USE_ONLY,
199            DigestAlgorithm::Sha256 => &digest::SHA256,
200            DigestAlgorithm::Sha384 => &digest::SHA384,
201            DigestAlgorithm::Sha512 => &digest::SHA512,
202        })
203    }
204}
205
206impl DigestAlgorithm {
207    /// Obtain an object that can be used to digest content using this algorithm.
208    pub fn digester(&self) -> digest::Context {
209        digest::Context::from(*self)
210    }
211
212    /// Digest a slice of data.
213    pub fn digest_data(&self, data: &[u8]) -> Vec<u8> {
214        let mut h = self.digester();
215        h.update(data);
216        h.finish().as_ref().to_vec()
217    }
218
219    /// Digest content from a reader.
220    pub fn digest_reader<R: std::io::Read>(&self, fh: &mut R) -> Result<Vec<u8>, std::io::Error> {
221        let mut h = self.digester();
222
223        loop {
224            let mut buffer = [0u8; 16384];
225            let count = fh.read(&mut buffer)?;
226
227            h.update(&buffer[0..count]);
228
229            if count < buffer.len() {
230                break;
231            }
232        }
233
234        Ok(h.finish().as_ref().to_vec())
235    }
236
237    /// Digest the content of a path.
238    pub fn digest_path(&self, path: &std::path::Path) -> Result<Vec<u8>, std::io::Error> {
239        self.digest_reader(&mut std::fs::File::open(path)?)
240    }
241
242    /// EMSA-PKCS1-v1_5 padding procedure.
243    ///
244    /// As defined by https://tools.ietf.org/html/rfc3447#section-9.2.
245    ///
246    /// `message` is the message to digest and encode.
247    ///
248    /// `target_length_in_bytes` is the target length of the padding. This should match the RSA
249    /// key length. e.g. 2048 bit keys are length 256.
250    pub fn rsa_pkcs1_encode(
251        &self,
252        message: &[u8],
253        target_length_in_bytes: usize,
254    ) -> Result<Vec<u8>, Error> {
255        let digest = self.digest_data(message);
256
257        let digest_info = DigestInfo {
258            algorithm: (*self).into(),
259            digest: OctetString::new(digest.into()),
260        };
261        let mut digest_info_der = vec![];
262        digest_info.write_encoded(bcder::Mode::Der, &mut digest_info_der)?;
263
264        let encoded_digest_len = digest_info_der.len();
265
266        // At least 8 bytes of padding are required. And there's a 2 byte header plus NULL
267        // termination of the padding. So the target length must be 11+ bytes longer than
268        // the encoded digest.
269        if encoded_digest_len + 11 > target_length_in_bytes {
270            return Err(Error::PkcsEncodeTooShort);
271        }
272
273        let pad_len = target_length_in_bytes - encoded_digest_len - 3;
274
275        let mut res = vec![0xff; target_length_in_bytes];
276        // Constant header.
277        res[0] = 0x00;
278        // Private key block type.
279        res[1] = 0x01;
280        // Padding bytes are already filled in.
281        // NULL terminate padding.
282        res[2 + pad_len] = 0x00;
283
284        let digest_destination = &mut res[3 + pad_len..];
285        digest_destination.copy_from_slice(&digest_info_der);
286
287        Ok(res)
288    }
289}
290
291/// An algorithm used to digitally sign content.
292///
293/// Instances can be converted to/from [Oid] via `From`/`Into`.
294///
295/// Similarly, instances can be converted to/from an ASN.1
296/// [AlgorithmIdentifier].
297///
298/// It is also possible to obtain a [signature::VerificationAlgorithm] from
299/// an instance. This type can perform actual cryptographic verification
300/// that was signed with this algorithm.
301#[derive(Copy, Clone, Debug, Eq, PartialEq)]
302pub enum SignatureAlgorithm {
303    /// SHA-1 with RSA encryption.
304    ///
305    /// Corresponds to OID 1.2.840.113549.1.1.5.
306    RsaSha1,
307
308    /// SHA-256 with RSA encryption.
309    ///
310    /// Corresponds to OID 1.2.840.113549.1.1.11.
311    RsaSha256,
312
313    /// SHA-384 with RSA encryption.
314    ///
315    /// Corresponds to OID 1.2.840.113549.1.1.12.
316    RsaSha384,
317
318    /// SHA-512 with RSA encryption.
319    ///
320    /// Corresponds to OID 1.2.840.113549.1.1.13.
321    RsaSha512,
322
323    /// ECDSA with SHA-256.
324    ///
325    /// Corresponds to OID 1.2.840.10045.4.3.2.
326    EcdsaSha256,
327
328    /// ECDSA with SHA-384.
329    ///
330    /// Corresponds to OID 1.2.840.10045.4.3.3.
331    EcdsaSha384,
332
333    /// ED25519
334    ///
335    /// Corresponds to OID 1.3.101.112.
336    Ed25519,
337
338    /// No signature with digest algorithm
339    ///
340    /// Corresponds to OID 1.3.6.1.5.5.7.6.2
341    NoSignature(DigestAlgorithm),
342}
343
344impl SignatureAlgorithm {
345    /// Attempt to resolve an instance from an OID, known [KeyAlgorithm], and optional [DigestAlgorithm].
346    ///
347    /// Signature algorithm OIDs in the wild are typically either:
348    ///
349    /// a) an OID that denotes the key algorithm and corresponding digest format (what this
350    ///    enumeration represents)
351    /// b) an OID that denotes just the key algorithm.
352    ///
353    /// What this function does is attempt to construct an instance from any OID.
354    /// If the OID defines a key + digest algorithm, we get a [SignatureAlgorithm]
355    /// from that. If we get a key algorithm we combine with the provided [DigestAlgorithm]
356    /// to resolve an appropriate [SignatureAlgorithm].
357    pub fn from_oid_and_digest_algorithm(
358        oid: &Oid,
359        digest_algorithm: DigestAlgorithm,
360    ) -> Result<Self, Error> {
361        match Self::try_from(oid) {
362            Ok(alg) => Ok(alg),
363            _ => match KeyAlgorithm::try_from(oid) {
364                Ok(key_alg) => match key_alg {
365                    KeyAlgorithm::Rsa => match digest_algorithm {
366                        DigestAlgorithm::Sha1 => Ok(Self::RsaSha1),
367                        DigestAlgorithm::Sha256 => Ok(Self::RsaSha256),
368                        DigestAlgorithm::Sha384 => Ok(Self::RsaSha384),
369                        DigestAlgorithm::Sha512 => Ok(Self::RsaSha512),
370                    },
371                    KeyAlgorithm::Ed25519 => Ok(Self::Ed25519),
372                    KeyAlgorithm::Ecdsa(_) => match digest_algorithm {
373                        DigestAlgorithm::Sha256 => Ok(Self::EcdsaSha256),
374                        DigestAlgorithm::Sha384 => Ok(Self::EcdsaSha384),
375                        DigestAlgorithm::Sha1 | DigestAlgorithm::Sha512 => {
376                            Err(Error::UnknownSignatureAlgorithm(format!(
377                                "cannot use digest {:?} with ECDSA",
378                                digest_algorithm
379                            )))
380                        }
381                    },
382                },
383                _ => {
384                    if oid == &OID_NO_SIGNATURE_ALGORITHM {
385                        Ok(Self::NoSignature(digest_algorithm))
386                    } else {
387                        Err(Error::UnknownSignatureAlgorithm(format!(
388                            "do not know how to resolve {} to a signature algorithm",
389                            oid
390                        )))
391                    }
392                }
393            },
394        }
395    }
396
397    /// Creates an instance with the noSignature mechanism and [DigestAlgorithm]
398    pub fn from_digest_algorithm(digest_algorithm: DigestAlgorithm) -> Self {
399        Self::NoSignature(digest_algorithm)
400    }
401
402    /// Attempt to resolve the verification algorithm using info about the signing key algorithm.
403    ///
404    /// Only specific combinations of methods are supported. e.g. you can only use
405    /// RSA verification with RSA signing keys. Same for ECDSA and ED25519.
406    pub fn resolve_verification_algorithm(
407        &self,
408        key_algorithm: KeyAlgorithm,
409    ) -> Result<&'static dyn signature::VerificationAlgorithm, Error> {
410        match key_algorithm {
411            KeyAlgorithm::Rsa => match self {
412                Self::RsaSha1 => Ok(&signature::RSA_PKCS1_2048_8192_SHA1_FOR_LEGACY_USE_ONLY),
413                Self::RsaSha256 => Ok(&signature::RSA_PKCS1_2048_8192_SHA256),
414                Self::RsaSha384 => Ok(&signature::RSA_PKCS1_2048_8192_SHA384),
415                Self::RsaSha512 => Ok(&signature::RSA_PKCS1_2048_8192_SHA512),
416                alg => Err(Error::UnsupportedSignatureVerification(key_algorithm, *alg)),
417            },
418            KeyAlgorithm::Ed25519 => match self {
419                Self::Ed25519 => Ok(&signature::ED25519),
420                alg => Err(Error::UnsupportedSignatureVerification(key_algorithm, *alg)),
421            },
422            KeyAlgorithm::Ecdsa(curve) => match curve {
423                EcdsaCurve::Secp256r1 => match self {
424                    Self::EcdsaSha256 => Ok(&signature::ECDSA_P256_SHA256_ASN1),
425                    Self::EcdsaSha384 => Ok(&signature::ECDSA_P256_SHA384_ASN1),
426                    alg => Err(Error::UnsupportedSignatureVerification(key_algorithm, *alg)),
427                },
428                EcdsaCurve::Secp384r1 => match self {
429                    Self::EcdsaSha256 => Ok(&signature::ECDSA_P384_SHA256_ASN1),
430                    Self::EcdsaSha384 => Ok(&signature::ECDSA_P384_SHA384_ASN1),
431                    alg => Err(Error::UnsupportedSignatureVerification(key_algorithm, *alg)),
432                },
433            },
434        }
435    }
436
437    /// Resolve the [DigestAlgorithm] for this signature algorithm.
438    pub fn digest_algorithm(&self) -> Option<DigestAlgorithm> {
439        match self {
440            SignatureAlgorithm::RsaSha1 => Some(DigestAlgorithm::Sha1),
441            SignatureAlgorithm::RsaSha256 => Some(DigestAlgorithm::Sha256),
442            SignatureAlgorithm::RsaSha384 => Some(DigestAlgorithm::Sha384),
443            SignatureAlgorithm::RsaSha512 => Some(DigestAlgorithm::Sha512),
444            SignatureAlgorithm::EcdsaSha256 => Some(DigestAlgorithm::Sha256),
445            SignatureAlgorithm::EcdsaSha384 => Some(DigestAlgorithm::Sha384),
446            // TODO there's got to be a digest algorithm, right?
447            SignatureAlgorithm::Ed25519 => None,
448            SignatureAlgorithm::NoSignature(digest_algorithm) => Some(*digest_algorithm),
449        }
450    }
451}
452
453impl Display for SignatureAlgorithm {
454    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
455        match self {
456            SignatureAlgorithm::RsaSha1 => f.write_str("SHA-1 with RSA encryption"),
457            SignatureAlgorithm::RsaSha256 => f.write_str("SHA-256 with RSA encryption"),
458            SignatureAlgorithm::RsaSha384 => f.write_str("SHA-384 with RSA encryption"),
459            SignatureAlgorithm::RsaSha512 => f.write_str("SHA-512 with RSA encryption"),
460            SignatureAlgorithm::EcdsaSha256 => f.write_str("ECDSA with SHA-256"),
461            SignatureAlgorithm::EcdsaSha384 => f.write_str("ECDSA with SHA-384"),
462            SignatureAlgorithm::Ed25519 => f.write_str("ED25519"),
463            SignatureAlgorithm::NoSignature(digest_algorithm) => {
464                f.write_fmt(format_args!("No signature with {}", digest_algorithm))
465            }
466        }
467    }
468}
469
470impl From<SignatureAlgorithm> for Oid {
471    fn from(alg: SignatureAlgorithm) -> Self {
472        Oid(match alg {
473            SignatureAlgorithm::RsaSha1 => OID_SHA1_RSA.as_ref(),
474            SignatureAlgorithm::RsaSha256 => OID_SHA256_RSA.as_ref(),
475            SignatureAlgorithm::RsaSha384 => OID_SHA384_RSA.as_ref(),
476            SignatureAlgorithm::RsaSha512 => OID_SHA512_RSA.as_ref(),
477            SignatureAlgorithm::EcdsaSha256 => OID_ECDSA_SHA256.as_ref(),
478            SignatureAlgorithm::EcdsaSha384 => OID_ECDSA_SHA384.as_ref(),
479            SignatureAlgorithm::Ed25519 => OID_ED25519_SIGNATURE_ALGORITHM.as_ref(),
480            SignatureAlgorithm::NoSignature(_) => OID_NO_SIGNATURE_ALGORITHM.as_ref(),
481        }
482        .into())
483    }
484}
485
486impl TryFrom<&Oid> for SignatureAlgorithm {
487    type Error = Error;
488
489    fn try_from(v: &Oid) -> Result<Self, Self::Error> {
490        if v == &OID_SHA1_RSA {
491            Ok(Self::RsaSha1)
492        } else if v == &OID_SHA256_RSA {
493            Ok(Self::RsaSha256)
494        } else if v == &OID_SHA384_RSA {
495            Ok(Self::RsaSha384)
496        } else if v == &OID_SHA512_RSA {
497            Ok(Self::RsaSha512)
498        } else if v == &OID_ECDSA_SHA256 {
499            Ok(Self::EcdsaSha256)
500        } else if v == &OID_ECDSA_SHA384 {
501            Ok(Self::EcdsaSha384)
502        } else if v == &OID_ED25519_SIGNATURE_ALGORITHM {
503            Ok(Self::Ed25519)
504        } else {
505            Err(Error::UnknownSignatureAlgorithm(format!("{}", v)))
506        }
507    }
508}
509
510impl TryFrom<&AlgorithmIdentifier> for SignatureAlgorithm {
511    type Error = Error;
512
513    fn try_from(v: &AlgorithmIdentifier) -> Result<Self, Self::Error> {
514        Self::try_from(&v.algorithm)
515    }
516}
517
518impl From<SignatureAlgorithm> for AlgorithmIdentifier {
519    fn from(alg: SignatureAlgorithm) -> Self {
520        Self {
521            algorithm: alg.into(),
522            parameters: None,
523        }
524    }
525}
526
527/// Represents a known curve used with ECDSA.
528#[derive(Copy, Clone, Debug, Eq, PartialEq)]
529pub enum EcdsaCurve {
530    Secp256r1,
531    Secp384r1,
532}
533
534impl EcdsaCurve {
535    /// Obtain all variants of this type.
536    pub fn all() -> &'static [Self] {
537        &[Self::Secp256r1, Self::Secp384r1]
538    }
539
540    /// Obtain the OID representing this elliptic curve.
541    pub fn as_signature_oid(&self) -> Oid {
542        Oid(match self {
543            Self::Secp256r1 => OID_EC_SECP256R1.as_ref().into(),
544            Self::Secp384r1 => OID_EC_SECP384R1.as_ref().into(),
545        })
546    }
547}
548
549impl TryFrom<&Oid> for EcdsaCurve {
550    type Error = Error;
551
552    fn try_from(v: &Oid) -> Result<Self, Self::Error> {
553        if v == &OID_EC_SECP256R1 {
554            Ok(Self::Secp256r1)
555        } else if v == &OID_EC_SECP384R1 {
556            Ok(Self::Secp384r1)
557        } else {
558            Err(Error::UnknownEllipticCurve(format!("{}", v)))
559        }
560    }
561}
562
563impl From<EcdsaCurve> for &'static signature::EcdsaSigningAlgorithm {
564    fn from(curve: EcdsaCurve) -> Self {
565        match curve {
566            EcdsaCurve::Secp256r1 => &signature::ECDSA_P256_SHA256_ASN1_SIGNING,
567            EcdsaCurve::Secp384r1 => &signature::ECDSA_P384_SHA384_ASN1_SIGNING,
568        }
569    }
570}
571
572/// Cryptographic algorithm used by a private key.
573///
574/// Instances can be converted to/from the underlying ASN.1 type and
575/// OIDs.
576#[derive(Copy, Clone, Debug, Eq, PartialEq)]
577pub enum KeyAlgorithm {
578    /// RSA
579    ///
580    /// Corresponds to OID 1.2.840.113549.1.1.1.
581    Rsa,
582
583    /// Corresponds to OID 1.2.840.10045.2.1
584    ///
585    /// The inner OID tracks the curve / parameter in use.
586    Ecdsa(EcdsaCurve),
587
588    /// Corresponds to OID 1.3.101.110
589    Ed25519,
590}
591
592impl Display for KeyAlgorithm {
593    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
594        match self {
595            Self::Rsa => f.write_str("RSA"),
596            Self::Ecdsa(_) => f.write_str("ECDSA"),
597            Self::Ed25519 => f.write_str("ED25519"),
598        }
599    }
600}
601
602impl TryFrom<&Oid> for KeyAlgorithm {
603    type Error = Error;
604
605    fn try_from(v: &Oid) -> Result<Self, Self::Error> {
606        if v == &OID_RSA {
607            Ok(Self::Rsa)
608        } else if v == &OID_EC_PUBLIC_KEY {
609            // Default to an arbitrary elliptic curve when just the OID is given to us.
610            Ok(Self::Ecdsa(EcdsaCurve::Secp384r1))
611        // ED25519 appears to use the signature algorithm OID for private key
612        // identification, so we need to accept both.
613        } else if v == &OID_ED25519_KEY_AGREEMENT || v == &OID_ED25519_SIGNATURE_ALGORITHM {
614            Ok(Self::Ed25519)
615        } else {
616            Err(Error::UnknownKeyAlgorithm(format!("{}", v)))
617        }
618    }
619}
620
621impl TryFrom<&ObjectIdentifier> for KeyAlgorithm {
622    type Error = Error;
623
624    fn try_from(v: &ObjectIdentifier) -> Result<Self, Self::Error> {
625        // Similar implementation as above.
626        match v.as_bytes() {
627            x if x == OID_RSA.as_ref() => Ok(Self::Rsa),
628            x if x == OID_EC_PUBLIC_KEY.as_ref() => Ok(Self::Ecdsa(EcdsaCurve::Secp384r1)),
629            x if x == OID_ED25519_KEY_AGREEMENT.as_ref()
630                || x == OID_ED25519_SIGNATURE_ALGORITHM.as_ref() =>
631            {
632                Ok(Self::Ed25519)
633            }
634            _ => Err(Error::UnknownKeyAlgorithm(v.to_string())),
635        }
636    }
637}
638
639impl From<KeyAlgorithm> for Oid {
640    fn from(alg: KeyAlgorithm) -> Self {
641        Oid(match alg {
642            KeyAlgorithm::Rsa => OID_RSA.as_ref(),
643            KeyAlgorithm::Ecdsa(_) => OID_EC_PUBLIC_KEY.as_ref(),
644            KeyAlgorithm::Ed25519 => OID_ED25519_KEY_AGREEMENT.as_ref(),
645        }
646        .into())
647    }
648}
649
650impl From<KeyAlgorithm> for ObjectIdentifier {
651    fn from(alg: KeyAlgorithm) -> Self {
652        let bytes = match alg {
653            KeyAlgorithm::Rsa => OID_RSA.as_ref(),
654            KeyAlgorithm::Ecdsa(_) => OID_EC_PUBLIC_KEY.as_ref(),
655            KeyAlgorithm::Ed25519 => OID_ED25519_KEY_AGREEMENT.as_ref(),
656        };
657
658        ObjectIdentifier::from_bytes(bytes).expect("OID bytes should be valid")
659    }
660}
661
662impl TryFrom<&AlgorithmIdentifier> for KeyAlgorithm {
663    type Error = Error;
664
665    fn try_from(v: &AlgorithmIdentifier) -> Result<Self, Self::Error> {
666        // This will obtain a generic instance with defaults for configurable
667        // parameters. So check for and apply parameters.
668        let ka = Self::try_from(&v.algorithm)?;
669
670        let ka = if let Some(params) = &v.parameters {
671            match ka {
672                Self::Ecdsa(_) => {
673                    let curve_oid = params.decode_oid()?;
674                    let curve = EcdsaCurve::try_from(&curve_oid)?;
675
676                    Ok(Self::Ecdsa(curve))
677                }
678                Self::Ed25519 => {
679                    // NULL is meaningless. Just a placeholder. Allow it through.
680                    if params.as_slice() == [0x05, 0x00] {
681                        Ok(ka)
682                    } else {
683                        Err(Error::UnhandledKeyAlgorithmParameters("on ED25519"))
684                    }
685                }
686                Self::Rsa => {
687                    // NULL is meaningless. Just a placeholder. Allow it through.
688                    if params.as_slice() == [0x05, 0x00] {
689                        Ok(ka)
690                    } else {
691                        Err(Error::UnhandledKeyAlgorithmParameters("on RSA"))
692                    }
693                }
694            }?
695        } else {
696            ka
697        };
698
699        Ok(ka)
700    }
701}
702
703impl From<KeyAlgorithm> for AlgorithmIdentifier {
704    fn from(alg: KeyAlgorithm) -> Self {
705        let parameters = match alg {
706            KeyAlgorithm::Ed25519 => None,
707            KeyAlgorithm::Rsa => None,
708            KeyAlgorithm::Ecdsa(curve) => {
709                Some(AlgorithmParameter::from_oid(curve.as_signature_oid()))
710            }
711        };
712
713        Self {
714            algorithm: alg.into(),
715            parameters,
716        }
717    }
718}
719
720#[cfg(test)]
721mod test {
722    use super::*;
723
724    #[test]
725    fn digest_pkcs1() -> Result<(), Error> {
726        let message = b"deadbeef";
727        let raw_digest = DigestAlgorithm::Sha256.digest_data(message);
728
729        // RSA 1024.
730        let encoded = DigestAlgorithm::Sha256.rsa_pkcs1_encode(message, 128)?;
731        assert_eq!(&encoded[0..3], &[0x00, 0x01, 0xff]);
732        assert_eq!(&encoded[96..], &raw_digest);
733
734        Ok(())
735    }
736
737    #[test]
738    fn key_algorithm_oids() -> Result<(), Error> {
739        let oid = ObjectIdentifier::from(KeyAlgorithm::Rsa);
740        assert_eq!(oid.to_string(), "1.2.840.113549.1.1.1");
741        let oid = ObjectIdentifier::new("1.2.840.113549.1.1.1").unwrap();
742        assert_eq!(KeyAlgorithm::try_from(&oid)?, KeyAlgorithm::Rsa);
743
744        let oid = ObjectIdentifier::from(KeyAlgorithm::Ecdsa(EcdsaCurve::Secp256r1));
745        assert_eq!(oid.to_string(), "1.2.840.10045.2.1");
746        let oid = ObjectIdentifier::new("1.2.840.10045.2.1").unwrap();
747        assert_eq!(
748            KeyAlgorithm::try_from(&oid)?,
749            KeyAlgorithm::Ecdsa(EcdsaCurve::Secp384r1)
750        );
751
752        let oid = ObjectIdentifier::from(KeyAlgorithm::Ed25519);
753        assert_eq!(oid.to_string(), "1.3.101.110");
754        let oid = ObjectIdentifier::new("1.3.101.110").unwrap();
755        assert_eq!(KeyAlgorithm::try_from(&oid)?, KeyAlgorithm::Ed25519);
756
757        Ok(())
758    }
759}