From 8191fb6ddf8b12d4ca8e39b8f6110fbfe22fb495 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Mon, 19 Aug 2024 06:40:59 +0000 Subject: [PATCH] pkcs8: use OctetString/BitString Signed-off-by: Arthur Gautier --- pkcs8/src/private_key_info.rs | 146 ++++++++++++++++++++-------------- pkcs8/tests/private_key.rs | 12 +-- 2 files changed, 93 insertions(+), 65 deletions(-) diff --git a/pkcs8/src/private_key_info.rs b/pkcs8/src/private_key_info.rs index f62212ee3..12697a223 100644 --- a/pkcs8/src/private_key_info.rs +++ b/pkcs8/src/private_key_info.rs @@ -4,15 +4,15 @@ use crate::{Error, Result, Version}; use core::fmt; use der::{ asn1::{AnyRef, BitStringRef, ContextSpecific, OctetStringRef}, - Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader, Sequence, TagMode, TagNumber, - Writer, + Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, Length, Reader, Sequence, TagMode, + TagNumber, Writer, }; use spki::AlgorithmIdentifier; #[cfg(feature = "alloc")] -use { - alloc::boxed::Box, - der::{asn1::Any, SecretDocument}, +use der::{ + asn1::{Any, BitString, OctetString}, + SecretDocument, }; #[cfg(feature = "encryption")] @@ -92,7 +92,7 @@ const PUBLIC_KEY_TAG: TagNumber = TagNumber::N1; /// [RFC 5208 Section 5]: https://tools.ietf.org/html/rfc5208#section-5 /// [RFC 5958 Section 2]: https://datatracker.ietf.org/doc/html/rfc5958#section-2 #[derive(Clone)] -pub struct PrivateKeyInfoInner { +pub struct PrivateKeyInfoInner { /// X.509 `AlgorithmIdentifier` for the private key type. pub algorithm: AlgorithmIdentifier, @@ -100,10 +100,10 @@ pub struct PrivateKeyInfoInner { pub private_key: Key, /// Public key data, optionally available if version is V2. - pub public_key: Option, + pub public_key: Option, } -impl PrivateKeyInfoInner { +impl PrivateKeyInfoInner { /// Create a new PKCS#8 [`PrivateKeyInfoInner`] message. /// /// This is a helper method which initializes `attributes` and `public_key` @@ -127,10 +127,14 @@ impl PrivateKeyInfoInner { } } } -impl<'a, Params, Key> PrivateKeyInfoInner + +impl<'a, Params, Key, PubKey> PrivateKeyInfoInner where Params: der::Choice<'a, Error = der::Error> + Encode, - Key: From<&'a [u8]> + AsRef<[u8]> + 'a, + Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, + Key: EncodeValue, + PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, + PubKey: BitStringLike, { /// Encrypt this private key using a symmetric encryption key derived /// from the provided password. @@ -164,30 +168,29 @@ where } } -impl<'a, Params, Key> PrivateKeyInfoInner +impl<'a, Params, Key, PubKey> PrivateKeyInfoInner where Params: der::Choice<'a> + Encode, - Key: AsRef<[u8]>, + PubKey: BitStringLike, { /// Get a `BIT STRING` representation of the public key, if present. - fn public_key_bit_string(&self) -> der::Result>>> { - self.public_key - .as_ref() - .map(|pk| { - BitStringRef::from_bytes(pk.as_ref()).map(|value| ContextSpecific { - tag_number: PUBLIC_KEY_TAG, - tag_mode: TagMode::Implicit, - value, - }) - }) - .transpose() + fn public_key_bit_string(&self) -> Option>> { + self.public_key.as_ref().map(|pk| { + let value = pk.as_bit_string(); + ContextSpecific { + tag_number: PUBLIC_KEY_TAG, + tag_mode: TagMode::Implicit, + value, + } + }) } } -impl<'a, Params, Key> DecodeValue<'a> for PrivateKeyInfoInner +impl<'a, Params, Key, PubKey> DecodeValue<'a> for PrivateKeyInfoInner where Params: der::Choice<'a, Error = der::Error> + Encode, - Key: From<&'a [u8]>, + Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, + PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, { type Error = der::Error; @@ -196,16 +199,9 @@ where // Parse and validate `version` INTEGER. let version = Version::decode(reader)?; let algorithm = reader.decode()?; - let private_key: &[u8] = OctetStringRef::decode(reader)?.into(); - let private_key = Key::try_from(private_key)?; - let public_key = reader - .context_specific::>(PUBLIC_KEY_TAG, TagMode::Implicit)? - .map(|bs| { - bs.as_bytes() - .ok_or_else(|| der::Tag::BitString.value_error()) - .map(Key::from) - }) - .transpose()?; + let private_key = Key::decode(reader)?; + let public_key = + reader.context_specific::(PUBLIC_KEY_TAG, TagMode::Implicit)?; if version.has_public_key() != public_key.is_some() { return Err(reader.error( @@ -232,38 +228,45 @@ where } } -impl<'a, Params, Key> EncodeValue for PrivateKeyInfoInner +impl<'a, Params, Key, PubKey> EncodeValue for PrivateKeyInfoInner where Params: der::Choice<'a, Error = der::Error> + Encode, - Key: AsRef<[u8]>, + Key: EncodeValue + FixedTag, + PubKey: BitStringLike, { fn value_len(&self) -> der::Result { self.version().encoded_len()? + self.algorithm.encoded_len()? - + OctetStringRef::new(self.private_key.as_ref())?.encoded_len()? - + self.public_key_bit_string()?.encoded_len()? + + self.private_key.encoded_len()? + + self.public_key_bit_string().encoded_len()? } fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> { self.version().encode(writer)?; self.algorithm.encode(writer)?; - OctetStringRef::new(self.private_key.as_ref())?.encode(writer)?; - self.public_key_bit_string()?.encode(writer)?; + self.private_key.encode(writer)?; + self.public_key_bit_string().encode(writer)?; Ok(()) } } -impl<'a, Params, Key> Sequence<'a> for PrivateKeyInfoInner +impl<'a, Params, Key, PubKey> Sequence<'a> for PrivateKeyInfoInner where Params: der::Choice<'a, Error = der::Error> + Encode, - Key: From<&'a [u8]> + AsRef<[u8]>, + Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, + Key: EncodeValue, + PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, + PubKey: BitStringLike, { } -impl<'a, Params, Key> TryFrom<&'a [u8]> for PrivateKeyInfoInner +impl<'a, Params, Key, PubKey> TryFrom<&'a [u8]> for PrivateKeyInfoInner where Params: der::Choice<'a, Error = der::Error> + Encode, - Key: From<&'a [u8]> + AsRef<[u8]> + 'a, + Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, + Key: EncodeValue, + PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, + PubKey: BitStringLike, { type Error = Error; @@ -272,10 +275,10 @@ where } } -impl fmt::Debug for PrivateKeyInfoInner +impl fmt::Debug for PrivateKeyInfoInner where Params: fmt::Debug, - Key: fmt::Debug, + PubKey: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PrivateKeyInfoInner") @@ -287,41 +290,48 @@ where } #[cfg(feature = "alloc")] -impl<'a, Params, Key> TryFrom> for SecretDocument +impl<'a, Params, Key, PubKey> TryFrom> for SecretDocument where Params: der::Choice<'a, Error = der::Error> + Encode, - Key: From<&'a [u8]> + AsRef<[u8]>, + Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, + Key: EncodeValue, + PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, + PubKey: BitStringLike, { type Error = Error; - fn try_from(private_key: PrivateKeyInfoInner) -> Result { + fn try_from(private_key: PrivateKeyInfoInner) -> Result { SecretDocument::try_from(&private_key) } } #[cfg(feature = "alloc")] -impl<'a, Params, Key> TryFrom<&PrivateKeyInfoInner> for SecretDocument +impl<'a, Params, Key, PubKey> TryFrom<&PrivateKeyInfoInner> for SecretDocument where Params: der::Choice<'a, Error = der::Error> + Encode, - Key: From<&'a [u8]> + AsRef<[u8]>, + Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, + Key: EncodeValue, + PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a, + PubKey: BitStringLike, { type Error = Error; - fn try_from(private_key: &PrivateKeyInfoInner) -> Result { + fn try_from(private_key: &PrivateKeyInfoInner) -> Result { Ok(Self::encode_msg(private_key)?) } } #[cfg(feature = "pem")] -impl PemLabel for PrivateKeyInfoInner { +impl PemLabel for PrivateKeyInfoInner { const PEM_LABEL: &'static str = "PRIVATE KEY"; } #[cfg(feature = "subtle")] -impl ConstantTimeEq for PrivateKeyInfoInner +impl ConstantTimeEq for PrivateKeyInfoInner where Params: Eq, Key: PartialEq + AsRef<[u8]>, + PubKey: PartialEq, { fn ct_eq(&self, other: &Self) -> Choice { // NOTE: public fields are not compared in constant time @@ -334,18 +344,20 @@ where } #[cfg(feature = "subtle")] -impl Eq for PrivateKeyInfoInner +impl Eq for PrivateKeyInfoInner where Params: Eq, Key: AsRef<[u8]> + Eq, + PubKey: Eq, { } #[cfg(feature = "subtle")] -impl PartialEq for PrivateKeyInfoInner +impl PartialEq for PrivateKeyInfoInner where Params: Eq, Key: PartialEq + AsRef<[u8]>, + PubKey: PartialEq, { fn eq(&self, other: &Self) -> bool { self.ct_eq(other).into() @@ -353,17 +365,33 @@ where } /// [`PrivateKeyInfoInner`] with [`AnyRef`] algorithm parameters, and `&[u8]` key. -pub type PrivateKeyInfo<'a> = PrivateKeyInfoInner, &'a [u8]>; +pub type PrivateKeyInfo<'a> = PrivateKeyInfoInner, OctetStringRef<'a>, BitStringRef<'a>>; /// [`PrivateKeyInfo`] with [`Any`] algorithm parameters, and `Box<[u8]>` key. #[cfg(feature = "alloc")] -pub type PrivateKeyInfoOwned = PrivateKeyInfoInner>; +pub type PrivateKeyInfoOwned = PrivateKeyInfoInner; + +pub trait BitStringLike { + fn as_bit_string(&self) -> BitStringRef<'_>; +} + +impl<'a> BitStringLike for BitStringRef<'a> { + fn as_bit_string(&self) -> BitStringRef<'_> { + BitStringRef::from(self) + } +} #[cfg(feature = "alloc")] mod allocating { use super::*; use der::referenced::*; + impl BitStringLike for BitString { + fn as_bit_string(&self) -> BitStringRef<'_> { + BitStringRef::from(self) + } + } + impl<'a> RefToOwned<'a> for PrivateKeyInfo<'a> { type Owned = PrivateKeyInfoOwned; fn ref_to_owned(&self) -> Self::Owned { diff --git a/pkcs8/tests/private_key.rs b/pkcs8/tests/private_key.rs index cba2308f3..002112c2d 100644 --- a/pkcs8/tests/private_key.rs +++ b/pkcs8/tests/private_key.rs @@ -1,6 +1,6 @@ //! PKCS#8 private key tests -use der::asn1::ObjectIdentifier; +use der::asn1::{ObjectIdentifier, OctetStringRef}; use hex_literal::hex; use pkcs8::{PrivateKeyInfo, Version}; @@ -90,7 +90,10 @@ fn decode_ec_bignp256_der() { // $ openssl asn1parse -inform der -in tests/examples/bign256-priv.der assert_eq!( pk.private_key, - &hex!("1F66B5B84B7339674533F0329C74F21834281FED0732429E0C79235FC273E269") + OctetStringRef::new(&hex!( + "1F66B5B84B7339674533F0329C74F21834281FED0732429E0C79235FC273E269" + )) + .unwrap() ) } @@ -127,10 +130,7 @@ fn decode_ed25519_der_v2() { assert_eq!(pk.algorithm.oid, "1.3.101.112".parse().unwrap()); assert_eq!(pk.algorithm.parameters, None); assert_eq!(pk.private_key.as_ref(), PRIV_KEY); - assert_eq!( - pk.public_key.as_ref().map(|p| p.as_ref()), - Some(&PUB_KEY[..]) - ); + assert_eq!(pk.public_key.and_then(|p| p.as_bytes()), Some(&PUB_KEY[..])); } #[test]