• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! PKCS#8 `PrivateKeyInfo`.
2 
3 use crate::{AlgorithmIdentifier, Error, Result, Version};
4 use core::fmt;
5 use der::{
6     asn1::{AnyRef, BitStringRef, ContextSpecific, OctetStringRef},
7     Decode, DecodeValue, Encode, Header, Reader, Sequence, TagMode, TagNumber,
8 };
9 
10 #[cfg(feature = "alloc")]
11 use der::SecretDocument;
12 
13 #[cfg(feature = "encryption")]
14 use {
15     crate::EncryptedPrivateKeyInfo,
16     der::zeroize::Zeroizing,
17     pkcs5::pbes2,
18     rand_core::{CryptoRng, RngCore},
19 };
20 
21 #[cfg(feature = "pem")]
22 use der::pem::PemLabel;
23 
24 #[cfg(feature = "subtle")]
25 use subtle::{Choice, ConstantTimeEq};
26 
27 /// Context-specific tag number for the public key.
28 const PUBLIC_KEY_TAG: TagNumber = TagNumber::N1;
29 
30 /// PKCS#8 `PrivateKeyInfo`.
31 ///
32 /// ASN.1 structure containing an [`AlgorithmIdentifier`], private key
33 /// data in an algorithm specific format, and optional attributes
34 /// (ignored by this implementation).
35 ///
36 /// Supports PKCS#8 v1 as described in [RFC 5208] and PKCS#8 v2 as described
37 /// in [RFC 5958]. PKCS#8 v2 keys include an additional public key field.
38 ///
39 /// # PKCS#8 v1 `PrivateKeyInfo`
40 ///
41 /// Described in [RFC 5208 Section 5]:
42 ///
43 /// ```text
44 /// PrivateKeyInfo ::= SEQUENCE {
45 ///         version                   Version,
46 ///         privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
47 ///         privateKey                PrivateKey,
48 ///         attributes           [0]  IMPLICIT Attributes OPTIONAL }
49 ///
50 /// Version ::= INTEGER
51 ///
52 /// PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
53 ///
54 /// PrivateKey ::= OCTET STRING
55 ///
56 /// Attributes ::= SET OF Attribute
57 /// ```
58 ///
59 /// # PKCS#8 v2 `OneAsymmetricKey`
60 ///
61 /// PKCS#8 `OneAsymmetricKey` as described in [RFC 5958 Section 2]:
62 ///
63 /// ```text
64 /// PrivateKeyInfo ::= OneAsymmetricKey
65 ///
66 /// OneAsymmetricKey ::= SEQUENCE {
67 ///     version                   Version,
68 ///     privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
69 ///     privateKey                PrivateKey,
70 ///     attributes            [0] Attributes OPTIONAL,
71 ///     ...,
72 ///     [[2: publicKey        [1] PublicKey OPTIONAL ]],
73 ///     ...
74 ///   }
75 ///
76 /// Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2)
77 ///
78 /// PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
79 ///
80 /// PrivateKey ::= OCTET STRING
81 ///
82 /// Attributes ::= SET OF Attribute
83 ///
84 /// PublicKey ::= BIT STRING
85 /// ```
86 ///
87 /// [RFC 5208]: https://tools.ietf.org/html/rfc5208
88 /// [RFC 5958]: https://datatracker.ietf.org/doc/html/rfc5958
89 /// [RFC 5208 Section 5]: https://tools.ietf.org/html/rfc5208#section-5
90 /// [RFC 5958 Section 2]: https://datatracker.ietf.org/doc/html/rfc5958#section-2
91 #[derive(Clone)]
92 pub struct PrivateKeyInfo<'a> {
93     /// X.509 [`AlgorithmIdentifier`] for the private key type.
94     pub algorithm: AlgorithmIdentifier<'a>,
95 
96     /// Private key data.
97     pub private_key: &'a [u8],
98 
99     /// Public key data, optionally available if version is V2.
100     pub public_key: Option<&'a [u8]>,
101 }
102 
103 impl<'a> PrivateKeyInfo<'a> {
104     /// Create a new PKCS#8 [`PrivateKeyInfo`] message.
105     ///
106     /// This is a helper method which initializes `attributes` and `public_key`
107     /// to `None`, helpful if you aren't using those.
new(algorithm: AlgorithmIdentifier<'a>, private_key: &'a [u8]) -> Self108     pub fn new(algorithm: AlgorithmIdentifier<'a>, private_key: &'a [u8]) -> Self {
109         Self {
110             algorithm,
111             private_key,
112             public_key: None,
113         }
114     }
115 
116     /// Get the PKCS#8 [`Version`] for this structure.
117     ///
118     /// [`Version::V1`] if `public_key` is `None`, [`Version::V2`] if `Some`.
version(&self) -> Version119     pub fn version(&self) -> Version {
120         if self.public_key.is_some() {
121             Version::V2
122         } else {
123             Version::V1
124         }
125     }
126 
127     /// Encrypt this private key using a symmetric encryption key derived
128     /// from the provided password.
129     ///
130     /// Uses the following algorithms for encryption:
131     /// - PBKDF: scrypt with default parameters:
132     ///   - log₂(N): 15
133     ///   - r: 8
134     ///   - p: 1
135     /// - Cipher: AES-256-CBC (best available option for PKCS#5 encryption)
136     #[cfg(feature = "encryption")]
137     #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
encrypt( &self, rng: impl CryptoRng + RngCore, password: impl AsRef<[u8]>, ) -> Result<SecretDocument>138     pub fn encrypt(
139         &self,
140         rng: impl CryptoRng + RngCore,
141         password: impl AsRef<[u8]>,
142     ) -> Result<SecretDocument> {
143         let der = Zeroizing::new(self.to_vec()?);
144         EncryptedPrivateKeyInfo::encrypt(rng, password, der.as_ref())
145     }
146 
147     /// Encrypt this private key using a symmetric encryption key derived
148     /// from the provided password and [`pbes2::Parameters`].
149     #[cfg(feature = "encryption")]
150     #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
encrypt_with_params( &self, pbes2_params: pbes2::Parameters<'_>, password: impl AsRef<[u8]>, ) -> Result<SecretDocument>151     pub fn encrypt_with_params(
152         &self,
153         pbes2_params: pbes2::Parameters<'_>,
154         password: impl AsRef<[u8]>,
155     ) -> Result<SecretDocument> {
156         let der = Zeroizing::new(self.to_vec()?);
157         EncryptedPrivateKeyInfo::encrypt_with(pbes2_params, password, der.as_ref())
158     }
159 }
160 
161 impl<'a> DecodeValue<'a> for PrivateKeyInfo<'a> {
decode_value<R: Reader<'a>>( reader: &mut R, header: Header, ) -> der::Result<PrivateKeyInfo<'a>>162     fn decode_value<R: Reader<'a>>(
163         reader: &mut R,
164         header: Header,
165     ) -> der::Result<PrivateKeyInfo<'a>> {
166         reader.read_nested(header.length, |reader| {
167             // Parse and validate `version` INTEGER.
168             let version = Version::decode(reader)?;
169             let algorithm = reader.decode()?;
170             let private_key = OctetStringRef::decode(reader)?.into();
171             let public_key = reader
172                 .context_specific::<BitStringRef<'_>>(PUBLIC_KEY_TAG, TagMode::Implicit)?
173                 .map(|bs| {
174                     bs.as_bytes()
175                         .ok_or_else(|| der::Tag::BitString.value_error())
176                 })
177                 .transpose()?;
178 
179             if version.has_public_key() != public_key.is_some() {
180                 return Err(reader.error(
181                     der::Tag::ContextSpecific {
182                         constructed: true,
183                         number: PUBLIC_KEY_TAG,
184                     }
185                     .value_error()
186                     .kind(),
187                 ));
188             }
189 
190             // Ignore any remaining extension fields
191             while !reader.is_finished() {
192                 reader.decode::<ContextSpecific<AnyRef<'_>>>()?;
193             }
194 
195             Ok(Self {
196                 algorithm,
197                 private_key,
198                 public_key,
199             })
200         })
201     }
202 }
203 
204 impl<'a> Sequence<'a> for PrivateKeyInfo<'a> {
fields<F, T>(&self, f: F) -> der::Result<T> where F: FnOnce(&[&dyn Encode]) -> der::Result<T>,205     fn fields<F, T>(&self, f: F) -> der::Result<T>
206     where
207         F: FnOnce(&[&dyn Encode]) -> der::Result<T>,
208     {
209         f(&[
210             &u8::from(self.version()),
211             &self.algorithm,
212             &OctetStringRef::new(self.private_key)?,
213             &self
214                 .public_key
215                 .map(|pk| {
216                     BitStringRef::from_bytes(pk).map(|value| ContextSpecific {
217                         tag_number: PUBLIC_KEY_TAG,
218                         tag_mode: TagMode::Implicit,
219                         value,
220                     })
221                 })
222                 .transpose()?,
223         ])
224     }
225 }
226 
227 impl<'a> TryFrom<&'a [u8]> for PrivateKeyInfo<'a> {
228     type Error = Error;
229 
try_from(bytes: &'a [u8]) -> Result<Self>230     fn try_from(bytes: &'a [u8]) -> Result<Self> {
231         Ok(Self::from_der(bytes)?)
232     }
233 }
234 
235 impl<'a> fmt::Debug for PrivateKeyInfo<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result236     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237         f.debug_struct("PrivateKeyInfo")
238             .field("version", &self.version())
239             .field("algorithm", &self.algorithm)
240             .field("public_key", &self.public_key)
241             .finish_non_exhaustive()
242     }
243 }
244 
245 #[cfg(feature = "alloc")]
246 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
247 impl TryFrom<PrivateKeyInfo<'_>> for SecretDocument {
248     type Error = Error;
249 
try_from(private_key: PrivateKeyInfo<'_>) -> Result<SecretDocument>250     fn try_from(private_key: PrivateKeyInfo<'_>) -> Result<SecretDocument> {
251         SecretDocument::try_from(&private_key)
252     }
253 }
254 
255 #[cfg(feature = "alloc")]
256 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
257 impl TryFrom<&PrivateKeyInfo<'_>> for SecretDocument {
258     type Error = Error;
259 
try_from(private_key: &PrivateKeyInfo<'_>) -> Result<SecretDocument>260     fn try_from(private_key: &PrivateKeyInfo<'_>) -> Result<SecretDocument> {
261         Ok(Self::encode_msg(private_key)?)
262     }
263 }
264 
265 #[cfg(feature = "pem")]
266 #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
267 impl PemLabel for PrivateKeyInfo<'_> {
268     const PEM_LABEL: &'static str = "PRIVATE KEY";
269 }
270 
271 #[cfg(feature = "subtle")]
272 #[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
273 impl<'a> ConstantTimeEq for PrivateKeyInfo<'a> {
ct_eq(&self, other: &Self) -> Choice274     fn ct_eq(&self, other: &Self) -> Choice {
275         // NOTE: public fields are not compared in constant time
276         let public_fields_eq =
277             self.algorithm == other.algorithm && self.public_key == other.public_key;
278 
279         self.private_key.ct_eq(other.private_key) & Choice::from(public_fields_eq as u8)
280     }
281 }
282 
283 #[cfg(feature = "subtle")]
284 #[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
285 impl<'a> Eq for PrivateKeyInfo<'a> {}
286 
287 #[cfg(feature = "subtle")]
288 #[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
289 impl<'a> PartialEq for PrivateKeyInfo<'a> {
eq(&self, other: &Self) -> bool290     fn eq(&self, other: &Self) -> bool {
291         self.ct_eq(other).into()
292     }
293 }
294