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
2 changes: 2 additions & 0 deletions src/rust/cryptography-key-parsing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

pub mod dsa;
pub mod ec;
pub mod pbe;
pub mod pem;
pub mod pkcs8;
pub mod rsa;
Expand Down Expand Up @@ -50,6 +51,7 @@ impl From<openssl::error::ErrorStack> for KeyParsingError {
pub type KeyParsingResult<T> = Result<T, KeyParsingError>;

pub enum KeySerializationError {
PasswordMustBeUtf8,
Write(asn1::WriteError),
OpenSSL(openssl::error::ErrorStack),
}
Expand Down
133 changes: 133 additions & 0 deletions src/rust/cryptography-key-parsing/src/pbe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// This file is dual licensed under the terms of the Apache License, Version
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
// for complete details.

use crate::{KeySerializationError, KeySerializationResult};

pub enum EncryptionAlgorithm {
PBESHA1And3KeyTripleDESCBC,
PBESv2SHA256AndAES256CBC,
}

impl EncryptionAlgorithm {
pub fn salt_length(&self) -> usize {
match self {
EncryptionAlgorithm::PBESHA1And3KeyTripleDESCBC => 8,
EncryptionAlgorithm::PBESv2SHA256AndAES256CBC => 16,
}
}

pub fn algorithm_identifier<'a>(
&self,
cipher_kdf_iter: u64,
salt: &'a [u8],
iv: &'a [u8],
) -> cryptography_x509::common::AlgorithmIdentifier<'a> {
match self {
EncryptionAlgorithm::PBESHA1And3KeyTripleDESCBC => {
cryptography_x509::common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: cryptography_x509::common::AlgorithmParameters::PbeWithShaAnd3KeyTripleDesCbc(cryptography_x509::common::Pkcs12PbeParams{
salt,
iterations: cipher_kdf_iter,
}),
}
}
EncryptionAlgorithm::PBESv2SHA256AndAES256CBC => {
let kdf_algorithm_identifier = cryptography_x509::common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: cryptography_x509::common::AlgorithmParameters::Pbkdf2(
cryptography_x509::common::PBKDF2Params {
salt,
iteration_count: cipher_kdf_iter,
key_length: None,
prf: Box::new(cryptography_x509::common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params:
cryptography_x509::common::AlgorithmParameters::HmacWithSha256(
Some(()),
),
}),
},
),
};
let encryption_algorithm_identifier =
cryptography_x509::common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: cryptography_x509::common::AlgorithmParameters::Aes256Cbc(
iv[..16].try_into().unwrap(),
),
};

cryptography_x509::common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: cryptography_x509::common::AlgorithmParameters::Pbes2(
cryptography_x509::common::PBES2Params {
key_derivation_func: Box::new(kdf_algorithm_identifier),
encryption_scheme: Box::new(encryption_algorithm_identifier),
},
),
}
}
}
}

pub fn encrypt(
&self,
password: &[u8],
cipher_kdf_iter: u64,
salt: &[u8],
iv: &[u8],
data: &[u8],
) -> KeySerializationResult<Vec<u8>> {
match self {
EncryptionAlgorithm::PBESHA1And3KeyTripleDESCBC => {
let password = std::str::from_utf8(password)
.map_err(|_| KeySerializationError::PasswordMustBeUtf8)?;

let key = cryptography_crypto::pkcs12::kdf(
password,
salt,
cryptography_crypto::pkcs12::KDF_ENCRYPTION_KEY_ID,
cipher_kdf_iter,
24,
openssl::hash::MessageDigest::sha1(),
)?;
let iv = cryptography_crypto::pkcs12::kdf(
password,
salt,
cryptography_crypto::pkcs12::KDF_IV_ID,
cipher_kdf_iter,
8,
openssl::hash::MessageDigest::sha1(),
)?;

Ok(openssl::symm::encrypt(
openssl::symm::Cipher::des_ede3_cbc(),
&key,
Some(&iv),
data,
)?)
}
EncryptionAlgorithm::PBESv2SHA256AndAES256CBC => {
let sha256 = openssl::hash::MessageDigest::sha256();

let mut key = [0; 32];
openssl::pkcs5::pbkdf2_hmac(
password,
salt,
cipher_kdf_iter.try_into().unwrap(),
sha256,
&mut key,
)?;

Ok(openssl::symm::encrypt(
openssl::symm::Cipher::aes_256_cbc(),
&key,
Some(iv),
data,
)?)
}
}
}
}
51 changes: 6 additions & 45 deletions src/rust/cryptography-key-parsing/src/pkcs8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use cryptography_x509::pkcs8::EncryptedPrivateKeyInfo;

#[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))]
use crate::MIN_DH_MODULUS_SIZE;
use crate::{ec, rsa, KeyParsingError, KeyParsingResult};
use crate::{ec, pbe, rsa, KeyParsingError, KeyParsingResult};

// RFC 5208 Section 5
#[derive(asn1::Asn1Read, asn1::Asn1Write)]
Expand Down Expand Up @@ -457,62 +457,23 @@ pub fn serialize_private_key(
Ok(asn1::write_single(&pki)?)
}

const KDF_ITERATION_COUNT: usize = 2048;
const KDF_ITERATION_COUNT: u64 = 2048;

pub fn serialize_encrypted_private_key(
pkey: &openssl::pkey::PKeyRef<openssl::pkey::Private>,
password: &[u8],
) -> crate::KeySerializationResult<Vec<u8>> {
let plaintext_der = serialize_private_key(pkey)?;

let e = pbe::EncryptionAlgorithm::PBESv2SHA256AndAES256CBC;

let mut salt = [0u8; 16];
let mut iv = [0u8; 16];
cryptography_openssl::rand::rand_bytes(&mut salt)?;
cryptography_openssl::rand::rand_bytes(&mut iv)?;

let cipher = openssl::symm::Cipher::aes_256_cbc();
let mut key = [0u8; 32];
openssl::pkcs5::pbkdf2_hmac(
password,
&salt,
KDF_ITERATION_COUNT,
openssl::hash::MessageDigest::sha256(),
&mut key,
)?;

let encrypted_data = openssl::symm::encrypt(cipher, &key, Some(&iv), &plaintext_der)?;

let kdf_algorithm = cryptography_x509::common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: cryptography_x509::common::AlgorithmParameters::Pbkdf2(
cryptography_x509::common::PBKDF2Params {
salt: &salt,
iteration_count: KDF_ITERATION_COUNT as u64,
key_length: None,
prf: Box::new(cryptography_x509::common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: cryptography_x509::common::AlgorithmParameters::HmacWithSha256(
Some(()),
),
}),
},
),
};

let encryption_algorithm = cryptography_x509::common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: cryptography_x509::common::AlgorithmParameters::Aes256Cbc(iv),
};

let encryption_alg = cryptography_x509::common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: cryptography_x509::common::AlgorithmParameters::Pbes2(
cryptography_x509::common::PBES2Params {
key_derivation_func: Box::new(kdf_algorithm),
encryption_scheme: Box::new(encryption_algorithm),
},
),
};
let encrypted_data = e.encrypt(password, KDF_ITERATION_COUNT, &salt, &iv, &plaintext_der)?;
let encryption_alg = e.algorithm_identifier(KDF_ITERATION_COUNT, &salt, &iv);

let epki = cryptography_x509::pkcs8::EncryptedPrivateKeyInfo {
encryption_algorithm: encryption_alg,
Expand Down
5 changes: 5 additions & 0 deletions src/rust/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ impl From<cryptography_key_parsing::KeyParsingError> for CryptographyError {
impl From<cryptography_key_parsing::KeySerializationError> for CryptographyError {
fn from(e: cryptography_key_parsing::KeySerializationError) -> CryptographyError {
match e {
cryptography_key_parsing::KeySerializationError::PasswordMustBeUtf8 => {
CryptographyError::Py(pyo3::exceptions::PyValueError::new_err(
"password must be valid UTF-8",
))
}
cryptography_key_parsing::KeySerializationError::Write(e) => {
CryptographyError::Asn1Write(e)
}
Expand Down
Loading