Skip to content

Commit 3ff961e

Browse files
authored
Merge pull request #34 from goto-opensource/feature/update-to-draft-03
Feature/update to draft 03
2 parents ccd5d64 + 987db3b commit 3ff961e

23 files changed

+2021
-641
lines changed

Diff for: Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ authors = [
1010
"Richard Haehne <[email protected]>",
1111
]
1212

13-
description = "pure rust implementation of SFrame draft-ietf-sframe-enc-01"
13+
description = "pure rust implementation of SFrame draft-ietf-sframe-enc-03"
1414
repository = "https://github.com/goto-opensource/sframe-rs"
1515
documentation = "https://docs.rs/sframe/"
1616
readme = "README.md"
@@ -45,6 +45,7 @@ rand = "0.8"
4545
serde = { version = "1.0", features = ["derive"] }
4646
serde_json = "1.0"
4747
strum_macros = "0.25"
48+
test-case = "3.1.0"
4849

4950
[features]
5051
default = ["ring"]

Diff for: README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,29 @@ Secure Frame (SFrame)
88
![maintenance](https://img.shields.io/maintenance/yes/2023)
99

1010

11-
This library is an implementation of [draft-ietf-sframe-enc-latest](https://sframe-wg.github.io/sframe/draft-ietf-sframe-enc.html) and provides and end-to-end encryption mechanism for media frames that is suited for WebRTC conferences.
11+
This library is an implementation of [draft-ietf-sframe-enc-03](https://datatracker.ietf.org/doc/html/draft-ietf-sframe-enc-03) and provides and end-to-end encryption mechanism for media frames that is suited for WebRTC conferences.
1212
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
1616
* ratcheting is not implemented
1717
* keyIds are used as senderIds
18-
* no metadata authentication
18+
* no metadata authentication
1919

2020
## Supported crypto libraries
2121
Currently two crypto libraries are supported:
22-
- [ring](https://crates.io/crates/ring)
22+
- [ring](https://crates.io/crates/ring)
2323
- is enabled per default with the feature `ring`
2424
- supports compilation to Wasm32
2525
- Aes-CTR mode ciphers are not supported
2626
- [openssl](https://crates.io/crates/openssl)
2727
- is enabled with the feature `openssl`
2828
- To build e.g. use `cargo build --features openssl --no-default-features`
2929
- uses rust bindings to OpenSSL.
30-
- 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/).
30+
- 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/).
3131
- Compilation to Wasm32 is [not yet supported](https://github.com/sfackler/rust-openssl/issues/1016)
3232

33-
Both cannot be enabled at the same time, thus on conflict `sframe` issues a compiler error.
33+
Both cannot be enabled at the same time, thus on conflict `sframe` issues a compiler error.
3434
## License
3535
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
3636

@@ -39,4 +39,4 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
3939
## Contribution
4040
Any help in form of descriptive and friendly issues or comprehensive pull requests are welcome!
4141

42-
The Changelog of this library is generated from its commit log, there any commit message must conform with https://www.conventionalcommits.org/en/v1.0.0/. For simplicity you could make your commits with convco.
42+
The Changelog of this library is generated from its commit log, there any commit message must conform with https://www.conventionalcommits.org/en/v1.0.0/. For simplicity you could make your commits with convco.

Diff for: benches/crypto.rs

+6
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ fn crypto_benches(c: &mut Criterion) {
108108
for variant in [
109109
CipherSuiteVariant::AesGcm128Sha256,
110110
CipherSuiteVariant::AesGcm256Sha512,
111+
#[cfg(feature = "openssl")]
112+
CipherSuiteVariant::AesCtr128HmacSha256_80,
113+
#[cfg(feature = "openssl")]
114+
CipherSuiteVariant::AesCtr128HmacSha256_64,
115+
#[cfg(feature = "openssl")]
116+
CipherSuiteVariant::AesCtr128HmacSha256_32,
111117
] {
112118
let mut ctx = CryptoBenches::from(variant);
113119
ctx.run_benches(c);

Diff for: src/crypto/aead.rs

+99-149
Original file line numberDiff line numberDiff line change
@@ -34,167 +34,117 @@ pub trait AeadDecrypt {
3434
#[cfg(test)]
3535
mod test {
3636

37-
mod aes_gcm {
38-
use crate::{
39-
crypto::{
40-
aead::AeadEncrypt,
41-
cipher_suite::{CipherSuite, CipherSuiteVariant},
42-
key_expansion::KeyExpansion,
43-
secret::Secret,
44-
},
45-
header::{Header, HeaderFields},
46-
};
47-
use rand::{thread_rng, Rng};
48-
const KEY_MATERIAL: &str = "THIS_IS_RANDOM";
49-
50-
#[test]
51-
fn encrypt_random_frame() {
52-
let mut data = vec![0u8; 1024];
53-
thread_rng().fill(data.as_mut_slice());
54-
let header = Header::default();
55-
let cipher_suite = CipherSuite::from(CipherSuiteVariant::AesGcm256Sha512);
56-
let secret = Secret::expand_from(&cipher_suite, KEY_MATERIAL.as_bytes()).unwrap();
57-
58-
let _tag = cipher_suite
59-
.encrypt(
60-
&mut data,
61-
&secret,
62-
&Vec::from(&header),
63-
header.frame_count(),
64-
)
65-
.unwrap();
66-
}
37+
use crate::crypto::key_derivation::KeyDerivation;
38+
use crate::header::{FrameCount, KeyId};
39+
use crate::test_vectors::{get_sframe_test_vector, SframeTest};
40+
use crate::util::test::assert_bytes_eq;
41+
use crate::{
42+
crypto::{
43+
aead::AeadDecrypt,
44+
aead::AeadEncrypt,
45+
cipher_suite::{CipherSuite, CipherSuiteVariant},
46+
secret::Secret,
47+
},
48+
header::{Header, HeaderFields},
49+
};
50+
51+
use test_case::test_case;
52+
53+
use rand::{thread_rng, Rng};
54+
55+
const KEY_MATERIAL: &str = "THIS_IS_RANDOM";
56+
57+
#[test]
58+
fn encrypt_random_frame() {
59+
let mut data = vec![0u8; 1024];
60+
thread_rng().fill(data.as_mut_slice());
61+
let header = Header::default();
62+
let cipher_suite = CipherSuite::from(CipherSuiteVariant::AesGcm256Sha512);
63+
let secret =
64+
Secret::expand_from(&cipher_suite, KEY_MATERIAL.as_bytes(), KeyId::default()).unwrap();
65+
66+
let _tag = cipher_suite
67+
.encrypt(
68+
&mut data,
69+
&secret,
70+
&Vec::from(&header),
71+
header.frame_count(),
72+
)
73+
.unwrap();
74+
}
6775

68-
mod test_vectors {
69-
70-
use crate::crypto::key_expansion::KeyExpansion;
71-
use crate::test_vectors::{get_test_vector, TestVector};
72-
73-
use crate::{
74-
crypto::{
75-
aead::{AeadDecrypt, AeadEncrypt},
76-
cipher_suite::{CipherSuite, CipherSuiteVariant},
77-
secret::Secret,
78-
},
79-
header::{FrameCount, Header, HeaderFields, KeyId},
80-
util::test::assert_bytes_eq,
81-
};
82-
83-
fn encrypt_test_vector(variant: CipherSuiteVariant) {
84-
let test_vector = get_test_vector(&variant.to_string());
85-
let cipher_suite = CipherSuite::from(variant);
86-
87-
let secret = prepare_secret(&cipher_suite, test_vector);
88-
89-
for enc in &test_vector.encryptions {
90-
let mut data = test_vector.plain_text.clone();
91-
let header = Header::with_frame_count(
92-
KeyId::from(enc.key_id),
93-
FrameCount::from(enc.frame_count),
94-
);
95-
let header_buffer = Vec::from(&header);
96-
let tag = cipher_suite
97-
.encrypt(&mut data, &secret, &header_buffer, header.frame_count())
98-
.unwrap();
99-
let full_frame: Vec<u8> = header_buffer
100-
.into_iter()
101-
.chain(data.into_iter())
102-
.chain(tag.as_ref().iter().cloned())
103-
.collect();
104-
105-
assert_bytes_eq(&full_frame, &enc.cipher_text);
106-
}
107-
}
76+
#[test_case(CipherSuiteVariant::AesGcm128Sha256; "AesGcm128Sha256")]
77+
#[test_case(CipherSuiteVariant::AesGcm256Sha512; "AesGcm256Sha512")]
78+
#[cfg_attr(feature = "openssl", test_case(CipherSuiteVariant::AesCtr128HmacSha256_80; "AesCtr128HmacSha256_80"))]
79+
#[cfg_attr(feature = "openssl", test_case(CipherSuiteVariant::AesCtr128HmacSha256_64; "AesCtr128HmacSha256_64"))]
80+
#[cfg_attr(feature = "openssl", test_case(CipherSuiteVariant::AesCtr128HmacSha256_32; "AesCtr128HmacSha256_32"))]
81+
fn encrypt_test_vector(variant: CipherSuiteVariant) {
82+
let test_vec = get_sframe_test_vector(&variant.to_string());
83+
let cipher_suite = CipherSuite::from(variant);
10884

109-
fn decrypt_test_vector(variant: CipherSuiteVariant) {
110-
let test_vector = get_test_vector(&variant.to_string());
111-
let cipher_suite = CipherSuite::from(variant);
85+
let secret = prepare_secret(&cipher_suite, test_vec);
11286

113-
let secret = prepare_secret(&cipher_suite, test_vector);
87+
let mut data_buffer = test_vec.plain_text.clone();
11488

115-
for enc in &test_vector.encryptions {
116-
let header = Header::with_frame_count(
117-
KeyId::from(enc.key_id),
118-
FrameCount::from(enc.frame_count),
119-
);
120-
let header_buffer = Vec::from(&header);
121-
let mut data = Vec::from(&enc.cipher_text[header.size()..]);
89+
let header = Header::with_frame_count(
90+
KeyId::from(test_vec.key_id),
91+
FrameCount::from(test_vec.frame_count),
92+
);
93+
let header_buffer = Vec::from(&header);
12294

123-
let decrypted = cipher_suite
124-
.decrypt(&mut data, &secret, &header_buffer, header.frame_count())
125-
.unwrap();
95+
let aad_buffer = [header_buffer.as_slice(), test_vec.metadata.as_slice()].concat();
12696

127-
assert_bytes_eq(decrypted, &test_vector.plain_text);
128-
}
129-
}
97+
let tag = cipher_suite
98+
.encrypt(&mut data_buffer, &secret, &aad_buffer, header.frame_count())
99+
.unwrap();
130100

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-
}
101+
let full_frame: Vec<u8> = header_buffer
102+
.into_iter()
103+
.chain(data_buffer)
104+
.chain(tag.as_ref().iter().cloned())
105+
.collect();
138106

139-
#[test]
140-
fn encrypt_test_vector_aes_gcm_128_sha256() {
141-
encrypt_test_vector(CipherSuiteVariant::AesGcm128Sha256);
142-
}
107+
assert_bytes_eq(&aad_buffer, &test_vec.aad);
108+
assert_bytes_eq(&full_frame, &test_vec.cipher_text);
109+
}
143110

144-
#[test]
145-
fn should_decrypt_test_vector_aes_gcm_128_sha256() {
146-
decrypt_test_vector(CipherSuiteVariant::AesGcm128Sha256);
147-
}
111+
#[test_case(CipherSuiteVariant::AesGcm128Sha256; "AesGcm128Sha256")]
112+
#[test_case(CipherSuiteVariant::AesGcm256Sha512; "AesGcm256Sha512")]
113+
#[cfg_attr(feature = "openssl", test_case(CipherSuiteVariant::AesCtr128HmacSha256_80; "AesCtr128HmacSha256_80"))]
114+
#[cfg_attr(feature = "openssl", test_case(CipherSuiteVariant::AesCtr128HmacSha256_64; "AesCtr128HmacSha256_64"))]
115+
#[cfg_attr(feature = "openssl", test_case(CipherSuiteVariant::AesCtr128HmacSha256_32; "AesCtr128HmacSha256_32"))]
116+
fn decrypt_test_vector(variant: CipherSuiteVariant) {
117+
let test_vec = get_sframe_test_vector(&variant.to_string());
118+
let cipher_suite = CipherSuite::from(variant);
148119

149-
#[test]
150-
fn encrypt_test_vectors_aes_gcm_256_sha512() {
151-
encrypt_test_vector(CipherSuiteVariant::AesGcm256Sha512);
152-
}
120+
let secret = prepare_secret(&cipher_suite, test_vec);
121+
let header = Header::with_frame_count(
122+
KeyId::from(test_vec.key_id),
123+
FrameCount::from(test_vec.frame_count),
124+
);
125+
let header_buffer = Vec::from(&header);
153126

154-
#[test]
155-
fn should_decrypt_test_vectors_aes_gcm_256_sha512() {
156-
decrypt_test_vector(CipherSuiteVariant::AesGcm256Sha512);
157-
}
127+
let aad_buffer = [header_buffer.as_slice(), test_vec.metadata.as_slice()].concat();
128+
assert_bytes_eq(&aad_buffer, &test_vec.aad);
129+
130+
let mut data = Vec::from(&test_vec.cipher_text[header.size()..]);
131+
132+
let decrypted = cipher_suite
133+
.decrypt(&mut data, &secret, &aad_buffer, header.frame_count())
134+
.unwrap();
135+
136+
assert_bytes_eq(decrypted, &test_vec.plain_text);
137+
}
158138

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-
}
139+
fn prepare_secret(cipher_suite: &CipherSuite, test_vec: &SframeTest) -> Secret {
140+
if cipher_suite.is_ctr_mode() {
141+
// the test vectors do not provide the auth key, so we have to expand here
142+
Secret::expand_from(cipher_suite, &test_vec.key_material, test_vec.key_id).unwrap()
143+
} else {
144+
Secret {
145+
key: test_vec.sframe_key.clone(),
146+
salt: test_vec.sframe_salt.clone(),
147+
auth: None,
198148
}
199149
}
200150
}

Diff for: src/crypto/cipher_suite.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
/// Depicts which AEAD algorithm is used for encryption
55
/// and which hashing function is used for the key expansion,
6-
/// see [sframe draft 00 4.4](https://datatracker.ietf.org/doc/html/draft-ietf-sframe-enc-01#name-ciphersuites)
6+
/// see [sframe draft 03 4.4](https://datatracker.ietf.org/doc/html/draft-ietf-sframe-enc-03#name-cipher-suites)
77
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
88
#[cfg_attr(test, derive(strum_macros::Display))]
99
pub enum CipherSuiteVariant {

0 commit comments

Comments
 (0)