Skip to content

Commit 4f41bdb

Browse files
authored
pkcs12: use der::BmpString for KDF (#1209)
Uses the `BmpString` type to implement transcoding from UTF-8 to BMP/UCS-2. This type notably restricts the use of UTF-16 surrogate pairs, something the previous implementation was not doing correctly. This entailed making the methods which transcode UTF-8 fallible, since we need to handle the error case that they encode characters outside BMP/UCS-2.
1 parent 2f6303b commit 4f41bdb

File tree

5 files changed

+71
-47
lines changed

5 files changed

+71
-47
lines changed

Cargo.lock

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

der/src/asn1/bmp_string.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ impl BmpString {
3131

3232
for maybe_char in char::decode_utf16(ret.codepoints()) {
3333
match maybe_char {
34-
// All surrogates paired and character is in the Basic Multilingual Plane
34+
// Character is in the Basic Multilingual Plane
3535
Ok(c) if (c as u64) < u64::from(u16::MAX) => (),
36-
// Unpaired surrogates or characters outside Basic Multilingual Plane
36+
// Characters outside Basic Multilingual Plane or unpaired surrogates
3737
_ => return Err(Tag::BmpString.value_error()),
3838
}
3939
}

pkcs12/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ edition = "2021"
1515
rust-version = "1.65"
1616

1717
[dependencies]
18+
der = { version = "0.7.8", features = ["alloc"] }
19+
zeroize = "1.6"
20+
21+
# optional dependencies
1822
digest = { version = "0.10.7", features = ["alloc"], optional = true }
19-
zeroize = "1.6.0"
2023

2124
[dev-dependencies]
2225
hex-literal = "0.4"

pkcs12/src/kdf.rs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,10 @@
22
//! [RFC 7292 Appendix B](https://datatracker.ietf.org/doc/html/rfc7292#appendix-B)
33
44
use alloc::{vec, vec::Vec};
5+
use der::asn1::BmpString;
56
use digest::{core_api::BlockSizeUser, Digest, FixedOutputReset, OutputSizeUser, Update};
67
use zeroize::{Zeroize, Zeroizing};
78

8-
/// Transform a utf-8 string in a unicode (utf16) string as binary array.
9-
/// The Utf16 code points are stored in big endian format with two trailing zero bytes.
10-
fn str_to_unicode(utf8_str: &str) -> Vec<u8> {
11-
let mut utf16_bytes = Vec::with_capacity(utf8_str.len() * 2 + 2);
12-
for code_point in utf8_str.encode_utf16().chain(Some(0)) {
13-
utf16_bytes.extend(code_point.to_be_bytes());
14-
}
15-
utf16_bytes
16-
}
17-
189
/// Specify the usage type of the generated key
1910
/// This allows to derive distinct encryption keys, IVs and MAC from the same password or text
2011
/// string.
@@ -35,7 +26,22 @@ pub enum Pkcs12KeyType {
3526
/// pkcs12::kdf::Pkcs12KeyType::EncryptionKey, 1000, 32);
3627
/// ```
3728
pub fn derive_key_utf8<D>(
38-
pass: &str,
29+
password: &str,
30+
salt: &[u8],
31+
id: Pkcs12KeyType,
32+
rounds: i32,
33+
key_len: usize,
34+
) -> der::Result<Vec<u8>>
35+
where
36+
D: Digest + FixedOutputReset + BlockSizeUser,
37+
{
38+
let password_bmp = BmpString::from_utf8(password)?;
39+
Ok(derive_key_bmp::<D>(password_bmp, salt, id, rounds, key_len))
40+
}
41+
42+
/// Derive
43+
pub fn derive_key_bmp<D>(
44+
password: BmpString,
3945
salt: &[u8],
4046
id: Pkcs12KeyType,
4147
rounds: i32,
@@ -44,8 +50,12 @@ pub fn derive_key_utf8<D>(
4450
where
4551
D: Digest + FixedOutputReset + BlockSizeUser,
4652
{
47-
let pass_utf16 = Zeroizing::new(str_to_unicode(pass));
48-
derive_key::<D>(&pass_utf16, salt, id, rounds, key_len)
53+
let mut password = Zeroizing::new(Vec::from(password.into_bytes()));
54+
55+
// Password is NULL terminated
56+
password.extend([0u8; 2]);
57+
58+
derive_key::<D>(&password, salt, id, rounds, key_len)
4959
}
5060

5161
/// Derives `key` of type `id` from `pass` and `salt` with length `key_len` using `rounds`

pkcs12/tests/kdf.rs

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@ fn pkcs12_key_derive_sha256() {
1818
Pkcs12KeyType::EncryptionKey,
1919
100,
2020
32
21-
),
21+
)
22+
.unwrap(),
2223
hex!("fae4d4957a3cc781e1180b9d4fb79c1e0c8579b746a3177e5b0768a3118bf863")
2324
);
2425

2526
assert_eq!(
26-
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 32),
27+
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 32).unwrap(),
2728
hex!("e5ff813bc6547de5155b14d2fada85b3201a977349db6e26ccc998d9e8f83d6c")
2829
);
2930

3031
assert_eq!(
31-
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 32),
32+
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 32)
33+
.unwrap(),
3234
hex!("136355ed9434516682534f46d63956db5ff06b844702c2c1f3b46321e2524a4d")
3335
);
3436

@@ -39,17 +41,19 @@ fn pkcs12_key_derive_sha256() {
3941
Pkcs12KeyType::EncryptionKey,
4042
100,
4143
20
42-
),
44+
)
45+
.unwrap(),
4346
hex!("fae4d4957a3cc781e1180b9d4fb79c1e0c8579b7")
4447
);
4548

4649
assert_eq!(
47-
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 20),
50+
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 20).unwrap(),
4851
hex!("e5ff813bc6547de5155b14d2fada85b3201a9773")
4952
);
5053

5154
assert_eq!(
52-
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 20),
55+
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 20)
56+
.unwrap(),
5357
hex!("136355ed9434516682534f46d63956db5ff06b84")
5458
);
5559

@@ -60,17 +64,19 @@ fn pkcs12_key_derive_sha256() {
6064
Pkcs12KeyType::EncryptionKey,
6165
100,
6266
12
63-
),
67+
)
68+
.unwrap(),
6469
hex!("fae4d4957a3cc781e1180b9d")
6570
);
6671

6772
assert_eq!(
68-
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 12),
73+
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 12).unwrap(),
6974
hex!("e5ff813bc6547de5155b14d2")
7075
);
7176

7277
assert_eq!(
73-
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 12),
78+
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 12)
79+
.unwrap(),
7480
hex!("136355ed9434516682534f46")
7581
);
7682

@@ -81,34 +87,38 @@ fn pkcs12_key_derive_sha256() {
8187
Pkcs12KeyType::EncryptionKey,
8288
1000,
8389
32
84-
),
90+
)
91+
.unwrap(),
8592
hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527")
8693
);
8794

8895
assert_eq!(
89-
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 1000, 32),
96+
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 1000, 32)
97+
.unwrap(),
9098
hex!("6472c0ebad3fab4123e8b5ed7834de21eeb20187b3eff78a7d1cdffa4034851d")
9199
);
92100

93101
assert_eq!(
94-
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 1000, 32),
102+
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 1000, 32)
103+
.unwrap(),
95104
hex!("3f9113f05c30a996c4a516409bdac9d065f44296ccd52bb75de3fcfdbe2bf130")
96105
);
97106

98107
assert_eq!(
99-
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 1000, 32),
108+
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 1000, 32)
109+
.unwrap(),
100110
hex!("3f9113f05c30a996c4a516409bdac9d065f44296ccd52bb75de3fcfdbe2bf130")
101111
);
102112

103113
assert_eq!(
104-
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 1000, 100),
105-
hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527d8d0ebe2ccbf768c51c4d8fbd1bb156be06c1c59cbb69e44052ffc37376fdb47b2de7f9e543de9d096d8e5474b220410ff1c5d8bb7e5bc0f61baeaa12fd0da1d7a970172")
106-
);
114+
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 1000, 100).unwrap(),
115+
hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527d8d0ebe2ccbf768c51c4d8fbd1bb156be06c1c59cbb69e44052ffc37376fdb47b2de7f9e543de9d096d8e5474b220410ff1c5d8bb7e5bc0f61baeaa12fd0da1d7a970172")
116+
);
107117

108118
assert_eq!(
109-
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 1000, 200),
110-
hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527d8d0ebe2ccbf768c51c4d8fbd1bb156be06c1c59cbb69e44052ffc37376fdb47b2de7f9e543de9d096d8e5474b220410ff1c5d8bb7e5bc0f61baeaa12fd0da1d7a9701729cea6014d7fe62a2ed926dc36b61307f119d64edbceb5a9c58133bbf75ba0bef000a1a5180e4b1de7d89c89528bcb7899a1e46fd4da0d9de8f8e65e8d0d775e33d1247e76d596a34303161b219f39afda448bf518a2835fc5e28f0b55a1b6137a2c70cf7")
111-
);
119+
derive_key_utf8::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 1000, 200).unwrap(),
120+
hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527d8d0ebe2ccbf768c51c4d8fbd1bb156be06c1c59cbb69e44052ffc37376fdb47b2de7f9e543de9d096d8e5474b220410ff1c5d8bb7e5bc0f61baeaa12fd0da1d7a9701729cea6014d7fe62a2ed926dc36b61307f119d64edbceb5a9c58133bbf75ba0bef000a1a5180e4b1de7d89c89528bcb7899a1e46fd4da0d9de8f8e65e8d0d775e33d1247e76d596a34303161b219f39afda448bf518a2835fc5e28f0b55a1b6137a2c70cf7")
121+
);
112122
}
113123

114124
#[test]
@@ -123,7 +133,8 @@ fn pkcs12_key_derive_sha512() {
123133
Pkcs12KeyType::EncryptionKey,
124134
100,
125135
32
126-
),
136+
)
137+
.unwrap(),
127138
hex!("b14a9f01bfd9dce4c9d66d2fe9937e5fd9f1afa59e370a6fa4fc81c1cc8ec8ee")
128139
);
129140
}
@@ -140,7 +151,8 @@ fn pkcs12_key_derive_whirlpool() {
140151
Pkcs12KeyType::EncryptionKey,
141152
100,
142153
32
143-
),
154+
)
155+
.unwrap(),
144156
hex!("3324282adb468bff0734d3b7e399094ec8500cb5b0a3604055da107577aaf766")
145157
);
146158
}
@@ -150,14 +162,12 @@ fn pkcs12_key_derive_special_chars() {
150162
const PASS_SHORT: &str = "🔥";
151163
const SALT_INC: [u8; 8] = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8];
152164

153-
assert_eq!(
154-
derive_key_utf8::<sha2::Sha256>(
155-
PASS_SHORT,
156-
&SALT_INC,
157-
Pkcs12KeyType::EncryptionKey,
158-
100,
159-
32
160-
),
161-
hex!("d01e72a940b4b1a7a5707fc8264a60cb7606ff9051dedff90930687d2513c006")
162-
);
165+
assert!(derive_key_utf8::<sha2::Sha256>(
166+
PASS_SHORT,
167+
&SALT_INC,
168+
Pkcs12KeyType::EncryptionKey,
169+
100,
170+
32
171+
)
172+
.is_err()); // Emoji is not in the Basic Multilingual Plane
163173
}

0 commit comments

Comments
 (0)