diff --git a/Cargo.lock b/Cargo.lock index 360fa8a6b..27f364814 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1005,6 +1005,7 @@ dependencies = [ name = "pkcs12" version = "0.0.0" dependencies = [ + "der", "digest", "hex-literal", "sha2", diff --git a/der/src/asn1/bmp_string.rs b/der/src/asn1/bmp_string.rs index b911e8f22..ad5e1a2d1 100644 --- a/der/src/asn1/bmp_string.rs +++ b/der/src/asn1/bmp_string.rs @@ -31,9 +31,9 @@ impl BmpString { for maybe_char in char::decode_utf16(ret.codepoints()) { match maybe_char { - // All surrogates paired and character is in the Basic Multilingual Plane + // Character is in the Basic Multilingual Plane Ok(c) if (c as u64) < u64::from(u16::MAX) => (), - // Unpaired surrogates or characters outside Basic Multilingual Plane + // Characters outside Basic Multilingual Plane or unpaired surrogates _ => return Err(Tag::BmpString.value_error()), } } diff --git a/pkcs12/Cargo.toml b/pkcs12/Cargo.toml index 206c5c5bd..2988718b8 100644 --- a/pkcs12/Cargo.toml +++ b/pkcs12/Cargo.toml @@ -15,8 +15,11 @@ edition = "2021" rust-version = "1.65" [dependencies] +der = { version = "0.7.8", features = ["alloc"] } +zeroize = "1.6" + +# optional dependencies digest = { version = "0.10.7", features = ["alloc"], optional = true } -zeroize = "1.6.0" [dev-dependencies] hex-literal = "0.4" diff --git a/pkcs12/src/kdf.rs b/pkcs12/src/kdf.rs index e411324f6..be53ef37b 100644 --- a/pkcs12/src/kdf.rs +++ b/pkcs12/src/kdf.rs @@ -2,19 +2,10 @@ //! [RFC 7292 Appendix B](https://datatracker.ietf.org/doc/html/rfc7292#appendix-B) use alloc::{vec, vec::Vec}; +use der::asn1::BmpString; use digest::{core_api::BlockSizeUser, Digest, FixedOutputReset, OutputSizeUser, Update}; use zeroize::{Zeroize, Zeroizing}; -/// Transform a utf-8 string in a unicode (utf16) string as binary array. -/// The Utf16 code points are stored in big endian format with two trailing zero bytes. -fn str_to_unicode(utf8_str: &str) -> Vec { - let mut utf16_bytes = Vec::with_capacity(utf8_str.len() * 2 + 2); - for code_point in utf8_str.encode_utf16().chain(Some(0)) { - utf16_bytes.extend(code_point.to_be_bytes()); - } - utf16_bytes -} - /// Specify the usage type of the generated key /// This allows to derive distinct encryption keys, IVs and MAC from the same password or text /// string. @@ -35,7 +26,22 @@ pub enum Pkcs12KeyType { /// pkcs12::kdf::Pkcs12KeyType::EncryptionKey, 1000, 32); /// ``` pub fn derive_key_utf8( - pass: &str, + password: &str, + salt: &[u8], + id: Pkcs12KeyType, + rounds: i32, + key_len: usize, +) -> der::Result> +where + D: Digest + FixedOutputReset + BlockSizeUser, +{ + let password_bmp = BmpString::from_utf8(password)?; + Ok(derive_key_bmp::(password_bmp, salt, id, rounds, key_len)) +} + +/// Derive +pub fn derive_key_bmp( + password: BmpString, salt: &[u8], id: Pkcs12KeyType, rounds: i32, @@ -44,8 +50,12 @@ pub fn derive_key_utf8( where D: Digest + FixedOutputReset + BlockSizeUser, { - let pass_utf16 = Zeroizing::new(str_to_unicode(pass)); - derive_key::(&pass_utf16, salt, id, rounds, key_len) + let mut password = Zeroizing::new(Vec::from(password.into_bytes())); + + // Password is NULL terminated + password.extend([0u8; 2]); + + derive_key::(&password, salt, id, rounds, key_len) } /// Derives `key` of type `id` from `pass` and `salt` with length `key_len` using `rounds` diff --git a/pkcs12/tests/kdf.rs b/pkcs12/tests/kdf.rs index 2b40efeb4..7fdc02a4d 100644 --- a/pkcs12/tests/kdf.rs +++ b/pkcs12/tests/kdf.rs @@ -18,17 +18,19 @@ fn pkcs12_key_derive_sha256() { Pkcs12KeyType::EncryptionKey, 100, 32 - ), + ) + .unwrap(), hex!("fae4d4957a3cc781e1180b9d4fb79c1e0c8579b746a3177e5b0768a3118bf863") ); assert_eq!( - derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 32), + derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 32).unwrap(), hex!("e5ff813bc6547de5155b14d2fada85b3201a977349db6e26ccc998d9e8f83d6c") ); assert_eq!( - derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 32), + derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 32) + .unwrap(), hex!("136355ed9434516682534f46d63956db5ff06b844702c2c1f3b46321e2524a4d") ); @@ -39,17 +41,19 @@ fn pkcs12_key_derive_sha256() { Pkcs12KeyType::EncryptionKey, 100, 20 - ), + ) + .unwrap(), hex!("fae4d4957a3cc781e1180b9d4fb79c1e0c8579b7") ); assert_eq!( - derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 20), + derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 20).unwrap(), hex!("e5ff813bc6547de5155b14d2fada85b3201a9773") ); assert_eq!( - derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 20), + derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 20) + .unwrap(), hex!("136355ed9434516682534f46d63956db5ff06b84") ); @@ -60,17 +64,19 @@ fn pkcs12_key_derive_sha256() { Pkcs12KeyType::EncryptionKey, 100, 12 - ), + ) + .unwrap(), hex!("fae4d4957a3cc781e1180b9d") ); assert_eq!( - derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 12), + derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 12).unwrap(), hex!("e5ff813bc6547de5155b14d2") ); assert_eq!( - derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 12), + derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 12) + .unwrap(), hex!("136355ed9434516682534f46") ); @@ -81,34 +87,38 @@ fn pkcs12_key_derive_sha256() { Pkcs12KeyType::EncryptionKey, 1000, 32 - ), + ) + .unwrap(), hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527") ); assert_eq!( - derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 1000, 32), + derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 1000, 32) + .unwrap(), hex!("6472c0ebad3fab4123e8b5ed7834de21eeb20187b3eff78a7d1cdffa4034851d") ); assert_eq!( - derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 1000, 32), + derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 1000, 32) + .unwrap(), hex!("3f9113f05c30a996c4a516409bdac9d065f44296ccd52bb75de3fcfdbe2bf130") ); assert_eq!( - derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 1000, 32), + derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 1000, 32) + .unwrap(), hex!("3f9113f05c30a996c4a516409bdac9d065f44296ccd52bb75de3fcfdbe2bf130") ); assert_eq!( - derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 1000, 100), - hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527d8d0ebe2ccbf768c51c4d8fbd1bb156be06c1c59cbb69e44052ffc37376fdb47b2de7f9e543de9d096d8e5474b220410ff1c5d8bb7e5bc0f61baeaa12fd0da1d7a970172") - ); + derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 1000, 100).unwrap(), + hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527d8d0ebe2ccbf768c51c4d8fbd1bb156be06c1c59cbb69e44052ffc37376fdb47b2de7f9e543de9d096d8e5474b220410ff1c5d8bb7e5bc0f61baeaa12fd0da1d7a970172") + ); assert_eq!( - derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 1000, 200), - hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527d8d0ebe2ccbf768c51c4d8fbd1bb156be06c1c59cbb69e44052ffc37376fdb47b2de7f9e543de9d096d8e5474b220410ff1c5d8bb7e5bc0f61baeaa12fd0da1d7a9701729cea6014d7fe62a2ed926dc36b61307f119d64edbceb5a9c58133bbf75ba0bef000a1a5180e4b1de7d89c89528bcb7899a1e46fd4da0d9de8f8e65e8d0d775e33d1247e76d596a34303161b219f39afda448bf518a2835fc5e28f0b55a1b6137a2c70cf7") - ); + derive_key_utf8::(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 1000, 200).unwrap(), + hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527d8d0ebe2ccbf768c51c4d8fbd1bb156be06c1c59cbb69e44052ffc37376fdb47b2de7f9e543de9d096d8e5474b220410ff1c5d8bb7e5bc0f61baeaa12fd0da1d7a9701729cea6014d7fe62a2ed926dc36b61307f119d64edbceb5a9c58133bbf75ba0bef000a1a5180e4b1de7d89c89528bcb7899a1e46fd4da0d9de8f8e65e8d0d775e33d1247e76d596a34303161b219f39afda448bf518a2835fc5e28f0b55a1b6137a2c70cf7") + ); } #[test] @@ -123,7 +133,8 @@ fn pkcs12_key_derive_sha512() { Pkcs12KeyType::EncryptionKey, 100, 32 - ), + ) + .unwrap(), hex!("b14a9f01bfd9dce4c9d66d2fe9937e5fd9f1afa59e370a6fa4fc81c1cc8ec8ee") ); } @@ -140,7 +151,8 @@ fn pkcs12_key_derive_whirlpool() { Pkcs12KeyType::EncryptionKey, 100, 32 - ), + ) + .unwrap(), hex!("3324282adb468bff0734d3b7e399094ec8500cb5b0a3604055da107577aaf766") ); } @@ -150,14 +162,12 @@ fn pkcs12_key_derive_special_chars() { const PASS_SHORT: &str = "🔥"; const SALT_INC: [u8; 8] = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]; - assert_eq!( - derive_key_utf8::( - PASS_SHORT, - &SALT_INC, - Pkcs12KeyType::EncryptionKey, - 100, - 32 - ), - hex!("d01e72a940b4b1a7a5707fc8264a60cb7606ff9051dedff90930687d2513c006") - ); + assert!(derive_key_utf8::( + PASS_SHORT, + &SALT_INC, + Pkcs12KeyType::EncryptionKey, + 100, + 32 + ) + .is_err()); // Emoji is not in the Basic Multilingual Plane }