Skip to content

Commit

Permalink
pkcs8: provide PrivateKeyInfoRef/PrivateKeyInfoOwned (#1483)
Browse files Browse the repository at this point in the history
This makes the `PrivateKeyInfo` generic over its backing storage, and this
provides two versions of it, namely `PrivateKeyInfoRef` and
`PrivateKeyInfoOwned` respectively borrowing the value and owning the
storage of the value.

Signed-off-by: Arthur Gautier <[email protected]>
  • Loading branch information
baloo authored Sep 4, 2024
1 parent 85b389d commit 6877a3e
Show file tree
Hide file tree
Showing 14 changed files with 358 additions and 180 deletions.
26 changes: 7 additions & 19 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,9 @@ x509-cert = { path = "./x509-cert" }
x509-ocsp = { path = "./x509-ocsp" }

# Temp patches to external crates
ecdsa = { git = "https://github.com/RustCrypto/signatures" }
# https://github.com/RustCrypto/signatures/pull/851
ecdsa = { git = "https://github.com/baloo/signatures", branch = "baloo/pkcs8/api-change" }
# https://github.com/RustCrypto/RSA/pull/446
rsa = { git = "https://github.com/baloo/RSA", branch = "baloo/pkcs8/api-changes" }
# https://github.com/RustCrypto/traits/pull/1650
elliptic-curve = { git = "https://github.com/baloo/traits.git", branch ="baloo/elliptic-curve/pkcs8-API-break" }
2 changes: 1 addition & 1 deletion cms/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ zeroize = { version = "1.8.1", optional = true }
getrandom = "0.2"
hex-literal = "0.4"
pem-rfc7468 = "1.0.0-rc.1"
pkcs5 = "=0.8.0-rc.0"
pkcs5 = "0.8.0-rc.1"
rand = "0.8.5"
rsa = { version = "=0.10.0-pre.2", features = ["sha2"] }
ecdsa = { version = "=0.17.0-pre.7", features = ["digest", "pem"] }
Expand Down
12 changes: 10 additions & 2 deletions pkcs1/src/private_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ pub(crate) mod other_prime_info;
use crate::{Error, Result, RsaPublicKey, Version};
use core::fmt;
use der::{
asn1::UintRef, Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader, Sequence, Tag,
Writer,
asn1::{OctetStringRef, UintRef},
Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader, Sequence, Tag, Writer,
};

#[cfg(feature = "alloc")]
Expand Down Expand Up @@ -172,6 +172,14 @@ impl<'a> TryFrom<&'a [u8]> for RsaPrivateKey<'a> {
}
}

impl<'a> TryFrom<OctetStringRef<'a>> for RsaPrivateKey<'a> {
type Error = Error;

fn try_from(bytes: OctetStringRef<'a>) -> Result<Self> {
Ok(Self::from_der(bytes.as_bytes())?)
}
}

impl fmt::Debug for RsaPrivateKey<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RsaPrivateKey")
Expand Down
11 changes: 6 additions & 5 deletions pkcs1/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use {
#[cfg(feature = "pkcs8")]
use {
crate::{ALGORITHM_ID, ALGORITHM_OID},
der::asn1::BitStringRef,
der::asn1::{BitStringRef, OctetStringRef},
};

#[cfg(feature = "std")]
Expand Down Expand Up @@ -157,10 +157,11 @@ pub trait EncodeRsaPublicKey {
#[cfg(feature = "pkcs8")]
impl<T> DecodeRsaPrivateKey for T
where
T: for<'a> TryFrom<pkcs8::PrivateKeyInfo<'a>, Error = pkcs8::Error>,
T: for<'a> TryFrom<pkcs8::PrivateKeyInfoRef<'a>, Error = pkcs8::Error>,
{
fn from_pkcs1_der(private_key: &[u8]) -> Result<Self> {
Ok(Self::try_from(pkcs8::PrivateKeyInfo {
let private_key = OctetStringRef::new(private_key)?;
Ok(Self::try_from(pkcs8::PrivateKeyInfoRef {
algorithm: ALGORITHM_ID,
private_key,
public_key: None,
Expand All @@ -185,9 +186,9 @@ where
impl<T: pkcs8::EncodePrivateKey> EncodeRsaPrivateKey for T {
fn to_pkcs1_der(&self) -> Result<SecretDocument> {
let pkcs8_doc = self.to_pkcs8_der()?;
let pkcs8_key = pkcs8::PrivateKeyInfo::from_der(pkcs8_doc.as_bytes())?;
let pkcs8_key = pkcs8::PrivateKeyInfoRef::from_der(pkcs8_doc.as_bytes())?;
pkcs8_key.algorithm.assert_algorithm_oid(ALGORITHM_OID)?;
RsaPrivateKey::from_der(pkcs8_key.private_key)?.try_into()
RsaPrivateKey::from_der(pkcs8_key.private_key.as_bytes())?.try_into()
}
}

Expand Down
10 changes: 5 additions & 5 deletions pkcs12/tests/cert_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use pkcs8::{
self,
pbes2::{AES_256_CBC_OID, HMAC_WITH_SHA256_OID, PBES2_OID, PBKDF2_OID},
},
EncryptedPrivateKeyInfo,
EncryptedPrivateKeyInfoRef,
};
use spki::AlgorithmIdentifierOwned;

Expand Down Expand Up @@ -242,9 +242,9 @@ fn decode_sample_pfx() {
for safe_bag in safe_bags {
match safe_bag.bag_id {
pkcs12::PKCS_12_PKCS8_KEY_BAG_OID => {
let cs: ContextSpecific<EncryptedPrivateKeyInfo> =
let cs: ContextSpecific<EncryptedPrivateKeyInfoRef<'_>> =
ContextSpecific::from_der(&safe_bag.bag_value).unwrap();
let mut ciphertext = cs.value.encrypted_data.to_vec();
let mut ciphertext = cs.value.encrypted_data.as_bytes().to_vec();
let plaintext = cs
.value
.encryption_algorithm
Expand Down Expand Up @@ -628,9 +628,9 @@ fn decode_sample_pfx2() {
for safe_bag in safe_bags {
match safe_bag.bag_id {
pkcs12::PKCS_12_PKCS8_KEY_BAG_OID => {
let cs: ContextSpecific<EncryptedPrivateKeyInfo> =
let cs: ContextSpecific<EncryptedPrivateKeyInfoRef<'_>> =
ContextSpecific::from_der(&safe_bag.bag_value).unwrap();
let mut ciphertext = cs.value.encrypted_data.to_vec();
let mut ciphertext = cs.value.encrypted_data.as_bytes().to_vec();
let plaintext = cs
.value
.encryption_algorithm
Expand Down
80 changes: 53 additions & 27 deletions pkcs8/src/encrypted_private_key_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
use crate::{Error, Result};
use core::fmt;
use der::{
asn1::OctetStringRef, Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader,
Sequence, Writer,
asn1::OctetStringRef, Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, Length,
Reader, Sequence, Writer,
};
use pkcs5::EncryptionScheme;

#[cfg(feature = "alloc")]
use der::SecretDocument;
use der::{asn1::OctetString, SecretDocument};

#[cfg(feature = "encryption")]
use {pkcs5::pbes2, rand_core::CryptoRngCore};
Expand Down Expand Up @@ -37,23 +37,27 @@ use der::pem::PemLabel;
///
/// [RFC 5208 Section 6]: https://tools.ietf.org/html/rfc5208#section-6
#[derive(Clone, Eq, PartialEq)]
pub struct EncryptedPrivateKeyInfo<'a> {
pub struct EncryptedPrivateKeyInfo<Data> {
/// Algorithm identifier describing a password-based symmetric encryption
/// scheme used to encrypt the `encrypted_data` field.
pub encryption_algorithm: EncryptionScheme,

/// Private key data
pub encrypted_data: &'a [u8],
pub encrypted_data: Data,
}

impl<'a> EncryptedPrivateKeyInfo<'a> {
impl<'a, Data> EncryptedPrivateKeyInfo<Data>
where
Data: DecodeValue<'a, Error = der::Error> + EncodeValue + FixedTag + 'a,
Data: AsRef<[u8]>,
{
/// Attempt to decrypt this encrypted private key using the provided
/// password to derive an encryption key.
#[cfg(feature = "encryption")]
pub fn decrypt(&self, password: impl AsRef<[u8]>) -> Result<SecretDocument> {
Ok(self
.encryption_algorithm
.decrypt(password, self.encrypted_data)?
.decrypt(password, self.encrypted_data.as_ref())?
.try_into()?)
}

Expand All @@ -66,7 +70,7 @@ impl<'a> EncryptedPrivateKeyInfo<'a> {
doc: &[u8],
) -> Result<SecretDocument> {
let pbes2_params = pbes2::Parameters::recommended(rng);
EncryptedPrivateKeyInfo::encrypt_with(pbes2_params, password, doc)
EncryptedPrivateKeyInfoOwned::encrypt_with(pbes2_params, password, doc)
}

/// Encrypt this private key using a symmetric encryption key derived
Expand All @@ -78,55 +82,64 @@ impl<'a> EncryptedPrivateKeyInfo<'a> {
doc: &[u8],
) -> Result<SecretDocument> {
let encrypted_data = pbes2_params.encrypt(password, doc)?;
let encrypted_data = OctetStringRef::new(&encrypted_data)?;

EncryptedPrivateKeyInfo {
encryption_algorithm: pbes2_params.into(),
encrypted_data: &encrypted_data,
encrypted_data,
}
.try_into()
}
}

impl<'a> DecodeValue<'a> for EncryptedPrivateKeyInfo<'a> {
impl<'a, Data> DecodeValue<'a> for EncryptedPrivateKeyInfo<Data>
where
Data: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
{
type Error = der::Error;

fn decode_value<R: Reader<'a>>(
reader: &mut R,
header: Header,
) -> der::Result<EncryptedPrivateKeyInfo<'a>> {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
reader.read_nested(header.length, |reader| {
Ok(Self {
encryption_algorithm: reader.decode()?,
encrypted_data: OctetStringRef::decode(reader)?.as_bytes(),
encrypted_data: reader.decode()?,
})
})
}
}

impl EncodeValue for EncryptedPrivateKeyInfo<'_> {
impl<Data> EncodeValue for EncryptedPrivateKeyInfo<Data>
where
Data: EncodeValue + FixedTag,
{
fn value_len(&self) -> der::Result<Length> {
self.encryption_algorithm.encoded_len()?
+ OctetStringRef::new(self.encrypted_data)?.encoded_len()?
self.encryption_algorithm.encoded_len()? + self.encrypted_data.encoded_len()?
}

fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
self.encryption_algorithm.encode(writer)?;
OctetStringRef::new(self.encrypted_data)?.encode(writer)?;
self.encrypted_data.encode(writer)?;
Ok(())
}
}

impl<'a> Sequence<'a> for EncryptedPrivateKeyInfo<'a> {}
impl<'a, Data> Sequence<'a> for EncryptedPrivateKeyInfo<Data> where
Data: DecodeValue<'a, Error = der::Error> + EncodeValue + FixedTag + 'a
{
}

impl<'a> TryFrom<&'a [u8]> for EncryptedPrivateKeyInfo<'a> {
impl<'a, Data> TryFrom<&'a [u8]> for EncryptedPrivateKeyInfo<Data>
where
Data: DecodeValue<'a, Error = der::Error> + EncodeValue + FixedTag + 'a,
{
type Error = Error;

fn try_from(bytes: &'a [u8]) -> Result<Self> {
Ok(Self::from_der(bytes)?)
}
}

impl<'a> fmt::Debug for EncryptedPrivateKeyInfo<'a> {
impl<Data> fmt::Debug for EncryptedPrivateKeyInfo<Data> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EncryptedPrivateKeyInfo")
.field("encryption_algorithm", &self.encryption_algorithm)
Expand All @@ -135,24 +148,37 @@ impl<'a> fmt::Debug for EncryptedPrivateKeyInfo<'a> {
}

#[cfg(feature = "alloc")]
impl TryFrom<EncryptedPrivateKeyInfo<'_>> for SecretDocument {
impl<'a, Data> TryFrom<EncryptedPrivateKeyInfo<Data>> for SecretDocument
where
Data: DecodeValue<'a, Error = der::Error> + EncodeValue + FixedTag + 'a,
{
type Error = Error;

fn try_from(encrypted_private_key: EncryptedPrivateKeyInfo<'_>) -> Result<SecretDocument> {
fn try_from(encrypted_private_key: EncryptedPrivateKeyInfo<Data>) -> Result<SecretDocument> {
SecretDocument::try_from(&encrypted_private_key)
}
}

#[cfg(feature = "alloc")]
impl TryFrom<&EncryptedPrivateKeyInfo<'_>> for SecretDocument {
impl<'a, Data> TryFrom<&EncryptedPrivateKeyInfo<Data>> for SecretDocument
where
Data: DecodeValue<'a, Error = der::Error> + EncodeValue + FixedTag + 'a,
{
type Error = Error;

fn try_from(encrypted_private_key: &EncryptedPrivateKeyInfo<'_>) -> Result<SecretDocument> {
fn try_from(encrypted_private_key: &EncryptedPrivateKeyInfo<Data>) -> Result<SecretDocument> {
Ok(Self::encode_msg(encrypted_private_key)?)
}
}

#[cfg(feature = "pem")]
impl PemLabel for EncryptedPrivateKeyInfo<'_> {
impl<Data> PemLabel for EncryptedPrivateKeyInfo<Data> {
const PEM_LABEL: &'static str = "ENCRYPTED PRIVATE KEY";
}

/// [`EncryptedPrivateKeyInfo`] with [`OctetStringRef`] encrypted data.
pub type EncryptedPrivateKeyInfoRef<'a> = EncryptedPrivateKeyInfo<OctetStringRef<'a>>;

#[cfg(feature = "alloc")]
/// [`EncryptedPrivateKeyInfo`] with [`OctetString`] encrypted data.
pub type EncryptedPrivateKeyInfoOwned = EncryptedPrivateKeyInfo<OctetString>;
Loading

0 comments on commit 6877a3e

Please sign in to comment.