1use crate::{Error, Result, Version};
4use core::fmt;
5use der::{
6 Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, Length, Reader, Sequence, TagMode,
7 TagNumber, Writer,
8 asn1::{AnyRef, BitStringRef, ContextSpecific, OctetStringRef, SequenceRef},
9};
10use spki::AlgorithmIdentifier;
11
12#[cfg(feature = "ctutils")]
13use ctutils::{Choice, CtEq};
14#[cfg(feature = "pem")]
15use der::pem::PemLabel;
16#[cfg(feature = "alloc")]
17use der::{
18 SecretDocument,
19 asn1::{Any, BitString, OctetString},
20};
21#[cfg(feature = "encryption")]
22use {
23 crate::EncryptedPrivateKeyInfoRef, der::zeroize::Zeroizing, pkcs5::pbes2,
24 rand_core::TryCryptoRng,
25};
26
27const ATTRIBUTES_TAG: TagNumber = TagNumber(0);
29
30const PUBLIC_KEY_TAG: TagNumber = TagNumber(1);
32
33#[derive(Clone)]
95pub struct PrivateKeyInfo<Params, Key, PubKey> {
96 pub algorithm: AlgorithmIdentifier<Params>,
98
99 pub private_key: Key,
101
102 pub public_key: Option<PubKey>,
104}
105
106impl<Params, Key, PubKey> PrivateKeyInfo<Params, Key, PubKey> {
107 pub fn new(algorithm: AlgorithmIdentifier<Params>, private_key: Key) -> Self {
112 Self {
113 algorithm,
114 private_key,
115 public_key: None,
116 }
117 }
118
119 pub fn version(&self) -> Version {
123 if self.public_key.is_some() {
124 Version::V2
125 } else {
126 Version::V1
127 }
128 }
129}
130
131impl<'a, Params, Key, PubKey> PrivateKeyInfo<Params, Key, PubKey>
132where
133 Params: der::Choice<'a, Error = der::Error> + Encode,
134 Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
135 Key: EncodeValue,
136 PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
137 PubKey: BitStringLike,
138{
139 #[cfg(feature = "getrandom")]
152 pub fn encrypt(&self, password: impl AsRef<[u8]>) -> Result<SecretDocument> {
153 let der = Zeroizing::new(self.to_der()?);
154 EncryptedPrivateKeyInfoRef::encrypt(password, der.as_ref())
155 }
156
157 #[cfg(feature = "encryption")]
165 pub fn encrypt_with_rng<R: TryCryptoRng>(
166 &self,
167 rng: &mut R,
168 password: impl AsRef<[u8]>,
169 ) -> Result<SecretDocument> {
170 let der = Zeroizing::new(self.to_der()?);
171 EncryptedPrivateKeyInfoRef::encrypt_with_rng(rng, password, der.as_ref())
172 }
173
174 #[cfg(feature = "encryption")]
181 pub fn encrypt_with_params(
182 &self,
183 pbes2_params: pbes2::Parameters,
184 password: impl AsRef<[u8]>,
185 ) -> Result<SecretDocument> {
186 let der = Zeroizing::new(self.to_der()?);
187 EncryptedPrivateKeyInfoRef::encrypt_with_params(pbes2_params, password, der.as_ref())
188 }
189}
190
191impl<'a, Params, Key, PubKey> PrivateKeyInfo<Params, Key, PubKey>
192where
193 Params: der::Choice<'a> + Encode,
194 PubKey: BitStringLike,
195{
196 fn public_key_bit_string(&self) -> Option<ContextSpecific<BitStringRef<'_>>> {
198 self.public_key.as_ref().map(|pk| {
199 let value = pk.as_bit_string();
200 ContextSpecific {
201 tag_number: PUBLIC_KEY_TAG,
202 tag_mode: TagMode::Implicit,
203 value,
204 }
205 })
206 }
207}
208
209impl<'a, Params, Key, PubKey> DecodeValue<'a> for PrivateKeyInfo<Params, Key, PubKey>
210where
211 Params: der::Choice<'a, Error = der::Error> + Encode,
212 Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
213 PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
214{
215 type Error = der::Error;
216
217 fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> der::Result<Self> {
218 let version = Version::decode(reader)?;
220 let algorithm = reader.decode()?;
221 let private_key = Key::decode(reader)?;
222
223 let _attributes =
224 reader.context_specific::<&SequenceRef>(ATTRIBUTES_TAG, TagMode::Implicit)?;
225
226 let public_key = reader.context_specific::<PubKey>(PUBLIC_KEY_TAG, TagMode::Implicit)?;
227
228 if version.has_public_key() != public_key.is_some() {
229 return Err(reader.error(
230 der::Tag::ContextSpecific {
231 constructed: true,
232 number: PUBLIC_KEY_TAG,
233 }
234 .value_error(),
235 ));
236 }
237
238 while !reader.is_finished() {
240 reader.decode::<ContextSpecific<AnyRef<'_>>>()?;
241 }
242
243 Ok(Self {
244 algorithm,
245 private_key,
246 public_key,
247 })
248 }
249}
250
251impl<'a, Params, Key, PubKey> EncodeValue for PrivateKeyInfo<Params, Key, PubKey>
252where
253 Params: der::Choice<'a, Error = der::Error> + Encode,
254 Key: EncodeValue + FixedTag,
255 PubKey: BitStringLike,
256{
257 fn value_len(&self) -> der::Result<Length> {
258 self.version().encoded_len()?
259 + self.algorithm.encoded_len()?
260 + self.private_key.encoded_len()?
261 + self.public_key_bit_string().encoded_len()?
262 }
263
264 fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
265 self.version().encode(writer)?;
266 self.algorithm.encode(writer)?;
267 self.private_key.encode(writer)?;
268 self.public_key_bit_string().encode(writer)?;
269 Ok(())
270 }
271}
272
273impl<'a, Params, Key, PubKey> Sequence<'a> for PrivateKeyInfo<Params, Key, PubKey>
274where
275 Params: der::Choice<'a, Error = der::Error> + Encode,
276 Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
277 Key: EncodeValue,
278 PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
279 PubKey: BitStringLike,
280{
281}
282
283impl<'a, Params, Key, PubKey> TryFrom<&'a [u8]> for PrivateKeyInfo<Params, Key, PubKey>
284where
285 Params: der::Choice<'a, Error = der::Error> + Encode,
286 Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
287 Key: EncodeValue,
288 PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
289 PubKey: BitStringLike,
290{
291 type Error = Error;
292
293 fn try_from(bytes: &'a [u8]) -> Result<Self> {
294 Ok(Self::from_der(bytes)?)
295 }
296}
297
298impl<Params, Key, PubKey> fmt::Debug for PrivateKeyInfo<Params, Key, PubKey>
299where
300 Params: fmt::Debug,
301 PubKey: fmt::Debug,
302{
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 f.debug_struct("PrivateKeyInfo")
305 .field("version", &self.version())
306 .field("algorithm", &self.algorithm)
307 .field("public_key", &self.public_key)
308 .finish_non_exhaustive()
309 }
310}
311
312#[cfg(feature = "alloc")]
313impl<'a, Params, Key, PubKey> TryFrom<PrivateKeyInfo<Params, Key, PubKey>> for SecretDocument
314where
315 Params: der::Choice<'a, Error = der::Error> + Encode,
316 Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
317 Key: EncodeValue,
318 PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
319 PubKey: BitStringLike,
320{
321 type Error = Error;
322
323 fn try_from(private_key: PrivateKeyInfo<Params, Key, PubKey>) -> Result<SecretDocument> {
324 SecretDocument::try_from(&private_key)
325 }
326}
327
328#[cfg(feature = "alloc")]
329impl<'a, Params, Key, PubKey> TryFrom<&PrivateKeyInfo<Params, Key, PubKey>> for SecretDocument
330where
331 Params: der::Choice<'a, Error = der::Error> + Encode,
332 Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
333 Key: EncodeValue,
334 PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
335 PubKey: BitStringLike,
336{
337 type Error = Error;
338
339 fn try_from(private_key: &PrivateKeyInfo<Params, Key, PubKey>) -> Result<SecretDocument> {
340 Ok(Self::encode_msg(private_key)?)
341 }
342}
343
344#[cfg(feature = "pem")]
345impl<Params, Key, PubKey> PemLabel for PrivateKeyInfo<Params, Key, PubKey> {
346 const PEM_LABEL: &'static str = "PRIVATE KEY";
347}
348
349#[cfg(feature = "ctutils")]
350impl<Params, Key, PubKey> CtEq for PrivateKeyInfo<Params, Key, PubKey>
351where
352 Params: Eq,
353 Key: PartialEq + AsRef<[u8]>,
354 PubKey: PartialEq,
355{
356 fn ct_eq(&self, other: &Self) -> Choice {
357 let public_fields_eq =
359 self.algorithm == other.algorithm && self.public_key == other.public_key;
360
361 self.private_key.as_ref().ct_eq(other.private_key.as_ref())
362 & Choice::from(u8::from(public_fields_eq))
363 }
364}
365
366#[cfg(feature = "ctutils")]
367impl<Params, Key, PubKey> Eq for PrivateKeyInfo<Params, Key, PubKey>
368where
369 Params: Eq,
370 Key: AsRef<[u8]> + Eq,
371 PubKey: Eq,
372{
373}
374
375#[cfg(feature = "ctutils")]
376impl<Params, Key, PubKey> PartialEq for PrivateKeyInfo<Params, Key, PubKey>
377where
378 Params: Eq,
379 Key: PartialEq + AsRef<[u8]>,
380 PubKey: PartialEq,
381{
382 fn eq(&self, other: &Self) -> bool {
383 self.ct_eq(other).into()
384 }
385}
386
387pub type PrivateKeyInfoRef<'a> = PrivateKeyInfo<AnyRef<'a>, &'a OctetStringRef, BitStringRef<'a>>;
389
390pub trait BitStringLike {
395 fn as_bit_string(&self) -> BitStringRef<'_>;
396}
397
398impl BitStringLike for BitStringRef<'_> {
399 fn as_bit_string(&self) -> BitStringRef<'_> {
400 BitStringRef::from(self)
401 }
402}
403
404#[cfg(feature = "alloc")]
405pub(crate) mod allocating {
406 use super::*;
407 use crate::{DecodePrivateKey, EncodePrivateKey};
408 use alloc::borrow::ToOwned;
409 use core::borrow::Borrow;
410 use der::referenced::*;
411
412 #[cfg(feature = "pem")]
413 use der::DecodePem;
414
415 pub type PrivateKeyInfoOwned = PrivateKeyInfo<Any, OctetString, BitString>;
417
418 impl DecodePrivateKey for PrivateKeyInfoOwned {
419 fn from_pkcs8_der(bytes: &[u8]) -> Result<Self> {
420 Ok(Self::from_der(bytes)?)
421 }
422
423 #[cfg(feature = "pem")]
424 fn from_pkcs8_pem(pem: &str) -> Result<Self> {
425 Ok(Self::from_pem(pem)?)
426 }
427 }
428
429 impl EncodePrivateKey for PrivateKeyInfoOwned {
430 fn to_pkcs8_der(&self) -> Result<SecretDocument> {
431 self.try_into()
432 }
433 }
434
435 impl EncodePrivateKey for PrivateKeyInfoRef<'_> {
436 fn to_pkcs8_der(&self) -> Result<SecretDocument> {
437 self.try_into()
438 }
439 }
440
441 impl<'a> RefToOwned<'a> for PrivateKeyInfoRef<'a> {
442 type Owned = PrivateKeyInfoOwned;
443 fn ref_to_owned(&self) -> Self::Owned {
444 PrivateKeyInfoOwned {
445 algorithm: self.algorithm.ref_to_owned(),
446 private_key: self.private_key.to_owned(),
447 public_key: self.public_key.ref_to_owned(),
448 }
449 }
450 }
451
452 impl OwnedToRef for PrivateKeyInfoOwned {
453 type Borrowed<'a> = PrivateKeyInfoRef<'a>;
454 fn owned_to_ref(&self) -> Self::Borrowed<'_> {
455 PrivateKeyInfoRef {
456 algorithm: self.algorithm.owned_to_ref(),
457 private_key: self.private_key.borrow(),
458 public_key: self.public_key.owned_to_ref(),
459 }
460 }
461 }
462
463 impl BitStringLike for BitString {
464 fn as_bit_string(&self) -> BitStringRef<'_> {
465 BitStringRef::from(self)
466 }
467 }
468}