Skip to content

Commit 0cd9e42

Browse files
committed
Refactor and de-duplicate the PKCS#8/#12 encryption code
1 parent fe8438c commit 0cd9e42

File tree

6 files changed

+169
-182
lines changed

6 files changed

+169
-182
lines changed

src/rust/cryptography-key-parsing/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
pub mod dsa;
1010
pub mod ec;
11+
pub mod pbe;
1112
pub mod pem;
1213
pub mod pkcs8;
1314
pub mod rsa;
@@ -50,6 +51,7 @@ impl From<openssl::error::ErrorStack> for KeyParsingError {
5051
pub type KeyParsingResult<T> = Result<T, KeyParsingError>;
5152

5253
pub enum KeySerializationError {
54+
PasswordMustBeUtf8,
5355
Write(asn1::WriteError),
5456
OpenSSL(openssl::error::ErrorStack),
5557
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// This file is dual licensed under the terms of the Apache License, Version
2+
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
3+
// for complete details.
4+
5+
use crate::{KeySerializationError, KeySerializationResult};
6+
7+
pub enum EncryptionAlgorithm {
8+
PBESHA1And3KeyTripleDESCBC,
9+
PBESv2SHA256AndAES256CBC,
10+
}
11+
12+
impl EncryptionAlgorithm {
13+
pub fn salt_length(&self) -> usize {
14+
match self {
15+
EncryptionAlgorithm::PBESHA1And3KeyTripleDESCBC => 8,
16+
EncryptionAlgorithm::PBESv2SHA256AndAES256CBC => 16,
17+
}
18+
}
19+
20+
pub fn algorithm_identifier<'a>(
21+
&self,
22+
cipher_kdf_iter: u64,
23+
salt: &'a [u8],
24+
iv: &'a [u8],
25+
) -> cryptography_x509::common::AlgorithmIdentifier<'a> {
26+
match self {
27+
EncryptionAlgorithm::PBESHA1And3KeyTripleDESCBC => {
28+
cryptography_x509::common::AlgorithmIdentifier {
29+
oid: asn1::DefinedByMarker::marker(),
30+
params: cryptography_x509::common::AlgorithmParameters::PbeWithShaAnd3KeyTripleDesCbc(cryptography_x509::common::Pkcs12PbeParams{
31+
salt,
32+
iterations: cipher_kdf_iter,
33+
}),
34+
}
35+
}
36+
EncryptionAlgorithm::PBESv2SHA256AndAES256CBC => {
37+
let kdf_algorithm_identifier = cryptography_x509::common::AlgorithmIdentifier {
38+
oid: asn1::DefinedByMarker::marker(),
39+
params: cryptography_x509::common::AlgorithmParameters::Pbkdf2(
40+
cryptography_x509::common::PBKDF2Params {
41+
salt,
42+
iteration_count: cipher_kdf_iter,
43+
key_length: None,
44+
prf: Box::new(cryptography_x509::common::AlgorithmIdentifier {
45+
oid: asn1::DefinedByMarker::marker(),
46+
params:
47+
cryptography_x509::common::AlgorithmParameters::HmacWithSha256(
48+
Some(()),
49+
),
50+
}),
51+
},
52+
),
53+
};
54+
let encryption_algorithm_identifier =
55+
cryptography_x509::common::AlgorithmIdentifier {
56+
oid: asn1::DefinedByMarker::marker(),
57+
params: cryptography_x509::common::AlgorithmParameters::Aes256Cbc(
58+
iv[..16].try_into().unwrap(),
59+
),
60+
};
61+
62+
cryptography_x509::common::AlgorithmIdentifier {
63+
oid: asn1::DefinedByMarker::marker(),
64+
params: cryptography_x509::common::AlgorithmParameters::Pbes2(
65+
cryptography_x509::common::PBES2Params {
66+
key_derivation_func: Box::new(kdf_algorithm_identifier),
67+
encryption_scheme: Box::new(encryption_algorithm_identifier),
68+
},
69+
),
70+
}
71+
}
72+
}
73+
}
74+
75+
pub fn encrypt(
76+
&self,
77+
password: &[u8],
78+
cipher_kdf_iter: u64,
79+
salt: &[u8],
80+
iv: &[u8],
81+
data: &[u8],
82+
) -> KeySerializationResult<Vec<u8>> {
83+
match self {
84+
EncryptionAlgorithm::PBESHA1And3KeyTripleDESCBC => {
85+
let password = std::str::from_utf8(password)
86+
.map_err(|_| KeySerializationError::PasswordMustBeUtf8)?;
87+
88+
let key = cryptography_crypto::pkcs12::kdf(
89+
password,
90+
salt,
91+
cryptography_crypto::pkcs12::KDF_ENCRYPTION_KEY_ID,
92+
cipher_kdf_iter,
93+
24,
94+
openssl::hash::MessageDigest::sha1(),
95+
)?;
96+
let iv = cryptography_crypto::pkcs12::kdf(
97+
password,
98+
salt,
99+
cryptography_crypto::pkcs12::KDF_IV_ID,
100+
cipher_kdf_iter,
101+
8,
102+
openssl::hash::MessageDigest::sha1(),
103+
)?;
104+
105+
Ok(openssl::symm::encrypt(
106+
openssl::symm::Cipher::des_ede3_cbc(),
107+
&key,
108+
Some(&iv),
109+
data,
110+
)?)
111+
}
112+
EncryptionAlgorithm::PBESv2SHA256AndAES256CBC => {
113+
let sha256 = openssl::hash::MessageDigest::sha256();
114+
115+
let mut key = [0; 32];
116+
openssl::pkcs5::pbkdf2_hmac(
117+
password,
118+
salt,
119+
cipher_kdf_iter.try_into().unwrap(),
120+
sha256,
121+
&mut key,
122+
)?;
123+
124+
Ok(openssl::symm::encrypt(
125+
openssl::symm::Cipher::aes_256_cbc(),
126+
&key,
127+
Some(iv),
128+
data,
129+
)?)
130+
}
131+
}
132+
}
133+
}

src/rust/cryptography-key-parsing/src/pkcs8.rs

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use cryptography_x509::pkcs8::EncryptedPrivateKeyInfo;
1010

1111
#[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))]
1212
use crate::MIN_DH_MODULUS_SIZE;
13-
use crate::{ec, rsa, KeyParsingError, KeyParsingResult};
13+
use crate::{ec, pbe, rsa, KeyParsingError, KeyParsingResult};
1414

1515
// RFC 5208 Section 5
1616
#[derive(asn1::Asn1Read, asn1::Asn1Write)]
@@ -457,62 +457,23 @@ pub fn serialize_private_key(
457457
Ok(asn1::write_single(&pki)?)
458458
}
459459

460-
const KDF_ITERATION_COUNT: usize = 2048;
460+
const KDF_ITERATION_COUNT: u64 = 2048;
461461

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

468+
let e = pbe::EncryptionAlgorithm::PBESv2SHA256AndAES256CBC;
469+
468470
let mut salt = [0u8; 16];
469471
let mut iv = [0u8; 16];
470472
cryptography_openssl::rand::rand_bytes(&mut salt)?;
471473
cryptography_openssl::rand::rand_bytes(&mut iv)?;
472474

473-
let cipher = openssl::symm::Cipher::aes_256_cbc();
474-
let mut key = [0u8; 32];
475-
openssl::pkcs5::pbkdf2_hmac(
476-
password,
477-
&salt,
478-
KDF_ITERATION_COUNT,
479-
openssl::hash::MessageDigest::sha256(),
480-
&mut key,
481-
)?;
482-
483-
let encrypted_data = openssl::symm::encrypt(cipher, &key, Some(&iv), &plaintext_der)?;
484-
485-
let kdf_algorithm = cryptography_x509::common::AlgorithmIdentifier {
486-
oid: asn1::DefinedByMarker::marker(),
487-
params: cryptography_x509::common::AlgorithmParameters::Pbkdf2(
488-
cryptography_x509::common::PBKDF2Params {
489-
salt: &salt,
490-
iteration_count: KDF_ITERATION_COUNT as u64,
491-
key_length: None,
492-
prf: Box::new(cryptography_x509::common::AlgorithmIdentifier {
493-
oid: asn1::DefinedByMarker::marker(),
494-
params: cryptography_x509::common::AlgorithmParameters::HmacWithSha256(
495-
Some(()),
496-
),
497-
}),
498-
},
499-
),
500-
};
501-
502-
let encryption_algorithm = cryptography_x509::common::AlgorithmIdentifier {
503-
oid: asn1::DefinedByMarker::marker(),
504-
params: cryptography_x509::common::AlgorithmParameters::Aes256Cbc(iv),
505-
};
506-
507-
let encryption_alg = cryptography_x509::common::AlgorithmIdentifier {
508-
oid: asn1::DefinedByMarker::marker(),
509-
params: cryptography_x509::common::AlgorithmParameters::Pbes2(
510-
cryptography_x509::common::PBES2Params {
511-
key_derivation_func: Box::new(kdf_algorithm),
512-
encryption_scheme: Box::new(encryption_algorithm),
513-
},
514-
),
515-
};
475+
let encrypted_data = e.encrypt(password, KDF_ITERATION_COUNT, &salt, &iv, &plaintext_der)?;
476+
let encryption_alg = e.algorithm_identifier(KDF_ITERATION_COUNT, &salt, &iv);
516477

517478
let epki = cryptography_x509::pkcs8::EncryptedPrivateKeyInfo {
518479
encryption_algorithm: encryption_alg,

src/rust/src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ impl From<cryptography_key_parsing::KeyParsingError> for CryptographyError {
136136
impl From<cryptography_key_parsing::KeySerializationError> for CryptographyError {
137137
fn from(e: cryptography_key_parsing::KeySerializationError) -> CryptographyError {
138138
match e {
139+
cryptography_key_parsing::KeySerializationError::PasswordMustBeUtf8 => {
140+
CryptographyError::Py(pyo3::exceptions::PyValueError::new_err(
141+
"password must be valid UTF-8",
142+
))
143+
}
139144
cryptography_key_parsing::KeySerializationError::Write(e) => {
140145
CryptographyError::Asn1Write(e)
141146
}

0 commit comments

Comments
 (0)