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 #[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 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 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 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 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 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 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}