Skip to content

Commit a25f202

Browse files
authored
Merge pull request #32 from goto-opensource/feature/aes-ctr-ciphers
aes ctr ciphers
2 parents 0af96e6 + 6d20128 commit a25f202

15 files changed

+435
-143
lines changed

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,24 @@ 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`
2829
- uses rust bindings to OpenSSL.
2930
- Per default the OpenSSL library is locally compiled and then statically linked. The build process requires a C compiler, `perl` (and `perl-core`), and `make`. For further options see the [openssl crate documentation](https://docs.rs/openssl/0.10.55/openssl/).
3031
- Compilation to Wasm32 is [not yet supported](https://github.com/sfackler/rust-openssl/issues/1016)
3132

32-
Both cannot be enabled at the same time, thus on conflict `sframe` fallsback to using `ring`.
33+
Both cannot be enabled at the same time, thus on conflict `sframe` issues a compiler error.
3334
## License
3435
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
3536

src/crypto/aead.rs

+57-13
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ mod test {
3939
crypto::{
4040
aead::AeadEncrypt,
4141
cipher_suite::{CipherSuite, CipherSuiteVariant},
42-
key_expansion::{ExpandAsSecret, KeyMaterial},
42+
key_expansion::KeyExpansion,
43+
secret::Secret,
4344
},
4445
header::{Header, HeaderFields},
4546
};
@@ -52,9 +53,7 @@ mod test {
5253
thread_rng().fill(data.as_mut_slice());
5354
let header = Header::default();
5455
let cipher_suite = CipherSuite::from(CipherSuiteVariant::AesGcm256Sha512);
55-
let secret = KeyMaterial(KEY_MATERIAL.as_bytes())
56-
.expand_as_secret(&cipher_suite)
57-
.unwrap();
56+
let secret = Secret::expand_from(&cipher_suite, KEY_MATERIAL.as_bytes()).unwrap();
5857

5958
let _tag = cipher_suite
6059
.encrypt(
@@ -67,7 +66,9 @@ mod test {
6766
}
6867

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

7273
use crate::{
7374
crypto::{
@@ -83,10 +84,7 @@ mod test {
8384
let test_vector = get_test_vector(&variant.to_string());
8485
let cipher_suite = CipherSuite::from(variant);
8586

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

9189
for enc in &test_vector.encryptions {
9290
let mut data = test_vector.plain_text.clone();
@@ -112,10 +110,7 @@ mod test {
112110
let test_vector = get_test_vector(&variant.to_string());
113111
let cipher_suite = CipherSuite::from(variant);
114112

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

120115
for enc in &test_vector.encryptions {
121116
let header = Header::with_frame_count(
@@ -133,6 +128,14 @@ mod test {
133128
}
134129
}
135130

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+
136139
#[test]
137140
fn encrypt_test_vector_aes_gcm_128_sha256() {
138141
encrypt_test_vector(CipherSuiteVariant::AesGcm128Sha256);
@@ -152,6 +155,47 @@ mod test {
152155
fn should_decrypt_test_vectors_aes_gcm_256_sha512() {
153156
decrypt_test_vector(CipherSuiteVariant::AesGcm256Sha512);
154157
}
158+
159+
#[cfg(feature = "openssl")]
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+
185+
#[test]
186+
// AesCtr128HmacSha256_80 is not available in the test vectors
187+
#[ignore]
188+
fn should_encrypt_test_vectors_aes_ctr_64_hmac_sha256_80() {
189+
encrypt_test_vector(CipherSuiteVariant::AesCtr128HmacSha256_32);
190+
}
191+
192+
#[test]
193+
// AesCtr128HmacSha256_80 is not available in the test vectors
194+
#[ignore]
195+
fn should_decrypt_test_vectors_aes_ctr_64_hmac_sha256_80() {
196+
decrypt_test_vector(CipherSuiteVariant::AesCtr128HmacSha256_32);
197+
}
198+
}
155199
}
156200
}
157201
}

src/crypto/cipher_suite.rs

+44-6
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(feature = "openssl")]
12+
AesCtr128HmacSha256_80,
13+
#[cfg(feature = "openssl")]
14+
AesCtr128HmacSha256_64,
15+
#[cfg(feature = "openssl")]
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(feature = "openssl")]
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(feature = "openssl")]
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(feature = "openssl")]
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+
#[cfg(any(feature = "openssl", test))]
80+
pub(crate) fn is_ctr_mode(&self) -> bool {
81+
match self.variant {
82+
#[cfg(feature = "openssl")]
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

+57-22
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,82 @@
44
use super::{cipher_suite::CipherSuite, secret::Secret};
55
use crate::error::Result;
66

7-
#[derive(Debug, Default, Clone, Copy)]
8-
pub struct KeyMaterial<'a>(pub &'a [u8]);
9-
10-
pub trait ExpandAsSecret {
11-
fn expand_as_secret(&self, cipher_suite: &CipherSuite) -> Result<Secret>;
7+
pub trait KeyExpansion {
8+
fn expand_from<T>(cipher_suite: &CipherSuite, key_material: T) -> Result<Secret>
9+
where
10+
T: AsRef<[u8]>;
1211
}
1312

14-
pub const SFRAME_HKDF_SALT: &[u8] = "SFrame10".as_bytes();
15-
pub const SFRAME_HKDF_KEY_EXPAND_INFO: &[u8] = "key".as_bytes();
16-
pub const SFRAME_HDKF_SALT_EXPAND_INFO: &[u8] = "salt".as_bytes();
13+
pub const SFRAME_HKDF_SALT: &[u8] = b"SFrame10";
14+
pub const SFRAME_HKDF_KEY_EXPAND_INFO: &[u8] = b"key";
15+
pub const SFRAME_HDKF_SALT_EXPAND_INFO: &[u8] = b"salt";
16+
17+
#[cfg(feature = "openssl")]
18+
pub const SFRAME_HKDF_SUB_SALT: &[u8] = b"SFrame10 AES CTR AEAD";
19+
#[cfg(feature = "openssl")]
20+
pub const SFRAME_HKDF_SUB_ENC_EXPAND_INFO: &[u8] = b"enc";
21+
#[cfg(feature = "openssl")]
22+
pub const SFRAME_HDKF_SUB_AUTH_EXPAND_INFO: &[u8] = b"auth";
1723

1824
#[cfg(test)]
1925
mod test {
26+
use crate::crypto::cipher_suite::CipherSuite;
27+
use crate::crypto::secret::Secret;
2028
use crate::test_vectors::get_test_vector;
2129

22-
use crate::{
23-
crypto::{
24-
cipher_suite::{CipherSuite, CipherSuiteVariant},
25-
key_expansion::KeyMaterial,
26-
},
27-
util::test::assert_bytes_eq,
28-
};
30+
use crate::{crypto::cipher_suite::CipherSuiteVariant, util::test::assert_bytes_eq};
2931

30-
use super::ExpandAsSecret;
32+
use super::KeyExpansion;
3133

32-
fn derive_correct_keys(variant: CipherSuiteVariant) {
34+
fn derive_correct_base_keys(variant: CipherSuiteVariant) {
3335
let test_vector = get_test_vector(&variant.to_string());
34-
let secret = KeyMaterial(&test_vector.key_material)
35-
.expand_as_secret(&CipherSuite::from(variant))
36-
.unwrap();
36+
let secret =
37+
Secret::expand_from(&CipherSuite::from(variant), &test_vector.key_material).unwrap();
38+
3739
assert_bytes_eq(&secret.key, &test_vector.key);
3840
assert_bytes_eq(&secret.salt, &test_vector.salt);
3941
}
4042

4143
#[test]
4244
fn derive_correct_keys_aes_gcm_128_sha256() {
43-
derive_correct_keys(CipherSuiteVariant::AesGcm128Sha256);
45+
derive_correct_base_keys(CipherSuiteVariant::AesGcm128Sha256);
4446
}
4547

4648
#[test]
4749
fn derive_correct_keys_aes_gcm_256_sha512() {
48-
derive_correct_keys(CipherSuiteVariant::AesGcm256Sha512);
50+
derive_correct_base_keys(CipherSuiteVariant::AesGcm256Sha512);
51+
}
52+
53+
#[cfg(feature = "openssl")]
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+
#[test]
79+
// AesCtr128HmacSha256_80 is not available in the test vectors
80+
#[ignore]
81+
fn derive_correct_keys_aes_ctr_128_hmac_sha256_80() {
82+
derive_correct_sub_keys(CipherSuiteVariant::AesCtr128HmacSha256_80);
83+
}
4984
}
5085
}

src/crypto/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ if #[cfg(all(not(feature = "openssl"), feature = "ring"))]{
1313
else if #[cfg(all(feature = "openssl", not(feature = "ring")))] {
1414
mod openssl;
1515
} else {
16-
// fallback to ring
16+
compile_error!("Cannot configure multiple crypto backends at the same time.");
1717
mod ring;
1818
}
1919
}

0 commit comments

Comments
 (0)