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