Skip to content

Commit 81d78d9

Browse files
Tobias WaurickTobTheRock
authored andcommitted
feat: Aes ctr mode ciphers for openssl
I added the necessary key expansion and crypto methods stated in the draft. They can be verified working with the provided test vectors.
1 parent d2e43c5 commit 81d78d9

File tree

11 files changed

+368
-91
lines changed

11 files changed

+368
-91
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ It is in it's current form a subset of the specification.
1313
There is an alternative implementation under [goto-opensource/secure-frame-ts](https://github.com/goto-opensource/secure-frame-ts)
1414

1515
## Differences from the sframe draft
16-
* Aes-CTR is not implemented
1716
* ratcheting is not implemented
1817
* keyIds are used as senderIds
18+
* no metadata authentication
1919

2020
## Supported crypto libraries
2121
Currently two crypto libraries are supported:
2222
- [ring](https://crates.io/crates/ring)
2323
- is enabled per default with the feature `ring`
2424
- supports compilation to Wasm32
25+
- Aes-CTR mode ciphers are not supported
2526
- [openssl](https://crates.io/crates/openssl)
2627
- is enabled with the feature `openssl`
2728
- To build e.g. use `cargo build --features openssl --no-default-features`

src/crypto/aead.rs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ mod test {
6666
}
6767

6868
mod test_vectors {
69-
use crate::test_vectors::get_test_vector;
69+
70+
use crate::crypto::key_expansion::KeyExpansion;
71+
use crate::test_vectors::{get_test_vector, TestVector};
7072

7173
use crate::{
7274
crypto::{
@@ -82,10 +84,7 @@ mod test {
8284
let test_vector = get_test_vector(&variant.to_string());
8385
let cipher_suite = CipherSuite::from(variant);
8486

85-
let secret = Secret {
86-
key: test_vector.key.clone(),
87-
salt: test_vector.salt.clone(),
88-
};
87+
let secret = prepare_secret(&cipher_suite, test_vector);
8988

9089
for enc in &test_vector.encryptions {
9190
let mut data = test_vector.plain_text.clone();
@@ -111,10 +110,7 @@ mod test {
111110
let test_vector = get_test_vector(&variant.to_string());
112111
let cipher_suite = CipherSuite::from(variant);
113112

114-
let secret = Secret {
115-
key: test_vector.key.clone(),
116-
salt: test_vector.salt.clone(),
117-
};
113+
let secret = prepare_secret(&cipher_suite, test_vector);
118114

119115
for enc in &test_vector.encryptions {
120116
let header = Header::with_frame_count(
@@ -132,6 +128,14 @@ mod test {
132128
}
133129
}
134130

131+
fn prepare_secret(cipher_suite: &CipherSuite, test_vector: &TestVector) -> Secret {
132+
if cipher_suite.is_ctr_mode() {
133+
Secret::expand_from(cipher_suite, &test_vector.key_material).unwrap()
134+
} else {
135+
Secret::from_test_vector(test_vector)
136+
}
137+
}
138+
135139
#[test]
136140
fn encrypt_test_vector_aes_gcm_128_sha256() {
137141
encrypt_test_vector(CipherSuiteVariant::AesGcm128Sha256);
@@ -151,6 +155,33 @@ mod test {
151155
fn should_decrypt_test_vectors_aes_gcm_256_sha512() {
152156
decrypt_test_vector(CipherSuiteVariant::AesGcm256Sha512);
153157
}
158+
159+
#[cfg(not(feature = "ring"))]
160+
mod aes_ctr {
161+
use crate::CipherSuiteVariant;
162+
163+
use super::{decrypt_test_vector, encrypt_test_vector};
164+
165+
#[test]
166+
fn should_encrypt_test_vectors_aes_ctr_64_hmac_sha256_64() {
167+
encrypt_test_vector(CipherSuiteVariant::AesCtr128HmacSha256_64);
168+
}
169+
170+
#[test]
171+
fn should_decrypt_test_vectors_aes_ctr_64_hmac_sha256_64() {
172+
decrypt_test_vector(CipherSuiteVariant::AesCtr128HmacSha256_64);
173+
}
174+
175+
#[test]
176+
fn should_encrypt_test_vectors_aes_ctr_64_hmac_sha256_32() {
177+
encrypt_test_vector(CipherSuiteVariant::AesCtr128HmacSha256_32);
178+
}
179+
180+
#[test]
181+
fn should_decrypt_test_vectors_aes_ctr_64_hmac_sha256_32() {
182+
decrypt_test_vector(CipherSuiteVariant::AesCtr128HmacSha256_32);
183+
}
184+
}
154185
}
155186
}
156187
}

src/crypto/cipher_suite.rs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
#[cfg_attr(test, derive(strum_macros::Display))]
99
pub enum CipherSuiteVariant {
1010
// /// counter mode is [not implemented in ring](https://github.com/briansmith/ring/issues/656)
11-
// AesCtr128HmacSha256_80,
12-
// AesCtr128HmacSha256_64,
13-
// AesCtr128HmacSha256_32,
11+
#[cfg(not(feature = "ring"))]
12+
AesCtr128HmacSha256_80,
13+
#[cfg(not(feature = "ring"))]
14+
AesCtr128HmacSha256_64,
15+
#[cfg(not(feature = "ring"))]
16+
AesCtr128HmacSha256_32,
1417
/// encryption: AES GCM 128, key expansion: HKDF with SHA256
1518
AesGcm128Sha256,
1619
/// encryption: AES GCM 256, key expansion: HKDF with SHA512
@@ -29,9 +32,31 @@ pub struct CipherSuite {
2932
impl From<CipherSuiteVariant> for CipherSuite {
3033
fn from(variant: CipherSuiteVariant) -> Self {
3134
match variant {
32-
// CipherSuiteVariant::AesCtr128HmacSha256_80 => unimplemented!(),
33-
// CipherSuiteVariant::AesCtr128HmacSha256_64 => unimplemented!(),
34-
// CipherSuiteVariant::AesCtr128HmacSha256_32 => unimplemented!(),
35+
#[cfg(not(feature = "ring"))]
36+
CipherSuiteVariant::AesCtr128HmacSha256_80 => CipherSuite {
37+
variant,
38+
hash_len: 32,
39+
key_len: 16,
40+
nonce_len: 12,
41+
auth_tag_len: 10,
42+
},
43+
44+
#[cfg(not(feature = "ring"))]
45+
CipherSuiteVariant::AesCtr128HmacSha256_64 => CipherSuite {
46+
variant,
47+
hash_len: 32,
48+
key_len: 16,
49+
nonce_len: 12,
50+
auth_tag_len: 8,
51+
},
52+
#[cfg(not(feature = "ring"))]
53+
CipherSuiteVariant::AesCtr128HmacSha256_32 => CipherSuite {
54+
variant,
55+
hash_len: 32,
56+
key_len: 16,
57+
nonce_len: 12,
58+
auth_tag_len: 4,
59+
},
3560
CipherSuiteVariant::AesGcm128Sha256 => CipherSuite {
3661
variant,
3762
hash_len: 32,
@@ -49,3 +74,16 @@ impl From<CipherSuiteVariant> for CipherSuite {
4974
}
5075
}
5176
}
77+
78+
impl CipherSuite {
79+
#[allow(dead_code)]
80+
pub(crate) fn is_ctr_mode(&self) -> bool {
81+
match self.variant {
82+
#[cfg(not(feature = "ring"))]
83+
CipherSuiteVariant::AesCtr128HmacSha256_80
84+
| CipherSuiteVariant::AesCtr128HmacSha256_64
85+
| CipherSuiteVariant::AesCtr128HmacSha256_32 => true,
86+
CipherSuiteVariant::AesGcm128Sha256 | CipherSuiteVariant::AesGcm256Sha512 => false,
87+
}
88+
}
89+
}

src/crypto/key_expansion.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ pub const SFRAME_HKDF_SALT: &[u8] = "SFrame10".as_bytes();
1414
pub const SFRAME_HKDF_KEY_EXPAND_INFO: &[u8] = "key".as_bytes();
1515
pub const SFRAME_HDKF_SALT_EXPAND_INFO: &[u8] = "salt".as_bytes();
1616

17+
#[cfg(not(feature = "ring"))]
18+
pub const SFRAME_HKDF_SUB_SALT: &[u8] = "SFrame10 AES CTR AEAD".as_bytes();
19+
#[cfg(not(feature = "ring"))]
20+
pub const SFRAME_HKDF_SUB_ENC_EXPAND_INFO: &[u8] = "enc".as_bytes();
21+
#[cfg(not(feature = "ring"))]
22+
pub const SFRAME_HDKF_SUB_AUTH_EXPAND_INFO: &[u8] = "auth".as_bytes();
23+
1724
#[cfg(test)]
1825
mod test {
1926
use crate::crypto::cipher_suite::CipherSuite;
@@ -24,7 +31,7 @@ mod test {
2431

2532
use super::KeyExpansion;
2633

27-
fn derive_correct_keys(variant: CipherSuiteVariant) {
34+
fn derive_correct_base_keys(variant: CipherSuiteVariant) {
2835
let test_vector = get_test_vector(&variant.to_string());
2936
let secret =
3037
Secret::expand_from(&CipherSuite::from(variant), &test_vector.key_material).unwrap();
@@ -35,11 +42,43 @@ mod test {
3542

3643
#[test]
3744
fn derive_correct_keys_aes_gcm_128_sha256() {
38-
derive_correct_keys(CipherSuiteVariant::AesGcm128Sha256);
45+
derive_correct_base_keys(CipherSuiteVariant::AesGcm128Sha256);
3946
}
4047

4148
#[test]
4249
fn derive_correct_keys_aes_gcm_256_sha512() {
43-
derive_correct_keys(CipherSuiteVariant::AesGcm256Sha512);
50+
derive_correct_base_keys(CipherSuiteVariant::AesGcm256Sha512);
51+
}
52+
53+
#[cfg(not(feature = "ring"))]
54+
mod aes_ctr {
55+
use super::*;
56+
57+
fn derive_correct_sub_keys(variant: CipherSuiteVariant) {
58+
let test_vector = get_test_vector(&variant.to_string());
59+
let cipher_suite = CipherSuite::from(variant);
60+
let secret = Secret::expand_from(&cipher_suite, &test_vector.key_material).unwrap();
61+
62+
assert_bytes_eq(&secret.salt, &test_vector.salt);
63+
// the subkeys stored in secret.key and secret.auth are not included in the test vectors
64+
assert_eq!(secret.auth.unwrap().len(), cipher_suite.hash_len);
65+
assert_eq!(secret.key.len(), cipher_suite.key_len);
66+
}
67+
68+
#[test]
69+
fn derive_correct_keys_aes_ctr_128_hmac_sha256_64() {
70+
derive_correct_sub_keys(CipherSuiteVariant::AesCtr128HmacSha256_64);
71+
}
72+
73+
#[test]
74+
fn derive_correct_keys_aes_ctr_128_hmac_sha256_32() {
75+
derive_correct_sub_keys(CipherSuiteVariant::AesCtr128HmacSha256_32);
76+
}
77+
78+
// AesCtr128HmacSha256_80 is not available in the test vectors
79+
// #[test]
80+
// fn derive_correct_keys_aes_ctr_128_hmac_sha256_80() {
81+
// derive_correct_sub_keys(CipherSuiteVariant::AesCtr128HmacSha256_80);
82+
// }
4483
}
4584
}

0 commit comments

Comments
 (0)