Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkcs8: provide PrivateKeyInfoRef/PrivateKeyInfoOwned #1483

Merged
merged 14 commits into from
Sep 4, 2024
Merged
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
Loading