-
Notifications
You must be signed in to change notification settings - Fork 172
pkcs12: KDF support #1154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
pkcs12: KDF support #1154
Changes from 12 commits
6dc0d09
e611e76
e14c270
6125b80
5286894
217783b
02a8193
236b957
c6b4928
1183d10
7e11f44
87cc22a
3dd8ece
282baa5
7537728
ebe05c2
8622bfd
30960c8
b04da8c
f669db8
23c364a
bf455d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| //! Implementation of the key derivation function | ||
| //! [RFC 7292 Appendix B](https://datatracker.ietf.org/doc/html/rfc7292#appendix-B) | ||
| use alloc::vec::Vec; | ||
| use digest::{core_api::BlockSizeUser, Digest, FixedOutputReset, OutputSizeUser, Update}; | ||
| use zeroize::Zeroize; | ||
|
|
||
| /// 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<u8> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The RFC talks about
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I guess we should really add
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit uncomfortable with the use of I don't know what the best course of action is here though, the best I can think of would be to add a
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (sorry last comment was sent before I refreshed the page, well, go for a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The format of an ASN1 Openssl provides similar functions for However, I am not sure how to interpret the remark "There are no Unicode byte order marks." in the RFC.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. last commit does not really fix the issue with unicode characters that do not encode on two bytes.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A BOM would precede the string, and if it had one in this case would be
We can worry about that all later though. It isn't needed for an initial PR. |
||
| let mut utf16_bytes = Vec::new(); | ||
| // reserve max number of required bytes to avoid re-allocation | ||
| utf16_bytes.reserve(utf8_str.len() * 2 + 2); | ||
xemwebe marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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. | ||
| pub enum Pkcs12KeyType { | ||
| /// Use key for encryption | ||
| EncryptionKey = 1, | ||
| /// Use key as initial vector | ||
| Iv = 2, | ||
| /// Use key as MAC | ||
| Mac = 3, | ||
| } | ||
|
|
||
| /// Derives `key` of type `id` from `pass` and `salt` with length `key_len` using `rounds` | ||
| /// iterations of the algorithm | ||
| /// ```rust | ||
| /// let key = pkcs12::kdf::derive_key::<sha2::Sha256>("top-secret", &[0x1, 0x2, 0x3, 0x4], | ||
| /// pkcs12::kdf::Pkcs12KeyType::EncryptionKey, 1000, 32); | ||
| /// ``` | ||
| pub fn derive_key<D>( | ||
| pass: &str, | ||
| salt: &[u8], | ||
| id: Pkcs12KeyType, | ||
| rounds: i32, | ||
| key_len: usize, | ||
| ) -> Vec<u8> | ||
| where | ||
| D: Digest + FixedOutputReset + BlockSizeUser, | ||
| { | ||
| let mut digest = D::new(); | ||
| let mut pass_utf16 = str_to_unicode(pass); | ||
| let output_size = <D as OutputSizeUser>::output_size(); | ||
| let block_size = D::block_size(); | ||
| let slen = block_size * ((salt.len() + block_size - 1) / block_size); | ||
| let plen = block_size * ((pass_utf16.len() + block_size - 1) / block_size); | ||
| let ilen = slen + plen; | ||
| let mut init_key = vec![0u8; ilen]; | ||
| for i in 0..slen { | ||
| init_key[i] = salt[i % salt.len()]; | ||
| } | ||
| for i in 0..plen { | ||
| init_key[slen + i] = pass_utf16[i % pass_utf16.len()]; | ||
| } | ||
| pass_utf16.zeroize(); | ||
|
||
|
|
||
| let id_block = match id { | ||
| Pkcs12KeyType::EncryptionKey => vec![1u8; block_size], | ||
| Pkcs12KeyType::Iv => vec![2u8; block_size], | ||
| Pkcs12KeyType::Mac => vec![3u8; block_size], | ||
baloo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| let mut m = key_len; | ||
| let mut n = 0; | ||
| let mut out = vec![0u8; key_len]; | ||
| loop { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RFC calls for a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a |
||
| <D as Update>::update(&mut digest, &id_block); | ||
| <D as Update>::update(&mut digest, &init_key); | ||
| let mut result = digest.finalize_fixed_reset(); | ||
| for _ in 1..rounds { | ||
| <D as Update>::update(&mut digest, &result[0..output_size]); | ||
| result = digest.finalize_fixed_reset(); | ||
| } | ||
| let new_bytes_num = m.min(output_size); | ||
| out[n..n + new_bytes_num].copy_from_slice(&result[0..new_bytes_num]); | ||
| n += new_bytes_num; | ||
| if m <= new_bytes_num { | ||
| break; | ||
| } | ||
|
|
||
| // prepare `init_key` for next block if `ouput_size` is smaller than `key_len` | ||
| m -= new_bytes_num; | ||
| let mut j = 0; | ||
| while j < ilen { | ||
| let mut c = 1_u16; | ||
| let mut k = block_size - 1; | ||
| loop { | ||
| c += init_key[k + j] as u16 + result[k % output_size] as u16; | ||
| init_key[j + k] = (c & 0x00ff) as u8; | ||
| c >>= 8; | ||
| if k == 0 { | ||
| break; | ||
| } | ||
| k -= 1; | ||
| } | ||
| j += block_size; | ||
| } | ||
| } | ||
| init_key.zeroize(); | ||
| out | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| #![cfg(feature = "alloc")] | ||
|
|
||
| #[test] | ||
| fn pkcs12_key_derive_sha256() { | ||
| use hex_literal::hex; | ||
| use pkcs12::kdf::{derive_key, Pkcs12KeyType}; | ||
|
|
||
| const PASS_SHORT: &str = "ge@äheim"; | ||
| const SALT_INC: [u8; 8] = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]; | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 100, 32), | ||
| hex!("fae4d4957a3cc781e1180b9d4fb79c1e0c8579b746a3177e5b0768a3118bf863") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 32), | ||
| hex!("e5ff813bc6547de5155b14d2fada85b3201a977349db6e26ccc998d9e8f83d6c") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 32), | ||
| hex!("136355ed9434516682534f46d63956db5ff06b844702c2c1f3b46321e2524a4d") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 100, 20), | ||
| hex!("fae4d4957a3cc781e1180b9d4fb79c1e0c8579b7") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 20), | ||
| hex!("e5ff813bc6547de5155b14d2fada85b3201a9773") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 20), | ||
| hex!("136355ed9434516682534f46d63956db5ff06b84") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 100, 12), | ||
| hex!("fae4d4957a3cc781e1180b9d") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 100, 12), | ||
| hex!("e5ff813bc6547de5155b14d2") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 100, 12), | ||
| hex!("136355ed9434516682534f46") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>( | ||
| PASS_SHORT, | ||
| &SALT_INC, | ||
| Pkcs12KeyType::EncryptionKey, | ||
| 1000, | ||
| 32 | ||
| ), | ||
| hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Iv, 1000, 32), | ||
| hex!("6472c0ebad3fab4123e8b5ed7834de21eeb20187b3eff78a7d1cdffa4034851d") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 1000, 32), | ||
| hex!("3f9113f05c30a996c4a516409bdac9d065f44296ccd52bb75de3fcfdbe2bf130") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::Mac, 1000, 32), | ||
| hex!("3f9113f05c30a996c4a516409bdac9d065f44296ccd52bb75de3fcfdbe2bf130") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 1000, 100), | ||
| hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527d8d0ebe2ccbf768c51c4d8fbd1bb156be06c1c59cbb69e44052ffc37376fdb47b2de7f9e543de9d096d8e5474b220410ff1c5d8bb7e5bc0f61baeaa12fd0da1d7a970172") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha256>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 1000, 200), | ||
| hex!("2b95a0569b63f641fae1efca32e84db3699ab74540628ba66283b58cf5400527d8d0ebe2ccbf768c51c4d8fbd1bb156be06c1c59cbb69e44052ffc37376fdb47b2de7f9e543de9d096d8e5474b220410ff1c5d8bb7e5bc0f61baeaa12fd0da1d7a9701729cea6014d7fe62a2ed926dc36b61307f119d64edbceb5a9c58133bbf75ba0bef000a1a5180e4b1de7d89c89528bcb7899a1e46fd4da0d9de8f8e65e8d0d775e33d1247e76d596a34303161b219f39afda448bf518a2835fc5e28f0b55a1b6137a2c70cf7") | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn pkcs12_key_derive_sha512() { | ||
| use hex_literal::hex; | ||
| use pkcs12::kdf::{derive_key, Pkcs12KeyType}; | ||
|
|
||
| const PASS_SHORT: &str = "ge@äheim"; | ||
| const SALT_INC: [u8; 8] = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]; | ||
|
|
||
| assert_eq!( | ||
| derive_key::<sha2::Sha512>(PASS_SHORT, &SALT_INC, Pkcs12KeyType::EncryptionKey, 100, 32), | ||
| hex!("b14a9f01bfd9dce4c9d66d2fe9937e5fd9f1afa59e370a6fa4fc81c1cc8ec8ee") | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn pkcs12_key_derive_whirlpool() { | ||
| use hex_literal::hex; | ||
| use pkcs12::kdf::{derive_key, Pkcs12KeyType}; | ||
|
|
||
| const PASS_SHORT: &str = "ge@äheim"; | ||
| const SALT_INC: [u8; 8] = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]; | ||
|
|
||
| assert_eq!( | ||
| derive_key::<whirlpool::Whirlpool>( | ||
| PASS_SHORT, | ||
| &SALT_INC, | ||
| Pkcs12KeyType::EncryptionKey, | ||
| 100, | ||
| 32 | ||
| ), | ||
| hex!("3324282adb468bff0734d3b7e399094ec8500cb5b0a3604055da107577aaf766") | ||
| ); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.