Skip to content

Commit 2dbc115

Browse files
committed
temporarily copied over kdf.rs from RustCrypto#1154 and refactored decrypt support to use it. added a test case with a pfx from PKITS to exercise the kdf. added implementation of EncryptedPrivateKeyInfo.
1 parent 2db1587 commit 2dbc115

10 files changed

Lines changed: 375 additions & 53 deletions

File tree

Cargo.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkcs12/Cargo.toml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,27 @@ all-features = true
1919
rustdoc-args = ["--cfg", "docsrs"]
2020

2121
[dependencies]
22+
cbc = { version = "0.1.2", optional = true }
2223
der = { version = "0.7.7", features = ["alloc", "derive", "oid", "pem"] }
2324
spki = { version = "0.7" }
2425
x509-cert = { version = "0.2.3", default-features = false, features = ["pem"] }
2526
const-oid = { version = "0.9", features = ["db"] } # TODO: path = "../const-oid"
2627
cms = "0.2.1"
2728
pkcs8 = { version = "0.10.2", features = ["pkcs5"], optional = true }
2829
pkcs5 = {version = "0.7.1", features = ["pbes2"], optional = true}
30+
digest = { version = "0.10.7", features=["alloc"], optional = true }
31+
zeroize = "1.6.0"
32+
sha1 = { version = "0.10.5", optional = true }
33+
des = { version = "0.8.1", optional = true, default-features = false }
2934

3035
[dev-dependencies]
3136
hex-literal = "0.3.3"
32-
subtle-encoding = "0.5.1"
3337
pkcs8 = { version = "0.10.2", features = ["pkcs5"] }
34-
pkcs5 = {version = "0.7.1", features = ["pbes2"]}
38+
pkcs5 = {version = "0.7.1", features = ["pbes2", "3des"]}
39+
subtle-encoding = "0.5.1"
40+
sha2 = "0.10.7"
3541

3642
[features]
37-
decrypt = ["pkcs8", "pkcs5"]
38-
insecure = ["pkcs5/sha1-insecure", "pkcs5/des-insecure"]
43+
decrypt = ["pkcs8", "pkcs5", "cbc", "kdf"]
44+
insecure = ["pkcs5/sha1-insecure", "pkcs5/des-insecure", "des", "sha1"]
45+
kdf = ["dep:digest"]

pkcs12/src/decrypt.rs

Lines changed: 89 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
33
use crate::authenticated_safe::AuthenticatedSafe;
44
use crate::cert_type::CertBag;
5+
use crate::pbe_params::EncryptedPrivateKeyInfo as OtherEncryptedPrivateKeyInfo;
6+
use core::str::Utf8Error;
7+
8+
#[cfg(all(feature = "kdf", feature = "insecure"))]
9+
use crate::decrypt_kdf::*;
10+
511
use crate::pfx::Pfx;
612
use crate::safe_bag::{PrivateKeyInfo, SafeContents};
713
use cms::encrypted_data::EncryptedData;
@@ -10,7 +16,7 @@ use const_oid::ObjectIdentifier;
1016
use der::asn1::ContextSpecific;
1117
use der::asn1::OctetString;
1218
use der::{Any, Decode, Encode};
13-
use pkcs5::pbes2;
19+
use pkcs5::pbes2::PBES2_OID;
1420
use pkcs8::EncryptedPrivateKeyInfo;
1521
use x509_cert::Certificate;
1622

@@ -41,8 +47,12 @@ pub enum Error {
4147

4248
/// Missing expected content
4349
UnexpectedAlgorithm(ObjectIdentifier),
50+
51+
/// String conversion error
52+
Utf8Error(Utf8Error),
4453
}
45-
type Result<T> = core::result::Result<T, Error>;
54+
/// Result type for PKCS #12 der
55+
pub type Result<T> = core::result::Result<T, Error>;
4656

4757
fn process_safe_contents(
4858
data: &[u8],
@@ -67,19 +77,46 @@ fn process_safe_contents(
6777
}
6878
}
6979
crate::PKCS_12_PKCS8_KEY_BAG_OID => {
70-
let cs: ContextSpecific<EncryptedPrivateKeyInfo<'_>> =
80+
let cs_tmp: ContextSpecific<OtherEncryptedPrivateKeyInfo> =
7181
ContextSpecific::from_der(&safe_bag.bag_value).map_err(|e| Error::Asn1(e))?;
72-
let mut ciphertext = cs.value.encrypted_data.to_vec();
73-
let plaintext = cs
74-
.value
75-
.encryption_algorithm
76-
.decrypt_in_place(password, &mut ciphertext)
77-
.map_err(|e| Error::Pkcs5(e))?;
78-
if key.is_none() {
79-
key = Some(PrivateKeyInfo::from_der(plaintext).map_err(|e| Error::Asn1(e))?);
80-
} else {
81-
return Err(Error::UnexpectedSafeBag);
82-
}
82+
match cs_tmp.value.encryption_algorithm.oid {
83+
PBES2_OID => {
84+
let cs: ContextSpecific<EncryptedPrivateKeyInfo<'_>> =
85+
ContextSpecific::from_der(&safe_bag.bag_value)
86+
.map_err(|e| Error::Asn1(e))?;
87+
let mut ciphertext = cs.value.encrypted_data.to_vec();
88+
let plaintext = cs
89+
.value
90+
.encryption_algorithm
91+
.decrypt_in_place(password, &mut ciphertext)
92+
.map_err(|e| Error::Pkcs5(e))?;
93+
if key.is_none() {
94+
key = Some(
95+
PrivateKeyInfo::from_der(plaintext).map_err(|e| Error::Asn1(e))?,
96+
);
97+
} else {
98+
return Err(Error::UnexpectedSafeBag);
99+
}
100+
}
101+
#[cfg(all(feature = "kdf", feature = "insecure"))]
102+
_ => {
103+
let cur_key = pkcs12_pbe_key(
104+
cs_tmp.value.encrypted_data,
105+
password,
106+
&cs_tmp.value.encryption_algorithm,
107+
)?;
108+
if key.is_some() {
109+
return Err(Error::UnexpectedAuthSafe);
110+
}
111+
key = Some(cur_key);
112+
}
113+
#[cfg(not(all(feature = "kdf", feature = "insecure")))]
114+
_ => {
115+
return Err(Error::UnexpectedAlgorithm(
116+
cs_tmp.value.encryption_algorithm.oid,
117+
))
118+
}
119+
};
83120
}
84121
crate::PKCS_12_KEY_BAG_OID => {
85122
if key.is_none() {
@@ -103,38 +140,50 @@ fn process_encrypted_data(
103140
) -> Result<(Option<PrivateKeyInfo>, Option<Certificate>)> {
104141
let enc_data_os = &data.to_der().map_err(|e| Error::Asn1(e))?;
105142
let enc_data = EncryptedData::from_der(enc_data_os.as_slice()).map_err(|e| Error::Asn1(e))?;
106-
let enc_params = match enc_data
107-
.enc_content_info
108-
.content_enc_alg
109-
.parameters
110-
.as_ref()
111-
{
112-
Some(params) => params.to_der().map_err(|e| Error::Asn1(e))?,
113-
None => return Err(Error::MissingParameters),
114-
};
115-
116-
let params = match enc_data.enc_content_info.content_enc_alg.oid {
117-
pbes2::PBES2_OID => {
118-
pkcs8::pkcs5::pbes2::Parameters::from_der(&enc_params).map_err(|e| Error::Asn1(e))?
143+
144+
match enc_data.enc_content_info.content_enc_alg.oid {
145+
PBES2_OID => {
146+
let enc_params = match enc_data
147+
.enc_content_info
148+
.content_enc_alg
149+
.parameters
150+
.as_ref()
151+
{
152+
Some(params) => params.to_der().map_err(|e| Error::Asn1(e))?,
153+
None => return Err(Error::MissingParameters),
154+
};
155+
let params = pkcs8::pkcs5::pbes2::Parameters::from_der(&enc_params)
156+
.map_err(|e| Error::Asn1(e))?;
157+
let scheme = pkcs5::EncryptionScheme::try_from(params.clone())
158+
.map_err(|_e| Error::EncryptionScheme)?;
159+
match enc_data.enc_content_info.encrypted_content {
160+
Some(content) => {
161+
let mut ciphertext = content.as_bytes().to_vec();
162+
let plaintext = scheme
163+
.decrypt_in_place(password, &mut ciphertext)
164+
.map_err(|e| Error::Pkcs5(e))?;
165+
process_safe_contents(plaintext, password)
166+
}
167+
None => return Err(Error::MissingContent),
168+
}
169+
}
170+
#[cfg(all(feature = "kdf", feature = "insecure"))]
171+
crate::PKCS_12_PBE_WITH_SHAAND3_KEY_TRIPLE_DES_CBC => {
172+
let plaintext = match enc_data.enc_content_info.encrypted_content {
173+
Some(encrypted_content) => pkcs12_pbe(
174+
encrypted_content,
175+
password,
176+
&enc_data.enc_content_info.content_enc_alg,
177+
)?,
178+
None => return Err(Error::MissingParameters),
179+
};
180+
process_safe_contents(&plaintext, password)
119181
}
120182
_ => {
121183
return Err(Error::UnexpectedAlgorithm(
122184
enc_data.enc_content_info.content_enc_alg.oid,
123185
))
124186
}
125-
};
126-
127-
let scheme =
128-
pkcs5::EncryptionScheme::try_from(params.clone()).map_err(|_e| Error::EncryptionScheme)?;
129-
match enc_data.enc_content_info.encrypted_content {
130-
Some(content) => {
131-
let mut ciphertext = content.as_bytes().to_vec();
132-
let plaintext = scheme
133-
.decrypt_in_place(password, &mut ciphertext)
134-
.map_err(|e| Error::Pkcs5(e))?;
135-
process_safe_contents(plaintext, password)
136-
}
137-
None => return Err(Error::MissingContent),
138187
}
139188
}
140189

pkcs12/src/decrypt_kdf.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//! Convenience functions for working with some common PKCS #12 use cases
2+
3+
use crate::pbe_params::Pkcs12PbeParams;
4+
use alloc::vec::Vec;
5+
use pkcs5::pbes2::EncryptionScheme;
6+
use spki::AlgorithmIdentifierOwned;
7+
8+
use crate::decrypt::{Error, Result};
9+
use crate::safe_bag::PrivateKeyInfo;
10+
use der::asn1::OctetString;
11+
use der::{Decode, Encode};
12+
13+
#[cfg(all(feature = "kdf", feature = "insecure", feature = "decrypt"))]
14+
pub(crate) fn pkcs12_pbe_key(
15+
encrypted_content: OctetString,
16+
password: &[u8],
17+
alg: &AlgorithmIdentifierOwned,
18+
) -> Result<PrivateKeyInfo> {
19+
let plaintext = pkcs12_pbe(encrypted_content, password, alg)?;
20+
PrivateKeyInfo::from_der(&plaintext).map_err(|e| Error::Asn1(e))
21+
}
22+
23+
#[cfg(all(feature = "kdf", feature = "insecure", feature = "decrypt"))]
24+
pub(crate) fn pkcs12_pbe(
25+
encrypted_content: OctetString,
26+
password: &[u8],
27+
alg: &AlgorithmIdentifierOwned,
28+
) -> Result<Vec<u8>> {
29+
use crate::kdf::*;
30+
use cbc::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit};
31+
use core::str;
32+
use sha1::Sha1;
33+
34+
let enc_params = match &alg.parameters {
35+
Some(params) => params.to_der().map_err(|e| Error::Asn1(e))?,
36+
None => return Err(Error::MissingParameters),
37+
};
38+
39+
let p12_pbe_params = Pkcs12PbeParams::from_der(&enc_params).map_err(|e| Error::Asn1(e))?;
40+
let s = str::from_utf8(password).map_err(|e| Error::Utf8Error(e))?;
41+
42+
let iv = derive_key::<Sha1>(
43+
&s,
44+
p12_pbe_params.salt.as_bytes(),
45+
Pkcs12KeyType::Iv,
46+
2048,
47+
8,
48+
);
49+
let es = match alg.oid {
50+
crate::PKCS_12_PBE_WITH_SHAAND3_KEY_TRIPLE_DES_CBC => EncryptionScheme::DesEde3Cbc {
51+
iv: iv.as_slice().try_into().unwrap(),
52+
},
53+
// PKCS_12_PBE_WITH_SHAAND2_KEY_TRIPLE_DES_CBC
54+
// PKCS_12_PBE_WITH_SHAAND128_BIT_RC4
55+
// PKCS_12_PBE_WITH_SHAAND40_BIT_RC4
56+
// PKCS_12_PBE_WITH_SHAAND128_BIT_RC2_CBC
57+
// PKCS_12_PBEWITH_SHAAND40_BIT_RC2_CBC
58+
_ => return Err(Error::UnexpectedAlgorithm(alg.oid)),
59+
};
60+
let key = derive_key::<Sha1>(
61+
&s,
62+
p12_pbe_params.salt.as_bytes(),
63+
Pkcs12KeyType::EncryptionKey,
64+
p12_pbe_params.iterations,
65+
es.key_size(),
66+
);
67+
68+
let mut ciphertext = encrypted_content.as_bytes().to_vec();
69+
let plaintext = cbc::Decryptor::<des::TdesEde3>::new_from_slices(key.as_slice(), iv.as_slice())
70+
.map_err(|_| es.to_alg_params_invalid())
71+
.map_err(|_| Error::EncryptionScheme)?
72+
.decrypt_padded_mut::<Pkcs7>(&mut ciphertext)
73+
.unwrap();
74+
75+
Ok(plaintext.to_vec())
76+
}

0 commit comments

Comments
 (0)