Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion pkcs8/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ encryption = ["alloc", "pkcs5/alloc", "pkcs5/pbes2", "rand_core"]
pem = ["alloc", "der/pem", "spki/pem"]
sha1-insecure = ["encryption", "pkcs5/sha1-insecure"]

[lints]
workspace = true

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
4 changes: 4 additions & 0 deletions pkcs8/src/encrypted_private_key_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ where
{
/// Attempt to decrypt this encrypted private key using the provided
/// password to derive an encryption key.
///
/// # Errors
/// - Returns errors in the event the file could not be decrypted successfully.
/// - Returns errors if the file decrypted but the resulting plaintext failed to decode.
#[cfg(feature = "encryption")]
pub fn decrypt(&self, password: impl AsRef<[u8]>) -> Result<SecretDocument> {
Ok(self
Expand Down
19 changes: 6 additions & 13 deletions pkcs8/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![forbid(unsafe_code)]
#![warn(
clippy::mod_module_files,
clippy::unwrap_used,
missing_docs,
rust_2018_idioms,
unused_lifetimes,
unused_qualifications
)]

//! ## About this crate
//! This library provides generalized PKCS#8 support designed to work with a
Expand Down Expand Up @@ -54,15 +46,16 @@
//! private keys encrypted with DES-CBC and DES-EDE3-CBC (3DES or Triple DES) symmetric
//! encryption, respectively.
//!
//! ⚠️ WARNING ⚠️
//! <div class="warning">
//! <b>Security Warning</b>
//!
//! DES support (gated behind the `des-insecure` feature) is implemented to
//! allow for decryption of legacy PKCS#8 files only.
//! DES support (gated behind the `des-insecure` feature) is implemented to allow for decryption of
//! legacy PKCS#8 files only.
//!
//! Such PKCS#8 documents should be considered *INSECURE* due to the short
//! 56-bit key size of DES.
//! Such PKCS#8 documents should be considered *INSECURE* due to the short 56-bit key size of DES.
//!
//! New keys should use AES instead.
//! </div>
//!
//! [RFC 5208]: https://tools.ietf.org/html/rfc5208
//! [RFC 5958]: https://tools.ietf.org/html/rfc5958
Expand Down
13 changes: 11 additions & 2 deletions pkcs8/src/private_key_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ where
/// - r: 8
/// - p: 1
/// - Cipher: AES-256-CBC (best available option for PKCS#5 encryption)
///
/// # Errors
/// - Propagates errors from calling [`Encode::to_der`] on `Self`.
/// - Returns errors in the event encryption failed.
#[cfg(feature = "encryption")]
pub fn encrypt<R: CryptoRng>(
&self,
Expand All @@ -159,6 +163,10 @@ where

/// Encrypt this private key using a symmetric encryption key derived
/// from the provided password and [`pbes2::Parameters`].
///
/// # Errors
/// - Propagates errors from calling [`Encode::to_der`] on `Self`.
/// - Returns errors in the event encryption failed.
#[cfg(feature = "encryption")]
pub fn encrypt_with_params(
&self,
Expand Down Expand Up @@ -341,7 +349,7 @@ where
self.algorithm == other.algorithm && self.public_key == other.public_key;

self.private_key.as_ref().ct_eq(other.private_key.as_ref())
& Choice::from(public_fields_eq as u8)
& Choice::from(u8::from(public_fields_eq))
}
}

Expand Down Expand Up @@ -373,9 +381,10 @@ pub type PrivateKeyInfoRef<'a> = PrivateKeyInfo<AnyRef<'a>, &'a OctetStringRef,
#[cfg(feature = "alloc")]
pub type PrivateKeyInfoOwned = PrivateKeyInfo<Any, OctetString, BitString>;

/// [`BitStringLike`] marks object that will act like a BitString.
/// [`BitStringLike`] marks object that will act like a `BitString`.
///
/// It will allow to get a [`BitStringRef`] that points back to the underlying bytes.
// TODO(tarcieri): replace this with `AsRef<BitStringRef>` when we can have `&BitStringRef`.
pub trait BitStringLike {
fn as_bit_string(&self) -> BitStringRef<'_>;
}
Expand Down
70 changes: 58 additions & 12 deletions pkcs8/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@ use std::path::Path;

/// Parse a private key object from a PKCS#8 encoded document.
pub trait DecodePrivateKey: Sized {
/// Deserialize PKCS#8 private key from ASN.1 DER-encoded data
/// (binary format).
/// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format).
///
/// # Errors
/// Returns format-specific errors in the event the document failed to parse.
fn from_pkcs8_der(bytes: &[u8]) -> Result<Self>;

/// Deserialize encrypted PKCS#8 private key from ASN.1 DER-encoded data
/// (binary format) and attempt to decrypt it using the provided password.
/// Deserialize encrypted PKCS#8 private key from ASN.1 DER-encoded data (binary format) and
/// attempt to decrypt it using the provided password.
///
/// # Errors
/// - Returns errors if the DER failed to decode
/// - Returns errors if the ciphertext failed to decrypt under the given password
#[cfg(feature = "encryption")]
fn from_pkcs8_encrypted_der(bytes: &[u8], password: impl AsRef<[u8]>) -> Result<Self> {
let doc = EncryptedPrivateKeyInfoRef::try_from(bytes)?.decrypt(password)?;
Expand All @@ -42,6 +48,10 @@ pub trait DecodePrivateKey: Sized {
/// ```text
/// -----BEGIN PRIVATE KEY-----
/// ```
///
/// # Errors
/// - Returns [`Error::Asn1`] in the event of a decoding error (PEM or DER).
/// - Returns the same errors as [`DecodePrivateKey::from_pkcs8_der`].
#[cfg(feature = "pem")]
fn from_pkcs8_pem(s: &str) -> Result<Self> {
// Validate PEM label
Expand All @@ -52,29 +62,42 @@ pub trait DecodePrivateKey: Sized {
Self::from_pkcs8_der(doc.as_bytes())
}

/// Deserialize encrypted PKCS#8-encoded private key from PEM and attempt
/// to decrypt it using the provided password.
/// Deserialize encrypted PKCS#8-encoded private key from PEM and attempt to decrypt it using
/// the provided password.
///
/// Keys in this format begin with the following delimiter:
///
/// ```text
/// -----BEGIN ENCRYPTED PRIVATE KEY-----
/// ```
///
/// # Errors
/// - Returns [`Error::Asn1`] in the event of a decoding error (PEM or DER).
/// - Returns the same errors as [`DecodePrivateKey::from_pkcs8_encrypted_der`].
#[cfg(all(feature = "encryption", feature = "pem"))]
fn from_pkcs8_encrypted_pem(s: &str, password: impl AsRef<[u8]>) -> Result<Self> {
let (label, doc) = SecretDocument::from_pem(s)?;
EncryptedPrivateKeyInfoRef::validate_pem_label(label)?;
Self::from_pkcs8_encrypted_der(doc.as_bytes(), password)
}

/// Load PKCS#8 private key from an ASN.1 DER-encoded file on the local
/// filesystem (binary format).
/// Load PKCS#8 private key from an ASN.1 DER-encoded file (binary format) on the local
/// filesystem.
///
/// # Errors
/// - Returns the same errors as [`DecodePrivateKey::from_pkcs8_der`].
/// - Returns errors in event the file cannot be read from the filesystem.
#[cfg(feature = "std")]
fn read_pkcs8_der_file(path: impl AsRef<Path>) -> Result<Self> {
Self::from_pkcs8_der(SecretDocument::read_der_file(path)?.as_bytes())
}

/// Load PKCS#8 private key from a PEM-encoded file on the local filesystem.
///
/// # Errors
/// - Returns the same errors as [`SecretDocument::read_pem_file`].
/// - Returns the same errors as [`DecodePrivateKey::from_pkcs8_der`].
/// - Returns errors in event the file cannot be read from the filesystem.
#[cfg(all(feature = "pem", feature = "std"))]
fn read_pkcs8_pem_file(path: impl AsRef<Path>) -> Result<Self> {
let (label, doc) = SecretDocument::read_pem_file(path)?;
Expand All @@ -96,10 +119,17 @@ where
#[cfg(feature = "alloc")]
pub trait EncodePrivateKey {
/// Serialize a [`SecretDocument`] containing a PKCS#8-encoded private key.
///
/// # Errors
/// Returns format-specific errors in the event the document failed to serialize.
fn to_pkcs8_der(&self) -> Result<SecretDocument>;

/// Create an [`SecretDocument`] containing the ciphertext of
/// a PKCS#8 encoded private key encrypted under the given `password`.
/// Create an [`SecretDocument`] containing the ciphertext of a PKCS#8 encoded private key
/// encrypted under the given `password`.
///
/// # Errors
/// - Returns format-specific errors in the event the document failed to serialize.
/// - Returns algorithm-specific errors in the event the document couldn't be encrypted.
#[cfg(feature = "encryption")]
fn to_pkcs8_encrypted_der<R: CryptoRng>(
&self,
Expand All @@ -110,6 +140,10 @@ pub trait EncodePrivateKey {
}

/// Serialize this private key as PEM-encoded PKCS#8 with the given [`LineEnding`].
///
/// # Errors
/// - Returns the same errors as [`EncodePrivateKey::to_pkcs8_der`].
/// - Returns the same errors as [`SecretDocument::to_pem`].
#[cfg(feature = "pem")]
fn to_pkcs8_pem(&self, line_ending: LineEnding) -> Result<Zeroizing<String>> {
let doc = self.to_pkcs8_der()?;
Expand All @@ -118,6 +152,10 @@ pub trait EncodePrivateKey {

/// Serialize this private key as an encrypted PEM-encoded PKCS#8 private
/// key using the `provided` to derive an encryption key.
///
/// # Errors
/// - Returns the same errors as [`EncodePrivateKey::to_pkcs8_encrypted_der`].
/// - Returns the same errors as [`SecretDocument::to_pem`].
#[cfg(all(feature = "encryption", feature = "pem"))]
fn to_pkcs8_encrypted_pem<R: CryptoRng>(
&self,
Expand All @@ -129,13 +167,21 @@ pub trait EncodePrivateKey {
Ok(doc.to_pem(EncryptedPrivateKeyInfoRef::PEM_LABEL, line_ending)?)
}

/// Write ASN.1 DER-encoded PKCS#8 private key to the given path
/// Write ASN.1 DER-encoded PKCS#8 private key to the given path.
///
/// # Errors
/// - Returns the same errors as [`EncodePrivateKey::to_pkcs8_der`].
/// - Returns errors in the event the file could not be written to the filesystem.
#[cfg(feature = "std")]
fn write_pkcs8_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
Ok(self.to_pkcs8_der()?.write_der_file(path)?)
}

/// Write ASN.1 PEM-encoded PKCS#8 private key to the given path
/// Write ASN.1 PEM-encoded PKCS#8 private key to the given path.
///
/// # Errors
/// - Returns the same errors as [`EncodePrivateKey::to_pkcs8_der`].
/// - Returns errors in the event the file could not be written to the filesystem.
#[cfg(all(feature = "pem", feature = "std"))]
fn write_pkcs8_pem_file(&self, path: impl AsRef<Path>, line_ending: LineEnding) -> Result<()> {
let doc = self.to_pkcs8_der()?;
Expand Down
1 change: 1 addition & 0 deletions pkcs8/src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub enum Version {

impl Version {
/// Is this version expected to have a public key?
#[must_use]
pub fn has_public_key(self) -> bool {
match self {
Version::V1 => false,
Expand Down
4 changes: 2 additions & 2 deletions pkcs8/tests/encrypted_private_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ fn decrypt_ed25519_der_encpriv_aes256_scrypt() {
#[cfg(feature = "encryption")]
#[test]
fn encrypt_ed25519_der_encpriv_aes256_pbkdf2_sha256() {
let pbes2_params = pkcs5::pbes2::Parameters::generate_pbkdf2_sha256_aes256cbc(
let pbes2_params = pbes2::Parameters::generate_pbkdf2_sha256_aes256cbc(
2048,
&hex!("79d982e70df91a88"),
hex!("b2d02d78b2efd9dff694cf8e0af40925"),
Expand All @@ -191,7 +191,7 @@ fn encrypt_ed25519_der_encpriv_aes256_pbkdf2_sha256() {
#[cfg(feature = "encryption")]
#[test]
fn encrypt_ed25519_der_encpriv_aes256_scrypt() {
let scrypt_params = pkcs5::pbes2::Parameters::generate_scrypt_aes256cbc(
let scrypt_params = pbes2::Parameters::generate_scrypt_aes256cbc(
pkcs5::scrypt::Params::new(15, 8, 1).unwrap(),
&hex!("E6211E2348AD69E0"),
hex!("9BD0A6251F2254F9FD5963887C27CF01"),
Expand Down
2 changes: 1 addition & 1 deletion pkcs8/tests/private_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ fn decode_ec_bignp256_der() {
"1F66B5B84B7339674533F0329C74F21834281FED0732429E0C79235FC273E269"
))
.unwrap()
)
);
}

// Test vector from RFC8410 Section 10.3:
Expand Down
1 change: 1 addition & 0 deletions pkcs8/tests/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const ED25519_DER_EXAMPLE: &[u8] = include_bytes!("examples/ed25519-priv-pkcs8v1
const ED25519_PEM_EXAMPLE: &str = include_str!("examples/ed25519-priv-pkcs8v1.pem");

/// Mock key type for testing trait impls against.
#[derive(Debug)]
pub struct MockKey(Vec<u8>);

impl AsRef<[u8]> for MockKey {
Expand Down