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