cli/key/external_key/
mod.rs

1// SPDX-License-Identifier: GPL-3-0-or-later
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5mod ecc;
6mod rsa;
7
8pub use ecc::*;
9pub use rsa::*;
10
11use crate::key::KeyError;
12use rasn::{
13    types::{Any, Integer, ObjectIdentifier, OctetString, SetOf},
14    AsnType, Decode, Decoder, Encode,
15};
16use std::{borrow::Cow, fmt};
17
18pub const OID_EC_PUBLIC_KEY: ObjectIdentifier =
19    ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 2, 840, 10_045, 2, 1]));
20pub const OID_SHA1_WITH_RSA_ENCRYPTION: ObjectIdentifier =
21    ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 2, 840, 113_549, 1, 1, 5]));
22pub const OID_SHA256_WITH_RSA_ENCRYPTION: ObjectIdentifier =
23    ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 2, 840, 113_549, 1, 1, 11]));
24pub const OID_SHA384_WITH_RSA_ENCRYPTION: ObjectIdentifier =
25    ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 2, 840, 113_549, 1, 1, 12]));
26pub const OID_SHA512_WITH_RSA_ENCRYPTION: ObjectIdentifier =
27    ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 2, 840, 113_549, 1, 1, 13]));
28pub const OID_ECDSA_WITH_SHA256: ObjectIdentifier =
29    ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 2, 840, 10045, 4, 3, 2]));
30pub const OID_ECDSA_WITH_SHA384: ObjectIdentifier =
31    ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 2, 840, 10045, 4, 3, 3]));
32pub const OID_ECDSA_WITH_SHA512: ObjectIdentifier =
33    ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 2, 840, 10045, 4, 3, 4]));
34pub const OID_RSA_ENCRYPTION: ObjectIdentifier =
35    ObjectIdentifier::new_unchecked(Cow::Borrowed(&[1, 2, 840, 113_549, 1, 1, 1]));
36
37#[derive(AsnType, Decode, Encode, Debug)]
38pub struct Pkcs8AlgorithmIdentifier {
39    pub algorithm: ObjectIdentifier,
40    pub parameters: Option<Any>,
41}
42
43#[derive(AsnType, Decode, Encode, Debug)]
44pub struct Pkcs8PrivateKeyInfo {
45    pub version: Integer,
46    pub private_key_algorithm: Pkcs8AlgorithmIdentifier,
47    pub private_key: OctetString,
48    #[rasn(tag(context, 0))]
49    pub attributes: Option<SetOf<Any>>,
50}
51
52#[derive(Clone)]
53pub enum ExternalKey {
54    Rsa2048(Box<::rsa::RsaPrivateKey>),
55    Rsa3072(Box<::rsa::RsaPrivateKey>),
56    Rsa4096(Box<::rsa::RsaPrivateKey>),
57    EccP256(Box<::p256::SecretKey>),
58    EccP384(Box<::p384::SecretKey>),
59    EccP521(Box<::p521::SecretKey>),
60}
61
62impl fmt::Debug for ExternalKey {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        match self {
65            Self::Rsa2048(_) => f.debug_tuple("rsa-2048").field(&"<sensitive>").finish(),
66            Self::Rsa3072(_) => f.debug_tuple("rsa-3072").field(&"<sensitive>").finish(),
67            Self::Rsa4096(_) => f.debug_tuple("rsa-4096").field(&"<sensitive>").finish(),
68            Self::EccP256(_) => f
69                .debug_tuple("ecc-nist-p256")
70                .field(&"<sensitive>")
71                .finish(),
72            Self::EccP384(_) => f
73                .debug_tuple("ecc-nist-p384")
74                .field(&"<sensitive>")
75                .finish(),
76            Self::EccP521(_) => f
77                .debug_tuple("ecc-nist-p521")
78                .field(&"<sensitive>")
79                .finish(),
80        }
81    }
82}
83
84impl ExternalKey {
85    /// Load and parse a DER-encoded private key from a byte slice.
86    ///
87    /// # Errors
88    ///
89    /// Returns `KeyError` on parsing failure.
90    pub fn from_der(der_bytes: &[u8]) -> Result<ExternalKey, KeyError> {
91        if let Ok(pkcs8_key) = rasn::der::decode::<Pkcs8PrivateKeyInfo>(der_bytes) {
92            let oid = &pkcs8_key.private_key_algorithm.algorithm;
93            let inner_key_bytes = pkcs8_key.private_key.as_ref();
94
95            if oid == &OID_RSA_ENCRYPTION {
96                return parse_rsa_from_der(inner_key_bytes);
97            }
98            if oid == &OID_EC_PUBLIC_KEY {
99                let params_any = pkcs8_key.private_key_algorithm.parameters.as_ref().ok_or(
100                    KeyError::ValueConversionFailed(
101                        "missing curve OID in AlgorithmIdentifier".to_string(),
102                    ),
103                )?;
104                let curve_oid: ObjectIdentifier = rasn::der::decode(params_any.as_ref())?;
105                return parse_ecc_from_der(inner_key_bytes, Some(&curve_oid));
106            }
107            return Err(KeyError::UnsupportedOid(oid.to_string()));
108        }
109
110        if let Ok(key) = parse_rsa_from_der(der_bytes) {
111            return Ok(key);
112        }
113
114        if let Ok(key) = parse_ecc_from_der(der_bytes, None) {
115            return Ok(key);
116        }
117
118        Err(KeyError::InvalidFormat)
119    }
120}