image4_pki/
keys.rs

1use crate::{error::DecodeError, Signature};
2use const_oid::db::rfc5912::{ID_EC_PUBLIC_KEY, RSA_ENCRYPTION};
3use der::{Document, SecretDocument};
4use pkcs8::DecodePrivateKey;
5use rsa::{
6    pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey},
7    pkcs8::{AssociatedOid, EncodePrivateKey, PrivateKeyInfo},
8};
9use sec1::DecodeEcPrivateKey;
10use signature::{Keypair, Signer};
11use spki::{
12    AlgorithmIdentifierOwned, DecodePublicKey, DynSignatureAlgorithmIdentifier, EncodePublicKey,
13    SubjectPublicKeyInfoRef,
14};
15use std::path::Path;
16
17macro_rules! def_key {
18    (
19        $name:ident, $rsa_base:ident, $ec_base:ident, $op_key:ident, $which:literal,
20        $dec_res:ty, $dec_info:ty,
21        $enc_trait:ident, $enc_method:ident, $enc_res:ty
22    ) => {
23        #[doc = concat!(
24            "A ", $which, " key for any algorithm supported by the crate.\n\n",
25            "This key is split from the [`", stringify!($op_key), "`] type so that it could be ",
26            "decoded and encoded using the [`", stringify!($dec_trait), "`], [`",
27            stringify!($dec_rsa_trait), "`] and [`", stringify!($enc_trait), "`] traits."
28        )]
29        #[derive(Clone, Eq, PartialEq, Debug)]
30        #[non_exhaustive]
31        // TODO: find a good way to remove this
32        #[allow(clippy::large_enum_variant)]
33        pub enum $name {
34            #[doc = concat!("PKCS#1 v1.5 RSA ", $which, " key.")]
35            Rsa(::rsa::$rsa_base),
36            #[doc = concat!("ECDSA with NIST P-256 curve ", $which, " key.")]
37            EcP256(::p256::$ec_base),
38            #[doc = concat!("ECDSA with NIST P-384 curve ", $which, " key.")]
39            EcP384(::p384::$ec_base),
40        }
41
42        impl From<::rsa::$rsa_base> for $name {
43            fn from(value: ::rsa::$rsa_base) -> Self {
44                Self::Rsa(value)
45            }
46        }
47
48        impl From<::p256::$ec_base> for $name {
49            fn from(value: ::p256::$ec_base) -> Self {
50                Self::EcP256(value)
51            }
52        }
53
54        impl From<::p384::$ec_base> for $name {
55            fn from(value: ::p384::$ec_base) -> Self {
56                Self::EcP384(value)
57            }
58        }
59
60        impl ::core::convert::TryFrom<$dec_info> for $name {
61            type Error = $dec_res;
62
63            fn try_from(info: $dec_info) -> Result<Self, $dec_res> {
64                if info.algorithm.oid == RSA_ENCRYPTION {
65                    ::core::convert::TryFrom::try_from(info).map(Self::Rsa)
66                } else if info.algorithm.oid == ID_EC_PUBLIC_KEY {
67                    let params: ::spki::ObjectIdentifier = info
68                        .algorithm
69                        .parameters
70                        .map(::der::asn1::AnyRef::decode_as)
71                        .transpose()?
72                        .ok_or(::spki::Error::AlgorithmParametersMissing)?;
73
74                    if params == ::p256::NistP256::OID {
75                        ::core::convert::TryFrom::try_from(info).map(Self::EcP256)
76                    } else if params == ::p384::NistP384::OID {
77                        ::core::convert::TryFrom::try_from(info).map(Self::EcP384)
78                    } else {
79                        ::core::result::Result::Err(::spki::Error::OidUnknown {
80                            oid: params
81                        }.into())
82                    }
83                } else {
84                    ::core::result::Result::Err(::spki::Error::OidUnknown {
85                        oid: info.algorithm.oid,
86                    }
87                    .into())
88                }
89            }
90        }
91
92        impl $enc_trait for $name {
93            fn $enc_method(&self) -> $enc_res {
94                match self {
95                    Self::Rsa(key) => key.$enc_method(),
96                    Self::EcP256(key) => key.$enc_method(),
97                    Self::EcP384(key) => key.$enc_method(),
98                }
99            }
100        }
101    };
102}
103
104def_key!(
105    PrivateKey,
106    RsaPrivateKey,
107    SecretKey,
108    SigningKey,
109    "private",
110    pkcs8::Error,
111    PrivateKeyInfo<'_>,
112    EncodePrivateKey,
113    to_pkcs8_der,
114    pkcs8::Result<SecretDocument>
115);
116
117impl PrivateKey {
118    fn from_pem_label_and_doc(label: String, doc: &SecretDocument) -> Result<Self, DecodeError> {
119        match label.as_str() {
120            "RSA PRIVATE KEY" => Ok(PrivateKey::from_pkcs1_der(doc.as_bytes())?),
121            "PRIVATE KEY" => Ok(PrivateKey::from_pkcs8_der(doc.as_bytes())?),
122            "EC PRIVATE KEY" => Ok(PrivateKey::from_sec1_der(doc.as_bytes())?),
123            _ => Err(DecodeError::UnexpectedLabel(label)),
124        }
125    }
126
127    /// Tries to parse a PEM file at the specified path as a private key.
128    pub fn read_pem_file(path: impl AsRef<Path>) -> Result<Self, DecodeError> {
129        let (label, doc) = SecretDocument::read_pem_file(path)?;
130
131        Self::from_pem_label_and_doc(label, &doc)
132    }
133
134    /// Tries to parse an in-memory PEM file as a private key.
135    pub fn from_pem(pem: &str) -> Result<Self, DecodeError> {
136        let (label, doc) = SecretDocument::from_pem(pem)?;
137
138        Self::from_pem_label_and_doc(label.to_string(), &doc)
139    }
140
141    /// Creates a public key from a private key.
142    pub fn to_public_key(&self) -> PublicKey {
143        match self {
144            PrivateKey::Rsa(key) => PublicKey::Rsa(key.to_public_key()),
145            PrivateKey::EcP256(key) => PublicKey::EcP256(key.public_key()),
146            PrivateKey::EcP384(key) => PublicKey::EcP384(key.public_key()),
147        }
148    }
149}
150
151def_key!(
152    PublicKey,
153    RsaPublicKey,
154    PublicKey,
155    VerifyingKey,
156    "public",
157    spki::Error,
158    SubjectPublicKeyInfoRef<'_>,
159    EncodePublicKey,
160    to_public_key_der,
161    spki::Result<Document>
162);
163
164impl PublicKey {
165    fn from_pem_label_and_doc(label: &str, doc: &Document) -> spki::Result<Self> {
166        let bytes = doc.as_bytes();
167
168        match label {
169            "RSA PUBLIC KEY" => Ok(PublicKey::from_pkcs1_der(bytes)?),
170            "PUBLIC KEY" => PublicKey::from_public_key_der(bytes),
171            _ => Err(spki::Error::Asn1(
172                der::pem::Error::UnexpectedTypeLabel {
173                    expected: "PUBLIC KEY\" or \"RSA PUBLIC KEY",
174                }
175                .into(),
176            )),
177        }
178    }
179
180    /// Tries to parse a PEM file at the specified path as a public key.
181    pub fn read_pem_file(path: impl AsRef<Path>) -> spki::Result<Self> {
182        let (label, doc) = Document::read_pem_file(path)?;
183
184        Self::from_pem_label_and_doc(&label, &doc)
185    }
186
187    /// Tries to parse an in-memory PEM file as a public key.
188    pub fn from_pem(pem: &str) -> spki::Result<Self> {
189        let (label, doc) = Document::from_pem(pem)?;
190
191        Self::from_pem_label_and_doc(label, &doc)
192    }
193}
194
195macro_rules! def_op_key {
196    ($name:ident, $base:ident, $what:literal, $enc_trait:ident, $enc_method:ident, $enc_res:ty) => {
197        #[derive(Clone, Debug)]
198        #[doc = concat!("A ", $what, " key for any algorithm supported by the crate.")]
199        #[non_exhaustive]
200        pub enum $name {
201            #[doc = concat!("PKCS#1 v1.5 RSA ", $what, " key which uses SHA-1 as the digest.")]
202            RsaWithSha1(::rsa::pkcs1v15::$name<::sha1::Sha1>),
203            #[doc = concat!("PKCS#1 v1.5 RSA ", $what, " key which uses SHA-256 as the digest.")]
204            RsaWithSha256(::rsa::pkcs1v15::$name<::sha2::Sha256>),
205            #[doc = concat!("PKCS#1 v1.5 RSA ", $what, " key which uses SHA-384 as the digest.")]
206            RsaWithSha384(::rsa::pkcs1v15::$name<::sha2::Sha384>),
207            #[doc = concat!("ECDSA/P-256 ", $what, " key (uses SHA-256 as the digest primitive).")]
208            EcP256(::p256::ecdsa::$name),
209            #[doc = concat!("ECDSA/P-384 ", $what, " key (uses SHA-384 as the digest primitive).")]
210            EcP384(::p384::ecdsa::$name),
211        }
212
213        impl $name {
214            #[doc = concat!(
215                "Creates a new [`", stringify!($name), "`] from a [`", stringify!($base), "`] and",
216                " a [`DigestAlgo`]."
217            )]
218            /// If no digest algorithm is provided, a default one is used.
219            ///
220            /// For RSA PKCS#1 v1.5 signatures the default is SHA-384, for elliptic curve algorithms the
221            /// default and the only supported digest algorithms are SHA-256 for NIST P-256 and SHA-384 for
222            /// NIST P-384.
223            ///
224            /// [`DigestAlgo`]: crate::DigestAlgo
225            pub fn from_key_and_algo(
226                key: $base,
227                algo: ::core::option::Option<$crate::DigestAlgo>
228            ) -> ::core::result::Result<Self, $crate::error::UnsupportedDigest> {
229                match (key, algo) {
230                    ($base::Rsa(key), Some($crate::DigestAlgo::Sha1)) => Ok(Self::RsaWithSha1(::rsa::pkcs1v15::$name::new(key))),
231                    ($base::Rsa(key), Some($crate::DigestAlgo::Sha256)) => Ok(Self::RsaWithSha256(::rsa::pkcs1v15::$name::new(key))),
232                    ($base::Rsa(key), Some($crate::DigestAlgo::Sha384) | None) => Ok(Self::RsaWithSha384(::rsa::pkcs1v15::$name::new(key))),
233                    ($base::EcP256(key), Some($crate::DigestAlgo::Sha256) | None) => Ok(Self::EcP256(key.into())),
234                    ($base::EcP384(key), Some($crate::DigestAlgo::Sha384) | None) => Ok(Self::EcP384(key.into())),
235                    ($base::EcP256(_), Some(other)) => Err($crate::error::UnsupportedDigest {
236                        signing_algo: $crate::SigningAlgo::EcP256,
237                        digest_algo: other,
238                    }),
239                    ($base::EcP384(_), Some(other)) => Err($crate::error::UnsupportedDigest {
240                        signing_algo: $crate::SigningAlgo::EcP384,
241                        digest_algo: other,
242                    })
243                }
244            }
245        }
246
247        impl $enc_trait for $name {
248            fn $enc_method(&self) -> $enc_res {
249                match self {
250                    Self::RsaWithSha1(key) => key.$enc_method(),
251                    Self::RsaWithSha256(key) => key.$enc_method(),
252                    Self::RsaWithSha384(key) => key.$enc_method(),
253                    Self::EcP256(key) => key.$enc_method(),
254                    Self::EcP384(key) => key.$enc_method(),
255                }
256            }
257        }
258
259        impl From<$name> for $base {
260            fn from(value: $name) -> Self {
261                match value {
262                    $name::RsaWithSha1(key) => Self::Rsa(key.into()),
263                    $name::RsaWithSha256(key) => Self::Rsa(key.into()),
264                    $name::RsaWithSha384(key) => Self::Rsa(key.into()),
265                    $name::EcP256(key) => Self::EcP256(key.into()),
266                    $name::EcP384(key) => Self::EcP384(key.into()),
267                }
268            }
269        }
270    };
271}
272
273def_op_key!(
274    SigningKey,
275    PrivateKey,
276    "signing",
277    EncodePrivateKey,
278    to_pkcs8_der,
279    pkcs8::Result<SecretDocument>
280);
281
282def_op_key!(
283    VerifyingKey,
284    PublicKey,
285    "verifying",
286    EncodePublicKey,
287    to_public_key_der,
288    spki::Result<Document>
289);
290
291impl Signer<Signature> for SigningKey {
292    fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
293        match self {
294            SigningKey::RsaWithSha1(key) => key.try_sign(msg).map(Into::into),
295            SigningKey::RsaWithSha256(key) => key.try_sign(msg).map(Into::into),
296            SigningKey::RsaWithSha384(key) => key.try_sign(msg).map(Into::into),
297            SigningKey::EcP256(key) => {
298                <p256::ecdsa::SigningKey as Signer<p256::ecdsa::Signature>>::try_sign(key, msg)
299                    .map(Into::into)
300            }
301            SigningKey::EcP384(key) => {
302                <p384::ecdsa::SigningKey as Signer<p384::ecdsa::Signature>>::try_sign(key, msg)
303                    .map(Into::into)
304            }
305        }
306    }
307}
308
309impl Keypair for SigningKey {
310    type VerifyingKey = VerifyingKey;
311
312    fn verifying_key(&self) -> Self::VerifyingKey {
313        match self {
314            SigningKey::RsaWithSha1(key) => VerifyingKey::RsaWithSha1(key.verifying_key()),
315            SigningKey::RsaWithSha256(key) => VerifyingKey::RsaWithSha256(key.verifying_key()),
316            SigningKey::RsaWithSha384(key) => VerifyingKey::RsaWithSha384(key.verifying_key()),
317            SigningKey::EcP256(key) => VerifyingKey::EcP256(*key.verifying_key()),
318            SigningKey::EcP384(key) => VerifyingKey::EcP384(*key.verifying_key()),
319        }
320    }
321}
322
323impl DynSignatureAlgorithmIdentifier for SigningKey {
324    fn signature_algorithm_identifier(&self) -> spki::Result<AlgorithmIdentifierOwned> {
325        match self {
326            SigningKey::RsaWithSha1(key) => key.signature_algorithm_identifier(),
327            SigningKey::RsaWithSha256(key) => key.signature_algorithm_identifier(),
328            SigningKey::RsaWithSha384(key) => key.signature_algorithm_identifier(),
329            SigningKey::EcP256(key) => key.signature_algorithm_identifier(),
330            SigningKey::EcP384(key) => key.signature_algorithm_identifier(),
331        }
332    }
333}