Skip to content

Commit

Permalink
Test vector generation and testing - TreeMath and Encryption (openmls…
Browse files Browse the repository at this point in the history
…#299)

This implements test vector generation and testing for treemath
and encryption. It follows the basic idea of
https://github.com/mlswg/mls-implementations/blob/87e47925116b95c01db0e7b2a03b71b5a44b8a30/test-vectors.md
but slightly changes the test vector definition and uses JSON
serialised representations.
This version should be used to discuss the exact test vectors we
want for MLS and will probably change later on.

Also see mlswg/mls-implementations#32
for a discussion.

Note that generating and testing for the encryption is currently
pretty cumbersome and ugly due to OpenMLS' code structure. This
should change in future when the code is refactored to respect
logic boundaries between the different parts of MLS.
  • Loading branch information
franziskuskiefer authored Jan 20, 2021
1 parent 583e19b commit c12cf07
Show file tree
Hide file tree
Showing 17 changed files with 60,626 additions and 25 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Tests
run: cargo test --verbose
- name: KAT
run: cargo test --verbose --features="test-vectors" kat
checks:
runs-on: ubuntu-latest
steps:
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ evercrypt = { version = "0.0.6", features = ["serialization"] }
[features]
default = ["rust-crypto"]
rust-crypto = ["evercrypt/rust-crypto-aes"]
test-vectors = []

[dev-dependencies]
criterion = "^0.3"
flexi_logger = "0.17"
pretty_env_logger = "0.4.0"
itertools = "0.10"


[[bench]]
name = "benchmark"
Expand Down
1 change: 1 addition & 0 deletions src/ciphersuite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub(crate) const TAG_BYTES: usize = 16;

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u16)]
pub enum CiphersuiteName {
MLS10_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 = 0x0001,
MLS10_128_DHKEMP256_AES128GCM_SHA256_P256 = 0x0002,
Expand Down
5 changes: 5 additions & 0 deletions src/framing/ciphertext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ impl MLSCiphertext {
content: mls_ciphertext_content.content,
signature: mls_ciphertext_content.signature,
};

if cfg!(feature = "test-vectors") {
return Ok(mls_plaintext);
}

let credential = match indexed_members.get(&sender_data.sender) {
Some(c) => c,
None => {
Expand Down
10 changes: 10 additions & 0 deletions src/group/mls_group/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,16 @@ impl MlsGroup {
pub(crate) fn secret_tree_mut(&self) -> RefMut<SecretTree> {
self.secret_tree.borrow_mut()
}

#[cfg(all(test, feature = "test-vectors"))]
pub(crate) fn epoch_secrets_mut(&mut self) -> &mut EpochSecrets {
&mut self.epoch_secrets
}

#[cfg(all(test, feature = "test-vectors"))]
pub(crate) fn context_mut(&mut self) -> &mut GroupContext {
&mut self.group_context
}
}

// Helper functions
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ pub mod tree;

/// Single place, re-exporting the most used public functions.
pub mod prelude;

#[cfg(test)]
mod test_util;
47 changes: 47 additions & 0 deletions src/schedule/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,11 +320,28 @@ impl EncryptionSecret {

/// Create a random `EncryptionSecret`. For testing purposes only.
#[cfg(test)]
#[doc(hidden)]
pub fn from_random(length: usize) -> Self {
EncryptionSecret {
secret: Secret::random(length),
}
}

#[cfg(all(test, feature = "test-vectors"))]
#[doc(hidden)]
pub fn to_vec(&self) -> Vec<u8> {
self.secret.to_vec()
}
}

#[cfg(test)]
#[doc(hidden)]
impl From<&[u8]> for EncryptionSecret {
fn from(bytes: &[u8]) -> Self {
Self {
secret: Secret::from(bytes),
}
}
}

/// A secret that we can derive secrets from, that are used outside of OpenMLS.
Expand Down Expand Up @@ -367,6 +384,30 @@ impl SenderDataSecret {
pub(crate) fn secret(&self) -> &Secret {
&self.secret
}

#[cfg(all(test, feature = "test-vectors"))]
#[doc(hidden)]
pub fn from_random(length: usize) -> Self {
Self {
secret: Secret::random(length),
}
}

#[cfg(all(test, feature = "test-vectors"))]
#[doc(hidden)]
pub fn to_vec(&self) -> Vec<u8> {
self.secret.to_vec()
}
}

#[cfg(test)]
#[doc(hidden)]
impl From<&[u8]> for SenderDataSecret {
fn from(bytes: &[u8]) -> Self {
Self {
secret: Secret::from(bytes),
}
}
}

/// The `EpochSecrets` contain keys (or secrets), which are accessible outside
Expand Down Expand Up @@ -412,4 +453,10 @@ impl EpochSecrets {
};
(epoch_secrets, init_secret, encryption_secret)
}

#[cfg(all(test, feature = "test-vectors"))]
#[doc(hidden)]
pub(crate) fn sender_data_secret_mut(&mut self) -> &mut SenderDataSecret {
&mut self.sender_data_secret
}
}
62 changes: 62 additions & 0 deletions src/test_util/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//! Test utilities
//!
#![allow(dead_code)]

use serde::{self, de::DeserializeOwned, Serialize};
use std::{
fs::File,
io::{BufReader, Write},
};

pub fn write(file_name: &str, obj: impl Serialize) {
let mut file = match File::create(file_name) {
Ok(f) => f,
Err(_) => panic!("Couldn't open file {}.", file_name),
};
file.write_all(
serde_json::to_string_pretty(&obj)
.expect("Error serializing test vectors")
.as_bytes(),
)
.expect("Error writing test vector file");
}

pub fn read<T: DeserializeOwned>(file_name: &str) -> T {
let file = match File::open(file_name) {
Ok(f) => f,
Err(_) => panic!("Couldn't open file {}.", file_name),
};
let reader = BufReader::new(file);
match serde_json::from_reader(reader) {
Ok(r) => return r,
Err(e) => panic!("Error reading file.\n{:?}", e),
};
}

/// Convert `bytes` to a hex string.
pub fn bytes_to_hex(bytes: &[u8]) -> String {
let mut hex = String::new();
for &b in bytes {
hex += &format!("{:02X}", b);
}
hex
}

/// Convert a hex string to a byte vector.
pub fn hex_to_bytes(hex: &str) -> Vec<u8> {
assert!(hex.len() % 2 == 0);
let mut bytes = Vec::new();
for i in 0..(hex.len() / 2) {
bytes.push(u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap());
}
bytes
}

/// Convert a hex string to a byte vector.
/// If the input is `None`, this returns an empty vector.
pub fn hex_to_bytes_option(hex: Option<String>) -> Vec<u8> {
match hex {
Some(s) => hex_to_bytes(&s),
None => vec![],
}
}
Loading

0 comments on commit c12cf07

Please sign in to comment.