From 2f9acdc6350b7ad13c22cf8853f09a2f93ff9f31 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Wed, 19 Apr 2023 23:10:11 +0200 Subject: [PATCH 01/54] WIP: Draft-20 OpenMLS Async Edition --- Cargo.toml | 17 +- basic_credential/Cargo.toml | 12 +- basic_credential/src/lib.rs | 168 +- cli/Cargo.toml | 5 +- cli/src/backend.rs | 31 +- cli/src/identity.rs | 15 +- cli/src/main.rs | 94 +- cli/src/networking.rs | 18 +- cli/src/user.rs | 223 +- delivery-service/ds-lib/Cargo.toml | 5 +- delivery-service/ds-lib/tests/test_codec.rs | 13 +- delivery-service/ds/Cargo.toml | 2 +- delivery-service/ds/src/test.rs | 29 +- evercrypt_backend/CHANGELOG.md | 15 - evercrypt_backend/README.md | 6 - evercrypt_backend/src/lib.rs | 29 - fuzz/Cargo.toml | 2 +- interop_client/src/main.rs | 94 +- memory_keystore/Cargo.toml | 4 +- memory_keystore/src/lib.rs | 18 +- openmls/Cargo.toml | 14 +- openmls/benches/benchmark.rs | 89 +- .../array_representation/kat_treemath.rs | 18 +- openmls/src/ciphersuite/aead.rs | 2 +- openmls/src/ciphersuite/hpke.rs | 2 +- .../ciphersuite/tests/kat_crypto_basics.rs | 16 +- .../src/ciphersuite/tests/test_ciphersuite.rs | 17 +- openmls/src/ciphersuite/tests/test_secrets.rs | 4 +- openmls/src/credentials/mod.rs | 12 +- .../src/extensions/external_pub_extension.rs | 23 +- .../extensions/external_sender_extension.rs | 5 +- openmls/src/extensions/test_extensions.rs | 29 +- openmls/src/framing/test_framing.rs | 78 +- .../group/core_group/kat_passive_client.rs | 334 +-- openmls/src/group/core_group/kat_welcome.rs | 16 +- openmls/src/group/core_group/mod.rs | 49 +- .../core_group/new_from_external_init.rs | 6 +- .../src/group/core_group/new_from_welcome.rs | 11 +- openmls/src/group/core_group/process.rs | 45 +- openmls/src/group/core_group/staged_commit.rs | 35 +- .../src/group/core_group/test_core_group.rs | 120 +- .../core_group/test_create_commit_params.rs | 2 +- .../group/core_group/test_external_init.rs | 39 +- .../src/group/core_group/test_past_secrets.rs | 4 +- .../src/group/core_group/test_proposals.rs | 69 +- openmls/src/group/mls_group/creation.rs | 41 +- openmls/src/group/mls_group/membership.rs | 9 +- openmls/src/group/mls_group/mod.rs | 12 +- openmls/src/group/mls_group/processing.rs | 33 +- openmls/src/group/mls_group/proposal.rs | 4 +- openmls/src/group/mls_group/test_mls_group.rs | 128 +- openmls/src/group/mls_group/updates.rs | 19 +- openmls/src/group/mod.rs | 2 - openmls/src/group/public_group/tests.rs | 23 +- .../src/group/tests/external_add_proposal.rs | 75 +- .../group/tests/external_remove_proposal.rs | 65 +- openmls/src/group/tests/kat_messages.rs | 315 +-- .../src/group/tests/kat_transcript_hashes.rs | 138 +- .../src/group/tests/test_commit_validation.rs | 103 +- openmls/src/group/tests/test_encoding.rs | 41 +- .../tests/test_external_commit_validation.rs | 91 +- openmls/src/group/tests/test_framing.rs | 9 +- .../group/tests/test_framing_validation.rs | 88 +- openmls/src/group/tests/test_group.rs | 209 +- openmls/src/group/tests/test_past_secrets.rs | 22 +- .../group/tests/test_proposal_validation.rs | 246 +- .../src/group/tests/test_remove_operation.rs | 30 +- .../group/tests/test_wire_format_policy.rs | 32 +- openmls/src/group/tests/utils.rs | 33 +- openmls/src/key_packages/mod.rs | 38 +- openmls/src/key_packages/test_key_packages.rs | 31 +- openmls/src/messages/mod.rs | 4 +- openmls/src/messages/tests/test_codec.rs | 4 +- .../messages/tests/test_export_group_info.rs | 4 +- openmls/src/messages/tests/test_proposals.rs | 2 +- openmls/src/messages/tests/test_welcome.rs | 59 +- openmls/src/schedule/kat_key_schedule.rs | 30 +- openmls/src/schedule/kat_psk_secret.rs | 39 +- openmls/src/schedule/mod.rs | 2 +- openmls/src/schedule/psk.rs | 10 +- openmls/src/schedule/unit_tests.rs | 9 +- openmls/src/test_utils/mod.rs | 117 +- .../src/test_utils/test_framework/client.rs | 75 +- openmls/src/test_utils/test_framework/mod.rs | 205 +- .../tests_and_kats/kats/kat_encryption.rs | 94 +- .../kats/kat_message_protection.rs | 120 +- .../tree/tests_and_kats/kats/secret_tree.rs | 2 +- .../unit_tests/test_secret_tree.rs | 6 +- .../unit_tests/test_sender_ratchet.rs | 6 +- openmls/src/treesync/mod.rs | 4 +- openmls/src/treesync/node/encryption_keys.rs | 40 +- openmls/src/treesync/node/leaf_node.rs | 6 +- .../treesync/node/leaf_node/capabilities.rs | 1 + .../kats/kat_tree_operations.rs | 9 +- .../kats/kat_tree_validation.rs | 9 +- .../tests_and_kats/kats/kat_treekem.rs | 6 +- openmls/src/treesync/tests_and_kats/tests.rs | 25 +- .../tests_and_kats/tests/test_diff.rs | 20 +- openmls/test_vectors/crypto-basics.json | 266 +- openmls/test_vectors/message-protection.json | 212 +- openmls/test_vectors/psk_secret.json | 35 + openmls/test_vectors/transcript-hashes.json | 2424 +---------------- openmls/tests/book_code.rs | 149 +- openmls/tests/key_store.rs | 10 +- openmls/tests/test_decryption_key_index.rs | 10 +- openmls/tests/test_external_commit.rs | 63 +- openmls/tests/test_interop_scenarios.rs | 58 +- openmls/tests/test_managed_api.rs | 10 +- openmls/tests/test_mls_group.rs | 149 +- openmls_rust_crypto/Cargo.toml | 20 +- openmls_rust_crypto/src/provider.rs | 603 +++- traits/Cargo.toml | 4 +- traits/src/crypto.rs | 8 +- traits/src/key_store.rs | 18 +- traits/src/random.rs | 10 +- traits/src/types.rs | 3 + 116 files changed, 3535 insertions(+), 5101 deletions(-) delete mode 100644 evercrypt_backend/CHANGELOG.md delete mode 100644 evercrypt_backend/README.md delete mode 100644 evercrypt_backend/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 980515f479..6bd8e37287 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,27 +5,14 @@ members = [ "openmls_rust_crypto", "fuzz", "cli", - "interop_client", + # "interop_client", "memory_keystore", - "evercrypt_backend", "delivery-service/ds", "delivery-service/ds-lib", "basic_credential" ] resolver = "2" -# Patching unreleased crates [workspace.dependencies] tls_codec = { version = "0.3.0-pre.3", features = ["derive", "serde", "mls"] } - -[patch.crates-io.hpke-rs] -git = "https://github.com/franziskuskiefer/hpke-rs.git" - -[patch.crates-io.hpke-rs-crypto] -git = "https://github.com/franziskuskiefer/hpke-rs.git" - -[patch.crates-io.hpke-rs-evercrypt] -git = "https://github.com/franziskuskiefer/hpke-rs.git" - -[patch.crates-io.hpke-rs-rust-crypto] -git = "https://github.com/franziskuskiefer/hpke-rs.git" +async-trait = "0.1" diff --git a/basic_credential/Cargo.toml b/basic_credential/Cargo.toml index 6552f9fcb8..aefbaa6b75 100644 --- a/basic_credential/Cargo.toml +++ b/basic_credential/Cargo.toml @@ -12,13 +12,17 @@ readme = "README.md" [dependencies] openmls_traits = { version = "0.1.0", path = "../traits" } tls_codec = { workspace = true } +async-trait = { workspace = true } serde = "1.0" # Rust Crypto -ed25519-dalek = { version = "1.0" } -p256 = { version = "0.11" } -rand-07 = {version = "0.7", package = "rand" } # only needed because of ed25519-dalek -rand = "0.8" +ed25519-dalek = { version = "2.0.0-rc.2", features = ["rand_core"] } +p256 = "0.13" +p384 = "0.13" +secrecy = { version = "0.8", features = ["serde"] } +zeroize = "1.6" +rand_core = "0.6" +signature = "2.1" [features] clonable = [] # Make the keys clonable diff --git a/basic_credential/src/lib.rs b/basic_credential/src/lib.rs index 510e387fb7..67a64a6fda 100644 --- a/basic_credential/src/lib.rs +++ b/basic_credential/src/lib.rs @@ -4,34 +4,83 @@ //! //! For now this credential uses only RustCrypto. +use secrecy::{ExposeSecret, SecretVec}; +use signature::Signer; use std::fmt::Debug; use openmls_traits::{ key_store::{MlsEntity, MlsEntityId, OpenMlsKeyStore}, - signatures::Signer, types::{CryptoError, Error, SignatureScheme}, }; -use p256::ecdsa::SigningKey; - -// See https://github.com/rust-analyzer/rust-analyzer/issues/7243 -// for the rust-analyzer issue with the following line. -use ed25519_dalek::Signer as DalekSigner; -use rand::rngs::OsRng; -use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize}; +fn expose_sk(data: &SecretVec, ser: S) -> Result { + use serde::ser::SerializeSeq as _; + let exposed = data.expose_secret(); + let mut seq = ser.serialize_seq(Some(exposed.len()))?; + for b in exposed.iter() { + seq.serialize_element(b)?; + } + seq.end() +} /// A signature key pair for the basic credential. /// /// This can be used as keys to implement the MLS basic credential. It is a simple /// private and public key pair with corresponding signature scheme. -#[derive(TlsSerialize, TlsSize, TlsDeserialize, serde::Serialize, serde::Deserialize)] -#[cfg_attr(feature = "clonable", derive(Clone))] +#[derive(serde::Serialize, serde::Deserialize)] pub struct SignatureKeyPair { - private: Vec, + #[serde(serialize_with = "expose_sk")] + private: SecretVec, public: Vec, signature_scheme: SignatureScheme, } +#[cfg(feature = "clonable")] +impl Clone for SignatureKeyPair { + fn clone(&self) -> Self { + Self { + private: self.private.expose_secret().clone().into(), + public: self.public.clone(), + signature_scheme: self.signature_scheme.clone(), + } + } +} + +impl secrecy::SerializableSecret for SignatureKeyPair {} + +impl tls_codec::Size for SignatureKeyPair { + fn tls_serialized_len(&self) -> usize { + self.private.expose_secret().tls_serialized_len() + + self.public.tls_serialized_len() + + self.signature_scheme.tls_serialized_len() + } +} + +impl tls_codec::Deserialize for SignatureKeyPair { + fn tls_deserialize(bytes: &mut R) -> Result + where + Self: Sized, + { + let private = Vec::::tls_deserialize(bytes)?.into(); + let public = Vec::::tls_deserialize(bytes)?; + let signature_scheme = SignatureScheme::tls_deserialize(bytes)?; + Ok(Self { + private, + public, + signature_scheme, + }) + } +} + +impl tls_codec::Serialize for SignatureKeyPair { + fn tls_serialize(&self, writer: &mut W) -> Result { + let mut written = self.private.expose_secret().tls_serialize(writer)?; + written += self.public.tls_serialize(writer)?; + written += self.signature_scheme.tls_serialize(writer)?; + Ok(written) + } +} + impl Debug for SignatureKeyPair { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("SignatureKeyPair") @@ -42,18 +91,47 @@ impl Debug for SignatureKeyPair { } } -impl Signer for SignatureKeyPair { +impl openmls_traits::signatures::Signer for SignatureKeyPair { fn sign(&self, payload: &[u8]) -> Result, Error> { match self.signature_scheme { SignatureScheme::ECDSA_SECP256R1_SHA256 => { - let k = SigningKey::from_bytes(&self.private).map_err(|_| Error::SigningError)?; - let signature = k.sign(payload); + let k = p256::ecdsa::SigningKey::from_bytes( + self.private.expose_secret().as_slice().into(), + ) + .map_err(|_| Error::SigningError)?; + let signature: p256::ecdsa::Signature = + k.try_sign(payload).map_err(|_| Error::SigningError)?; + Ok(signature.to_der().to_bytes().into()) + } + SignatureScheme::ECDSA_SECP384R1_SHA384 => { + let k = p384::ecdsa::SigningKey::from_bytes( + self.private.expose_secret().as_slice().into(), + ) + .map_err(|_| Error::SigningError)?; + let signature: p384::ecdsa::Signature = + k.try_sign(payload).map_err(|_| Error::SigningError)?; Ok(signature.to_der().to_bytes().into()) } SignatureScheme::ED25519 => { - let k = ed25519_dalek::Keypair::from_bytes(&self.private) - .map_err(|_| Error::SigningError)?; - let signature = k.sign(payload); + let exposed = self.private.expose_secret(); + let k = match exposed.len() { + // Compat layer for legacy keypairs [seed, pk] + ed25519_dalek::KEYPAIR_LENGTH => { + let mut sk = zeroize::Zeroizing::new([0u8; ed25519_dalek::KEYPAIR_LENGTH]); + sk.copy_from_slice(exposed.as_slice()); + ed25519_dalek::SigningKey::from_keypair_bytes(&sk) + .map_err(|_| Error::SigningError)? + } + ed25519_dalek::SECRET_KEY_LENGTH => { + let mut sk = + zeroize::Zeroizing::new([0u8; ed25519_dalek::SECRET_KEY_LENGTH]); + sk.copy_from_slice(exposed.as_slice()); + ed25519_dalek::SigningKey::from_bytes(&sk) + } + _ => return Err(Error::SigningError), + }; + + let signature = k.try_sign(payload).map_err(|_| Error::SigningError)?; Ok(signature.to_bytes().into()) } _ => Err(Error::SigningError), @@ -65,35 +143,33 @@ impl Signer for SignatureKeyPair { } } -/// Compute the ID for a [`Signature`] in the key store. -fn id(public_key: &[u8], signature_scheme: SignatureScheme) -> Vec { - const LABEL: &[u8; 22] = b"RustCryptoSignatureKey"; - let mut id = public_key.to_vec(); - id.extend_from_slice(LABEL); - let signature_scheme = (signature_scheme as u16).to_be_bytes(); - id.extend_from_slice(&signature_scheme); - id -} - impl MlsEntity for SignatureKeyPair { const ID: MlsEntityId = MlsEntityId::SignatureKeyPair; } impl SignatureKeyPair { /// Generates a fresh signature keypair using the [`SignatureScheme`]. - pub fn new(signature_scheme: SignatureScheme) -> Result { - let (private, public) = match signature_scheme { + pub fn new( + signature_scheme: SignatureScheme, + csprng: &mut impl rand_core::CryptoRngCore, + ) -> Result { + let (private, public): (SecretVec, Vec) = match signature_scheme { SignatureScheme::ECDSA_SECP256R1_SHA256 => { - let k = SigningKey::random(&mut OsRng); - let pk = k.verifying_key().to_encoded_point(false).as_bytes().into(); - (k.to_bytes().as_slice().into(), pk) + let sk = p256::ecdsa::SigningKey::random(csprng); + let pk = sk.verifying_key().to_encoded_point(false).to_bytes().into(); + (sk.to_bytes().to_vec().into(), pk) + } + SignatureScheme::ECDSA_SECP384R1_SHA384 => { + let sk = p384::ecdsa::SigningKey::random(csprng); + let pk = sk.verifying_key().to_encoded_point(false).to_bytes().into(); + (sk.to_bytes().to_vec().into(), pk) } SignatureScheme::ED25519 => { - let k = ed25519_dalek::Keypair::generate(&mut rand_07::rngs::OsRng).to_bytes(); - let pk = k[ed25519_dalek::SECRET_KEY_LENGTH..].to_vec(); + let sk = ed25519_dalek::SigningKey::generate(csprng); + let pk = sk.verifying_key(); // full key here because we need it to sign... - let sk_pk = k.into(); - (sk_pk, pk) + let sk_pk: Vec = sk.to_bytes().into(); + (sk_pk.into(), pk.to_bytes().into()) } _ => return Err(CryptoError::UnsupportedSignatureScheme), }; @@ -108,31 +184,23 @@ impl SignatureKeyPair { /// Create a new signature key pair from the raw keys. pub fn from_raw(signature_scheme: SignatureScheme, private: Vec, public: Vec) -> Self { Self { - private, + private: private.into(), public, signature_scheme, } } - fn id(&self) -> Vec { - id(&self.public, self.signature_scheme) - } - /// Store this signature key pair in the key store. - pub fn store(&self, key_store: &T) -> Result<(), ::Error> + pub async fn store(&self, key_store: &T) -> Result<(), ::Error> where T: OpenMlsKeyStore, { - key_store.store(&self.id(), self) + key_store.store(&self.public, self).await } /// Read a signature key pair from the key store. - pub fn read( - key_store: &impl OpenMlsKeyStore, - public_key: &[u8], - signature_scheme: SignatureScheme, - ) -> Option { - key_store.read(&id(public_key, signature_scheme)) + pub async fn read(key_store: &impl OpenMlsKeyStore, public_key: &[u8]) -> Option { + key_store.read(public_key).await } /// Get the public key as byte slice. @@ -152,6 +220,6 @@ impl SignatureKeyPair { #[cfg(feature = "test-utils")] pub fn private(&self) -> &[u8] { - &self.private + &self.private.expose_secret() } } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index c4e02e6786..986e32e2b5 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -2,11 +2,12 @@ name = "cli" version = "0.1.0" authors = ["OpenMLS Authors"] -edition = "2018" +edition = "2021" [dependencies] url = "2.2" -reqwest = { version = "0.11", features = ["blocking", "json"] } +reqwest = { version = "0.11", features = ["json"] } +tokio = { version = "1.24", features = ["full"] } base64 = "0.13" log = "0.4" pretty_env_logger = "0.4" diff --git a/cli/src/backend.rs b/cli/src/backend.rs index 09852e959d..d7647d0c66 100644 --- a/cli/src/backend.rs +++ b/cli/src/backend.rs @@ -15,29 +15,30 @@ pub struct Backend { impl Backend { /// Register a new client with the server. - pub fn register_client(&self, user: &User) -> Result { + pub async fn register_client(&self, user: &User) -> Result { let mut url = self.ds_url.clone(); url.set_path("/clients/register"); let client_info = ClientInfo::new( user.username.clone(), user.key_packages() + .await .into_iter() .map(|(b, kp)| (b, KeyPackageIn::from(kp))) .collect(), ); - let response = post(&url, &client_info)?; + let response = post(&url, &client_info).await?; Ok(String::from_utf8(response).unwrap()) } /// Get a list of all clients with name, ID, and key packages from the /// server. - pub fn list_clients(&self) -> Result, String> { + pub async fn list_clients(&self) -> Result, String> { let mut url = self.ds_url.clone(); url.set_path("/clients/list"); - let response = get(&url)?; + let response = get(&url).await?; match TlsVecU32::::tls_deserialize(&mut response.as_slice()) { Ok(clients) => Ok(clients.into()), Err(e) => Err(format!("Error decoding server response: {e:?}")), @@ -45,13 +46,13 @@ impl Backend { } /// Get a list of key packages for a client. - pub fn get_client(&self, client_id: &[u8]) -> Result { + pub async fn get_client(&self, client_id: &[u8]) -> Result { let mut url = self.ds_url.clone(); let path = "/clients/key_packages/".to_string() + &base64::encode_config(client_id, base64::URL_SAFE); url.set_path(&path); - let response = get(&url)?; + let response = get(&url).await?; match ClientKeyPackages::tls_deserialize(&mut response.as_slice()) { Ok(ckp) => Ok(ckp), Err(e) => Err(format!("Error decoding server response: {e:?}")), @@ -59,33 +60,33 @@ impl Backend { } /// Send a welcome message. - pub fn send_welcome(&self, welcome_msg: &MlsMessageOut) -> Result<(), String> { + pub async fn send_welcome(&self, welcome_msg: &MlsMessageOut) -> Result<(), String> { let mut url = self.ds_url.clone(); url.set_path("/send/welcome"); // The response should be empty. - let _response = post(&url, welcome_msg)?; + let _response = post(&url, welcome_msg).await?; Ok(()) } /// Send a group message. - pub fn send_msg(&self, group_msg: &GroupMessage) -> Result<(), String> { + pub async fn send_msg(&self, group_msg: &GroupMessage) -> Result<(), String> { let mut url = self.ds_url.clone(); url.set_path("/send/message"); // The response should be empty. - let _response = post(&url, group_msg)?; + let _response = post(&url, group_msg).await?; Ok(()) } /// Get a list of all new messages for the user. - pub fn recv_msgs(&self, user: &User) -> Result, String> { + pub async fn recv_msgs(&self, user: &User) -> Result, String> { let mut url = self.ds_url.clone(); let path = "/recv/".to_string() - + &base64::encode_config(user.identity.borrow().identity(), base64::URL_SAFE); + + &base64::encode_config(user.identity.read().await.identity(), base64::URL_SAFE); url.set_path(&path); - let response = get(&url)?; + let response = get(&url).await?; match TlsVecU16::::tls_deserialize(&mut response.as_slice()) { Ok(r) => Ok(r.into()), Err(e) => Err(format!("Invalid message list: {e:?}")), @@ -93,10 +94,10 @@ impl Backend { } /// Reset the DS. - pub fn reset_server(&self) { + pub async fn reset_server(&self) { let mut url = self.ds_url.clone(); url.set_path("reset"); - get(&url).unwrap(); + get(&url).await.unwrap(); } } diff --git a/cli/src/identity.rs b/cli/src/identity.rs index 4f034dc188..8004d48e60 100644 --- a/cli/src/identity.rs +++ b/cli/src/identity.rs @@ -10,14 +10,22 @@ pub struct Identity { } impl Identity { - pub(crate) fn new(ciphersuite: Ciphersuite, crypto: &OpenMlsRustCrypto, id: &[u8]) -> Self { + pub(crate) async fn new( + ciphersuite: Ciphersuite, + crypto: &OpenMlsRustCrypto, + id: &[u8], + ) -> Self { let credential = Credential::new(id.to_vec(), CredentialType::Basic).unwrap(); - let signature_keys = SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); + let signature_keys = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *crypto.rand().borrow_rand().unwrap(), + ) + .unwrap(); let credential_with_key = CredentialWithKey { credential, signature_key: signature_keys.to_public_vec().into(), }; - signature_keys.store(crypto.key_store()).unwrap(); + signature_keys.store(crypto.key_store()).await.unwrap(); let key_package = KeyPackage::builder() .build( @@ -29,6 +37,7 @@ impl Identity { &signature_keys, credential_with_key.clone(), ) + .await .unwrap(); Self { diff --git a/cli/src/main.rs b/cli/src/main.rs index 36890ce4e0..d5d800e12d 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -25,8 +25,8 @@ const HELP: &str = " "; -fn update(client: &mut user::User, group_id: Option, stdout: &mut StdoutLock) { - let messages = client.update(group_id).unwrap(); +async fn update(client: &mut user::User, group_id: Option, stdout: &mut StdoutLock<'_>) { + let messages = client.update(group_id).await.unwrap(); stdout.write_all(b" >>> Updated client :)\n").unwrap(); if !messages.is_empty() { stdout.write_all(b" New messages:\n\n").unwrap(); @@ -39,7 +39,8 @@ fn update(client: &mut user::User, group_id: Option, stdout: &mut Stdout stdout.write_all(b"\n").unwrap(); } -fn main() { +#[tokio::main] +async fn main() { pretty_env_logger::init(); let stdout = stdout(); @@ -60,7 +61,7 @@ fn main() { // There's no persistence. So once the client app stops you have to // register a new client. if let Some(client_name) = op.strip_prefix("register ") { - client = Some(user::User::new(client_name.to_string())); + client = Some(user::User::new(client_name.to_string()).await); stdout .write_all(format!("registered new client {client_name}\n\n").as_bytes()) .unwrap(); @@ -70,7 +71,7 @@ fn main() { // Create a new group. if let Some(group_name) = op.strip_prefix("create group ") { if let Some(client) = &mut client { - client.create_group(group_name.to_string()); + client.create_group(group_name.to_string()).await; stdout .write_all(format!(" >>> Created group {group_name} :)\n\n").as_bytes()) .unwrap(); @@ -92,7 +93,7 @@ fn main() { // Send a message to the group. if let Some(msg) = op2.strip_prefix("send ") { - client.send_msg(msg, group_name.to_string()).unwrap(); + client.send_msg(msg, group_name.to_string()).await.unwrap(); stdout .write_all(format!("sent message to {group_name}\n\n").as_bytes()) .unwrap(); @@ -103,6 +104,7 @@ fn main() { if let Some(new_client) = op2.strip_prefix("invite ") { client .invite(new_client.to_string(), group_name.to_string()) + .await .unwrap(); stdout .write_all( @@ -114,7 +116,7 @@ fn main() { // Read messages sent to the group. if op2 == "read" { - let messages = client.read_msgs(group_name.to_string()).unwrap(); + let messages = client.read_msgs(group_name.to_string()).await.unwrap(); if let Some(messages) = messages { stdout .write_all( @@ -136,7 +138,7 @@ fn main() { // Update the client state. if op2 == "update" { - update(client, Some(group_name.to_string()), &mut stdout); + update(client, Some(group_name.to_string()), &mut stdout).await; continue; } @@ -159,7 +161,7 @@ fn main() { // Update the client state. if op == "update" { if let Some(client) = &mut client { - update(client, None, &mut stdout); + update(client, None, &mut stdout).await; } else { stdout .write_all(b" >>> No client to update :(\n\n") @@ -170,7 +172,7 @@ fn main() { // Reset the server and client. if op == "reset" { - backend::Backend::default().reset_server(); + backend::Backend::default().reset_server().await; client = None; stdout.write_all(b" >>> Reset server :)\n\n").unwrap(); continue; @@ -188,102 +190,122 @@ fn main() { } } -#[test] +#[tokio::test] #[ignore] -fn basic_test() { +async fn basic_test() { // Reset the server before doing anything for testing. - backend::Backend::default().reset_server(); + backend::Backend::default().reset_server().await; const MESSAGE_1: &str = "Thanks for adding me Client1."; const MESSAGE_2: &str = "Welcome Client3."; const MESSAGE_3: &str = "Thanks so much for the warm welcome! 😊"; // Create one client - let mut client_1 = user::User::new("Client1".to_string()); + let mut client_1 = user::User::new("Client1".to_string()).await; // Create another client - let mut client_2 = user::User::new("Client2".to_string()); + let mut client_2 = user::User::new("Client2".to_string()).await; // Create another client - let mut client_3 = user::User::new("Client3".to_string()); + let mut client_3 = user::User::new("Client3".to_string()).await; // Update the clients to know about the other clients. - client_1.update(None).unwrap(); - client_2.update(None).unwrap(); - client_3.update(None).unwrap(); + client_1.update(None).await.unwrap(); + client_2.update(None).await.unwrap(); + client_3.update(None).await.unwrap(); // Client 1 creates a group. - client_1.create_group("MLS Discussions".to_string()); + client_1.create_group("MLS Discussions".to_string()).await; // Client 1 adds Client 2 to the group. client_1 .invite("Client2".to_string(), "MLS Discussions".to_string()) + .await .unwrap(); // Client 2 retrieves messages. - client_2.update(None).unwrap(); + client_2.update(None).await.unwrap(); // Client 2 sends a message. client_2 .send_msg(MESSAGE_1, "MLS Discussions".to_string()) + .await .unwrap(); // Client 1 retrieves messages. - client_1.update(None).unwrap(); + client_1.update(None).await.unwrap(); // Check that Client 1 received the message assert_eq!( - client_1.read_msgs("MLS Discussions".to_string()).unwrap(), + client_1 + .read_msgs("MLS Discussions".to_string()) + .await + .unwrap(), Some(vec![MESSAGE_1.into()]) ); // Client 2 adds Client 3 to the group. client_2 .invite("Client3".to_string(), "MLS Discussions".to_string()) + .await .unwrap(); // Everyone updates. - client_1.update(None).unwrap(); - client_2.update(None).unwrap(); - client_3.update(None).unwrap(); + client_1.update(None).await.unwrap(); + client_2.update(None).await.unwrap(); + client_3.update(None).await.unwrap(); // Client 1 sends a message. client_1 .send_msg(MESSAGE_2, "MLS Discussions".to_string()) + .await .unwrap(); // Everyone updates. - client_1.update(None).unwrap(); - client_2.update(None).unwrap(); - client_3.update(None).unwrap(); + client_1.update(None).await.unwrap(); + client_2.update(None).await.unwrap(); + client_3.update(None).await.unwrap(); // Check that Client 2 and Client 3 received the message assert_eq!( - client_2.read_msgs("MLS Discussions".to_string()).unwrap(), + client_2 + .read_msgs("MLS Discussions".to_string()) + .await + .unwrap(), Some(vec![MESSAGE_2.into()]) ); assert_eq!( - client_3.read_msgs("MLS Discussions".to_string()).unwrap(), + client_3 + .read_msgs("MLS Discussions".to_string()) + .await + .unwrap(), Some(vec![MESSAGE_2.into()]) ); // Client 3 sends a message. client_3 .send_msg(MESSAGE_3, "MLS Discussions".to_string()) + .await .unwrap(); // Everyone updates. - client_1.update(None).unwrap(); - client_2.update(None).unwrap(); - client_3.update(None).unwrap(); + client_1.update(None).await.unwrap(); + client_2.update(None).await.unwrap(); + client_3.update(None).await.unwrap(); // Check that Client 1 and Client 2 received the message assert_eq!( - client_1.read_msgs("MLS Discussions".to_string()).unwrap(), + client_1 + .read_msgs("MLS Discussions".to_string()) + .await + .unwrap(), Some(vec![MESSAGE_1.into(), MESSAGE_3.into()]) ); assert_eq!( - client_2.read_msgs("MLS Discussions".to_string()).unwrap(), + client_2 + .read_msgs("MLS Discussions".to_string()) + .await + .unwrap(), Some(vec![MESSAGE_2.into(), MESSAGE_3.into()]) ); } diff --git a/cli/src/networking.rs b/cli/src/networking.rs index c0e0b0c6f8..0360f623de 100644 --- a/cli/src/networking.rs +++ b/cli/src/networking.rs @@ -1,21 +1,25 @@ -use reqwest::{self, blocking::Client, StatusCode}; +use reqwest::{self, Client, StatusCode}; use url::Url; use tls_codec::Serialize; // TODO: return objects not bytes. -pub fn post(url: &Url, msg: &impl Serialize) -> Result, String> { +pub async fn post(url: &Url, msg: &impl Serialize) -> Result, String> { let serialized_msg = msg.tls_serialize_detached().unwrap(); log::debug!("Post {:?}", url); log::trace!("Payload: {:?}", serialized_msg); let client = Client::new(); - let response = client.post(url.to_string()).body(serialized_msg).send(); + let response = client + .post(url.to_string()) + .body(serialized_msg) + .send() + .await; if let Ok(r) = response { if r.status() != StatusCode::OK { return Err(format!("Error status code {:?}", r.status())); } - match r.bytes() { + match r.bytes().await { Ok(bytes) => Ok(bytes.as_ref().to_vec()), Err(e) => Err(format!("Error retrieving bytes from response: {e:?}")), } @@ -24,15 +28,15 @@ pub fn post(url: &Url, msg: &impl Serialize) -> Result, String> { } } -pub fn get(url: &Url) -> Result, String> { +pub async fn get(url: &Url) -> Result, String> { log::debug!("Get {:?}", url); let client = Client::new(); - let response = client.get(url.to_string()).send(); + let response = client.get(url.to_string()).send().await; if let Ok(r) = response { if r.status() != StatusCode::OK { return Err(format!("Error status code {:?}", r.status())); } - match r.bytes() { + match r.bytes().await { Ok(bytes) => Ok(bytes.as_ref().to_vec()), Err(e) => Err(format!("Error retrieving bytes from response: {e:?}")), } diff --git a/cli/src/user.rs b/cli/src/user.rs index d1c3bca681..a64c97c0e6 100644 --- a/cli/src/user.rs +++ b/cli/src/user.rs @@ -1,4 +1,5 @@ -use std::{cell::RefCell, collections::HashMap}; +use std::collections::HashMap; +use tokio::sync::RwLock; use ds_lib::{ClientKeyPackages, GroupMessage}; use openmls::prelude::*; @@ -20,32 +21,32 @@ pub struct Contact { pub struct Group { group_name: String, conversation: Conversation, - mls_group: RefCell, + mls_group: RwLock, } pub struct User { pub(crate) username: String, pub(crate) contacts: HashMap, Contact>, - pub(crate) groups: RefCell, Group>>, - pub(crate) identity: RefCell, + pub(crate) groups: RwLock, Group>>, + pub(crate) identity: RwLock, backend: Backend, crypto: OpenMlsRustCrypto, } impl User { /// Create a new user with the given name and a fresh set of credentials. - pub fn new(username: String) -> Self { + pub async fn new(username: String) -> Self { let crypto = OpenMlsRustCrypto::default(); let out = Self { username: username.clone(), - groups: RefCell::new(HashMap::new()), + groups: RwLock::new(HashMap::new()), contacts: HashMap::new(), - identity: RefCell::new(Identity::new(CIPHERSUITE, &crypto, username.as_bytes())), + identity: RwLock::new(Identity::new(CIPHERSUITE, &crypto, username.as_bytes()).await), backend: Backend::default(), crypto, }; - match out.backend.register_client(&out) { + match out.backend.register_client(&out).await { Ok(r) => log::debug!("Created new user: {:?}", r), Err(e) => log::error!("Error creating user: {:?}", e), } @@ -54,24 +55,25 @@ impl User { } /// Get the key packages fo this user. - pub fn key_packages(&self) -> Vec<(Vec, KeyPackage)> { + pub async fn key_packages(&self) -> Vec<(Vec, KeyPackage)> { vec![( self.identity - .borrow() + .read() + .await .kp .hash_ref(self.crypto.crypto()) .unwrap() .as_slice() .to_vec(), - self.identity.borrow().kp.clone(), + self.identity.read().await.kp.clone(), )] } /// Get a list of clients in the group to send messages to. - fn recipients(&self, group: &Group) -> Vec> { + async fn recipients(&self, group: &Group) -> Vec> { let mut recipients = Vec::new(); - let mls_group = group.mls_group.borrow(); + let mls_group = group.mls_group.read().await; for Member { index: _, encryption_key: _, @@ -81,7 +83,8 @@ impl User { { if self .identity - .borrow() + .read() + .await .credential_with_key .signature_key .as_slice() @@ -98,8 +101,8 @@ impl User { } /// Return the last 100 messages sent to the group. - pub fn read_msgs(&self, group_name: String) -> Result>, String> { - let groups = self.groups.borrow(); + pub async fn read_msgs(&self, group_name: String) -> Result>, String> { + let groups = self.groups.read().await; groups.get(group_name.as_bytes()).map_or_else( || Err("Unknown group".to_string()), |g| Ok(g.conversation.get(100).map(|messages| messages.to_vec())), @@ -107,8 +110,8 @@ impl User { } /// Send an application message to the group. - pub fn send_msg(&self, msg: &str, group: String) -> Result<(), String> { - let groups = self.groups.borrow(); + pub async fn send_msg(&self, msg: &str, group: String) -> Result<(), String> { + let groups = self.groups.read().await; let group = match groups.get(group.as_bytes()) { Some(g) => g, None => return Err("Unknown group".to_string()), @@ -116,13 +119,18 @@ impl User { let message_out = group .mls_group - .borrow_mut() - .create_message(&self.crypto, &self.identity.borrow().signer, msg.as_bytes()) + .write() + .await + .create_message( + &self.crypto, + &self.identity.read().await.signer, + msg.as_bytes(), + ) .map_err(|e| format!("{e}"))?; - let msg = GroupMessage::new(message_out.into(), &self.recipients(group)); + let msg = GroupMessage::new(message_out.into(), &self.recipients(group).await); log::debug!(" >>> send: {:?}", msg); - self.backend.send_msg(&msg)?; + self.backend.send_msg(&msg).await?; // XXX: Need to update the client's local view of the conversation to include // the message they sent. @@ -130,80 +138,94 @@ impl User { Ok(()) } + async fn process_protocol_message( + &self, + group_name: Option<&str>, + message: ProtocolMessage, + ) -> Result, String> { + let mut groups = self.groups.write().await; + + let group = match groups.get_mut(message.group_id().as_slice()) { + Some(g) => g, + None => { + log::error!( + "Error getting group {:?} for a message. Dropping message.", + message.group_id() + ); + return Err("error".to_string()); + } + }; + let mut mls_group = group.mls_group.write().await; + + let processed_message = match mls_group.process_message(&self.crypto, message).await { + Ok(msg) => msg, + Err(e) => { + log::error!( + "Error processing unverified message: {:?} - Dropping message.", + e + ); + return Err("error".to_string()); + } + }; + + let mut message_ret = None; + + match processed_message.into_content() { + ProcessedMessageContent::ApplicationMessage(application_message) => { + let application_message = + String::from_utf8(application_message.into_bytes()).unwrap(); + if group_name.is_none() || group_name.clone().unwrap() == group.group_name { + message_ret.replace(application_message.clone()); + } + group.conversation.add(application_message); + } + ProcessedMessageContent::ProposalMessage(_proposal_ptr) => { + // intentionally left blank. + } + ProcessedMessageContent::ExternalJoinProposalMessage(_external_proposal_ptr) => { + // intentionally left blank. + } + ProcessedMessageContent::StagedCommitMessage(commit_ptr) => { + mls_group + .merge_staged_commit(&self.crypto, *commit_ptr) + .await + .map_err(|_| "error")?; + } + } + + Ok(message_ret) + } + /// Update the user. This involves: /// * retrieving all new messages from the server /// * update the contacts with all other clients known to the server - pub fn update(&mut self, group_name: Option) -> Result, String> { + pub async fn update(&mut self, group_name: Option) -> Result, String> { log::debug!("Updating {} ...", self.username); let mut messages_out = Vec::new(); - let mut process_protocol_message = |message: ProtocolMessage| { - let mut groups = self.groups.borrow_mut(); - - let group = match groups.get_mut(message.group_id().as_slice()) { - Some(g) => g, - None => { - log::error!( - "Error getting group {:?} for a message. Dropping message.", - message.group_id() - ); - return Err("error"); - } - }; - let mut mls_group = group.mls_group.borrow_mut(); - - let processed_message = match mls_group.process_message(&self.crypto, message) { - Ok(msg) => msg, - Err(e) => { - log::error!( - "Error processing unverified message: {:?} - Dropping message.", - e - ); - return Err("error"); - } - }; - - match processed_message.into_content() { - ProcessedMessageContent::ApplicationMessage(application_message) => { - let application_message = - String::from_utf8(application_message.into_bytes()).unwrap(); - if group_name.is_none() || group_name.clone().unwrap() == group.group_name { - messages_out.push(application_message.clone()); - } - group.conversation.add(application_message); - } - ProcessedMessageContent::ProposalMessage(_proposal_ptr) => { - // intentionally left blank. - } - ProcessedMessageContent::ExternalJoinProposalMessage(_external_proposal_ptr) => { - // intentionally left blank. - } - ProcessedMessageContent::StagedCommitMessage(commit_ptr) => { - mls_group - .merge_staged_commit(&self.crypto, *commit_ptr) - .map_err(|_| "error")?; - } - } - Ok(()) - }; - // Go through the list of messages and process or store them. - for message in self.backend.recv_msgs(self)?.drain(..) { + for message in self.backend.recv_msgs(self).await?.drain(..) { match message.extract() { MlsMessageInBody::Welcome(welcome) => { // Join the group. (Later we should ask the user to // approve first ...) - self.join_group(welcome)?; + self.join_group(welcome).await?; } MlsMessageInBody::PrivateMessage(message) => { - if process_protocol_message(message.into()).is_err() { - continue; + if let Ok(Some(message)) = self + .process_protocol_message(group_name.as_deref(), message.into()) + .await + { + messages_out.push(message); } } MlsMessageInBody::PublicMessage(message) => { - if process_protocol_message(message.into()).is_err() { - continue; + if let Ok(Some(message)) = self + .process_protocol_message(group_name.as_deref(), message.into()) + .await + { + messages_out.push(message); } } _ => panic!("Unsupported message type"), @@ -211,8 +233,8 @@ impl User { } log::trace!("done with messages ..."); - for c in self.backend.list_clients()?.drain(..) { - if c.id != self.identity.borrow().identity() + for c in self.backend.list_clients().await?.drain(..) { + if c.id != self.identity.read().await.identity() && self .contacts .insert( @@ -234,7 +256,7 @@ impl User { } /// Create a group with the given name. - pub fn create_group(&mut self, name: String) { + pub async fn create_group(&mut self, name: String) { log::debug!("{} creates group {}", self.username, name); let group_id = name.as_bytes(); let mut group_aad = group_id.to_vec(); @@ -248,22 +270,24 @@ impl User { let mut mls_group = MlsGroup::new_with_group_id( &self.crypto, - &self.identity.borrow().signer, + &self.identity.read().await.signer, &group_config, GroupId::from_slice(group_id), - self.identity.borrow().credential_with_key.clone(), + self.identity.read().await.credential_with_key.clone(), ) + .await .expect("Failed to create MlsGroup"); mls_group.set_aad(group_aad.as_slice()); let group = Group { group_name: name.clone(), conversation: Conversation::default(), - mls_group: RefCell::new(mls_group), + mls_group: RwLock::new(mls_group), }; if self .groups - .borrow_mut() + .write() + .await .insert(group_id.to_vec(), group) .is_some() { @@ -272,7 +296,7 @@ impl User { } /// Invite user with the given name to the group. - pub fn invite(&mut self, name: String, group: String) -> Result<(), String> { + pub async fn invite(&mut self, name: String, group: String) -> Result<(), String> { // First we need to get the key package for {id} from the DS. // We just take the first key package we get back from the server. let contact = match self.contacts.values().find(|c| c.username == name) { @@ -282,6 +306,7 @@ impl User { let (_hash, joiner_key_package) = self .backend .get_client(&contact.id) + .await .unwrap() .0 .pop() @@ -289,7 +314,7 @@ impl User { // Build a proposal with this key package and do the MLS bits. let group_id = group.as_bytes(); - let mut groups = self.groups.borrow_mut(); + let mut groups = self.groups.write().await; let group = match groups.get_mut(group_id) { Some(g) => g, None => return Err(format!("No group with name {group} known.")), @@ -297,40 +322,45 @@ impl User { let (out_messages, welcome, _group_info) = group .mls_group - .borrow_mut() + .write() + .await .add_members( &self.crypto, - &self.identity.borrow().signer, + &self.identity.read().await.signer, &[joiner_key_package.into()], ) + .await .map_err(|e| format!("Failed to add member to group - {e}"))?; // First, process the invitation on our end. group .mls_group - .borrow_mut() + .write() + .await .merge_pending_commit(&self.crypto) + .await .expect("error merging pending commit"); // Second, send Welcome to the joiner. log::trace!("Sending welcome"); self.backend .send_welcome(&welcome) + .await .expect("Error sending Welcome message"); // Finally, send the MlsMessages to the group. log::trace!("Sending proposal"); let group = groups.get_mut(group_id).unwrap(); // XXX: not cool. - let group_recipients = self.recipients(group); + let group_recipients = self.recipients(group).await; let msg = GroupMessage::new(out_messages.into(), &group_recipients); - self.backend.send_msg(&msg)?; + self.backend.send_msg(&msg).await?; Ok(()) } /// Join a group with the provided welcome message. - fn join_group(&self, welcome: Welcome) -> Result<(), String> { + async fn join_group(&self, welcome: Welcome) -> Result<(), String> { log::debug!("{} joining group ...", self.username); // NOTE: Since the DS currently doesn't distribute copies of the group's ratchet @@ -339,6 +369,7 @@ impl User { .use_ratchet_tree_extension(true) .build(); let mut mls_group = MlsGroup::new_from_welcome(&self.crypto, &group_config, welcome, None) + .await .expect("Failed to create MlsGroup"); let group_id = mls_group.group_id().to_vec(); @@ -351,12 +382,12 @@ impl User { let group = Group { group_name: group_name.clone(), conversation: Conversation::default(), - mls_group: RefCell::new(mls_group), + mls_group: RwLock::new(mls_group), }; log::trace!(" {}", group_name); - match self.groups.borrow_mut().insert(group_id, group) { + match self.groups.write().await.insert(group_id, group) { Some(old) => Err(format!("Overrode the group {:?}", old.group_name)), None => Ok(()), } diff --git a/delivery-service/ds-lib/Cargo.toml b/delivery-service/ds-lib/Cargo.toml index 74b4734894..2ebbb6d8b8 100644 --- a/delivery-service/ds-lib/Cargo.toml +++ b/delivery-service/ds-lib/Cargo.toml @@ -2,7 +2,7 @@ name = "ds-lib" version = "0.1.0" authors = ["OpenMLS Authors"] -edition = "2018" +edition = "2021" description = "Types to interact with the OpenMLS DS." [dependencies] @@ -12,3 +12,6 @@ openmls_traits = { path = "../../traits" } openmls_rust_crypto = { path = "../../openmls_rust_crypto" } openmls_memory_keystore = { path = "../../memory_keystore" } openmls_basic_credential = { path = "../../basic_credential" } + +[dev-dependencies] +tokio = { version = "1.24", features = ["full"] } diff --git a/delivery-service/ds-lib/tests/test_codec.rs b/delivery-service/ds-lib/tests/test_codec.rs index db4dfe4648..85ff68409c 100644 --- a/delivery-service/ds-lib/tests/test_codec.rs +++ b/delivery-service/ds-lib/tests/test_codec.rs @@ -5,20 +5,24 @@ use openmls_rust_crypto::OpenMlsRustCrypto; use openmls_traits::OpenMlsCryptoProvider; use tls_codec::{Deserialize, Serialize}; -#[test] -fn test_client_info() { +#[tokio::test] +async fn test_client_info() { let crypto = &OpenMlsRustCrypto::default(); let client_name = "Client1"; let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519; let credential = Credential::new(client_name.as_bytes().to_vec(), CredentialType::Basic).unwrap(); - let signature_keys = SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); + let signature_keys = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *crypto.rand().borrow_rand().unwrap(), + ) + .unwrap(); let credential_with_key = CredentialWithKey { credential, signature_key: signature_keys.to_public_vec().into(), }; - signature_keys.store(crypto.key_store()).unwrap(); + signature_keys.store(crypto.key_store()).await.unwrap(); let client_key_package = KeyPackage::builder() .build( @@ -30,6 +34,7 @@ fn test_client_info() { &signature_keys, credential_with_key, ) + .await .unwrap(); let client_key_package = vec![( diff --git a/delivery-service/ds/Cargo.toml b/delivery-service/ds/Cargo.toml index 6a77b8093c..f42a0bf92f 100644 --- a/delivery-service/ds/Cargo.toml +++ b/delivery-service/ds/Cargo.toml @@ -2,7 +2,7 @@ name = "mls-ds" version = "0.1.0" authors = ["OpenMLS Authors"] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/delivery-service/ds/src/test.rs b/delivery-service/ds/src/test.rs index 979848715a..14cc13c3f9 100644 --- a/delivery-service/ds/src/test.rs +++ b/delivery-service/ds/src/test.rs @@ -10,9 +10,14 @@ use tls_codec::{TlsByteVecU8, TlsVecU16}; fn generate_credential( identity: Vec, signature_scheme: SignatureScheme, + crypto_backend: &impl OpenMlsCryptoProvider, ) -> (CredentialWithKey, SignatureKeyPair) { let credential = Credential::new(identity, CredentialType::Basic).unwrap(); - let signature_keys = SignatureKeyPair::new(signature_scheme).unwrap(); + let signature_keys = SignatureKeyPair::new( + signature_scheme, + &mut *crypto_backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); let credential_with_key = CredentialWithKey { credential, signature_key: signature_keys.to_public_vec().into(), @@ -21,7 +26,7 @@ fn generate_credential( (credential_with_key, signature_keys) } -fn generate_key_package( +async fn generate_key_package( ciphersuite: Ciphersuite, credential_with_key: CredentialWithKey, extensions: Extensions, @@ -39,6 +44,7 @@ fn generate_key_package( signer, credential_with_key, ) + .await .unwrap() } @@ -79,8 +85,11 @@ async fn test_list_clients() { let client_name = "Client1"; let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519; let crypto = &OpenMlsRustCrypto::default(); - let (credential_with_key, signer) = - generate_credential(client_name.into(), SignatureScheme::from(ciphersuite)); + let (credential_with_key, signer) = generate_credential( + client_name.into(), + SignatureScheme::from(ciphersuite), + crypto, + ); let client_id = credential_with_key.credential.identity().to_vec(); let client_key_package = generate_key_package( ciphersuite, @@ -88,7 +97,8 @@ async fn test_list_clients() { Extensions::empty(), crypto, &signer, - ); + ) + .await; let client_key_package = vec![( client_key_package .hash_ref(crypto.crypto()) @@ -184,6 +194,7 @@ async fn test_group() { let (credential_with_key, signer) = generate_credential( client_name.as_bytes().to_vec(), SignatureScheme::from(ciphersuite), + crypto, ); let client_key_package = generate_key_package( ciphersuite, @@ -191,7 +202,8 @@ async fn test_group() { Extensions::empty(), crypto, &signer, - ); + ) + .await; let client_data = ClientInfo::new( client_name.to_string(), vec![( @@ -229,6 +241,7 @@ async fn test_group() { group_id, credential_with_key_1, ) + .await .expect("An unexpected error occurred."); // === Client1 invites Client2 === @@ -262,9 +275,11 @@ async fn test_group() { // locally.) let (_out_messages, welcome_msg, _group_info) = group .add_members(crypto, &signer_1, &[client2_key_package.into()]) + .await .expect("Could not add member to group."); group .merge_pending_commit(crypto) + .await .expect("error merging pending commit"); // Send welcome message for Client2 @@ -308,6 +323,7 @@ async fn test_group() { .expect("Unexpected message type."), Some(group.export_ratchet_tree().into()), // delivered out of band ) + .await .expect("Error creating group from Welcome"); assert_eq!( @@ -367,6 +383,7 @@ async fn test_group() { // Decrypt the message on Client1 let processed_message = group .process_message(crypto, protocol_message) + .await .expect("Could not process unverified message."); if let ProcessedMessageContent::ApplicationMessage(application_message) = processed_message.into_content() diff --git a/evercrypt_backend/CHANGELOG.md b/evercrypt_backend/CHANGELOG.md deleted file mode 100644 index cb2e3c5365..0000000000 --- a/evercrypt_backend/CHANGELOG.md +++ /dev/null @@ -1,15 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -### Changed -- [#909](https://github.com/openmls/openmls/pull/909): Use thiserror crate for errors - -## 0.1.0 (2022-02-28) -- initial release - -*Please disregard any previous versions.* diff --git a/evercrypt_backend/README.md b/evercrypt_backend/README.md deleted file mode 100644 index 35130459e7..0000000000 --- a/evercrypt_backend/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Evercrypt Backend - -This crate implements the [OpenMLS traits](../traits/Readme.md) using the following [Evercrypt rust bindings]. - -[evercrypt]: https://github.com/project-everest/hacl-star/ -[evercrypt rust bindings]: https://crates.io/crates/evercrypt diff --git a/evercrypt_backend/src/lib.rs b/evercrypt_backend/src/lib.rs deleted file mode 100644 index 5144023bc9..0000000000 --- a/evercrypt_backend/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -use openmls_memory_keystore::MemoryKeyStore; -use openmls_traits::OpenMlsCryptoProvider; - -mod provider; -pub use provider::*; - -#[derive(Default, Debug)] -pub struct OpenMlsEvercrypt { - crypto: EvercryptProvider, - key_store: MemoryKeyStore, -} - -impl OpenMlsCryptoProvider for OpenMlsEvercrypt { - type CryptoProvider = EvercryptProvider; - type RandProvider = EvercryptProvider; - type KeyStoreProvider = MemoryKeyStore; - - fn crypto(&self) -> &Self::CryptoProvider { - &self.crypto - } - - fn rand(&self) -> &Self::RandProvider { - &self.crypto - } - - fn key_store(&self) -> &Self::KeyStoreProvider { - &self.key_store - } -} diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index fc987d6573..85780c7d68 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -3,7 +3,7 @@ name = "openmls-fuzz" version = "0.0.0" authors = ["Automatically generated"] publish = false -edition = "2018" +edition = "2021" [package.metadata] cargo-fuzz = true diff --git a/interop_client/src/main.rs b/interop_client/src/main.rs index 5a0524bfe4..4feabbe29e 100644 --- a/interop_client/src/main.rs +++ b/interop_client/src/main.rs @@ -3,7 +3,7 @@ //! //! It is based on the Mock client in that repository. -use std::{collections::HashMap, convert::TryFrom, fmt::Display, fs::File, io::Write, sync::Mutex}; +use std::{collections::HashMap, convert::TryFrom, fmt::Display, fs::File, io::Write}; use clap::Parser; use clap_derive::*; @@ -29,6 +29,7 @@ use openmls::{ }, versions::ProtocolVersion, }; + use openmls_basic_credential::SignatureKeyPair; use openmls_rust_crypto::OpenMlsRustCrypto; use openmls_traits::{ @@ -37,7 +38,9 @@ use openmls_traits::{ types::{Ciphersuite, HpkeKeyPair}, OpenMlsCryptoProvider, }; +use serde::{self, Serialize}; use tls_codec::{Deserialize, Serialize}; +use tokio::sync::Mutex; use tonic::{async_trait, transport::Server, Code, Request, Response, Status}; use tracing::{debug, error, info, instrument, trace, Span}; use tracing_subscriber::EnvFilter; @@ -94,7 +97,7 @@ impl MlsClientImpl { } fn into_status(e: E) -> Status { - let message = "mls group error ".to_string() + &e.to_string(); + let message = format!("mls group error {e}"); error!("{message}"); Status::new(Code::Aborted, message) } @@ -112,6 +115,7 @@ fn to_ciphersuite(cs: u32) -> Result<&'static Ciphersuite, Status> { let ciphersuites = &[ Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, + Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, ]; match ciphersuites.iter().find(|&&cs| cs == cs_name) { @@ -175,7 +179,7 @@ where } } -#[async_trait] +#[async_trait(?Send)] impl MlsClient for MlsClientImpl { #[instrument(skip_all)] async fn name(&self, request: Request) -> Result, Status> { @@ -199,6 +203,7 @@ impl MlsClient for MlsClientImpl { let ciphersuites = &[ Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, + Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, ]; let response = SupportedCiphersuitesResponse { @@ -223,8 +228,13 @@ impl MlsClient for MlsClientImpl { let ciphersuite = Ciphersuite::try_from(request.cipher_suite as u16).unwrap(); let credential = Credential::new(request.identity.clone(), CredentialType::Basic).unwrap(); - let signature_keys = SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); - signature_keys.store(backend.key_store()).unwrap(); + + let signature_keys = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); + signature_keys.store(backend.key_store()).await.unwrap(); let wire_format_policy = wire_format_policy(request.encrypt_handshake); // Note: We just use some values here that make live testing work. @@ -248,6 +258,7 @@ impl MlsClient for MlsClientImpl { signature_key: signature_keys.public().into(), }, ) + .await .map_err(into_status)?; Span::current().record("actor", bytes_to_string(group.own_identity().unwrap())); @@ -261,7 +272,7 @@ impl MlsClient for MlsClientImpl { crypto_provider: backend, }; - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let state_id = groups.len() as u32; groups.push(interop_group); @@ -289,7 +300,11 @@ impl MlsClient for MlsClientImpl { ); let credential = Credential::new(identity, CredentialType::Basic).unwrap(); - let signature_keys = SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); + let signature_keys = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *crypto_provider.rand().borrow_rand().unwrap(), + ) + .unwrap(); let key_package = KeyPackage::builder() .leaf_node_capabilities(Capabilities::new( @@ -315,14 +330,17 @@ impl MlsClient for MlsClientImpl { signature_key: signature_keys.public().into(), }, ) + .await .unwrap(); let private_key = crypto_provider .key_store() .read::(key_package.hpke_init_key().as_slice()) + .await .unwrap(); let encryption_key_pair = - read_keys_from_key_store(&crypto_provider, key_package.leaf_node().encryption_key()); + read_keys_from_key_store(&crypto_provider, key_package.leaf_node().encryption_key()) + .await; let transaction_id: [u8; 4] = crypto_provider.rand().random_array().unwrap(); let transaction_id = u32::from_be_bytes(transaction_id); @@ -343,9 +361,9 @@ impl MlsClient for MlsClientImpl { self.transaction_id_map .lock() - .unwrap() + .await .insert(transaction_id, request.identity.clone()); - self.pending_state.lock().unwrap().insert( + self.pending_state.lock().await.insert( request.identity.clone(), ( key_package, @@ -383,7 +401,7 @@ impl MlsClient for MlsClientImpl { .wire_format_policy(wire_format_policy) .build(); - let mut pending_key_packages = self.pending_state.lock().unwrap(); + let mut pending_key_packages = self.pending_state.lock().await; let ( my_key_package, private_key, @@ -402,6 +420,7 @@ impl MlsClient for MlsClientImpl { crypto_provider .key_store() .store(my_key_package.hpke_init_key().as_slice(), &private_key) + .await .map_err(|_| Status::aborted("failed to interact with the key store"))?; // Store the key package in the key store with the hash reference as id @@ -415,6 +434,7 @@ impl MlsClient for MlsClientImpl { .as_slice(), &my_key_package, ) + .await .map_err(into_status)?; // Store the encryption key pair in the key store. @@ -425,6 +445,7 @@ impl MlsClient for MlsClientImpl { crypto_provider .key_store() .store::(my_key_package.hpke_init_key().as_slice(), &private_key) + .await .map_err(into_status)?; let welcome_msg = MlsMessageIn::tls_deserialize(&mut request.welcome.as_slice()) @@ -438,6 +459,7 @@ impl MlsClient for MlsClientImpl { let group = MlsGroup::new_from_welcome(&crypto_provider, &mls_group_config, welcome, ratchet_tree) + .await .map_err(into_status)?; let interop_group = InteropGroup { @@ -459,7 +481,7 @@ impl MlsClient for MlsClientImpl { .as_slice() .to_vec(); - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let state_id = groups.len() as u32; groups.push(interop_group); @@ -585,7 +607,7 @@ impl MlsClient for MlsClientImpl { let request = request.get_ref(); info!(?request, "Request"); - let groups = self.groups.lock().unwrap(); + let groups = self.groups.lock().await; let interop_group = groups .get(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -613,7 +635,7 @@ impl MlsClient for MlsClientImpl { let request = request.get_ref(); info!(?request, "Request"); - let groups = self.groups.lock().unwrap(); + let groups = self.groups.lock().await; let interop_group = groups .get(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -647,7 +669,7 @@ impl MlsClient for MlsClientImpl { let request = request.get_ref(); info!(?request, "Request"); - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let interop_group = groups .get_mut(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -680,7 +702,7 @@ impl MlsClient for MlsClientImpl { let request = request.get_ref(); info!(?request, "Request"); - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let interop_group = groups .get_mut(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -696,6 +718,7 @@ impl MlsClient for MlsClientImpl { let processed_message = interop_group .group .process_message(&interop_group.crypto_provider, message) + .await .map_err(into_status)?; debug!("Processed."); trace!(?processed_message); @@ -731,7 +754,7 @@ impl MlsClient for MlsClientImpl { trace!(" psk_id {:x?}", raw_psk_id); let external_psk = Psk::External(ExternalPsk::new(raw_psk_id)); - fn store( + async fn store( ciphersuite: Ciphersuite, crypto_provider: &OpenMlsRustCrypto, external_psk: Psk, @@ -741,16 +764,17 @@ impl MlsClient for MlsClientImpl { .map_err(|_| Status::internal("unable to create PreSharedKeyId from raw psk_id"))?; psk_id .write_to_key_store(crypto_provider, ciphersuite, secret) + .await .map_err(|_| Status::new(Code::Internal, "unable to store PSK"))?; Ok(()) } // This might be for a transaction ID or a state ID, so either a group, or not. // Transaction IDs are random. We assume that if it exists, it is what we want. - let transaction_id_map = self.transaction_id_map.lock().unwrap(); + let transaction_id_map = self.transaction_id_map.lock().await; let pending_state_id = transaction_id_map.get(&request.state_or_transaction_id); if let Some(pending_state_id) = pending_state_id { - let mut pending_state = self.pending_state.lock().unwrap(); + let mut pending_state = self.pending_state.lock().await; let pending_state = pending_state .get_mut(pending_state_id) .ok_or(Status::internal("Unable to retrieve pending state"))?; @@ -760,10 +784,11 @@ impl MlsClient for MlsClientImpl { &pending_state.5, external_psk, &request.psk_secret, - )?; + ) + .await?; } else { // So we have a group - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let interop_group = groups .get_mut(request.state_or_transaction_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -778,7 +803,8 @@ impl MlsClient for MlsClientImpl { &interop_group.crypto_provider, external_psk, &request.psk_secret, - )?; + ) + .await?; } let response = StorePskResponse::default(); @@ -795,7 +821,7 @@ impl MlsClient for MlsClientImpl { let request = request.get_ref(); info!(?request, "Request"); - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let interop_group = groups .get_mut(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -853,7 +879,7 @@ impl MlsClient for MlsClientImpl { let request = request.get_ref(); info!(?request, "Request"); - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let interop_group = groups .get_mut(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -880,6 +906,7 @@ impl MlsClient for MlsClientImpl { &interop_group.signature_keys, None, ) + .await .map_err(into_status)?; // Store the proposal for potential future use. @@ -907,7 +934,7 @@ impl MlsClient for MlsClientImpl { Credential::new(request.removed_id.clone(), CredentialType::Basic).unwrap(); trace!(" for credential: {removed_credential:x?}"); - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let interop_group = groups .get_mut(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -972,7 +999,7 @@ impl MlsClient for MlsClientImpl { let request = request.get_ref(); info!(?request, "Request"); - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let interop_group = groups .get_mut(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -995,6 +1022,7 @@ impl MlsClient for MlsClientImpl { trace!("Processing proposal ..."); let processed_message = group .process_message(&interop_group.crypto_provider, message) + .await .map_err(into_status)?; trace!("... done"); @@ -1105,6 +1133,7 @@ impl MlsClient for MlsClientImpl { &interop_group.crypto_provider, &interop_group.signature_keys, ) + .await .map_err(into_status)?; let commit = commit.to_bytes().unwrap(); @@ -1121,6 +1150,7 @@ impl MlsClient for MlsClientImpl { group .merge_pending_commit(&interop_group.crypto_provider) + .await .map_err(into_status)?; debug!("Merged pending commit."); @@ -1152,7 +1182,7 @@ impl MlsClient for MlsClientImpl { let request = request.get_ref(); info!(?request, "Request"); - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let interop_group = groups .get_mut(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -1174,6 +1204,7 @@ impl MlsClient for MlsClientImpl { trace!(" processing proposal ..."); let processed_message = group .process_message(&interop_group.crypto_provider, message) + .await .map_err(into_status)?; trace!(" done"); match processed_message.into_content() { @@ -1198,6 +1229,7 @@ impl MlsClient for MlsClientImpl { debug!("Processing message."); let processed_message = group .process_message(&interop_group.crypto_provider, message) + .await .map_err(into_status)?; debug!("Processed."); trace!(?processed_message); @@ -1210,6 +1242,7 @@ impl MlsClient for MlsClientImpl { debug!(commit=?staged_commit, "Merging staged commit."); group .merge_staged_commit(&interop_group.crypto_provider, *staged_commit) + .await .map_err(into_status)?; } } @@ -1235,7 +1268,7 @@ impl MlsClient for MlsClientImpl { let request = request.get_ref(); info!(?request, "Request"); - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let interop_group = groups .get_mut(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -1247,6 +1280,7 @@ impl MlsClient for MlsClientImpl { trace!(commit=?group.pending_commit(), "Merging pending commit."); group .merge_pending_commit(&interop_group.crypto_provider) + .await .map_err(|e| { trace!("Error merging pending commit: `{e:?}`"); Status::aborted("failed to apply pending commits") @@ -1271,7 +1305,7 @@ impl MlsClient for MlsClientImpl { let request = request.get_ref(); info!(?request, "Request"); - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let interop_group = groups .get_mut(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; @@ -1315,7 +1349,7 @@ impl MlsClient for MlsClientImpl { let request = request.get_ref(); info!(?request, "Request"); - let mut groups = self.groups.lock().unwrap(); + let mut groups = self.groups.lock().await; let interop_group = groups .get_mut(request.state_id as usize) .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; diff --git a/memory_keystore/Cargo.toml b/memory_keystore/Cargo.toml index 6919386828..d955523765 100644 --- a/memory_keystore/Cargo.toml +++ b/memory_keystore/Cargo.toml @@ -2,7 +2,7 @@ name = "openmls_memory_keystore" authors = ["OpenMLS Authors"] version = "0.1.0" -edition = "2018" +edition = "2021" description = "A very basic key store for OpenMLS implementing openmls_traits." license = "MIT" documentation = "https://docs.rs/openmls_memory_keystore" @@ -13,3 +13,5 @@ readme = "README.md" openmls_traits = { version = "0.1.0", path = "../traits" } thiserror = "1.0" serde_json = "1.0" +async-trait = { workspace = true } +async-lock = "2.7" diff --git a/memory_keystore/src/lib.rs b/memory_keystore/src/lib.rs index d2194bbe95..6fec7af8ae 100644 --- a/memory_keystore/src/lib.rs +++ b/memory_keystore/src/lib.rs @@ -1,11 +1,13 @@ +use async_lock::RwLock; use openmls_traits::key_store::{MlsEntity, OpenMlsKeyStore}; -use std::{collections::HashMap, sync::RwLock}; +use std::collections::HashMap; #[derive(Debug, Default)] pub struct MemoryKeyStore { values: RwLock, Vec>>, } +#[async_trait::async_trait(?Send)] impl OpenMlsKeyStore for MemoryKeyStore { /// The error type returned by the [`OpenMlsKeyStore`]. type Error = MemoryKeyStoreError; @@ -14,12 +16,12 @@ impl OpenMlsKeyStore for MemoryKeyStore { /// serialization for ID `k`. /// /// Returns an error if storing fails. - fn store(&self, k: &[u8], v: &V) -> Result<(), Self::Error> { + async fn store(&self, k: &[u8], v: &V) -> Result<(), Self::Error> { let value = serde_json::to_vec(v).map_err(|_| MemoryKeyStoreError::SerializationError)?; // We unwrap here, because this is the only function claiming a write // lock on `credential_bundles`. It only holds the lock very briefly and // should not panic during that period. - let mut values = self.values.write().unwrap(); + let mut values = self.values.write().await; values.insert(k.to_vec(), value); Ok(()) } @@ -28,11 +30,11 @@ impl OpenMlsKeyStore for MemoryKeyStore { /// [`FromKeyStoreValue`] trait for deserialization. /// /// Returns [`None`] if no value is stored for `k` or reading fails. - fn read(&self, k: &[u8]) -> Option { + async fn read(&self, k: &[u8]) -> Option { // We unwrap here, because the two functions claiming a write lock on // `init_key_package_bundles` (this one and `generate_key_package_bundle`) only // hold the lock very briefly and should not panic during that period. - let values = self.values.read().unwrap(); + let values = self.values.read().await; if let Some(value) = values.get(k) { serde_json::from_slice(value).ok() } else { @@ -43,9 +45,9 @@ impl OpenMlsKeyStore for MemoryKeyStore { /// Delete a value stored for ID `k`. /// /// Returns an error if storing fails. - fn delete(&self, k: &[u8]) -> Result<(), Self::Error> { + async fn delete(&self, k: &[u8]) -> Result<(), Self::Error> { // We just delete both ... - let mut values = self.values.write().unwrap(); + let mut values = self.values.write().await; values.remove(k); Ok(()) } @@ -61,3 +63,5 @@ pub enum MemoryKeyStoreError { #[error("Error serializing value.")] SerializationError, } + +unsafe impl Send for MemoryKeyStoreError {} diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index e2a8e716ba..3f72e9f263 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "openmls" -version = "0.4.1" +version = "0.20.0" authors = ["OpenMLS Authors"] edition = "2021" -description = "This is a WIP Rust implementation of the Messaging Layer Security (MLS) protocol based on draft 12+." +description = "This is a WIP Rust implementation of the Messaging Layer Security (MLS) protocol based on draft 20-ish." license = "MIT" documentation = "https://openmls.github.io/openmls/" repository = "https://github.com/openmls/openmls/" @@ -17,16 +17,17 @@ tls_codec = { workspace = true } rayon = "^1.5.0" thiserror = "^1.0" backtrace = "0.3" +async-trait = { workspace = true } # Only required for tests. rand = { version = "0.8", optional = true } serde_json = { version = "1.0", optional = true } # Crypto backends required for KAT and testing - "test-utils" feature itertools = { version = "0.10", optional = true } openmls_rust_crypto = { version = "0.1.0", path = "../openmls_rust_crypto", optional = true } -openmls_evercrypt = { version = "0.1.0", path = "../evercrypt_backend", optional = true } openmls_basic_credential = { version = "0.1.0", path = "../basic_credential", optional = true, features = ["clonable", "test-utils"] } rstest = { version = "^0.16", optional = true } rstest_reuse = { version = "0.4", optional = true } +tokio = { version = "1.24", features = ["full"], optional = true } [features] default = [] @@ -36,18 +37,17 @@ test-utils = [ "dep:itertools", "dep:openmls_rust_crypto", "dep:rand", + "dep:tokio", "dep:rstest", "dep:rstest_reuse", "dep:openmls_basic_credential", ] -evercrypt = ["dep:openmls_evercrypt"] # Evercrypt needs to be enabled individually crypto-debug = [] # ☣️ Enable logging of sensitive cryptographic information content-debug = [] # ☣️ Enable logging of sensitive message content [dev-dependencies] backtrace = "0.3" - -criterion = "^0.4" +criterion = { version = "0.4", features = ["async_futures"] } hex = { version = "0.4", features = ["serde"] } itertools = "0.10" lazy_static = "1.4" @@ -61,7 +61,7 @@ tempfile = "3" # x64 targets get evercrypt compiled into dev-dependencies. [target.'cfg(target_arch = "x86_64")'.dev-dependencies.openmls] path = "." -features = ["test-utils", "evercrypt"] +features = ["test-utils"] [[bench]] name = "benchmark" diff --git a/openmls/benches/benchmark.rs b/openmls/benches/benchmark.rs index e079f0c824..85a3c7ac95 100644 --- a/openmls/benches/benchmark.rs +++ b/openmls/benches/benchmark.rs @@ -1,70 +1,47 @@ -#[macro_use] -extern crate criterion; -extern crate openmls; -extern crate rand; - -use criterion::Criterion; +use criterion::{ + async_executor::FuturesExecutor, criterion_group, criterion_main, BenchmarkId, Criterion, +}; use openmls::prelude::{config::CryptoConfig, *}; use openmls_basic_credential::SignatureKeyPair; use openmls_rust_crypto::OpenMlsRustCrypto; use openmls_traits::{crypto::OpenMlsCrypto, OpenMlsCryptoProvider}; -fn criterion_kp_bundle(c: &mut Criterion, backend: &impl OpenMlsCryptoProvider) { +fn criterion_benchmark(c: &mut Criterion) { + let backend = OpenMlsRustCrypto::default(); for &ciphersuite in backend.crypto().supported_ciphersuites().iter() { - c.bench_function( - &format!("KeyPackage create bundle with ciphersuite: {ciphersuite:?}"), - move |b| { - b.iter_with_setup( - || { - let credential = - Credential::new(vec![1, 2, 3], CredentialType::Basic).unwrap(); - let signer = - SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); - let credential_with_key = CredentialWithKey { - credential, - signature_key: signer.to_public_vec().into(), - }; + let credential = Credential::new(vec![1, 2, 3], CredentialType::Basic).unwrap(); + let signer = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); + let credential_with_key = CredentialWithKey { + credential, + signature_key: signer.to_public_vec().into(), + }; - (credential_with_key, signer) - }, - |(credential_with_key, signer)| { - let _key_package = KeyPackage::builder() - .build( - CryptoConfig { - ciphersuite, - version: ProtocolVersion::default(), - }, - backend, - &signer, - credential_with_key, - ) - .expect("An unexpected error occurred."); - }, - ); + c.bench_with_input( + BenchmarkId::new( + format!("KeyPackage create bundle with ciphersuite"), + ciphersuite, + ), + &(&backend, signer, credential_with_key), + |b, (crypto, signer, credential_with_key)| { + b.to_async(FuturesExecutor).iter(|| { + KeyPackage::builder().build( + CryptoConfig { + ciphersuite, + version: ProtocolVersion::default(), + }, + *crypto, + signer, + credential_with_key.clone(), + ) + }) }, ); } } -fn kp_bundle_rust_crypto(c: &mut Criterion) { - let backend = &OpenMlsRustCrypto::default(); - println!("Backend: RustCrypto"); - criterion_kp_bundle(c, backend); -} - -#[cfg(feature = "evercrypt")] -fn kp_bundle_evercrypt(c: &mut Criterion) { - use openmls_evercrypt::OpenMlsEvercrypt; - let backend = &OpenMlsEvercrypt::default(); - println!("Backend: Evercrypt"); - criterion_kp_bundle(c, backend); -} - -fn criterion_benchmark(c: &mut Criterion) { - kp_bundle_rust_crypto(c); - #[cfg(feature = "evercrypt")] - kp_bundle_evercrypt(c); -} - criterion_group!(benches, criterion_benchmark); criterion_main!(benches); diff --git a/openmls/src/binary_tree/array_representation/kat_treemath.rs b/openmls/src/binary_tree/array_representation/kat_treemath.rs index 2f91954e54..d1a6262a5d 100644 --- a/openmls/src/binary_tree/array_representation/kat_treemath.rs +++ b/openmls/src/binary_tree/array_representation/kat_treemath.rs @@ -113,17 +113,17 @@ pub fn generate_test_vector(n_leaves: u32) -> TreeMathTestVector { test_vector } -#[test] -fn write_test_vectors() { - let mut tests = Vec::new(); +// #[test] +// fn write_test_vectors() { +// let mut tests = Vec::new(); - for n_leaves in 0..10 { - let test_vector = generate_test_vector(1 << n_leaves); - tests.push(test_vector); - } +// for n_leaves in 0..10 { +// let test_vector = generate_test_vector(1 << n_leaves); +// tests.push(test_vector); +// } - write("test_vectors/tree-math-new.json", &tests); -} +// write("test_vectors/tree-math-new.json", &tests); +// } #[cfg(any(feature = "test-utils", test))] pub fn run_test_vector(test_vector: TreeMathTestVector) -> Result<(), TmTestVectorError> { diff --git a/openmls/src/ciphersuite/aead.rs b/openmls/src/ciphersuite/aead.rs index a60355fe2d..8ea69e156c 100644 --- a/openmls/src/ciphersuite/aead.rs +++ b/openmls/src/ciphersuite/aead.rs @@ -165,7 +165,7 @@ mod unit_tests { /// it has changed, xoring it again and testing that it's back in its original /// state. #[apply(backends)] - fn test_xor(backend: &impl OpenMlsCryptoProvider) { + async fn test_xor(backend: &impl OpenMlsCryptoProvider) { let reuse_guard: ReuseGuard = ReuseGuard::try_from_random(backend).expect("An unexpected error occurred."); let original_nonce = AeadNonce::random(backend); diff --git a/openmls/src/ciphersuite/hpke.rs b/openmls/src/ciphersuite/hpke.rs index 46d2805ba1..cb2ae8320f 100644 --- a/openmls/src/ciphersuite/hpke.rs +++ b/openmls/src/ciphersuite/hpke.rs @@ -115,7 +115,7 @@ pub(crate) fn encrypt_with_label( &context, &[], plaintext, - ); + )?; log_crypto!(debug, "* ciphertext: {:x?}", cipher); diff --git a/openmls/src/ciphersuite/tests/kat_crypto_basics.rs b/openmls/src/ciphersuite/tests/kat_crypto_basics.rs index e697f52dde..fffb649528 100644 --- a/openmls/src/ciphersuite/tests/kat_crypto_basics.rs +++ b/openmls/src/ciphersuite/tests/kat_crypto_basics.rs @@ -280,21 +280,7 @@ pub fn run_test_vector( let mut parsed = ParsedSignWithLabel { key: SignatureKeyPair::from_raw( ciphersuite.signature_algorithm(), - { - if matches!( - ciphersuite, - Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 - | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 - | Ciphersuite::MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 - ) { - let mut private: Vec = private; - // For the RC crypto provider we need to have the public key in here. - private.append(&mut public.clone()); - private - } else { - private - } - }, + private, public.clone(), ), content, diff --git a/openmls/src/ciphersuite/tests/test_ciphersuite.rs b/openmls/src/ciphersuite/tests/test_ciphersuite.rs index 2dd7e46d5a..f13263a52f 100644 --- a/openmls/src/ciphersuite/tests/test_ciphersuite.rs +++ b/openmls/src/ciphersuite/tests/test_ciphersuite.rs @@ -6,14 +6,17 @@ use crate::{ciphersuite::*, test_utils::*}; // Spot test to make sure hpke seal/open work. #[apply(ciphersuites_and_backends)] -fn test_hpke_seal_open(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_hpke_seal_open(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let plaintext = &[1, 2, 3]; - let kp = backend.crypto().derive_hpke_keypair( - ciphersuite.hpke_config(), - Secret::random(ciphersuite, backend, None) - .expect("Not enough randomness.") - .as_slice(), - ); + let kp = backend + .crypto() + .derive_hpke_keypair( + ciphersuite.hpke_config(), + Secret::random(ciphersuite, backend, None) + .expect("Not enough randomness.") + .as_slice(), + ) + .unwrap(); let ciphertext = hpke::encrypt_with_label( &kp.public, "label", diff --git a/openmls/src/ciphersuite/tests/test_secrets.rs b/openmls/src/ciphersuite/tests/test_secrets.rs index 092f637a8f..806a0271e8 100644 --- a/openmls/src/ciphersuite/tests/test_secrets.rs +++ b/openmls/src/ciphersuite/tests/test_secrets.rs @@ -7,7 +7,7 @@ use crate::{ }; #[apply(ciphersuites_and_backends)] -fn secret_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn secret_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // These two secrets must be incompatible let default_secret = Secret::random(ciphersuite, backend, None).expect("Not enough randomness."); @@ -21,7 +21,7 @@ fn secret_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { #[should_panic] #[apply(ciphersuites_and_backends)] -fn secret_incompatible(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn secret_incompatible(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // These two secrets must be incompatible let default_secret = Secret::random(ciphersuite, backend, None).expect("Not enough randomness."); diff --git a/openmls/src/credentials/mod.rs b/openmls/src/credentials/mod.rs index 6505ad2ca3..fbd4f3340e 100644 --- a/openmls/src/credentials/mod.rs +++ b/openmls/src/credentials/mod.rs @@ -268,7 +268,7 @@ impl CredentialWithKey { #[cfg(any(test, feature = "test-utils"))] pub mod test_utils { use openmls_basic_credential::SignatureKeyPair; - use openmls_traits::{types::SignatureScheme, OpenMlsCryptoProvider}; + use openmls_traits::{random::OpenMlsRand, types::SignatureScheme, OpenMlsCryptoProvider}; use super::{Credential, CredentialType, CredentialWithKey}; @@ -277,15 +277,19 @@ pub mod test_utils { /// The signature keys are stored in the key store. /// /// Returns the [`Credential`] and the [`SignatureKeyPair`]. - pub fn new_credential( + pub async fn new_credential( backend: &impl OpenMlsCryptoProvider, identity: &[u8], credential_type: CredentialType, signature_scheme: SignatureScheme, ) -> (CredentialWithKey, SignatureKeyPair) { let credential = Credential::new(identity.into(), credential_type).unwrap(); - let signature_keys = SignatureKeyPair::new(signature_scheme).unwrap(); - signature_keys.store(backend.key_store()).unwrap(); + let signature_keys = SignatureKeyPair::new( + signature_scheme, + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); + signature_keys.store(backend.key_store()).await.unwrap(); ( CredentialWithKey { diff --git a/openmls/src/extensions/external_pub_extension.rs b/openmls/src/extensions/external_pub_extension.rs index c078ed863f..0478198eed 100644 --- a/openmls/src/extensions/external_pub_extension.rs +++ b/openmls/src/extensions/external_pub_extension.rs @@ -45,21 +45,22 @@ mod test { let mut external_pub_extensions = Vec::new(); for _ in 0..8 { - let hpke_public_key = { - let ikm = Secret::random( - Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, - &backend, - ProtocolVersion::default(), - ) - .unwrap(); - let init_key = backend.crypto().derive_hpke_keypair( + let hpke_public_key = + { + let ikm = Secret::random( + Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, + &backend, + ProtocolVersion::default(), + ) + .unwrap(); + let init_key = backend.crypto().derive_hpke_keypair( Ciphersuite::hpke_config( &Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, ), ikm.as_slice(), - ); - init_key.public - }; + ).unwrap(); + init_key.public + }; external_pub_extensions.push(ExternalPubExtension::new(hpke_public_key.into())); } diff --git a/openmls/src/extensions/external_sender_extension.rs b/openmls/src/extensions/external_sender_extension.rs index c45dd56b64..0df444c85e 100644 --- a/openmls/src/extensions/external_sender_extension.rs +++ b/openmls/src/extensions/external_sender_extension.rs @@ -73,14 +73,15 @@ mod test { use crate::{credentials::CredentialType, test_utils::*}; #[apply(ciphersuites)] - fn test_serialize_deserialize(ciphersuite: Ciphersuite) { + async fn test_serialize_deserialize(ciphersuite: Ciphersuite) { + let mut rng = rand::thread_rng(); let tests = { let mut external_sender_extensions = Vec::new(); for _ in 0..8 { let credential = Credential::new(b"Alice".to_vec(), CredentialType::Basic).unwrap(); let signature_keys = - SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); + SignatureKeyPair::new(ciphersuite.signature_algorithm(), &mut rng).unwrap(); external_sender_extensions.push(ExternalSender { signature_key: signature_keys.to_public_vec().into(), diff --git a/openmls/src/extensions/test_extensions.rs b/openmls/src/extensions/test_extensions.rs index 07aa8b6aa6..317b5970b0 100644 --- a/openmls/src/extensions/test_extensions.rs +++ b/openmls/src/extensions/test_extensions.rs @@ -35,7 +35,7 @@ fn application_id() { // This tests the ratchet tree extension to deliver the public ratcheting tree // in-band #[apply(ciphersuites_and_backends)] -fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Basic group setup. let group_aad = b"Alice's test group"; let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); @@ -46,13 +46,15 @@ fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let (bob_credential_with_key, bob_signature_keys) = test_utils::new_credential( backend, b"Bob", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; // Generate KeyPackages let bob_key_package_bundle = KeyPackageBundle::new( @@ -60,7 +62,8 @@ fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto &bob_signature_keys, ciphersuite, bob_credential_with_key.clone(), - ); + ) + .await; let bob_key_package = bob_key_package_bundle.key_package(); let config = CoreGroupConfig { @@ -75,6 +78,7 @@ fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto ) .with_config(config) .build(backend, &alice_signature_keys) + .await .expect("Error creating group."); // === Alice adds Bob === @@ -98,10 +102,12 @@ fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto .build(); let create_commit_result = alice_group .create_commit(params, backend, &alice_signature_keys) + .await .expect("Error creating commit"); alice_group .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging commit"); let bob_group = match CoreGroup::new_from_welcome( @@ -112,7 +118,9 @@ fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto bob_key_package_bundle, backend, ResumptionPskStore::new(1024), - ) { + ) + .await + { Ok(g) => g, Err(e) => panic!("Could not join group with ratchet tree extension {e}"), }; @@ -135,7 +143,8 @@ fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto &bob_signature_keys, ciphersuite, bob_credential_with_key, - ); + ) + .await; let bob_key_package = bob_key_package_bundle.key_package(); let config = CoreGroupConfig { @@ -149,6 +158,7 @@ fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto ) .with_config(config) .build(backend, &alice_signature_keys) + .await .expect("Error creating group."); // === Alice adds Bob === @@ -172,10 +182,12 @@ fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto .build(); let create_commit_result = alice_group .create_commit(params, backend, &alice_signature_keys) + .await .expect("Error creating commit"); alice_group .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging commit"); let error = CoreGroup::new_from_welcome( @@ -187,13 +199,14 @@ fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto backend, ResumptionPskStore::new(1024), ) + .await .err(); // We expect an error because the ratchet tree is missing - assert_eq!( + assert!(matches!( error.expect("We expected an error"), WelcomeError::MissingRatchetTree - ); + )); } #[test] diff --git a/openmls/src/framing/test_framing.rs b/openmls/src/framing/test_framing.rs index 690d59a7f8..d08100ba4c 100644 --- a/openmls/src/framing/test_framing.rs +++ b/openmls/src/framing/test_framing.rs @@ -26,13 +26,14 @@ use crate::{ /// This tests serializing/deserializing PublicMessage #[apply(ciphersuites_and_backends)] -fn codec_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn codec_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (_credential, signature_keys) = test_utils::new_credential( backend, b"Creator", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let sender = Sender::build_member(LeafNodeIndex::new(987543210)); let group_context = GroupContext::new( ciphersuite, @@ -78,13 +79,14 @@ fn codec_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide /// This tests serializing/deserializing PrivateMessage #[apply(ciphersuites_and_backends)] -fn codec_ciphertext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn codec_ciphertext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (_credential, signature_keys) = test_utils::new_credential( backend, b"Creator", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let sender = Sender::build_member(LeafNodeIndex::new(0)); let group_context = GroupContext::new( ciphersuite, @@ -155,10 +157,10 @@ fn codec_ciphertext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid /// This tests the correctness of wire format checks #[apply(ciphersuites_and_backends)] -fn wire_format_checks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn wire_format_checks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let configuration = &SenderRatchetConfiguration::default(); let (plaintext, _credential, _keys) = - create_content(ciphersuite, WireFormat::PrivateMessage, backend); + create_content(ciphersuite, WireFormat::PrivateMessage, backend).await; let mut message_secrets = MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)); let encryption_secret_bytes = backend @@ -229,7 +231,7 @@ fn wire_format_checks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv // Create and encrypt content with the wrong wire format let (plaintext, _credential, signature_keys) = - create_content(ciphersuite, WireFormat::PublicMessage, backend); + create_content(ciphersuite, WireFormat::PublicMessage, backend).await; let pk = OpenMlsSignaturePublicKey::new( signature_keys.public().into(), ciphersuite.signature_algorithm(), @@ -290,7 +292,7 @@ fn wire_format_checks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv ); } -fn create_content( +async fn create_content( ciphersuite: Ciphersuite, wire_format: WireFormat, backend: &impl OpenMlsCryptoProvider, @@ -300,7 +302,8 @@ fn create_content( b"Creator", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let sender = Sender::build_member(LeafNodeIndex::new(0)); let group_context = GroupContext::new( ciphersuite, @@ -330,13 +333,14 @@ fn create_content( } #[apply(ciphersuites_and_backends)] -fn membership_tag(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn membership_tag(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (_credential, signature_keys) = test_utils::new_credential( backend, b"Creator", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let group_context = GroupContext::new( ciphersuite, GroupId::random(backend), @@ -386,7 +390,7 @@ fn membership_tag(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider } #[apply(ciphersuites_and_backends)] -fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_aad = b"Alice's test group"; let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); let configuration = &SenderRatchetConfiguration::default(); @@ -397,23 +401,26 @@ fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let (bob_credential, bob_signature_keys) = test_utils::new_credential( backend, b"Bob", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let (charlie_credential, charlie_signature_keys) = test_utils::new_credential( backend, b"Charlie", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; // Generate KeyPackages let bob_key_package_bundle = - KeyPackageBundle::new(backend, &bob_signature_keys, ciphersuite, bob_credential); + KeyPackageBundle::new(backend, &bob_signature_keys, ciphersuite, bob_credential).await; let bob_key_package = bob_key_package_bundle.key_package(); let charlie_key_package_bundle = KeyPackageBundle::new( @@ -421,7 +428,8 @@ fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider &charlie_signature_keys, ciphersuite, charlie_credential, - ); + ) + .await; let charlie_key_package = charlie_key_package_bundle.key_package(); // Alice creates a group @@ -431,6 +439,7 @@ fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider alice_credential, ) .build(backend, &alice_signature_keys) + .await .expect("Error creating group."); // Alice adds Bob @@ -454,10 +463,12 @@ fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider .build(); let create_commit_result = group_alice .create_commit(params, backend, &alice_signature_keys) + .await .expect("Error creating Commit"); group_alice .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging pending commit"); let _group_bob = CoreGroup::new_from_welcome( @@ -469,6 +480,7 @@ fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider backend, ResumptionPskStore::new(1024), ) + .await .expect("Bob: Error creating group from Welcome"); // Alice adds Charlie @@ -498,10 +510,12 @@ fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider .build(); let create_commit_result = group_alice .create_commit(params, backend, &alice_signature_keys) + .await .expect("Error creating Commit"); group_alice .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging pending commit"); let mut group_charlie = CoreGroup::new_from_welcome( @@ -513,6 +527,7 @@ fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider backend, ResumptionPskStore::new(1024), ) + .await .expect("Charlie: Error creating group from Welcome"); // Alice removes Bob @@ -541,17 +556,21 @@ fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider .build(); let create_commit_result = group_alice .create_commit(params, backend, &alice_signature_keys) + .await .expect("Error creating Commit"); let staged_commit = group_charlie .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("Charlie: Could not stage Commit"); group_charlie .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); group_alice .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging pending commit"); group_alice.print_ratchet_tree("Alice tree"); @@ -590,9 +609,9 @@ fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider } #[apply(ciphersuites_and_backends)] -fn confirmation_tag_presence(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn confirmation_tag_presence(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (framing_parameters, group_alice, alice_signature_keys, group_bob, _, _) = - setup_alice_bob_group(ciphersuite, backend); + setup_alice_bob_group(ciphersuite, backend).await; // Alice does an update let proposal_store = ProposalStore::default(); @@ -604,18 +623,20 @@ fn confirmation_tag_presence(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry .build(); let mut create_commit_result = group_alice .create_commit(params, backend, &alice_signature_keys) + .await .expect("Error creating Commit"); create_commit_result.commit.unset_confirmation_tag(); let err = group_bob .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect_err("No error despite missing confirmation tag."); assert_eq!(err, StageCommitError::ConfirmationTagMissing); } -pub(crate) fn setup_alice_bob_group( +pub(crate) async fn setup_alice_bob_group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) -> ( @@ -635,13 +656,15 @@ pub(crate) fn setup_alice_bob_group( b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let (bob_credential, bob_signature_keys) = test_utils::new_credential( backend, b"Bob", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; // Generate KeyPackages let bob_key_package_bundle = KeyPackageBundle::new( @@ -649,7 +672,8 @@ pub(crate) fn setup_alice_bob_group( &bob_signature_keys, ciphersuite, bob_credential.clone(), - ); + ) + .await; let bob_key_package = bob_key_package_bundle.key_package(); // Alice creates a group @@ -659,6 +683,7 @@ pub(crate) fn setup_alice_bob_group( alice_credential, ) .build(backend, &alice_signature_keys) + .await .expect("Error creating group."); // Alice adds Bob @@ -683,6 +708,7 @@ pub(crate) fn setup_alice_bob_group( let create_commit_result = group_alice .create_commit(params, backend, &alice_signature_keys) + .await .expect("Error creating Commit"); let commit = match create_commit_result.commit.content() { @@ -695,6 +721,7 @@ pub(crate) fn setup_alice_bob_group( group_alice .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging pending commit"); // We have to create Bob's group so he can process the commit with the @@ -708,6 +735,7 @@ pub(crate) fn setup_alice_bob_group( backend, ResumptionPskStore::new(1024), ) + .await .expect("error creating group from welcome"); ( @@ -722,8 +750,8 @@ pub(crate) fn setup_alice_bob_group( /// Test divergent protocol versions in KeyPackages #[apply(ciphersuites_and_backends)] -fn key_package_version(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - let (mut key_package, _, _) = key_package(ciphersuite, backend); +async fn key_package_version(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { + let (mut key_package, _, _) = key_package(ciphersuite, backend).await; // Set an invalid protocol version key_package.set_version(ProtocolVersion::Mls10Draft11); diff --git a/openmls/src/group/core_group/kat_passive_client.rs b/openmls/src/group/core_group/kat_passive_client.rs index ea3340b317..94810080de 100644 --- a/openmls/src/group/core_group/kat_passive_client.rs +++ b/openmls/src/group/core_group/kat_passive_client.rs @@ -2,10 +2,10 @@ use log::{debug, info, warn}; use openmls_rust_crypto::OpenMlsRustCrypto; use openmls_traits::{crypto::OpenMlsCrypto, key_store::OpenMlsKeyStore, OpenMlsCryptoProvider}; use serde::{self, Deserialize, Serialize}; -use tls_codec::{Deserialize as TlsDeserialize, Serialize as TlsSerialize}; +use tls_codec::Deserialize as TlsDeserialize; use crate::{ - framing::{MlsMessageIn, MlsMessageInBody, MlsMessageOut, ProcessedMessageContent}, + framing::{MlsMessageIn, MlsMessageInBody, ProcessedMessageContent}, group::{config::CryptoConfig, *}, key_packages::*, schedule::psk::PreSharedKeyId, @@ -21,8 +21,6 @@ const TEST_VECTORS_PATH_READ: &[&str] = &[ "test_vectors/passive-client-random.json", "test_vectors/passive-client-handling-commit.json", ]; -const TEST_VECTOR_PATH_WRITE: &[&str] = &["test_vectors/passive-client-welcome-new.json"]; -const NUM_TESTS: usize = 25; /// ```json /// { @@ -94,21 +92,21 @@ pub struct TestEpoch { #[derive(Serialize, Deserialize, Debug, Clone, Default)] pub struct TestProposal(#[serde(with = "hex::serde")] Vec); -#[test] -fn test_read_vectors() { +#[tokio::test] +async fn test_read_vectors() { for file in TEST_VECTORS_PATH_READ { let scenario: Vec = read(file); info!("# {file}"); for (i, test_vector) in scenario.into_iter().enumerate() { info!("## {i:04} START"); - run_test_vector(test_vector); + run_test_vector(test_vector).await; info!("## {i:04} END"); } } } -pub fn run_test_vector(test_vector: PassiveClientWelcomeTestVector) { +pub async fn run_test_vector(test_vector: PassiveClientWelcomeTestVector) { let _ = pretty_env_logger::try_init(); let backend = OpenMlsRustCrypto::default(); @@ -128,24 +126,29 @@ pub fn run_test_vector(test_vector: PassiveClientWelcomeTestVector) { .number_of_resumption_psks(16) .build(); - let mut passive_client = PassiveClient::new(group_config, test_vector.external_psks.clone()); + let mut passive_client = + PassiveClient::new(group_config, test_vector.external_psks.clone()).await; - passive_client.inject_key_package( - test_vector.key_package, - test_vector.signature_priv, - test_vector.encryption_priv, - test_vector.init_priv, - ); + passive_client + .inject_key_package( + test_vector.key_package, + test_vector.signature_priv, + test_vector.encryption_priv, + test_vector.init_priv, + ) + .await; let ratchet_tree: Option = test_vector .ratchet_tree .as_ref() .map(|bytes| RatchetTreeIn::tls_deserialize_exact(bytes.0.as_slice()).unwrap()); - passive_client.join_by_welcome( - MlsMessageIn::tls_deserialize_exact(&test_vector.welcome).unwrap(), - ratchet_tree, - ); + passive_client + .join_by_welcome( + MlsMessageIn::tls_deserialize_exact(&test_vector.welcome).unwrap(), + ratchet_tree, + ) + .await; debug!( "Group ID {}", @@ -163,12 +166,12 @@ pub fn run_test_vector(test_vector: PassiveClientWelcomeTestVector) { for proposal in epoch.proposals { let message = MlsMessageIn::tls_deserialize_exact(&proposal.0).unwrap(); debug!("Proposal: {message:?}"); - passive_client.process_message(message); + passive_client.process_message(message).await; } let message = MlsMessageIn::tls_deserialize_exact(&epoch.commit).unwrap(); debug!("Commit: {message:#?}"); - passive_client.process_message(message); + passive_client.process_message(message).await; assert_eq!( epoch.epoch_authenticator, @@ -177,25 +180,6 @@ pub fn run_test_vector(test_vector: PassiveClientWelcomeTestVector) { } } -#[test] -fn test_write_vectors() { - let mut tests = Vec::new(); - - for _ in 0..NUM_TESTS { - for &ciphersuite in OpenMlsRustCrypto::default() - .crypto() - .supported_ciphersuites() - .iter() - { - let test = generate_test_vector(ciphersuite); - tests.push(test); - } - } - - // TODO(#1279) - write(TEST_VECTOR_PATH_WRITE[0], &tests); -} - struct PassiveClient { backend: OpenMlsRustCrypto, group_config: MlsGroupConfig, @@ -203,7 +187,7 @@ struct PassiveClient { } impl PassiveClient { - fn new(group_config: MlsGroupConfig, psks: Vec) -> Self { + async fn new(group_config: MlsGroupConfig, psks: Vec) -> Self { let backend = OpenMlsRustCrypto::default(); // Load all PSKs into key store. @@ -214,6 +198,7 @@ impl PassiveClient { let psk_id = PreSharedKeyId::external(psk.psk_id, vec![]); psk_id .write_to_key_store(&backend, group_config.crypto_config.ciphersuite, &psk.psk) + .await .unwrap(); } @@ -224,7 +209,7 @@ impl PassiveClient { } } - fn inject_key_package( + async fn inject_key_package( &self, key_package: Vec, _signature_priv: Vec, @@ -257,6 +242,7 @@ impl PassiveClient { .as_slice(), &key_package, ) + .await .unwrap(); // Store init key. @@ -266,6 +252,7 @@ impl PassiveClient { key_package.hpke_init_key().as_slice(), key_package_bundle.private_key(), ) + .await .unwrap(); // Store encryption key @@ -274,10 +261,10 @@ impl PassiveClient { EncryptionPrivateKey::from(encryption_priv), )); - key_pair.write_to_key_store(&self.backend).unwrap(); + key_pair.write_to_key_store(&self.backend).await.unwrap(); } - fn join_by_welcome( + async fn join_by_welcome( &mut self, mls_message_welcome: MlsMessageIn, ratchet_tree: Option, @@ -288,18 +275,20 @@ impl PassiveClient { mls_message_welcome.into_welcome().unwrap(), ratchet_tree, ) + .await .unwrap(); self.group = Some(group); } - fn process_message(&mut self, message: MlsMessageIn) { + async fn process_message(&mut self, message: MlsMessageIn) { println!("{:#?}", message); let processed_message = self .group .as_mut() .unwrap() .process_message(&self.backend, message.into_protocol_message().unwrap()) + .await .unwrap(); match processed_message.into_content() { @@ -314,6 +303,7 @@ impl PassiveClient { .as_mut() .unwrap() .merge_staged_commit(&self.backend, *staged_commit) + .await .unwrap(); } _ => unimplemented!(), @@ -329,257 +319,3 @@ impl PassiveClient { .to_vec() } } - -pub fn generate_test_vector(cipher_suite: Ciphersuite) -> PassiveClientWelcomeTestVector { - let group_config = MlsGroupConfig::builder() - .crypto_config(CryptoConfig::with_default_version(cipher_suite)) - .use_ratchet_tree_extension(true) - .build(); - - let creator_backend = OpenMlsRustCrypto::default(); - - let creator = - generate_group_candidate(b"Alice (Creator)", cipher_suite, &creator_backend, true); - - let mut creator_group = MlsGroup::new( - &creator_backend, - &creator.signature_keypair, - &group_config, - creator - .credential_with_key_and_signer - .credential_with_key - .clone(), - ) - .unwrap(); - - let passive = generate_group_candidate( - b"Bob (Passive Client)", - cipher_suite, - &OpenMlsRustCrypto::default(), - false, - ); - - let (_, mls_message_welcome, _) = creator_group - .add_members( - &creator_backend, - &creator.signature_keypair, - &[passive.key_package.clone()], - ) - .unwrap(); - - creator_group - .merge_pending_commit(&creator_backend) - .unwrap(); - - let initial_epoch_authenticator = creator_group.epoch_authenticator().as_slice().to_vec(); - - let epoch1 = update_inline(&creator_backend, &creator, &mut creator_group); - - let epoch2 = { - let proposals = vec![propose_add( - cipher_suite, - &creator_backend, - &creator, - &mut creator_group, - b"Charlie", - )]; - - let commit = commit(&creator_backend, &creator, &mut creator_group); - - let epoch_authenticator = creator_group.epoch_authenticator().as_slice().to_vec(); - - TestEpoch { - proposals, - commit, - epoch_authenticator, - } - }; - - let epoch3 = { - let proposals = vec![propose_remove( - &creator_backend, - &creator, - &mut creator_group, - b"Charlie", - )]; - - let commit = commit(&creator_backend, &creator, &mut creator_group); - - let epoch_authenticator = creator_group.epoch_authenticator().as_slice().to_vec(); - - TestEpoch { - proposals, - commit, - epoch_authenticator, - } - }; - - let epoch4 = { - let proposals = vec![ - propose_add( - cipher_suite, - &creator_backend, - &creator, - &mut creator_group, - b"Daniel", - ), - propose_add( - cipher_suite, - &creator_backend, - &creator, - &mut creator_group, - b"Evelin", - ), - ]; - - let commit = commit(&creator_backend, &creator, &mut creator_group); - - let epoch_authenticator = creator_group.epoch_authenticator().as_slice().to_vec(); - - TestEpoch { - proposals, - commit, - epoch_authenticator, - } - }; - - let epoch5 = { - let proposals = vec![ - propose_remove(&creator_backend, &creator, &mut creator_group, b"Daniel"), - propose_add( - cipher_suite, - &creator_backend, - &creator, - &mut creator_group, - b"Fardi", - ), - ]; - - let commit = commit(&creator_backend, &creator, &mut creator_group); - - let epoch_authenticator = creator_group.epoch_authenticator().as_slice().to_vec(); - - TestEpoch { - proposals, - commit, - epoch_authenticator, - } - }; - - let epoch6 = { - let proposals = vec![ - propose_remove(&creator_backend, &creator, &mut creator_group, b"Fardi"), - propose_remove(&creator_backend, &creator, &mut creator_group, b"Evelin"), - ]; - - let commit = commit(&creator_backend, &creator, &mut creator_group); - - let epoch_authenticator = creator_group.epoch_authenticator().as_slice().to_vec(); - - TestEpoch { - proposals, - commit, - epoch_authenticator, - } - }; - - let epochs = vec![epoch1, epoch2, epoch3, epoch4, epoch5, epoch6]; - - PassiveClientWelcomeTestVector { - cipher_suite: cipher_suite.into(), - external_psks: vec![], - - key_package: MlsMessageOut::from(passive.key_package) - .tls_serialize_detached() - .unwrap(), - - signature_priv: passive.signature_keypair.private().to_vec(), - encryption_priv: passive.encryption_keypair.private_key().key().to_vec(), - init_priv: passive.init_keypair.private.to_vec(), - - welcome: mls_message_welcome.tls_serialize_detached().unwrap(), - ratchet_tree: None, - initial_epoch_authenticator, - - epochs, - } -} - -// ------------------------------------------------------------------------------------------------- - -fn propose_add( - cipher_suite: Ciphersuite, - backend: &OpenMlsRustCrypto, - candidate: &GroupCandidate, - group: &mut MlsGroup, - add_identity: &[u8], -) -> TestProposal { - let add_candidate = generate_group_candidate( - add_identity, - cipher_suite, - &OpenMlsRustCrypto::default(), - false, - ); - - let mls_message_out_proposal = group - .propose_add_member( - backend, - &candidate.signature_keypair, - &add_candidate.key_package, - ) - .unwrap(); - group.merge_pending_commit(backend).unwrap(); - - TestProposal(mls_message_out_proposal.tls_serialize_detached().unwrap()) -} - -fn propose_remove( - backend: &OpenMlsRustCrypto, - candidate: &GroupCandidate, - group: &mut MlsGroup, - remove_identity: &[u8], -) -> TestProposal { - let remove = group - .members() - .find(|Member { credential, .. }| credential.identity() == remove_identity) - .unwrap() - .index; - - let mls_message_out_proposal = group - .propose_remove_member(backend, &candidate.signature_keypair, remove) - .unwrap(); - - TestProposal(mls_message_out_proposal.tls_serialize_detached().unwrap()) -} - -fn commit(backend: &OpenMlsRustCrypto, creator: &GroupCandidate, group: &mut MlsGroup) -> Vec { - let (mls_message_out_commit, _, _) = group - .commit_to_pending_proposals(backend, &creator.signature_keypair) - .unwrap(); - group.merge_pending_commit(backend).unwrap(); - - mls_message_out_commit.tls_serialize_detached().unwrap() -} - -fn update_inline( - backend: &OpenMlsRustCrypto, - candidate: &GroupCandidate, - group: &mut MlsGroup, -) -> TestEpoch { - let (mls_message_out_commit, _, _) = group - .self_update(backend, &candidate.signature_keypair) - .unwrap(); - group.merge_pending_commit(backend).unwrap(); - - let proposals = vec![]; - - let commit = mls_message_out_commit.tls_serialize_detached().unwrap(); - - let epoch_authenticator = group.epoch_authenticator().as_slice().to_vec(); - - TestEpoch { - proposals, - commit, - epoch_authenticator, - } -} diff --git a/openmls/src/group/core_group/kat_welcome.rs b/openmls/src/group/core_group/kat_welcome.rs index 1ba32e7d88..423a960756 100644 --- a/openmls/src/group/core_group/kat_welcome.rs +++ b/openmls/src/group/core_group/kat_welcome.rs @@ -67,13 +67,13 @@ pub struct WelcomeTestVector { welcome: Vec, } -#[test] -fn test_read_vectors() { +#[tokio::test] +async fn test_read_vectors() { let test_vectors: Vec = read(TEST_VECTOR_PATH_READ); for (i, test_vector) in test_vectors.into_iter().enumerate() { println!("# {i:04}"); - match run_test_vector(test_vector) { + match run_test_vector(test_vector).await { Ok(_) => {} Err(e) => panic!("Error while checking messages test vector.\n{e:?}"), } @@ -104,7 +104,7 @@ fn test_read_vectors() { // unimplemented!() // } -pub fn run_test_vector(test_vector: WelcomeTestVector) -> Result<(), &'static str> { +pub async fn run_test_vector(test_vector: WelcomeTestVector) -> Result<(), &'static str> { let _ = pretty_env_logger::formatted_builder() .is_test(true) .try_init(); @@ -172,6 +172,7 @@ pub fn run_test_vector(test_vector: WelcomeTestVector) -> Result<(), &'static st key_package.hash_ref(backend.crypto()).unwrap().as_slice(), &key_package, ) + .await .unwrap(); backend @@ -180,6 +181,7 @@ pub fn run_test_vector(test_vector: WelcomeTestVector) -> Result<(), &'static st key_package.hpke_init_key().as_slice(), key_package_bundle.private_key(), ) + .await .unwrap(); // Verification: @@ -210,9 +212,11 @@ pub fn run_test_vector(test_vector: WelcomeTestVector) -> Result<(), &'static st let psk_secret = { let resumption_psk_store = ResumptionPskStore::new(1024); - let psks = load_psks(backend.key_store(), &resumption_psk_store, &[]).unwrap(); + let psks = load_psks(backend.key_store(), &resumption_psk_store, &[]) + .await + .unwrap(); - PskSecret::new(&backend, cipher_suite, psks).unwrap() + PskSecret::new(&backend, cipher_suite, psks).await.unwrap() }; let mut key_schedule = KeySchedule::init( diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index ffec5c1b4a..0fbc432ee2 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -76,12 +76,8 @@ use crate::{ versions::ProtocolVersion, }; -#[cfg(test)] -use super::errors::CreateGroupContextExtProposalError; #[cfg(test)] use crate::treesync::node::leaf_node::TreePosition; -#[cfg(test)] -use std::io::{Error, Read, Write}; #[derive(Debug)] pub(crate) struct CreateCommitResult { @@ -216,7 +212,7 @@ impl CoreGroupBuilder { /// /// This function performs cryptographic operations and there requires an /// [`OpenMlsCryptoProvider`]. - pub(crate) fn build( + pub(crate) async fn build( self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -252,9 +248,9 @@ impl CoreGroupBuilder { // Prepare the PskSecret let psk_secret = { - let psks = load_psks(backend.key_store(), &resumption_psk_store, &self.psk_ids)?; + let psks = load_psks(backend.key_store(), &resumption_psk_store, &self.psk_ids).await?; - PskSecret::new(backend, ciphersuite, psks)? + PskSecret::new(backend, ciphersuite, psks).await? }; let mut key_schedule = KeySchedule::init(ciphersuite, backend, &joiner_secret, psk_secret)?; @@ -296,6 +292,7 @@ impl CoreGroupBuilder { // Store the private key of the own leaf in the key store as an epoch keypair. group .store_epoch_keypairs(backend, &[leaf_keypair]) + .await .map_err(CoreGroupBuildError::KeyStoreError)?; Ok(group) @@ -423,7 +420,7 @@ impl CoreGroup { framing_parameters: FramingParameters, extensions: Extensions, signer: &impl Signer, - ) -> Result { + ) -> Result { // Ensure that the group supports all the extensions that are wanted. let required_extension = extensions @@ -558,19 +555,22 @@ impl CoreGroup { .group_epoch_secrets() .external_secret() .derive_external_keypair(backend.crypto(), self.ciphersuite()) + .map_err(LibraryError::unexpected_crypto_error)? .public; - Extension::ExternalPub(ExternalPubExtension::new(HpkePublicKey::from(external_pub))) + Ok(Extension::ExternalPub(ExternalPubExtension::new( + HpkePublicKey::from(external_pub), + ))) }; if with_ratchet_tree { - Extensions::from_vec(vec![ratchet_tree_extension(), external_pub_extension()]) + Extensions::from_vec(vec![ratchet_tree_extension(), external_pub_extension()?]) .map_err(|_| { LibraryError::custom( "There should not have been duplicate extensions here.", ) })? } else { - Extensions::single(external_pub_extension()) + Extensions::single(external_pub_extension()?) } }; @@ -603,13 +603,13 @@ impl CoreGroup { /// Loads the state from persisted state #[cfg(test)] - pub(crate) fn load(reader: R) -> Result { + pub(crate) fn load(reader: impl std::io::Read) -> Result { serde_json::from_reader(reader).map_err(|e| e.into()) } /// Persists the state #[cfg(test)] - pub(crate) fn save(&self, writer: &mut W) -> Result<(), Error> { + pub(crate) fn save(&self, writer: &mut impl std::io::Write) -> Result<(), std::io::Error> { let serialized_core_group = serde_json::to_string_pretty(self)?; writer.write_all(&serialized_core_group.into_bytes()) } @@ -754,7 +754,7 @@ impl CoreGroup { /// indexed by this group's [`GroupId`] and [`GroupEpoch`]. /// /// Returns an error if access to the key store fails. - pub(super) fn store_epoch_keypairs( + pub(super) async fn store_epoch_keypairs( &self, backend: &impl OpenMlsCryptoProvider, keypair_references: &[EncryptionKeyPair], @@ -767,13 +767,14 @@ impl CoreGroup { backend .key_store() .store(&k.0, &keypair_references.to_vec()) + .await } /// Read the [`EncryptionKeyPair`]s of this group and its current /// [`GroupEpoch`] from the `backend`'s key store. /// /// Returns `None` if access to the key store fails. - pub(super) fn read_epoch_keypairs( + pub(super) async fn read_epoch_keypairs( &self, backend: &impl OpenMlsCryptoProvider, ) -> Vec { @@ -785,6 +786,7 @@ impl CoreGroup { backend .key_store() .read::>(&k.0) + .await .unwrap_or_default() } @@ -792,7 +794,7 @@ impl CoreGroup { /// the `backend`'s key store. /// /// Returns an error if access to the key store fails. - pub(super) fn delete_previous_epoch_keypairs( + pub(super) async fn delete_previous_epoch_keypairs( &self, backend: &impl OpenMlsCryptoProvider, ) -> Result<(), KeyStore::Error> { @@ -801,12 +803,15 @@ impl CoreGroup { self.context().epoch().as_u64() - 1, self.own_leaf_index(), ); - backend.key_store().delete::>(&k.0) + backend + .key_store() + .delete::>(&k.0) + .await } - pub(crate) fn create_commit( + pub(crate) async fn create_commit( &self, - mut params: CreateCommitParams, + mut params: CreateCommitParams<'_>, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, ) -> Result> { @@ -944,9 +949,10 @@ impl CoreGroup { backend.key_store(), &self.resumption_psk_store, &apply_proposals_values.presharedkeys, - )?; + ) + .await?; - PskSecret::new(backend, ciphersuite, psks)? + PskSecret::new(backend, ciphersuite, psks).await? }; // Create key schedule @@ -986,6 +992,7 @@ impl CoreGroup { let external_pub = provisional_epoch_secrets .external_secret() .derive_external_keypair(backend.crypto(), ciphersuite) + .map_err(LibraryError::unexpected_crypto_error)? .public; let external_pub_extension = Extension::ExternalPub(ExternalPubExtension::new(external_pub.into())); diff --git a/openmls/src/group/core_group/new_from_external_init.rs b/openmls/src/group/core_group/new_from_external_init.rs index 854e98e978..7fec0ca333 100644 --- a/openmls/src/group/core_group/new_from_external_init.rs +++ b/openmls/src/group/core_group/new_from_external_init.rs @@ -24,10 +24,10 @@ impl CoreGroup { /// /// Note: If there is a group member in the group with the same identity as us, /// this will create a remove proposal. - pub(crate) fn join_by_external_commit( + pub(crate) async fn join_by_external_commit( backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, - mut params: CreateCommitParams, + mut params: CreateCommitParams<'_>, ratchet_tree: Option, verifiable_group_info: VerifiableGroupInfo, ) -> Result { @@ -121,7 +121,7 @@ impl CoreGroup { .build(); // Immediately create the commit to add ourselves to the group. - let create_commit_result = group.create_commit(params, backend, signer); + let create_commit_result = group.create_commit(params, backend, signer).await; debug_assert!( create_commit_result.is_ok(), "Error creating commit {create_commit_result:?}" diff --git a/openmls/src/group/core_group/new_from_welcome.rs b/openmls/src/group/core_group/new_from_welcome.rs index dc9af3f374..c1c6c0b4fe 100644 --- a/openmls/src/group/core_group/new_from_welcome.rs +++ b/openmls/src/group/core_group/new_from_welcome.rs @@ -13,7 +13,7 @@ use crate::{ impl CoreGroup { // Join a group from a welcome message - pub fn new_from_welcome( + pub async fn new_from_welcome( welcome: Welcome, ratchet_tree: Option, key_package_bundle: KeyPackageBundle, @@ -29,9 +29,12 @@ impl CoreGroup { backend, key_package_bundle.key_package.leaf_node().encryption_key(), ) + .await .ok_or(WelcomeError::NoMatchingEncryptionKey)?; + leaf_keypair .delete_from_key_store(backend) + .await .map_err(|_| WelcomeError::NoMatchingEncryptionKey)?; let ciphersuite = welcome.ciphersuite(); @@ -67,9 +70,10 @@ impl CoreGroup { backend.key_store(), &resumption_psk_store, &group_secrets.psks, - )?; + ) + .await?; - PskSecret::new(backend, ciphersuite, psks)? + PskSecret::new(backend, ciphersuite, psks).await? }; // Create key schedule @@ -239,6 +243,7 @@ impl CoreGroup { }; group .store_epoch_keypairs(backend, group_keypairs.as_slice()) + .await .map_err(WelcomeError::KeyStoreError)?; Ok(group) diff --git a/openmls/src/group/core_group/process.rs b/openmls/src/group/core_group/process.rs index c8a00f3ebd..846e0880df 100644 --- a/openmls/src/group/core_group/process.rs +++ b/openmls/src/group/core_group/process.rs @@ -38,7 +38,7 @@ impl CoreGroup { /// - ValSem243 /// - ValSem244 /// - ValSem246 (as part of ValSem010) - pub(crate) fn process_unverified_message( + pub(crate) async fn process_unverified_message( &self, backend: &impl OpenMlsCryptoProvider, unverified_message: UnverifiedMessage, @@ -76,13 +76,15 @@ impl CoreGroup { } } FramedContentBody::Commit(_) => { - let staged_commit = self.stage_commit( - &content, - proposal_store, - old_epoch_keypairs, - leaf_node_keypairs, - backend, - )?; + let staged_commit = self + .stage_commit( + &content, + proposal_store, + old_epoch_keypairs, + leaf_node_keypairs, + backend, + ) + .await?; ProcessedMessageContent::StagedCommitMessage(Box::new(staged_commit)) } }; @@ -166,7 +168,7 @@ impl CoreGroup { /// - ValSem244 /// - ValSem245 /// - ValSem246 (as part of ValSem010) - pub(crate) fn process_message( + pub(crate) async fn process_message( &mut self, backend: &impl OpenMlsCryptoProvider, message: impl Into, @@ -192,7 +194,8 @@ impl CoreGroup { // If this is a commit, we need to load the private key material we need for decryption. let (old_epoch_keypairs, leaf_node_keypairs) = if let ContentType::Commit = unverified_message.content_type() { - self.read_decryption_keypairs(backend, own_leaf_nodes)? + self.read_decryption_keypairs(backend, own_leaf_nodes) + .await? } else { (vec![], vec![]) }; @@ -204,6 +207,7 @@ impl CoreGroup { old_epoch_keypairs, leaf_node_keypairs, ) + .await } /// Performs framing validation and, if necessary, decrypts the given message. @@ -263,30 +267,31 @@ impl CoreGroup { } /// Helper function to read decryption keypairs. - pub(super) fn read_decryption_keypairs( + pub(super) async fn read_decryption_keypairs( &self, backend: &impl OpenMlsCryptoProvider, own_leaf_nodes: &[LeafNode], ) -> Result<(Vec, Vec), StageCommitError> { // All keys from the previous epoch are potential decryption keypairs. - let old_epoch_keypairs = self.read_epoch_keypairs(backend); + let old_epoch_keypairs = self.read_epoch_keypairs(backend).await; // If we are processing an update proposal that originally came from // us, the keypair corresponding to the leaf in the update is also a // potential decryption keypair. - let leaf_node_keypairs = own_leaf_nodes - .iter() - .map(|leaf_node| { + let mut leaf_node_keypairs = Vec::with_capacity(own_leaf_nodes.len()); + for leaf_node in own_leaf_nodes { + leaf_node_keypairs.push( EncryptionKeyPair::read_from_key_store(backend, leaf_node.encryption_key()) - .ok_or(StageCommitError::MissingDecryptionKey) - }) - .collect::, StageCommitError>>()?; + .await + .ok_or(StageCommitError::MissingDecryptionKey)?, + ); + } Ok((old_epoch_keypairs, leaf_node_keypairs)) } /// Merge a [StagedCommit] into the group after inspection - pub(crate) fn merge_staged_commit( + pub(crate) async fn merge_staged_commit( &mut self, backend: &impl OpenMlsCryptoProvider, staged_commit: StagedCommit, @@ -298,7 +303,7 @@ impl CoreGroup { let leaves = self.public_group().members().collect(); // Merge the staged commit into the group state and store the secret tree from the // previous epoch in the message secrets store. - if let Some(message_secrets) = self.merge_commit(backend, staged_commit)? { + if let Some(message_secrets) = self.merge_commit(backend, staged_commit).await? { self.message_secrets_store .add(past_epoch, message_secrets, leaves); } diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index 8fa01e2ada..05ac541000 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -11,7 +11,7 @@ use crate::{ }; impl CoreGroup { - fn derive_epoch_secrets( + async fn derive_epoch_secrets( &self, backend: &impl OpenMlsCryptoProvider, apply_proposals_values: ApplyProposalsValues, @@ -28,7 +28,9 @@ impl CoreGroup { let external_priv = epoch_secrets .external_secret() .derive_external_keypair(backend.crypto(), self.ciphersuite()) - .private; + .map_err(LibraryError::unexpected_crypto_error)? + .private + .into(); let init_secret = InitSecret::from_kem_output( backend, self.ciphersuite(), @@ -59,9 +61,9 @@ impl CoreGroup { backend.key_store(), &self.resumption_psk_store, &apply_proposals_values.presharedkeys, - )?; + ).await?; - PskSecret::new(backend, self.ciphersuite(), psks)? + PskSecret::new(backend, self.ciphersuite(), psks).await? }; // Create key schedule @@ -111,7 +113,7 @@ impl CoreGroup { /// - ValSem244 /// Returns an error if the given commit was sent by the owner of this /// group. - pub(crate) fn stage_commit( + pub(crate) async fn stage_commit( &self, mls_content: &AuthenticatedContent, proposal_store: &ProposalStore, @@ -228,7 +230,8 @@ impl CoreGroup { self.group_epoch_secrets(), commit_secret, &serialized_provisional_group_context, - )? + ) + .await? .split_secrets( serialized_provisional_group_context, diff.tree_size(), @@ -271,14 +274,14 @@ impl CoreGroup { /// /// This function should not fail and only returns a [`Result`], because it /// might throw a `LibraryError`. - pub(crate) fn merge_commit( + pub(crate) async fn merge_commit( &mut self, backend: &impl OpenMlsCryptoProvider, staged_commit: StagedCommit, ) -> Result, MergeCommitError> { // Get all keypairs from the old epoch, so we can later store the ones // that are still relevant in the new epoch. - let old_epoch_keypairs = self.read_epoch_keypairs(backend); + let old_epoch_keypairs = self.read_epoch_keypairs(backend).await; match staged_commit.state { StagedCommitState::PublicState(staged_diff) => { self.public_group.merge_diff(*staged_diff); @@ -310,6 +313,7 @@ impl CoreGroup { let new_owned_encryption_keys = self .public_group() .owned_encryption_keys(self.own_leaf_index()); + // From the old and new keys, keep the ones that are still relevant in the new epoch. let epoch_keypairs: Vec = old_epoch_keypairs .into_iter() @@ -318,7 +322,6 @@ impl CoreGroup { .filter(|keypair| new_owned_encryption_keys.contains(keypair.public_key())) .collect(); // We should have private keys for all owned encryption keys. - debug_assert_eq!(new_owned_encryption_keys.len(), epoch_keypairs.len()); if new_owned_encryption_keys.len() != epoch_keypairs.len() { return Err(LibraryError::custom( @@ -326,15 +329,21 @@ impl CoreGroup { ) .into()); } + // Store the relevant keys under the new epoch self.store_epoch_keypairs(backend, epoch_keypairs.as_slice()) + .await .map_err(MergeCommitError::KeyStoreError)?; + // Delete the old keys. self.delete_previous_epoch_keypairs(backend) + .await .map_err(MergeCommitError::KeyStoreError)?; + if let Some(keypair) = state.new_leaf_keypair_option { keypair .delete_from_key_store(backend) + .await .map_err(MergeCommitError::KeyStoreError)?; } @@ -346,15 +355,16 @@ impl CoreGroup { #[cfg(test)] /// Helper function that reads the decryption keys from the key store /// (unwrapping the result) and stages the given commit. - pub(crate) fn read_keys_and_stage_commit( + pub(crate) async fn read_keys_and_stage_commit( &self, mls_content: &AuthenticatedContent, proposal_store: &ProposalStore, own_leaf_nodes: &[LeafNode], backend: &impl OpenMlsCryptoProvider, ) -> Result { - let (old_epoch_keypairs, leaf_node_keypairs) = - self.read_decryption_keypairs(backend, own_leaf_nodes)?; + let (old_epoch_keypairs, leaf_node_keypairs) = self + .read_decryption_keypairs(backend, own_leaf_nodes) + .await?; self.stage_commit( mls_content, @@ -363,6 +373,7 @@ impl CoreGroup { leaf_node_keypairs, backend, ) + .await } } diff --git a/openmls/src/group/core_group/test_core_group.rs b/openmls/src/group/core_group/test_core_group.rs index 83a3c5d4b5..2f3fd4be97 100644 --- a/openmls/src/group/core_group/test_core_group.rs +++ b/openmls/src/group/core_group/test_core_group.rs @@ -16,7 +16,7 @@ use crate::{ treesync::{errors::ApplyUpdatePathError, node::leaf_node::TreeInfoTbs}, }; -pub(crate) fn setup_alice_group( +pub(crate) async fn setup_alice_group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) -> ( @@ -31,7 +31,8 @@ pub(crate) fn setup_alice_group( b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let pk = OpenMlsSignaturePublicKey::new( alice_signature_keys.to_public_vec().into(), ciphersuite.signature_algorithm(), @@ -45,13 +46,17 @@ pub(crate) fn setup_alice_group( alice_credential_with_key.clone(), ) .build(backend, &alice_signature_keys) + .await .expect("Error creating group."); (group, alice_credential_with_key, alice_signature_keys, pk) } #[apply(ciphersuites_and_backends)] -fn test_core_group_persistence(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - let (alice_group, _, _, _) = setup_alice_group(ciphersuite, backend); +async fn test_core_group_persistence( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let (alice_group, _, _, _) = setup_alice_group(ciphersuite, backend).await; let mut file_out = tempfile::NamedTempFile::new().expect("Could not create file"); alice_group @@ -78,7 +83,7 @@ pub fn flip_last_byte(ctxt: &mut HpkeCiphertext) { } #[apply(ciphersuites_and_backends)] -fn test_failed_groupinfo_decryption( +async fn test_failed_groupinfo_decryption( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -97,14 +102,16 @@ fn test_failed_groupinfo_decryption( b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let key_package_bundle = KeyPackageBundle::new( backend, &alice_signature_keys, ciphersuite, alice_credential_with_key, - ); + ) + .await; let group_info_tbs = { let group_context = GroupContext::new( @@ -129,12 +136,15 @@ fn test_failed_groupinfo_decryption( let welcome_nonce = AeadNonce::random(backend); // Generate receiver key pair. - let receiver_key_pair = backend.crypto().derive_hpke_keypair( - ciphersuite.hpke_config(), - Secret::random(ciphersuite, backend, None) - .expect("Not enough randomness.") - .as_slice(), - ); + let receiver_key_pair = backend + .crypto() + .derive_hpke_keypair( + ciphersuite.hpke_config(), + Secret::random(ciphersuite, backend, None) + .expect("Not enough randomness.") + .as_slice(), + ) + .unwrap(); let hpke_context = b"group info welcome test info"; let group_secrets = b"these should be the group secrets"; let mut encrypted_group_secrets = hpke::encrypt_with_label( @@ -184,18 +194,19 @@ fn test_failed_groupinfo_decryption( backend, ResumptionPskStore::new(1024), ) + .await .expect_err("Creation of core group from a broken Welcome was successful."); - assert_eq!( + assert!(matches!( error, WelcomeError::GroupSecrets(GroupSecretsError::DecryptionFailed) - ) + )) } /// Test what happens if the KEM ciphertext for the receiver in the UpdatePath /// is broken. #[apply(ciphersuites_and_backends)] -fn test_update_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_update_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // === Alice creates a group with her and Bob === let ( framing_parameters, @@ -204,7 +215,7 @@ fn test_update_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid group_bob, bob_signature_keys, _bob_credential_with_key, - ) = test_framing::setup_alice_bob_group(ciphersuite, backend); + ) = test_framing::setup_alice_bob_group(ciphersuite, backend).await; // === Bob updates and commits === let bob_old_leaf = group_bob.own_leaf_node().unwrap(); @@ -215,6 +226,7 @@ fn test_update_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid backend, &bob_signature_keys, ) + .await .unwrap(); let update_proposal_bob = group_bob @@ -239,6 +251,7 @@ fn test_update_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid .build(); let create_commit_result = group_bob .create_commit(params, backend, &bob_signature_keys) + .await .expect("An unexpected error occurred."); // Now we break Alice's HPKE ciphertext in Bob's commit by breaking @@ -285,15 +298,16 @@ fn test_update_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid broken_plaintext.confirmation_tag() ); - let staged_commit_res = - group_alice.read_keys_and_stage_commit(&broken_plaintext, &proposal_store, &[], backend); + let staged_commit_res = group_alice + .read_keys_and_stage_commit(&broken_plaintext, &proposal_store, &[], backend) + .await; assert_eq!( staged_commit_res.expect_err("Successful processing of a broken commit."), StageCommitError::UpdatePathError(ApplyUpdatePathError::UnableToDecrypt) ); } -fn setup_alice_bob( +async fn setup_alice_bob( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) -> ( @@ -308,17 +322,19 @@ fn setup_alice_bob( b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let (bob_credential_with_key, bob_signer) = test_utils::new_credential( backend, b"Bob", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; // Generate Bob's KeyPackage let bob_key_package_bundle = - KeyPackageBundle::new(backend, &bob_signer, ciphersuite, bob_credential_with_key); + KeyPackageBundle::new(backend, &bob_signer, ciphersuite, bob_credential_with_key).await; ( alice_credential_with_key, @@ -330,7 +346,7 @@ fn setup_alice_bob( // Test several scenarios when PSKs are used in a group #[apply(ciphersuites_and_backends)] -fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Basic group setup. let group_aad = b"Alice's test group"; let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); @@ -340,7 +356,7 @@ fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { alice_signature_keys, bob_key_package_bundle, bob_signature_keys, - ) = setup_alice_bob(ciphersuite, backend); + ) = setup_alice_bob(ciphersuite, backend).await; // === Alice creates a group with a PSK === let psk_id = vec![1u8, 2, 3]; @@ -353,6 +369,7 @@ fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { .expect("An unexpected error occured."); preshared_key_id .write_to_key_store(backend, ciphersuite, secret.as_slice()) + .await .unwrap(); let mut alice_group = CoreGroup::builder( GroupId::random(backend), @@ -361,6 +378,7 @@ fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { ) .with_psk(vec![preshared_key_id.clone()]) .build(backend, &alice_signature_keys) + .await .expect("Error creating group."); // === Alice creates a PSK proposal === @@ -394,12 +412,14 @@ fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { .build(); let create_commit_result = alice_group .create_commit(params, backend, &alice_signature_keys) + .await .expect("Error creating commit"); log::info!(" >>> Staging & merging commit ..."); alice_group .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging pending commit"); let ratchet_tree = alice_group.public_group().export_ratchet_tree(); @@ -412,6 +432,7 @@ fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { backend, ResumptionPskStore::new(1024), ) + .await .expect("Could not create new group from Welcome"); // === Bob updates and commits === @@ -423,6 +444,7 @@ fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { backend, &bob_signature_keys, ) + .await .unwrap(); let update_proposal_bob = group_bob @@ -447,18 +469,22 @@ fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { .build(); let _create_commit_result = group_bob .create_commit(params, backend, &bob_signature_keys) + .await .expect("An unexpected error occurred."); } // Test several scenarios when PSKs are used in a group #[apply(ciphersuites_and_backends)] -fn test_staged_commit_creation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_staged_commit_creation( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { // Basic group setup. let group_aad = b"Alice's test group"; let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); let (alice_credential_with_key, alice_signature_keys, bob_key_package_bundle, _) = - setup_alice_bob(ciphersuite, backend); + setup_alice_bob(ciphersuite, backend).await; // === Alice creates a group === let mut alice_group = CoreGroup::builder( @@ -467,6 +493,7 @@ fn test_staged_commit_creation(ciphersuite: Ciphersuite, backend: &impl OpenMlsC alice_credential_with_key, ) .build(backend, &alice_signature_keys) + .await .expect("Error creating group."); // === Alice adds Bob === @@ -488,11 +515,13 @@ fn test_staged_commit_creation(ciphersuite: Ciphersuite, backend: &impl OpenMlsC .build(); let create_commit_result = alice_group .create_commit(params, backend, &alice_signature_keys) + .await .expect("Error creating commit"); // === Alice merges her own commit === alice_group .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error processing own staged commit"); // === Bob joins the group using Alice's tree === @@ -505,6 +534,7 @@ fn test_staged_commit_creation(ciphersuite: Ciphersuite, backend: &impl OpenMlsC backend, ResumptionPskStore::new(1024), ) + .await .expect("An unexpected error occurred."); // Let's make sure we end up in the same group state. @@ -520,7 +550,10 @@ fn test_staged_commit_creation(ciphersuite: Ciphersuite, backend: &impl OpenMlsC // Test processing of own commits #[apply(ciphersuites_and_backends)] -fn test_own_commit_processing(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_own_commit_processing( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { // Basic group setup. let group_aad = b"Alice's test group"; let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); @@ -531,7 +564,8 @@ fn test_own_commit_processing(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; // === Alice creates a group === let alice_group = CoreGroup::builder( @@ -540,6 +574,7 @@ fn test_own_commit_processing(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr alice_credential_with_key, ) .build(backend, &alice_signature_keys) + .await .expect("Error creating group."); let proposal_store = ProposalStore::default(); @@ -551,16 +586,18 @@ fn test_own_commit_processing(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr .build(); let create_commit_result = alice_group .create_commit(params, backend, &alice_signature_keys) + .await .expect("error creating commit"); // Alice attempts to process her own commit let error = alice_group .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect_err("no error while processing own commit"); assert_eq!(error, StageCommitError::OwnCommit); } -pub(crate) fn setup_client( +pub(crate) async fn setup_client( id: &str, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -575,7 +612,8 @@ pub(crate) fn setup_client( id.as_bytes(), CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let pk = OpenMlsSignaturePublicKey::new( signature_keys.to_public_vec().into(), ciphersuite.signature_algorithm(), @@ -588,12 +626,13 @@ pub(crate) fn setup_client( &signature_keys, ciphersuite, credential_with_key.clone(), - ); + ) + .await; (credential_with_key, key_package_bundle, signature_keys, pk) } #[apply(ciphersuites_and_backends)] -fn test_proposal_application_after_self_was_removed( +async fn test_proposal_application_after_self_was_removed( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -608,9 +647,9 @@ fn test_proposal_application_after_self_was_removed( let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); let (alice_credential_with_key, _, alice_signature_keys, _pk) = - setup_client("Alice", ciphersuite, backend); - let (_, bob_kpb, _, _) = setup_client("Bob", ciphersuite, backend); - let (_, charlie_kpb, _, _) = setup_client("Charlie", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; + let (_, bob_kpb, _, _) = setup_client("Bob", ciphersuite, backend).await; + let (_, charlie_kpb, _, _) = setup_client("Charlie", ciphersuite, backend).await; let mut alice_group = CoreGroup::builder( GroupId::random(backend), @@ -618,6 +657,7 @@ fn test_proposal_application_after_self_was_removed( alice_credential_with_key, ) .build(backend, &alice_signature_keys) + .await .expect("Error creating CoreGroup."); // Adding Bob @@ -641,10 +681,12 @@ fn test_proposal_application_after_self_was_removed( .build(); let add_commit_result = alice_group .create_commit(params, backend, &alice_signature_keys) + .await .expect("Error creating commit"); alice_group .merge_commit(backend, add_commit_result.staged_commit) + .await .expect("error merging pending commit"); let ratchet_tree = alice_group.public_group().export_ratchet_tree(); @@ -658,6 +700,7 @@ fn test_proposal_application_after_self_was_removed( backend, ResumptionPskStore::new(1024), ) + .await .expect("Error joining group."); // Alice adds Charlie and removes Bob in the same commit. @@ -709,6 +752,7 @@ fn test_proposal_application_after_self_was_removed( .build(); let remove_add_commit_result = alice_group .create_commit(params, backend, &alice_signature_keys) + .await .expect("Error creating commit"); let staged_commit = bob_group @@ -718,13 +762,16 @@ fn test_proposal_application_after_self_was_removed( &[], backend, ) + .await .expect("error staging commit"); bob_group .merge_commit(backend, staged_commit) + .await .expect("Error merging commit."); alice_group .merge_commit(backend, remove_add_commit_result.staged_commit) + .await .expect("Error merging commit."); let ratchet_tree = alice_group.public_group().export_ratchet_tree(); @@ -738,6 +785,7 @@ fn test_proposal_application_after_self_was_removed( backend, ResumptionPskStore::new(1024), ) + .await .expect("Error joining group."); // We can now check that Bob correctly processed his and applied the changes diff --git a/openmls/src/group/core_group/test_create_commit_params.rs b/openmls/src/group/core_group/test_create_commit_params.rs index 8e64b41c8f..0b1eac4d52 100644 --- a/openmls/src/group/core_group/test_create_commit_params.rs +++ b/openmls/src/group/core_group/test_create_commit_params.rs @@ -4,7 +4,7 @@ use super::*; // Tests that the builder for CreateCommitParams works as expected #[apply(backends)] -fn build_create_commit_params(backend: &impl OpenMlsCryptoProvider) { +async fn build_create_commit_params(backend: &impl OpenMlsCryptoProvider) { let _ = backend; let framing_parameters: FramingParameters = FramingParameters::new(&[1, 2, 3], WireFormat::PrivateMessage); diff --git a/openmls/src/group/core_group/test_external_init.rs b/openmls/src/group/core_group/test_external_init.rs index 2ac5381a72..a303855d44 100644 --- a/openmls/src/group/core_group/test_external_init.rs +++ b/openmls/src/group/core_group/test_external_init.rs @@ -18,7 +18,7 @@ use openmls_traits::{types::Ciphersuite, OpenMlsCryptoProvider}; use super::{proposals::ProposalStore, CoreGroup}; #[apply(ciphersuites_and_backends)] -fn test_external_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_external_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ( framing_parameters, mut group_alice, @@ -26,11 +26,11 @@ fn test_external_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv mut group_bob, bob_signer, bob_credential_with_key, - ) = setup_alice_bob_group(ciphersuite, backend); + ) = setup_alice_bob_group(ciphersuite, backend).await; // Now set up Charlie and try to init externally. let (charlie_credential, _charlie_kpb, charlie_signer, _charlie_pk) = - setup_client("Charlie", ciphersuite, backend); + setup_client("Charlie", ciphersuite, backend).await; // Have Alice export everything that Charly needs. let verifiable_group_info = group_alice @@ -51,6 +51,7 @@ fn test_external_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv None, verifiable_group_info, ) + .await .expect("Error initializing group externally."); // Have alice and bob process the commit resulting from external init. @@ -58,21 +59,26 @@ fn test_external_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv let staged_commit = group_alice .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("error staging commit"); group_alice .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); let staged_commit = group_bob .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("error staging commit"); group_bob .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); // Have charly process their own staged commit group_charly .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own external commit"); assert_eq!( @@ -93,17 +99,21 @@ fn test_external_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv .build(); let create_commit_result = group_charly .create_commit(params, backend, &charlie_signer) + .await .expect("Error creating commit"); let staged_commit = group_alice .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("error staging commit"); group_alice .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); group_charly .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging commit"); // Now we assume that Bob somehow lost his group state and wants to add @@ -129,6 +139,7 @@ fn test_external_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv Some(ratchet_tree.into()), verifiable_group_info, ) + .await .expect("Error initializing group externally."); // Let's make sure there's a remove in the commit. @@ -152,21 +163,26 @@ fn test_external_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv let proposal_store = ProposalStore::default(); let staged_commit = group_alice .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("error staging commit"); group_alice .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); let staged_commit = group_charly .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("error staging commit"); group_charly .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); // Have Bob process his own staged commit new_group_bob .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own external commit"); assert_eq!( @@ -181,12 +197,12 @@ fn test_external_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv } #[apply(ciphersuites_and_backends)] -fn test_external_init_single_member_group( +async fn test_external_init_single_member_group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { let (mut group_alice, _alice_credential_with_key, alice_signer, _alice_pk) = - setup_alice_group(ciphersuite, backend); + setup_alice_group(ciphersuite, backend).await; // Framing parameters let group_aad = b"Alice's test group"; @@ -194,7 +210,7 @@ fn test_external_init_single_member_group( // Now set up charly and try to init externally. let (charly_credential, _charly_kpb, charly_signer, _charly_pk) = - setup_client("Charly", ciphersuite, backend); + setup_client("Charly", ciphersuite, backend).await; // Have Alice export everything that Charly needs. let verifiable_group_info = group_alice @@ -216,19 +232,23 @@ fn test_external_init_single_member_group( Some(ratchet_tree.into()), verifiable_group_info, ) + .await .expect("Error initializing group externally."); // Have alice and bob process the commit resulting from external init. let proposal_store = ProposalStore::default(); let staged_commit = group_alice .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("error staging commit"); group_alice .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); group_charly .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own external commit"); assert_eq!( @@ -243,7 +263,7 @@ fn test_external_init_single_member_group( } #[apply(ciphersuites_and_backends)] -fn test_external_init_broken_signature( +async fn test_external_init_broken_signature( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -254,11 +274,11 @@ fn test_external_init_broken_signature( _group_bob, _bob_signer, _bob_credential_with_key, - ) = setup_alice_bob_group(ciphersuite, backend); + ) = setup_alice_bob_group(ciphersuite, backend).await; // Now set up charly and try to init externally. let (_charlie_credential, _charlie_kpb, charlie_signer, _charlie_pk) = - setup_client("Charlie", ciphersuite, backend); + setup_client("Charlie", ciphersuite, backend).await; let verifiable_group_info = { let mut verifiable_group_info = group_alice @@ -283,6 +303,7 @@ fn test_external_init_broken_signature( None, verifiable_group_info ) + .await .expect_err("Signature was corrupted. This should have failed.") ); } diff --git a/openmls/src/group/core_group/test_past_secrets.rs b/openmls/src/group/core_group/test_past_secrets.rs index f254da097b..60969eedab 100644 --- a/openmls/src/group/core_group/test_past_secrets.rs +++ b/openmls/src/group/core_group/test_past_secrets.rs @@ -6,7 +6,7 @@ use crate::{ }; #[apply(ciphersuites_and_backends)] -fn test_secret_tree_store(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_secret_tree_store(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Create a store that keeps up to 3 epochs let mut message_secrets_store = MessageSecretsStore::new_with_secret( 3, @@ -45,7 +45,7 @@ fn test_secret_tree_store(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto } #[apply(ciphersuites_and_backends)] -fn test_empty_secret_tree_store(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_empty_secret_tree_store(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Create a store that keeps no epochs let mut message_secrets_store = MessageSecretsStore::new_with_secret( 0, diff --git a/openmls/src/group/core_group/test_proposals.rs b/openmls/src/group/core_group/test_proposals.rs index f0f9cb224c..5cd6e139e7 100644 --- a/openmls/src/group/core_group/test_proposals.rs +++ b/openmls/src/group/core_group/test_proposals.rs @@ -30,18 +30,18 @@ use crate::{ /// used in `create_commit` to filter the epoch proposals. Expected result: /// `filtered_queued_proposals` returns only proposals of a certain type #[apply(ciphersuites_and_backends)] -fn proposal_queue_functions(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn proposal_queue_functions(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Framing parameters let framing_parameters = FramingParameters::new(&[], WireFormat::PublicMessage); // Define identities let (alice_credential, alice_key_package_bundle, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; let (_bob_credential_with_key, bob_key_package_bundle, _bob_signer, _bob_pk) = - setup_client("Bob", ciphersuite, backend); + setup_client("Bob", ciphersuite, backend).await; let bob_key_package = bob_key_package_bundle.key_package(); let alice_update_key_package_bundle = - KeyPackageBundle::new(backend, &alice_signer, ciphersuite, alice_credential); + KeyPackageBundle::new(backend, &alice_signer, ciphersuite, alice_credential).await; let alice_update_key_package = alice_update_key_package_bundle.key_package(); let kpi = KeyPackageIn::from(alice_update_key_package.clone()); assert!(kpi @@ -174,18 +174,18 @@ fn proposal_queue_functions(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp /// Test, that we QueuedProposalQueue is iterated in the right order. #[apply(ciphersuites_and_backends)] -fn proposal_queue_order(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn proposal_queue_order(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Framing parameters let framing_parameters = FramingParameters::new(&[], WireFormat::PublicMessage); // Define identities let (alice_credential, alice_key_package_bundle, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; let (_bob_credential_with_key, bob_key_package_bundle, _bob_signer, _bob_pk) = - setup_client("Bob", ciphersuite, backend); + setup_client("Bob", ciphersuite, backend).await; let bob_key_package = bob_key_package_bundle.key_package(); let alice_update_key_package_bundle = - KeyPackageBundle::new(backend, &alice_signer, ciphersuite, alice_credential); + KeyPackageBundle::new(backend, &alice_signer, ciphersuite, alice_credential).await; let alice_update_key_package = alice_update_key_package_bundle.key_package(); let kpi = KeyPackageIn::from(alice_update_key_package.clone()); assert!(kpi @@ -282,12 +282,12 @@ fn proposal_queue_order(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr } #[apply(ciphersuites_and_backends)] -fn test_required_unsupported_proposals( +async fn test_required_unsupported_proposals( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { let (alice_credential, _, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; // Set required capabilities let extensions = &[]; @@ -304,17 +304,18 @@ fn test_required_unsupported_proposals( ) .with_required_capabilities(required_capabilities) .build(backend, &alice_signer) + .await .expect_err( "CoreGroup creation must fail because AppAck proposals aren't supported in OpenMLS yet.", ); - assert_eq!( + assert!(matches!( e, CoreGroupBuildError::PublicGroupBuildError(PublicGroupBuildError::UnsupportedProposalType) - ) + )) } #[apply(ciphersuites_and_backends)] -fn test_required_extension_key_package_mismatch( +async fn test_required_extension_key_package_mismatch( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -323,9 +324,9 @@ fn test_required_extension_key_package_mismatch( let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); let (alice_credential, _, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; let (_bob_credential_with_key, bob_key_package_bundle, _, _) = - setup_client("Bob", ciphersuite, backend); + setup_client("Bob", ciphersuite, backend).await; let bob_key_package = bob_key_package_bundle.key_package(); // Set required capabilities @@ -350,6 +351,7 @@ fn test_required_extension_key_package_mismatch( ) .with_required_capabilities(required_capabilities) .build(backend, &alice_signer) + .await .expect("Error creating CoreGroup."); let e = alice_group @@ -366,15 +368,18 @@ fn test_required_extension_key_package_mismatch( } #[apply(ciphersuites_and_backends)] -fn test_group_context_extensions(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_group_context_extensions( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { // Basic group setup. let group_aad = b"Alice's test group"; let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); let (alice_credential, _, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; let (_bob_credential_with_key, bob_key_package_bundle, _, _) = - setup_client("Bob", ciphersuite, backend); + setup_client("Bob", ciphersuite, backend).await; let bob_key_package = bob_key_package_bundle.key_package(); @@ -397,6 +402,7 @@ fn test_group_context_extensions(ciphersuite: Ciphersuite, backend: &impl OpenMl ) .with_required_capabilities(required_capabilities) .build(backend, &alice_signer) + .await .expect("Error creating CoreGroup."); let bob_add_proposal = alice_group @@ -415,12 +421,14 @@ fn test_group_context_extensions(ciphersuite: Ciphersuite, backend: &impl OpenMl .build(); let create_commit_result = alice_group .create_commit(params, backend, &alice_signer) + .await .expect("Error creating commit"); log::info!(" >>> Staging & merging commit ..."); alice_group .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own staged commit"); let ratchet_tree = alice_group.public_group().export_ratchet_tree(); @@ -435,11 +443,12 @@ fn test_group_context_extensions(ciphersuite: Ciphersuite, backend: &impl OpenMl backend, ResumptionPskStore::new(1024), ) + .await .expect("Error joining group."); } #[apply(ciphersuites_and_backends)] -fn test_group_context_extension_proposal_fails( +async fn test_group_context_extension_proposal_fails( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -448,9 +457,9 @@ fn test_group_context_extension_proposal_fails( let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); let (alice_credential, _, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; let (_bob_credential_with_key, bob_key_package_bundle, _, _) = - setup_client("Bob", ciphersuite, backend); + setup_client("Bob", ciphersuite, backend).await; let bob_key_package = bob_key_package_bundle.key_package(); @@ -471,6 +480,7 @@ fn test_group_context_extension_proposal_fails( ) .with_required_capabilities(required_capabilities) .build(backend, &alice_signer) + .await .expect("Error creating CoreGroup."); // TODO: openmls/openmls#1130 add a test for unsupported required capabilities. @@ -512,12 +522,14 @@ fn test_group_context_extension_proposal_fails( .build(); let create_commit_result = alice_group .create_commit(params, backend, &alice_signer) + .await .expect("Error creating commit"); log::info!(" >>> Staging & merging commit ..."); alice_group .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging pending commit"); let ratchet_tree = alice_group.public_group().export_ratchet_tree(); @@ -530,6 +542,7 @@ fn test_group_context_extension_proposal_fails( backend, ResumptionPskStore::new(1024), ) + .await .expect("Error joining group."); // TODO: openmls/openmls#1130 re-enable @@ -552,7 +565,7 @@ fn test_group_context_extension_proposal_fails( } #[apply(ciphersuites_and_backends)] -fn test_group_context_extension_proposal( +async fn test_group_context_extension_proposal( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -561,9 +574,9 @@ fn test_group_context_extension_proposal( let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); let (alice_credential, _, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; let (_bob_credential_with_key, bob_key_package_bundle, _, _) = - setup_client("Bob", ciphersuite, backend); + setup_client("Bob", ciphersuite, backend).await; let bob_key_package = bob_key_package_bundle.key_package(); @@ -573,6 +586,7 @@ fn test_group_context_extension_proposal( alice_credential, ) .build(backend, &alice_signer) + .await .expect("Error creating CoreGroup."); // Adding Bob @@ -592,12 +606,14 @@ fn test_group_context_extension_proposal( .build(); let create_commit_results = alice_group .create_commit(params, backend, &alice_signer) + .await .expect("Error creating commit"); log::info!(" >>> Staging & merging commit ..."); alice_group .merge_commit(backend, create_commit_results.staged_commit) + .await .expect("error merging pending commit"); let ratchet_tree = alice_group.public_group().export_ratchet_tree(); @@ -611,6 +627,7 @@ fn test_group_context_extension_proposal( backend, ResumptionPskStore::new(1024), ) + .await .expect("Error joining group."); // Alice adds a required capability. @@ -640,19 +657,23 @@ fn test_group_context_extension_proposal( .build(); let create_commit_result = alice_group .create_commit(params, backend, &alice_signer) + .await .expect("Error creating commit"); log::info!(" >>> Staging & merging commit ..."); let staged_commit = bob_group .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("error staging commit"); bob_group .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); alice_group .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging pending commit"); assert_eq!( diff --git a/openmls/src/group/mls_group/creation.rs b/openmls/src/group/mls_group/creation.rs index 9c8d2c303c..e79e8d77c1 100644 --- a/openmls/src/group/mls_group/creation.rs +++ b/openmls/src/group/mls_group/creation.rs @@ -21,7 +21,7 @@ impl MlsGroup { /// /// This function removes the private key corresponding to the /// `key_package` from the key store. - pub fn new( + pub async fn new( backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, mls_group_config: &MlsGroupConfig, @@ -34,10 +34,11 @@ impl MlsGroup { GroupId::random(backend), credential_with_key, ) + .await } /// Creates a new group with a given group ID with the creator as the only member. - pub fn new_with_group_id( + pub async fn new_with_group_id( backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, mls_group_config: &MlsGroupConfig, @@ -60,6 +61,7 @@ impl MlsGroup { .with_max_past_epoch_secrets(mls_group_config.max_past_epochs) .with_lifetime(*mls_group_config.lifetime()) .build(backend, signer) + .await .map_err(|e| match e { CoreGroupBuildError::LibraryError(e) => e.into(), // We don't support PSKs yet @@ -103,7 +105,7 @@ impl MlsGroup { /// ([`WelcomeError::NoMatchingKeyPackage`]) if no [`KeyPackage`] /// can be found. // TODO: #1326 This should take an MlsMessage rather than a Welcome message. - pub fn new_from_welcome( + pub async fn new_from_welcome( backend: &impl OpenMlsCryptoProvider, mls_group_config: &MlsGroupConfig, welcome: Welcome, @@ -111,22 +113,24 @@ impl MlsGroup { ) -> Result> { let resumption_psk_store = ResumptionPskStore::new(mls_group_config.number_of_resumption_psks); - let (key_package, _) = welcome - .secrets() - .iter() - .find_map(|egs| { - let hash_ref = egs.new_member().as_slice().to_vec(); - backend - .key_store() - .read(&hash_ref) - .map(|kp: KeyPackage| (kp, hash_ref)) - }) - .ok_or(WelcomeError::NoMatchingKeyPackage)?; + + let mut key_package: Option = None; + for egs in welcome.secrets().iter() { + if let Some(kp) = backend.key_store().read(egs.new_member().as_slice()).await { + key_package.replace(kp); + break; + } + } + + let Some(key_package) = key_package.take() else { + return Err(WelcomeError::NoMatchingKeyPackage); + }; // TODO #751 let private_key = backend .key_store() .read::(key_package.hpke_init_key().as_slice()) + .await .ok_or(WelcomeError::NoMatchingKeyPackage)?; let key_package_bundle = KeyPackageBundle { key_package, @@ -138,6 +142,7 @@ impl MlsGroup { key_package_bundle .key_package .delete(backend) + .await .map_err(WelcomeError::KeyStoreError)?; let mut group = CoreGroup::new_from_welcome( @@ -146,7 +151,8 @@ impl MlsGroup { key_package_bundle, backend, resumption_psk_store, - )?; + ) + .await?; group.set_max_past_epochs(mls_group_config.max_past_epochs); let mls_group = MlsGroup { @@ -176,7 +182,7 @@ impl MlsGroup { /// /// Note: If there is a group member in the group with the same identity as us, /// this will create a remove proposal. - pub fn join_by_external_commit( + pub async fn join_by_external_commit( backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, ratchet_tree: Option, @@ -200,7 +206,8 @@ impl MlsGroup { params, ratchet_tree, verifiable_group_info, - )?; + ) + .await?; group.set_max_past_epochs(mls_group_config.max_past_epochs); let mls_group = MlsGroup { diff --git a/openmls/src/group/mls_group/membership.rs b/openmls/src/group/mls_group/membership.rs index 3401168657..9df7482db8 100644 --- a/openmls/src/group/mls_group/membership.rs +++ b/openmls/src/group/mls_group/membership.rs @@ -29,7 +29,7 @@ impl MlsGroup { /// Returns an error if there is a pending commit. // FIXME: #1217 #[allow(clippy::type_complexity)] - pub fn add_members( + pub async fn add_members( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -59,7 +59,7 @@ impl MlsGroup { .proposal_store(&self.proposal_store) .inline_proposals(inline_proposals) .build(); - let create_commit_result = self.group.create_commit(params, backend, signer)?; + let create_commit_result = self.group.create_commit(params, backend, signer).await?; let welcome = match create_commit_result.welcome_option { Some(welcome) => welcome, @@ -108,7 +108,7 @@ impl MlsGroup { /// Returns an error if there is a pending commit. // FIXME: #1217 #[allow(clippy::type_complexity)] - pub fn remove_members( + pub async fn remove_members( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -138,7 +138,8 @@ impl MlsGroup { .proposal_store(&self.proposal_store) .inline_proposals(inline_proposals) .build(); - let create_commit_result = self.group.create_commit(params, backend, signer)?; + + let create_commit_result = self.group.create_commit(params, backend, signer).await?; // Convert PublicMessage messages to MLSMessage and encrypt them if required by // the configuration diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 275ea2529f..bb4254572d 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -295,18 +295,22 @@ impl MlsGroup { // === Load & save === /// Loads the state from persisted state. - pub fn load(group_id: &GroupId, backend: &impl OpenMlsCryptoProvider) -> Option { - backend.key_store().read(group_id.as_slice()) + pub async fn load( + group_id: &GroupId, + backend: &impl OpenMlsCryptoProvider, + ) -> Option { + backend.key_store().read(group_id.as_slice()).await } /// Persists the state. - pub fn save( + pub async fn save( &mut self, backend: &impl OpenMlsCryptoProvider, ) -> Result<(), KeyStore::Error> { backend .key_store() - .store(self.group_id().as_slice(), &*self)?; + .store(self.group_id().as_slice(), &*self) + .await?; self.state_changed = InnerState::Persisted; Ok(()) diff --git a/openmls/src/group/mls_group/processing.rs b/openmls/src/group/mls_group/processing.rs index 4651e4ebaf..2c193c0995 100644 --- a/openmls/src/group/mls_group/processing.rs +++ b/openmls/src/group/mls_group/processing.rs @@ -23,7 +23,7 @@ impl MlsGroup { /// # Errors: /// Returns an [`ProcessMessageError`] when the validation checks fail /// with the exact reason of the failure. - pub fn process_message( + pub async fn process_message( &mut self, backend: &impl OpenMlsCryptoProvider, message: impl Into, @@ -54,13 +54,16 @@ impl MlsGroup { // Parse the message let sender_ratchet_configuration = self.configuration().sender_ratchet_configuration().clone(); - self.group.process_message( - backend, - message, - &sender_ratchet_configuration, - &self.proposal_store, - &self.own_leaf_nodes, - ) + + self.group + .process_message( + backend, + message, + &sender_ratchet_configuration, + &self.proposal_store, + &self.own_leaf_nodes, + ) + .await } /// Stores a standalone proposal in the internal [ProposalStore] @@ -81,7 +84,7 @@ impl MlsGroup { /// and `Welcome` are MlsMessages of the type [`MlsMessageOut`]. // FIXME: #1217 #[allow(clippy::type_complexity)] - pub fn commit_to_pending_proposals( + pub async fn commit_to_pending_proposals( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -97,7 +100,7 @@ impl MlsGroup { .framing_parameters(self.framing_parameters()) .proposal_store(&self.proposal_store) .build(); - let create_commit_result = self.group.create_commit(params, backend, signer)?; + let create_commit_result = self.group.create_commit(params, backend, signer).await?; // Convert PublicMessage messages to MLSMessage and encrypt them if required by // the configuration @@ -123,7 +126,7 @@ impl MlsGroup { /// Merge a [StagedCommit] into the group after inspection. As this advances /// the epoch of the group, it also clears any pending commits. - pub fn merge_staged_commit( + pub async fn merge_staged_commit( &mut self, backend: &impl OpenMlsCryptoProvider, staged_commit: StagedCommit, @@ -138,7 +141,8 @@ impl MlsGroup { // Merge staged commit self.group - .merge_staged_commit(backend, staged_commit, &mut self.proposal_store)?; + .merge_staged_commit(backend, staged_commit, &mut self.proposal_store) + .await?; // Extract and store the resumption psk for the current epoch let resumption_psk = self.group.group_epoch_secrets().resumption_psk(); @@ -157,7 +161,7 @@ impl MlsGroup { /// Merges the pending [`StagedCommit`] if there is one, and /// clears the field by setting it to `None`. - pub fn merge_pending_commit( + pub async fn merge_pending_commit( &mut self, backend: &impl OpenMlsCryptoProvider, ) -> Result<(), MergePendingCommitError> { @@ -165,7 +169,8 @@ impl MlsGroup { MlsGroupState::PendingCommit(_) => { let old_state = mem::replace(&mut self.group_state, MlsGroupState::Operational); if let MlsGroupState::PendingCommit(pending_commit_state) = old_state { - self.merge_staged_commit(backend, (*pending_commit_state).into())?; + self.merge_staged_commit(backend, (*pending_commit_state).into()) + .await?; } Ok(()) } diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index 7caa0738a7..5e1755fb8e 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -122,7 +122,7 @@ impl MlsGroup { ); /// Generate a proposal - pub fn propose( + pub async fn propose( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -142,9 +142,11 @@ impl MlsGroup { Propose::Update(leaf_node) => match ref_or_value { ProposalOrRefType::Proposal => self .propose_self_update_by_value(backend, signer, leaf_node) + .await .map_err(|e| e.into()), ProposalOrRefType::Reference => self .propose_self_update(backend, signer, leaf_node) + .await .map_err(|e| e.into()), }, diff --git a/openmls/src/group/mls_group/test_mls_group.rs b/openmls/src/group/mls_group/test_mls_group.rs index ad8af55125..fec6b237d2 100644 --- a/openmls/src/group/mls_group/test_mls_group.rs +++ b/openmls/src/group/mls_group/test_mls_group.rs @@ -15,11 +15,14 @@ use crate::{ }; #[apply(ciphersuites_and_backends)] -fn test_mls_group_persistence(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_mls_group_persistence( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { let group_id = GroupId::from_slice(b"Test Group"); let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::test_default(ciphersuite); @@ -32,6 +35,7 @@ fn test_mls_group_persistence(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr group_id.clone(), alice_credential_with_key, ) + .await .expect("An unexpected error occurred."); // Check the internal state has changed @@ -39,10 +43,12 @@ fn test_mls_group_persistence(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr alice_group .save(backend) + .await .expect("Could not write group state to file"); - let alice_group_deserialized = - MlsGroup::load(&group_id, backend).expect("Could not deserialize MlsGroup"); + let alice_group_deserialized = MlsGroup::load(&group_id, backend) + .await + .expect("Could not deserialize MlsGroup"); assert_eq!( ( @@ -59,14 +65,15 @@ fn test_mls_group_persistence(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr // This tests if the remover is correctly passed to the callback when one member // issues a RemoveProposal and another members issues the next Commit. #[apply(ciphersuites_and_backends)] -fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); - let (_bob_credential, bob_kpb, bob_signer, _bob_pk) = setup_client("Bob", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; + let (_bob_credential, bob_kpb, bob_signer, _bob_pk) = + setup_client("Bob", ciphersuite, backend).await; let (_charlie_credential, charlie_kpb, charlie_signer, _charlie_pk) = - setup_client("Charly", ciphersuite, backend); + setup_client("Charly", ciphersuite, backend).await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfigBuilder::new() @@ -81,15 +88,18 @@ fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { group_id, alice_credential_with_key, ) + .await .expect("An unexpected error occurred."); // === Alice adds Bob === let (_queued_message, welcome, _group_info) = alice_group .add_members(backend, &alice_signer, &[bob_kpb.key_package().clone()]) + .await .expect("Could not add member to group."); alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); let mut bob_group = MlsGroup::new_from_welcome( @@ -98,11 +108,13 @@ fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { welcome.into_welcome().expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // === Bob adds Charlie === let (queued_messages, welcome, _group_info) = bob_group .add_members(backend, &bob_signer, &[charlie_kpb.key_package().clone()]) + .await .unwrap(); let alice_processed_message = alice_group @@ -112,12 +124,14 @@ fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process messages."); if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = alice_processed_message.into_content() { alice_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging commit."); } else { unreachable!("Expected a StagedCommit."); @@ -125,6 +139,7 @@ fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { bob_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); let mut charlie_group = MlsGroup::new_from_welcome( @@ -133,6 +148,7 @@ fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { welcome.into_welcome().expect("Unexpected message type."), Some(bob_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // === Alice removes Bob & Charlie commits === @@ -148,6 +164,7 @@ fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process messages."); // Check that we received the correct proposals @@ -175,6 +192,7 @@ fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Charlie commits let (_queued_messages, _welcome, _group_info) = charlie_group .commit_to_pending_proposals(backend, &charlie_signer) + .await .expect("Could not commit proposal"); // Check that we receive the correct proposal @@ -193,17 +211,18 @@ fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { charlie_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // TODO #524: Check that Alice removed Bob } #[apply(ciphersuites_and_backends)] -fn export_secret(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn export_secret(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::test_default(ciphersuite); @@ -216,6 +235,7 @@ fn export_secret(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) group_id, alice_credential_with_key, ) + .await .expect("An unexpected error occurred."); assert!( @@ -237,7 +257,7 @@ fn export_secret(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) } #[apply(ciphersuites_and_backends)] -fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Some basic setup functions for the MlsGroup. let mls_group_config = MlsGroupConfig::test_default(ciphersuite); @@ -246,10 +266,12 @@ fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto mls_group_config, number_of_clients, CodecUse::StructMessages, - ); + ) + .await; // Create a basic group with more than 4 members to create a tree with intermediate nodes. let group_id = setup .create_random_group(10, ciphersuite) + .await .expect("An unexpected error occurred."); let mut groups = setup.groups.write().expect("An unexpected error occurred."); let group = groups @@ -270,6 +292,7 @@ fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto let (mls_message, _welcome_option, _group_info) = client .self_update(Commit, &group_id, None) + .await .expect("error creating self update"); // Store the context and membership key so that we can re-compute the membership tag later. @@ -310,6 +333,7 @@ fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto // We're the "no_client" id to prevent the original sender from treating // this message as his own and merging the pending commit. .distribute_to_members("no_client".as_bytes(), group, &msg_invalid_signature.into()) + .await .expect_err("No error when distributing message with invalid signature."); assert_eq!( @@ -323,6 +347,7 @@ fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto // We're the "no_client" id to prevent the original sender from treating // this message as his own and merging the pending commit. .distribute_to_members("no_client".as_bytes(), group, &msg_invalid_sender.into()) + .await .expect_err("No error when distributing message with invalid signature."); assert_eq!( @@ -334,12 +359,13 @@ fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto } #[apply(ciphersuites_and_backends)] -fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); - let (_bob_credential, bob_kpb, bob_signer, _bob_pk) = setup_client("Bob", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; + let (_bob_credential, bob_kpb, bob_signer, _bob_pk) = + setup_client("Bob", ciphersuite, backend).await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::test_default(ciphersuite); @@ -352,6 +378,7 @@ fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry group_id, alice_credential_with_key, ) + .await .expect("An unexpected error occurred."); // There should be no pending commit after group creation. @@ -366,6 +393,7 @@ fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry let alice_processed_message = alice_group .process_message(backend, proposal.into_protocol_message().unwrap()) + .await .expect("Could not process messages."); assert!(alice_group.pending_commit().is_none()); @@ -383,6 +411,7 @@ fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry println!("\nCreating commit with add proposal."); let (_msg, _welcome_option, _group_info) = alice_group .self_update(backend, &alice_signer) + .await .expect("error creating self-update commit"); println!("Done creating commit."); @@ -393,53 +422,58 @@ fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry // should fail. let error = alice_group .add_members(backend, &alice_signer, &[bob_key_package.clone()]) + .await .expect_err("no error committing while a commit is pending"); - assert_eq!( + assert!(matches!( error, AddMembersError::GroupStateError(MlsGroupStateError::PendingCommit) - ); + )); let error = alice_group .propose_add_member(backend, &alice_signer, bob_key_package) .expect_err("no error creating a proposal while a commit is pending"); - assert_eq!( + assert!(matches!( error, ProposeAddMemberError::GroupStateError(MlsGroupStateError::PendingCommit) - ); + )); let error = alice_group .remove_members(backend, &alice_signer, &[LeafNodeIndex::new(1)]) + .await .expect_err("no error committing while a commit is pending"); - assert_eq!( + assert!(matches!( error, RemoveMembersError::GroupStateError(MlsGroupStateError::PendingCommit) - ); + )); let error = alice_group .propose_remove_member(backend, &alice_signer, LeafNodeIndex::new(1)) .expect_err("no error creating a proposal while a commit is pending"); - assert_eq!( + assert!(matches!( error, ProposeRemoveMemberError::GroupStateError(MlsGroupStateError::PendingCommit) - ); + )); let error = alice_group .commit_to_pending_proposals(backend, &alice_signer) + .await .expect_err("no error committing while a commit is pending"); - assert_eq!( + assert!(matches!( error, CommitToPendingProposalsError::GroupStateError(MlsGroupStateError::PendingCommit) - ); + )); let error = alice_group .self_update(backend, &alice_signer) + .await .expect_err("no error committing while a commit is pending"); - assert_eq!( + assert!(matches!( error, SelfUpdateError::GroupStateError(MlsGroupStateError::PendingCommit) - ); + )); let error = alice_group .propose_self_update(backend, &alice_signer, None) + .await .expect_err("no error creating a proposal while a commit is pending"); - assert_eq!( + assert!(matches!( error, ProposeSelfUpdateError::GroupStateError(MlsGroupStateError::PendingCommit) - ); + )); // Clearing the pending commit should actually clear it. alice_group.clear_pending_commit(); @@ -448,12 +482,14 @@ fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry // Creating a new commit should commit the same proposals. let (_msg, welcome_option, _group_info) = alice_group .self_update(backend, &alice_signer) + .await .expect("error creating self-update commit"); // Merging the pending commit should clear the pending commit and we should // end up in the same state as bob. alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); assert!(alice_group.pending_commit().is_none()); @@ -466,6 +502,7 @@ fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry .expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("error creating group from welcome"); assert_eq!( @@ -480,14 +517,17 @@ fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry // While a commit is pending, merging Bob's commit should clear the pending commit. let (_msg, _welcome_option, _group_info) = alice_group .self_update(backend, &alice_signer) + .await .expect("error creating self-update commit"); let (msg, _welcome_option, _group_info) = bob_group .self_update(backend, &bob_signer) + .await .expect("error creating self-update commit"); let alice_processed_message = alice_group .process_message(backend, msg.into_protocol_message().unwrap()) + .await .expect("Could not process messages."); assert!(alice_group.pending_commit().is_some()); @@ -496,6 +536,7 @@ fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry { alice_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging commit."); } else { unreachable!("Expected a StagedCommit."); @@ -506,13 +547,13 @@ fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry // Test that the key package and the corresponding private key are deleted when // creating a new group for a welcome message. #[apply(ciphersuites_and_backends)] -fn key_package_deletion(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn key_package_deletion(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; let (_bob_credential_with_key, bob_kpb, _bob_signer, _bob_pk) = - setup_client("Bob", ciphersuite, backend); + setup_client("Bob", ciphersuite, backend).await; let bob_key_package = bob_kpb.key_package(); // Define the MlsGroup configuration @@ -528,14 +569,16 @@ fn key_package_deletion(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr group_id, alice_credential_with_key, ) + .await .expect("An unexpected error occurred."); // === Alice adds Bob === let (_queued_message, welcome, _group_info) = alice_group .add_members(backend, &alice_signer, &[bob_key_package.clone()]) + .await .unwrap(); - alice_group.merge_pending_commit(backend).unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); // === Bob joins the group === let _bob_group = MlsGroup::new_from_welcome( @@ -544,12 +587,13 @@ fn key_package_deletion(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr welcome.into_welcome().expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // TEST: The private key must be gone from the key store. assert!(backend .key_store() - .read::(bob_key_package.hpke_init_key().as_slice()) + .read::(bob_key_package.hpke_init_key().as_slice()).await .is_none(), "The HPKE private key is still in the key store after creating a new group from the key package."); @@ -563,22 +607,23 @@ fn key_package_deletion(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .unwrap() .as_slice() ) + .await .is_none(), "The key package is still in the key store after creating a new group from it." ); } #[apply(ciphersuites_and_backends)] -fn remove_prosposal_by_ref(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn remove_prosposal_by_ref(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; let (_bob_credential_with_key, bob_kpb, _bob_signer, _bob_pk) = - setup_client("Bob", ciphersuite, backend); + setup_client("Bob", ciphersuite, backend).await; let bob_key_package = bob_kpb.key_package().clone(); let (_charlie_credential_with_key, charlie_kpb, _charlie_signer, _charlie_pk) = - setup_client("Charlie", ciphersuite, backend); + setup_client("Charlie", ciphersuite, backend).await; let charlie_key_package = charlie_kpb.key_package(); // Define the MlsGroup configuration @@ -594,19 +639,22 @@ fn remove_prosposal_by_ref(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypt group_id, alice_credential_with_key, ) + .await .expect("An unexpected error occurred."); // alice adds bob and bob processes the welcome let (_, welcome, _) = alice_group .add_members(backend, &alice_signer, &[bob_key_package]) + .await .unwrap(); - alice_group.merge_pending_commit(backend).unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); let mut bob_group = MlsGroup::new_from_welcome( backend, &mls_group_config, welcome.into_welcome().unwrap(), Some(alice_group.export_ratchet_tree().into()), ) + .await .unwrap(); // alice proposes to add charlie let (_, reference) = alice_group @@ -627,9 +675,11 @@ fn remove_prosposal_by_ref(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypt // the commit should have no proposal let (commit, _, _) = alice_group .commit_to_pending_proposals(backend, &alice_signer) + .await .unwrap(); let msg = bob_group .process_message(backend, MlsMessageIn::from(commit)) + .await .unwrap(); match msg.into_content() { ProcessedMessageContent::StagedCommitMessage(commit) => { diff --git a/openmls/src/group/mls_group/updates.rs b/openmls/src/group/mls_group/updates.rs index 69cdbe333c..0e78269fbc 100644 --- a/openmls/src/group/mls_group/updates.rs +++ b/openmls/src/group/mls_group/updates.rs @@ -21,7 +21,7 @@ impl MlsGroup { /// commit. // FIXME: #1217 #[allow(clippy::type_complexity)] - pub fn self_update( + pub async fn self_update( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -37,7 +37,7 @@ impl MlsGroup { .build(); // Create Commit over all proposals. // TODO #751 - let create_commit_result = self.group.create_commit(params, backend, signer)?; + let create_commit_result = self.group.create_commit(params, backend, signer).await?; // Convert PublicMessage messages to MLSMessage and encrypt them if required by // the configuration @@ -64,7 +64,7 @@ impl MlsGroup { /// Creates a proposal to update the own leaf node. Optionally, a /// [`LeafNode`] can be provided to update the leaf node. Note that its /// private key must be manually added to the key store. - fn _propose_self_udpate( + pub(crate) async fn _propose_self_update( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -102,6 +102,7 @@ impl MlsGroup { // TODO #1207: Move to the top of the function. keypair .write_to_key_store(backend) + .await .map_err(ProposeSelfUpdateError::KeyStoreError)?; }; @@ -117,13 +118,15 @@ impl MlsGroup { } /// Creates a proposal to update the own leaf node. - pub fn propose_self_update( + pub async fn propose_self_update( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, leaf_node: Option, ) -> Result<(MlsMessageOut, ProposalRef), ProposeSelfUpdateError> { - let update_proposal = self._propose_self_udpate(backend, signer, leaf_node)?; + let update_proposal = self + ._propose_self_update(backend, signer, leaf_node) + .await?; let proposal = QueuedProposal::from_authenticated_content_by_ref( self.ciphersuite(), backend, @@ -141,13 +144,15 @@ impl MlsGroup { } /// Creates a proposal to update the own leaf node. - pub fn propose_self_update_by_value( + pub async fn propose_self_update_by_value( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, leaf_node: Option, ) -> Result<(MlsMessageOut, ProposalRef), ProposeSelfUpdateError> { - let update_proposal = self._propose_self_udpate(backend, signer, leaf_node)?; + let update_proposal = self + ._propose_self_update(backend, signer, leaf_node) + .await?; let proposal = QueuedProposal::from_authenticated_content_by_value( self.ciphersuite(), backend, diff --git a/openmls/src/group/mod.rs b/openmls/src/group/mod.rs index c916455e79..3f26c201f8 100644 --- a/openmls/src/group/mod.rs +++ b/openmls/src/group/mod.rs @@ -9,8 +9,6 @@ use std::fmt::Display; #[cfg(test)] use crate::ciphersuite::*; use crate::extensions::*; -#[cfg(test)] -use crate::utils::*; use openmls_traits::OpenMlsCryptoProvider; use serde::{Deserialize, Serialize}; diff --git a/openmls/src/group/public_group/tests.rs b/openmls/src/group/public_group/tests.rs index 6ccfe1f171..3cea877edc 100644 --- a/openmls/src/group/public_group/tests.rs +++ b/openmls/src/group/public_group/tests.rs @@ -19,14 +19,15 @@ use crate::{ use super::PublicGroup; #[apply(ciphersuites_and_backends)] -fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend); - let (_bob_credential, bob_kpb, bob_signer, _bob_pk) = setup_client("Bob", ciphersuite, backend); + setup_client("Alice", ciphersuite, backend).await; + let (_bob_credential, bob_kpb, bob_signer, _bob_pk) = + setup_client("Bob", ciphersuite, backend).await; let (_charlie_credential, charlie_kpb, charlie_signer, _charlie_pk) = - setup_client("Charly", ciphersuite, backend); + setup_client("Charly", ciphersuite, backend).await; // Define the MlsGroup configuration // Set plaintext wire format policy s.t. the public group can track changes. @@ -43,6 +44,7 @@ fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) group_id, alice_credential_with_key, ) + .await .expect("An unexpected error occurred."); // === Create a public group that tracks the changes throughout this test === @@ -63,10 +65,12 @@ fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) // === Alice adds Bob === let (message, welcome, _group_info) = alice_group .add_members(backend, &alice_signer, &[bob_kpb.key_package().clone()]) + .await .expect("Could not add member to group."); alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); let public_message = match message.into_protocol_message().unwrap() { @@ -98,11 +102,13 @@ fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) welcome.into_welcome().expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // === Bob adds Charlie === let (queued_messages, welcome, _group_info) = bob_group .add_members(backend, &bob_signer, &[charlie_kpb.key_package().clone()]) + .await .unwrap(); // Alice processes @@ -114,12 +120,14 @@ fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process messages."); if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = alice_processed_message.into_content() { alice_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging commit."); } else { unreachable!("Expected a StagedCommit."); @@ -134,6 +142,7 @@ fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) // Bob merges bob_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); let mut charlie_group = MlsGroup::new_from_welcome( @@ -142,6 +151,7 @@ fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) welcome.into_welcome().expect("Unexpected message type."), Some(bob_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // === Alice removes Bob & Charlie commits === @@ -158,6 +168,7 @@ fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process messages."); // The public group processes @@ -203,6 +214,7 @@ fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) // Charlie commits let (queued_messages, _welcome, _group_info) = charlie_group .commit_to_pending_proposals(backend, &charlie_signer) + .await .expect("Could not commit proposal"); // The public group processes @@ -227,6 +239,7 @@ fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) charlie_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Alice processes @@ -237,12 +250,14 @@ fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process messages."); if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = alice_processed_message.into_content() { alice_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging commit."); } else { unreachable!("Expected a StagedCommit."); diff --git a/openmls/src/group/tests/external_add_proposal.rs b/openmls/src/group/tests/external_add_proposal.rs index 94f1b85eda..c2492bcc3f 100644 --- a/openmls/src/group/tests/external_add_proposal.rs +++ b/openmls/src/group/tests/external_add_proposal.rs @@ -23,7 +23,7 @@ struct ProposalValidationTestSetup { } // Creates a standalone group -fn new_test_group( +async fn new_test_group( identity: &str, wire_format_policy: WireFormatPolicy, ciphersuite: Ciphersuite, @@ -33,7 +33,7 @@ fn new_test_group( // Generate credentials with keys let credential_with_keys = - generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend).await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() @@ -49,37 +49,41 @@ fn new_test_group( group_id, credential_with_keys.credential_with_key.clone(), ) + .await .unwrap(), credential_with_keys, ) } // Validation test setup -fn validation_test_setup( +async fn validation_test_setup( wire_format_policy: WireFormatPolicy, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) -> ProposalValidationTestSetup { // === Alice creates a group === let (mut alice_group, alice_signer_with_keys) = - new_test_group("Alice", wire_format_policy, ciphersuite, backend); + new_test_group("Alice", wire_format_policy, ciphersuite, backend).await; let bob_credential_with_key = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; let bob_key_package = generate_key_package( ciphersuite, Extensions::empty(), backend, bob_credential_with_key.clone(), - ); + ) + .await; let (_message, welcome, _group_info) = alice_group .add_members(backend, &alice_signer_with_keys.signer, &[bob_key_package]) + .await .expect("error adding Bob to group"); alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Define the MlsGroup configuration @@ -94,6 +98,7 @@ fn validation_test_setup( welcome.into_welcome().expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("error creating group from welcome"); ProposalValidationTestSetup { @@ -103,7 +108,7 @@ fn validation_test_setup( } #[apply(ciphersuites_and_backends)] -fn external_add_proposal_should_succeed( +async fn external_add_proposal_should_succeed( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -111,7 +116,7 @@ fn external_add_proposal_should_succeed( let ProposalValidationTestSetup { alice_group, bob_group, - } = validation_test_setup(policy, ciphersuite, backend); + } = validation_test_setup(policy, ciphersuite, backend).await; let (mut alice_group, alice_signer) = alice_group; let (mut bob_group, _bob_signer) = bob_group; @@ -123,14 +128,16 @@ fn external_add_proposal_should_succeed( "Charlie".into(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let charlie_kp = generate_key_package( ciphersuite, Extensions::empty(), backend, charlie_credential.clone(), - ); + ) + .await; let proposal = JoinProposal::new( charlie_kp.clone(), @@ -152,6 +159,7 @@ fn external_add_proposal_should_succeed( let msg = alice_group .process_message(backend, proposal.clone().into_protocol_message().unwrap()) + .await .unwrap(); match msg.into_content() { @@ -168,6 +176,7 @@ fn external_add_proposal_should_succeed( let msg = bob_group .process_message(backend, proposal.into_protocol_message().unwrap()) + .await .unwrap(); match msg.into_content() { @@ -180,18 +189,21 @@ fn external_add_proposal_should_succeed( // and Alice will commit it let (commit, welcome, _group_info) = alice_group .commit_to_pending_proposals(backend, &alice_signer) + .await .unwrap(); - alice_group.merge_pending_commit(backend).unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); assert_eq!(alice_group.members().count(), 3); // Bob will also process the commit let msg = bob_group .process_message(backend, commit.into_protocol_message().unwrap()) + .await .unwrap(); match msg.into_content() { - ProcessedMessageContent::StagedCommitMessage(commit) => { - bob_group.merge_staged_commit(backend, *commit).unwrap() - } + ProcessedMessageContent::StagedCommitMessage(commit) => bob_group + .merge_staged_commit(backend, *commit) + .await + .unwrap(), _ => unreachable!(), } assert_eq!(bob_group.members().count(), 3); @@ -207,36 +219,39 @@ fn external_add_proposal_should_succeed( welcome.unwrap().into_welcome().unwrap(), Some(alice_group.export_ratchet_tree().into()), ) + .await .unwrap(); assert_eq!(charlie_group.members().count(), 3); } } #[apply(ciphersuites_and_backends)] -fn external_add_proposal_should_be_signed_by_key_package_it_references( +async fn external_add_proposal_should_be_signed_by_key_package_it_references( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { let ProposalValidationTestSetup { alice_group, .. } = - validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let (mut alice_group, _alice_signer) = alice_group; let attacker_credential = generate_credential_with_key( "Attacker".into(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; // A new client, Charlie, will now ask joining with an external Add proposal let charlie_credential = - generate_credential_with_key("Charlie".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Charlie".into(), ciphersuite.signature_algorithm(), backend).await; let charlie_kp = generate_key_package( ciphersuite, Extensions::empty(), backend, attacker_credential, - ); + ) + .await; let invalid_proposal = JoinProposal::new( charlie_kp, @@ -250,6 +265,7 @@ fn external_add_proposal_should_be_signed_by_key_package_it_references( assert!(matches!( alice_group .process_message(backend, invalid_proposal.into_protocol_message().unwrap()) + .await .unwrap_err(), ProcessMessageError::InvalidSignature )); @@ -257,27 +273,28 @@ fn external_add_proposal_should_be_signed_by_key_package_it_references( // TODO #1093: move this test to a dedicated external proposal ValSem test module once all external proposals implemented #[apply(ciphersuites_and_backends)] -fn new_member_proposal_sender_should_be_reserved_for_join_proposals( +async fn new_member_proposal_sender_should_be_reserved_for_join_proposals( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { let ProposalValidationTestSetup { alice_group, bob_group, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let (mut alice_group, alice_signer) = alice_group; let (mut bob_group, _bob_signer) = bob_group; // Add proposal can have a 'new_member_proposal' sender let any_credential = - generate_credential_with_key("Any".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Any".into(), ciphersuite.signature_algorithm(), backend).await; let any_kp = generate_key_package( ciphersuite, Extensions::empty(), backend, any_credential.clone(), - ); + ) + .await; let join_proposal = JoinProposal::new( any_kp, @@ -300,6 +317,7 @@ fn new_member_proposal_sender_should_be_reserved_for_join_proposals( // Finally check that the message can be processed without errors assert!(bob_group .process_message(backend, join_proposal.into_protocol_message().unwrap()) + .await .is_ok()); } else { panic!() @@ -314,7 +332,10 @@ fn new_member_proposal_sender_should_be_reserved_for_join_proposals( if let MlsMessageInBody::PublicMessage(mut plaintext) = remove_proposal.body { plaintext.set_sender(Sender::NewMemberProposal); assert!(matches!( - bob_group.process_message(backend, plaintext).unwrap_err(), + bob_group + .process_message(backend, plaintext) + .await + .unwrap_err(), ProcessMessageError::ValidationError(ValidationError::NotAnExternalAddProposal) )); } else { @@ -325,12 +346,16 @@ fn new_member_proposal_sender_should_be_reserved_for_join_proposals( // Update proposal cannot have a 'new_member_proposal' sender let update_proposal = alice_group .propose_self_update(backend, &alice_signer, None) + .await .map(|(out, _)| MlsMessageIn::from(out)) .unwrap(); if let MlsMessageInBody::PublicMessage(mut plaintext) = update_proposal.body { plaintext.set_sender(Sender::NewMemberProposal); assert!(matches!( - bob_group.process_message(backend, plaintext).unwrap_err(), + bob_group + .process_message(backend, plaintext) + .await + .unwrap_err(), ProcessMessageError::ValidationError(ValidationError::NotAnExternalAddProposal) )); } else { diff --git a/openmls/src/group/tests/external_remove_proposal.rs b/openmls/src/group/tests/external_remove_proposal.rs index ce7f0e8431..c4597d0292 100644 --- a/openmls/src/group/tests/external_remove_proposal.rs +++ b/openmls/src/group/tests/external_remove_proposal.rs @@ -13,7 +13,7 @@ use openmls_traits::types::Ciphersuite; use super::utils::*; // Creates a standalone group -fn new_test_group( +async fn new_test_group( identity: &str, wire_format_policy: WireFormatPolicy, ciphersuite: Ciphersuite, @@ -24,7 +24,7 @@ fn new_test_group( // Generate credentials with keys let credential_with_keys = - generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend).await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() @@ -41,13 +41,14 @@ fn new_test_group( group_id, credential_with_keys.credential_with_key.clone(), ) + .await .unwrap(), credential_with_keys, ) } // Validation test setup -fn validation_test_setup( +async fn validation_test_setup( wire_format_policy: WireFormatPolicy, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -60,24 +61,28 @@ fn validation_test_setup( ciphersuite, backend, external_senders, - ); + ) + .await; let bob_credential_with_key = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; let bob_key_package = generate_key_package( ciphersuite, Extensions::empty(), backend, bob_credential_with_key, - ); + ) + .await; alice_group .add_members(backend, &alice_signer_when_keys.signer, &[bob_key_package]) + .await .expect("error adding Bob to group"); alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); assert_eq!(alice_group.members().count(), 2); @@ -85,7 +90,7 @@ fn validation_test_setup( } #[apply(ciphersuites_and_backends)] -fn external_remove_proposal_should_remove_member( +async fn external_remove_proposal_should_remove_member( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -94,7 +99,8 @@ fn external_remove_proposal_should_remove_member( "delivery-service".into(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let (mut alice_group, alice_credential) = validation_test_setup( PURE_PLAINTEXT_WIRE_FORMAT_POLICY, @@ -110,7 +116,8 @@ fn external_remove_proposal_should_remove_member( .credential .clone(), )], - ); + ) + .await; // DS is an allowed external sender of the group assert!(alice_group @@ -139,14 +146,16 @@ fn external_remove_proposal_should_remove_member( // Alice validates the message let processed_message = alice_group .process_message(backend, bob_external_remove_proposal) + .await .unwrap(); // commit the proposal let ProcessedMessageContent::ProposalMessage(remove_proposal) = processed_message.into_content() else { panic!("Not a remove proposal");}; alice_group.store_pending_proposal(*remove_proposal); alice_group .commit_to_pending_proposals(backend, &alice_credential.signer) + .await .unwrap(); - alice_group.merge_pending_commit(backend).unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); // Trying to do an external remove proposal of Bob now should fail as he no longer is in the group let invalid_bob_external_remove_proposal: MlsMessageIn = ExternalProposal::new_remove( @@ -161,24 +170,26 @@ fn external_remove_proposal_should_remove_member( .into(); let processed_message = alice_group .process_message(backend, invalid_bob_external_remove_proposal) + .await .unwrap(); // commit the proposal let ProcessedMessageContent::ProposalMessage(remove_proposal) = processed_message.into_content() else { panic!("Not a remove proposal");}; alice_group.store_pending_proposal(*remove_proposal); - assert_eq!( + assert!(matches!( alice_group .commit_to_pending_proposals(backend, &alice_credential.signer) + .await .unwrap_err(), CommitToPendingProposalsError::CreateCommitError( CreateCommitError::ProposalValidationError( ProposalValidationError::UnknownMemberRemoval ) ) - ); + )); } #[apply(ciphersuites_and_backends)] -fn external_remove_proposal_should_fail_when_invalid_external_senders_index( +async fn external_remove_proposal_should_fail_when_invalid_external_senders_index( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -187,7 +198,8 @@ fn external_remove_proposal_should_fail_when_invalid_external_senders_index( "delivery-service".into(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let (mut alice_group, _alice_credential) = validation_test_setup( PURE_PLAINTEXT_WIRE_FORMAT_POLICY, @@ -203,7 +215,8 @@ fn external_remove_proposal_should_fail_when_invalid_external_senders_index( .credential .clone(), )], - ); + ) + .await; // get Bob's index let bob_index = alice_group @@ -225,6 +238,7 @@ fn external_remove_proposal_should_fail_when_invalid_external_senders_index( // Alice tries to validate the message and should fail as sender is invalid let error = alice_group .process_message(backend, bob_external_remove_proposal) + .await .unwrap_err(); assert_eq!( error, @@ -233,7 +247,7 @@ fn external_remove_proposal_should_fail_when_invalid_external_senders_index( } #[apply(ciphersuites_and_backends)] -fn external_remove_proposal_should_fail_when_invalid_signature( +async fn external_remove_proposal_should_fail_when_invalid_signature( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -242,7 +256,8 @@ fn external_remove_proposal_should_fail_when_invalid_signature( "delivery-service".into(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let (mut alice_group, _alice_credential) = validation_test_setup( PURE_PLAINTEXT_WIRE_FORMAT_POLICY, @@ -255,13 +270,15 @@ fn external_remove_proposal_should_fail_when_invalid_signature( .clone(), ds_credential_with_key.credential_with_key.credential, )], - ); + ) + .await; let ds_invalid_credential_with_key = generate_credential_with_key( "delivery-service-invalid".into(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; // get Bob's index let bob_index = alice_group @@ -283,12 +300,13 @@ fn external_remove_proposal_should_fail_when_invalid_signature( // Alice tries to validate the message and should fail as sender is invalid let error = alice_group .process_message(backend, bob_external_remove_proposal) + .await .unwrap_err(); assert_eq!(error, ProcessMessageError::InvalidSignature); } #[apply(ciphersuites_and_backends)] -fn external_remove_proposal_should_fail_when_no_external_senders( +async fn external_remove_proposal_should_fail_when_no_external_senders( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -297,13 +315,15 @@ fn external_remove_proposal_should_fail_when_no_external_senders( ciphersuite, backend, vec![], - ); + ) + .await; // delivery service credentials. DS will craft an external remove proposal let ds_credential_with_key = generate_credential_with_key( "delivery-service".into(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; // get Bob's index let bob_index = alice_group @@ -325,6 +345,7 @@ fn external_remove_proposal_should_fail_when_no_external_senders( // Alice tries to validate the message and should fail as sender is invalid let error = alice_group .process_message(backend, bob_external_remove_proposal) + .await .unwrap_err(); assert_eq!( error, diff --git a/openmls/src/group/tests/kat_messages.rs b/openmls/src/group/tests/kat_messages.rs index ca90b13f6b..2213384720 100644 --- a/openmls/src/group/tests/kat_messages.rs +++ b/openmls/src/group/tests/kat_messages.rs @@ -4,37 +4,19 @@ //! See //! for more description on the test vectors. -use openmls_rust_crypto::OpenMlsRustCrypto; -use openmls_traits::{random::OpenMlsRand, types::SignatureScheme, OpenMlsCryptoProvider}; -use rand::{rngs::OsRng, RngCore}; use serde::{self, Deserialize, Serialize}; use thiserror::Error; use tls_codec::{Deserialize as TlsDeserialize, Serialize as TlsSerialize}; use crate::{ - binary_tree::array_representation::LeafNodeIndex, - ciphersuite::Mac, framing::*, - group::{ - config::CryptoConfig, - tests::utils::{generate_credential_with_key, generate_key_package, randombytes}, - *, - }, - key_packages::*, messages::{ proposals::*, proposals_in::{AddProposalIn, UpdateProposalIn}, *, }, - prelude::{CredentialType, LeafNode}, - schedule::psk::{store::ResumptionPskStore, *}, test_utils::*, - tree::sender_ratchet::*, - treesync::node::{ - leaf_node::{Capabilities, TreeInfoTbs}, - NodeIn, - }, - versions::ProtocolVersion, + treesync::node::NodeIn, }; /// ```json @@ -119,294 +101,7 @@ pub struct MessagesTestVector { private_message: Vec, } -pub fn generate_test_vector(ciphersuite: Ciphersuite) -> MessagesTestVector { - let crypto = OpenMlsRustCrypto::default(); - - let alice_credential_with_key_and_signer = generate_credential_with_key( - b"Alice".to_vec(), - SignatureScheme::from(ciphersuite), - &crypto, - ); - - // Create a proposal to update the user's key package. - let alice_key_package = generate_key_package( - ciphersuite, - Extensions::default(), - &crypto, - alice_credential_with_key_and_signer.clone(), - ); - - // Let's create a group - let mut alice_group = CoreGroup::builder( - GroupId::random(&crypto), - CryptoConfig::with_default_version(ciphersuite), - alice_credential_with_key_and_signer - .credential_with_key - .clone(), - ) - .with_max_past_epoch_secrets(2) - .build(&crypto, &alice_credential_with_key_and_signer.signer) - .unwrap(); - - let alice_ratchet_tree = alice_group.public_group().export_ratchet_tree(); - - let alice_group_info = alice_group - .export_group_info(&crypto, &alice_credential_with_key_and_signer.signer, true) - .unwrap(); - - let alice_leaf_node = { - let capabilities = Capabilities::new( - None, - Some(&[ - Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, - Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, - Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, - ]), - None, - Some(&[ProposalType::AppAck]), - Some(&[CredentialType::Basic]), - ); - - LeafNode::generate_update( - CryptoConfig { - ciphersuite, - version: ProtocolVersion::Mls10, - }, - alice_credential_with_key_and_signer - .credential_with_key - .clone(), - capabilities, - Extensions::default(), - TreeInfoTbs::Update(alice_group.own_tree_position()), - &crypto, - &alice_credential_with_key_and_signer.signer.clone(), - ) - .unwrap() - }; - - let update_proposal = UpdateProposal { - leaf_node: alice_leaf_node, - }; - - // Bob - let bob_credential_with_key_and_signer = - generate_credential_with_key(b"Bob".to_vec(), SignatureScheme::from(ciphersuite), &crypto); - - let bob_key_package_bundle = KeyPackageBundle::new( - &crypto, - &bob_credential_with_key_and_signer.signer, - ciphersuite, - bob_credential_with_key_and_signer.credential_with_key, - ); - - let add_proposal = AddProposal { - key_package: bob_key_package_bundle.key_package().clone(), - }; - - // Create proposal to remove a user - // TODO #525: This is not a valid RemoveProposal since random_u32() is not a valid KeyPackageRef. - let remove_proposal = RemoveProposal { - removed: LeafNodeIndex::new(random_u32()), - }; - - let psk_proposal = { - let psk_id = PreSharedKeyId::new( - ciphersuite, - crypto.rand(), - Psk::External(ExternalPsk::new( - crypto.rand().random_vec(ciphersuite.hash_length()).unwrap(), - )), - ) - .unwrap(); - - PreSharedKeyProposal::new(psk_id) - }; - - let reinit_proposal = ReInitProposal { - group_id: alice_group.group_id().clone(), - version: ProtocolVersion::Mls10, - ciphersuite, - extensions: Extensions::single(Extension::RatchetTree(RatchetTreeExtension::new( - alice_ratchet_tree.clone(), - ))), - }; - - let external_init_proposal = ExternalInitProposal::from(randombytes(32)); - - let group_context_extensions_proposal = - GroupContextExtensionProposal::new(Extensions::default()); - - let framing_parameters = FramingParameters::new(b"aad", WireFormat::PrivateMessage); - - let add_proposal_content = alice_group - .create_add_proposal( - framing_parameters, - bob_key_package_bundle.key_package.clone(), - &alice_credential_with_key_and_signer.signer.clone(), - ) - .unwrap(); - - let mut proposal_store = ProposalStore::from_queued_proposal( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - &crypto, - add_proposal_content.clone(), - ) - .unwrap(), - ); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) - .build(); - let create_commit_result = alice_group - .create_commit( - params, - &crypto, - &alice_credential_with_key_and_signer.signer, - ) - .unwrap(); - alice_group - .merge_staged_commit( - &crypto, - create_commit_result.staged_commit, - &mut proposal_store, - ) - .unwrap(); - - let commit = if let FramedContentBody::Commit(commit) = create_commit_result.commit.content() { - commit.clone() - } else { - panic!("Wrong content of MLS plaintext"); - }; - - let alice_welcome = create_commit_result.welcome_option.unwrap(); - - let mut receiver_group = CoreGroup::new_from_welcome( - alice_welcome.clone(), - Some(alice_group.public_group().export_ratchet_tree().into()), - bob_key_package_bundle, - &crypto, - ResumptionPskStore::new(1024), - ) - .expect("Error creating receiver group."); - - // Clone the secret tree to bypass FS restrictions - let private_message_application = alice_group - .create_application_message( - b"aad", - b"msg", - random_u8() as usize, - &crypto, - &alice_credential_with_key_and_signer.signer, - ) - .unwrap(); - // Replace the secret tree - let verifiable_public_message_application: VerifiableAuthenticatedContentIn = receiver_group - .decrypt( - &private_message_application.into(), - &crypto, - &SenderRatchetConfiguration::default(), - ) - .unwrap(); - let mls_content_application: AuthenticatedContent = - AuthenticatedContentIn::from(verifiable_public_message_application).into(); - - let encryption_target = match random_u32() % 3 { - 0 => create_commit_result.commit.clone(), - 1 => add_proposal_content.clone(), - 2 => mls_content_application.clone(), - _ => panic!("Modulo 3 of u32 shouldn't give us anything larger than 2"), - }; - - let mut mac_value = vec![0u8; alice_group.ciphersuite().hash_length()]; - OsRng.fill_bytes(&mut mac_value); - let random_membership_tag = MembershipTag(Mac { - mac_value: mac_value.into(), - }); - - let mut application_pt: PublicMessage = mls_content_application.into(); - application_pt.set_membership_tag_test(random_membership_tag.clone()); - - let mut proposal_pt: PublicMessage = add_proposal_content.into(); - proposal_pt.set_membership_tag_test(random_membership_tag.clone()); - - let mut commit_pt: PublicMessage = create_commit_result.commit.into(); - commit_pt.set_membership_tag_test(random_membership_tag); - - let private_message = alice_group - .encrypt(encryption_target, random_u8() as usize, &crypto) - .unwrap(); - - MessagesTestVector { - mls_welcome: MlsMessageOut::from_welcome(alice_welcome, ProtocolVersion::Mls10) - .tls_serialize_detached() - .unwrap(), - mls_group_info: MlsMessageOut::from(alice_group_info) - .tls_serialize_detached() - .unwrap(), - mls_key_package: MlsMessageOut::from(alice_key_package) - .tls_serialize_detached() - .unwrap(), - - group_secrets: GroupSecrets::random_encoded( - ciphersuite, - &crypto, - ProtocolVersion::default(), - ) - .unwrap(), - ratchet_tree: alice_ratchet_tree.tls_serialize_detached().unwrap(), - - add_proposal: add_proposal.tls_serialize_detached().unwrap(), - update_proposal: update_proposal.tls_serialize_detached().unwrap(), - remove_proposal: remove_proposal.tls_serialize_detached().unwrap(), - pre_shared_key_proposal: psk_proposal.tls_serialize_detached().unwrap(), - re_init_proposal: reinit_proposal.tls_serialize_detached().unwrap(), - external_init_proposal: external_init_proposal.tls_serialize_detached().unwrap(), - group_context_extensions_proposal: group_context_extensions_proposal - .tls_serialize_detached() - .unwrap(), - - commit: commit.tls_serialize_detached().unwrap(), - - public_message_application: MlsMessageOut::from(application_pt) - .tls_serialize_detached() - .unwrap(), - public_message_proposal: MlsMessageOut::from(proposal_pt) - .tls_serialize_detached() - .unwrap(), - public_message_commit: MlsMessageOut::from(commit_pt) - .tls_serialize_detached() - .unwrap(), - private_message: MlsMessageOut::from_private_message( - private_message, - ProtocolVersion::Mls10, - ) - .tls_serialize_detached() - .unwrap(), - } -} - -#[test] -fn write_test_vectors_msg() { - use openmls_traits::crypto::OpenMlsCrypto; - let mut tests = Vec::new(); - const NUM_TESTS: usize = 100; - - for &ciphersuite in OpenMlsRustCrypto::default() - .crypto() - .supported_ciphersuites() - .iter() - { - for _ in 0..NUM_TESTS { - let test = generate_test_vector(ciphersuite); - tests.push(test); - } - } - - write("test_vectors/messages-new.json", &tests); -} - -pub fn run_test_vector(tv: MessagesTestVector) -> Result<(), EncodingMismatch> { +pub async fn run_test_vector(tv: MessagesTestVector) -> Result<(), EncodingMismatch> { // Welcome let tv_mls_welcome = tv.mls_welcome; let my_mls_welcome = MlsMessageIn::tls_deserialize_exact(&tv_mls_welcome) @@ -639,12 +334,12 @@ pub fn run_test_vector(tv: MessagesTestVector) -> Result<(), EncodingMismatch> { Ok(()) } -#[test] -fn read_test_vectors_messages() { +#[tokio::test] +async fn read_test_vectors_messages() { let tests: Vec = read("test_vectors/messages.json"); for test_vector in tests { - match run_test_vector(test_vector) { + match run_test_vector(test_vector).await { Ok(_) => {} Err(e) => panic!("Error while checking messages test vector.\n{e:?}"), } diff --git a/openmls/src/group/tests/kat_transcript_hashes.rs b/openmls/src/group/tests/kat_transcript_hashes.rs index 73edc995e0..726cbb6bb7 100644 --- a/openmls/src/group/tests/kat_transcript_hashes.rs +++ b/openmls/src/group/tests/kat_transcript_hashes.rs @@ -6,28 +6,21 @@ use std::convert::TryFrom; use openmls_rust_crypto::OpenMlsRustCrypto; -use openmls_traits::{crypto::OpenMlsCrypto, random::OpenMlsRand, OpenMlsCryptoProvider}; +use openmls_traits::{crypto::OpenMlsCrypto, OpenMlsCryptoProvider}; use serde::{self, Deserialize, Serialize}; -use tls_codec::{Deserialize as TlsDeserializeTrait, Serialize as TlsSerializeTrait}; +use tls_codec::Deserialize as TlsDeserializeTrait; #[cfg(test)] -use crate::test_utils::{read, write}; +use crate::test_utils::read; use crate::{ - binary_tree::array_representation::LeafNodeIndex, framing::{mls_auth_content::AuthenticatedContent, *}, - group::{ - tests::utils::{generate_credential_with_key, randombytes}, - *, - }, - messages::*, + group::*, schedule::*, test_utils::*, versions::ProtocolVersion, }; const TEST_VECTOR_PATH_READ: &str = "test_vectors/transcript-hashes.json"; -const TEST_VECTOR_PATH_WRITE: &str = "test_vectors/transcript-hashes-new.json"; -const NUM_TESTS: usize = 100; /// ```json /// { @@ -139,126 +132,3 @@ pub fn run_test_vector(test_vector: TranscriptTestVector) { got_interim_transcript_hash_after ); } - -// ------------------------------------------------------------------------------------------------- - -#[test] -fn write_test_vectors() { - let mut tests = Vec::new(); - - for &ciphersuite in OpenMlsRustCrypto::default() - .crypto() - .supported_ciphersuites() - .iter() - { - for _ in 0..NUM_TESTS { - let test = generate_test_vector(ciphersuite); - tests.push(test); - } - } - - write(TEST_VECTOR_PATH_WRITE, &tests); -} - -pub fn generate_test_vector(ciphersuite: Ciphersuite) -> TranscriptTestVector { - let backend = OpenMlsRustCrypto::default(); - - let confirmation_key = ConfirmationKey::random(ciphersuite, &backend); - - let interim_transcript_hash_before = randombytes(ciphersuite.hash_length()); - - // Note: This does not have a valid `confirmation_tag` for now and is only used to - // calculate `confirmed_transcript_hash_after`. - let mut authenticated_content = { - let aad = backend.rand().random_vec(48).unwrap(); - let framing_parameters = FramingParameters::new(&aad, WireFormat::PublicMessage); - - // XXX: Use random but valid sender. - let sender = Sender::build_member(LeafNodeIndex::new(7)); - - let commit = Commit { - proposals: vec![], - path: None, - }; - - let group_context = { - let tree_hash_before = backend - .rand() - .random_vec(ciphersuite.hash_length()) - .unwrap(); - - let confirmed_transcript_hash_before = backend - .rand() - .random_vec(ciphersuite.hash_length()) - .unwrap(); - - GroupContext::new( - ciphersuite, - GroupId::random(&backend), - random_u64(), - tree_hash_before, - confirmed_transcript_hash_before, - Extensions::empty(), - ) - }; - - let signer = { - let credential_with_key_and_signer = generate_credential_with_key( - b"Alice".to_vec(), - ciphersuite.signature_algorithm(), - &backend, - ); - - credential_with_key_and_signer.signer - }; - - AuthenticatedContent::commit(framing_parameters, sender, commit, &group_context, &signer) - .unwrap() - }; - - // Now, calculate `confirmed_transcript_hash_after` ... - let confirmed_transcript_hash_after = { - let input = ConfirmedTranscriptHashInput::try_from(&authenticated_content).unwrap(); - - input - .calculate_confirmed_transcript_hash( - backend.crypto(), - ciphersuite, - &interim_transcript_hash_before, - ) - .unwrap() - }; - - // ... and the `confirmation_tag` ... - let confirmation_tag = { - confirmation_key - .tag(&backend, &confirmed_transcript_hash_after) - .unwrap() - }; - - // ... and set it in `authenticated_content`. - authenticated_content.set_confirmation_tag(confirmation_tag.clone()); - - let interim_transcript_hash_after = { - let input = InterimTranscriptHashInput::from(&confirmation_tag); - - input - .calculate_interim_transcript_hash( - backend.crypto(), - ciphersuite, - &confirmed_transcript_hash_after, - ) - .unwrap() - }; - - TranscriptTestVector { - cipher_suite: (&ciphersuite).into(), - - confirmation_key: confirmation_key.as_slice().to_vec(), - authenticated_content: authenticated_content.tls_serialize_detached().unwrap(), - interim_transcript_hash_before, - - confirmed_transcript_hash_after, - interim_transcript_hash_after, - } -} diff --git a/openmls/src/group/tests/test_commit_validation.rs b/openmls/src/group/tests/test_commit_validation.rs index b051f52a48..16ca6f2b5e 100644 --- a/openmls/src/group/tests/test_commit_validation.rs +++ b/openmls/src/group/tests/test_commit_validation.rs @@ -31,7 +31,7 @@ struct CommitValidationTestSetup { } // Validation test setup -fn validation_test_setup( +async fn validation_test_setup( wire_format_policy: WireFormatPolicy, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -40,24 +40,25 @@ fn validation_test_setup( // Generate credentials with keys let alice_credential = - generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend).await; let bob_credential = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; let charlie_credential = - generate_credential_with_key("Charlie".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Charlie".into(), ciphersuite.signature_algorithm(), backend).await; // Generate KeyPackages let bob_key_package = - generate_key_package(ciphersuite, Extensions::empty(), backend, bob_credential); + generate_key_package(ciphersuite, Extensions::empty(), backend, bob_credential).await; let charlie_key_package = generate_key_package( ciphersuite, Extensions::empty(), backend, charlie_credential, - ); + ) + .await; // Define the MlsGroup configuration @@ -74,6 +75,7 @@ fn validation_test_setup( group_id, alice_credential.credential_with_key.clone(), ) + .await .expect("An unexpected error occurred."); let (_message, welcome, _group_info) = alice_group @@ -82,10 +84,12 @@ fn validation_test_setup( &alice_credential.signer, &[bob_key_package, charlie_key_package], ) + .await .expect("error adding Bob to group"); alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); let welcome = welcome.into_welcome().expect("Unexpected message type."); @@ -96,6 +100,7 @@ fn validation_test_setup( welcome.clone(), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("error creating group from welcome"); let charlie_group = MlsGroup::new_from_welcome( @@ -104,6 +109,7 @@ fn validation_test_setup( welcome, Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("error creating group from welcome"); CommitValidationTestSetup { @@ -116,14 +122,14 @@ fn validation_test_setup( // ValSem200: Commit must not cover inline self Remove proposal #[apply(ciphersuites_and_backends)] -fn test_valsem200(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem200(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let CommitValidationTestSetup { mut alice_group, alice_credential, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Since Alice won't commit to her own removal directly, we have to create // proposal and commit independently and then insert the proposal into the @@ -158,6 +164,7 @@ fn test_valsem200(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Now let's stick it in the commit. let serialized_message = alice_group .self_update(backend, &alice_credential.signer) + .await .expect("Error creating self-update") .tls_serialize_detached() .expect("Could not serialize message."); @@ -219,6 +226,7 @@ fn test_valsem200(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, message_in) + .await .expect_err("Could process unverified message despite self remove."); assert_eq!( @@ -229,12 +237,13 @@ fn test_valsem200(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, ProtocolMessage::from(original_plaintext)) + .await .expect("Unexpected error."); } // ValSem201: Path must be present, if at least one proposal requires a path #[apply(ciphersuites_and_backends)] -fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let wire_format_policy = PURE_PLAINTEXT_WIRE_FORMAT_POLICY; // Test with PublicMessage let CommitValidationTestSetup { @@ -243,7 +252,7 @@ fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider mut bob_group, charlie_group, .. - } = validation_test_setup(wire_format_policy, ciphersuite, backend); + } = validation_test_setup(wire_format_policy, ciphersuite, backend).await; let queued = |proposal: Proposal| { QueuedProposal::from_proposal_and_sender( @@ -255,18 +264,18 @@ fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider .unwrap() }; - let add_proposal = || { + let add_proposal = || async { let dave_credential = - generate_credential_with_key("Dave".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Dave".into(), ciphersuite.signature_algorithm(), backend).await; let dave_key_package = - generate_key_package(ciphersuite, Extensions::empty(), backend, dave_credential); + generate_key_package(ciphersuite, Extensions::empty(), backend, dave_credential).await; queued(Proposal::Add(AddProposal { key_package: dave_key_package, })) }; - let psk_proposal = || { + let psk_proposal = || async { let secret = Secret::random(ciphersuite, backend, None).unwrap(); let rand = backend .rand() @@ -280,6 +289,7 @@ fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider .unwrap(); psk_id .write_to_key_store(backend, ciphersuite, secret.as_slice()) + .await .unwrap(); queued(Proposal::PreSharedKey(PreSharedKeyProposal::new(psk_id))) }; @@ -312,15 +322,15 @@ fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // cannot distinguish when the commit has a single ReInit proposal from the commit without proposals // in [CoreGroup::apply_proposals()] let cases = vec![ - (vec![add_proposal()], false), - (vec![psk_proposal()], false), + (vec![add_proposal().await], false), + (vec![psk_proposal().await], false), (vec![update_proposal.clone()], true), (vec![remove_proposal()], true), (vec![gce_proposal()], true), // !path_required + !path_required = !path_required - (vec![add_proposal(), psk_proposal()], false), + (vec![add_proposal().await, psk_proposal().await], false), // path_required + !path_required = path_required - (vec![remove_proposal(), add_proposal()], true), + (vec![remove_proposal(), add_proposal().await], true), // path_required + path_required = path_required (vec![update_proposal, remove_proposal()], true), // TODO: #566 this should work if GCE proposals validation were implemented @@ -342,6 +352,7 @@ fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let commit = alice_group .group() .create_commit(params, backend, &alice_credential.signer) + .await .unwrap() .commit; @@ -369,7 +380,7 @@ fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider &alice_group, &alice_credential.signer, ); - let processed_msg = bob_group.process_message(backend, commit_wo_path); + let processed_msg = bob_group.process_message(backend, commit_wo_path).await; assert_eq!( processed_msg.unwrap_err(), ProcessMessageError::InvalidCommit(StageCommitError::RequiredPathNotFound) @@ -377,7 +388,7 @@ fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider } // Positive case - let process_message_result = bob_group.process_message(backend, commit); + let process_message_result = bob_group.process_message(backend, commit).await; assert!(process_message_result.is_ok(), "{process_message_result:?}"); // cleanup & restore for next iteration @@ -418,14 +429,14 @@ fn erase_path( // ValSem202: Path must be the right length #[apply(ciphersuites_and_backends)] -fn test_valsem202(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem202(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let CommitValidationTestSetup { mut alice_group, alice_credential, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Have Alice generate a self-updating commit, remove a node from the path, // re-sign and have Bob process it. @@ -433,6 +444,7 @@ fn test_valsem202(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Create the self-update let serialized_update = alice_group .self_update(backend, &alice_credential.signer) + .await .expect("Error creating self-update") .tls_serialize_detached() .expect("Could not serialize message."); @@ -468,6 +480,7 @@ fn test_valsem202(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process unverified message despite path length mismatch."); assert_eq!( @@ -484,19 +497,20 @@ fn test_valsem202(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, original_update_plaintext) + .await .expect("Unexpected error."); } // ValSem203: Path secrets must decrypt correctly #[apply(ciphersuites_and_backends)] -fn test_valsem203(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem203(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let CommitValidationTestSetup { mut alice_group, alice_credential, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Have Alice generate a self-updating commit, scramble some ciphertexts and // have Bob process the resulting commit. @@ -504,6 +518,7 @@ fn test_valsem203(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Create the self-update let serialized_update = alice_group .self_update(backend, &alice_credential.signer) + .await .expect("Error creating self-update") .tls_serialize_detached() .expect("Could not serialize message."); @@ -541,6 +556,7 @@ fn test_valsem203(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process unverified message despite scrambled ciphertexts."); assert_eq!( @@ -557,19 +573,20 @@ fn test_valsem203(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, original_update_plaintext) + .await .expect("Unexpected error."); } // ValSem204: Public keys from Path must be verified and match the private keys from the direct path #[apply(ciphersuites_and_backends)] -fn test_valsem204(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem204(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let CommitValidationTestSetup { mut alice_group, alice_credential, mut bob_group, mut charlie_group, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Have Alice generate a self-updating commit, flip the last byte of one of // the public keys in the path and have Bob process the commit. @@ -577,6 +594,7 @@ fn test_valsem204(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Create the self-update let serialized_update = alice_group .self_update(backend, &alice_credential.signer) + .await .expect("Error creating self-update") .tls_serialize_detached() .expect("Could not serialize message."); @@ -600,10 +618,12 @@ fn test_valsem204(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // from them. let message = charlie_group .process_message(backend, original_plaintext.clone()) + .await .unwrap(); match message.into_content() { ProcessedMessageContent::StagedCommitMessage(staged_commit) => charlie_group .merge_staged_commit(backend, *staged_commit) + .await .unwrap(), _ => panic!("Unexpected message type."), } @@ -660,6 +680,7 @@ fn test_valsem204(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process unverified message despite modified public key in path."); assert_eq!( @@ -676,19 +697,20 @@ fn test_valsem204(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, original_update_plaintext) + .await .expect("Unexpected error."); } // ValSem205: Confirmation tag must be successfully verified #[apply(ciphersuites_and_backends)] -fn test_valsem205(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem205(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let CommitValidationTestSetup { mut alice_group, alice_credential, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Have Alice generate a self-updating commit, flip the last bit of the // confirmation tag and have Bob process the commit. @@ -696,6 +718,7 @@ fn test_valsem205(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Create the self-update let serialized_update = alice_group .self_update(backend, &alice_credential.signer) + .await .expect("Error creating self-update") .tls_serialize_detached() .expect("Could not serialize message."); @@ -732,6 +755,7 @@ fn test_valsem205(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process unverified message despite confirmation tag mismatch."); assert_eq!( @@ -742,19 +766,23 @@ fn test_valsem205(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, ProtocolMessage::from(original_plaintext)) + .await .expect("Unexpected error."); } // this ensures that a member can process commits not containing all the stored proposals #[apply(ciphersuites_and_backends)] -fn test_partial_proposal_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_partial_proposal_commit( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { // Test with PublicMessage let CommitValidationTestSetup { mut alice_group, alice_credential, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let charlie_index = alice_group .members() @@ -767,7 +795,10 @@ fn test_partial_proposal_commit(ciphersuite: Ciphersuite, backend: &impl OpenMls .propose_remove_member(backend, &alice_credential.signer, charlie_index) .map(|(out, _)| MlsMessageIn::from(out)) .unwrap(); - let proposal_1 = bob_group.process_message(backend, proposal_1).unwrap(); + let proposal_1 = bob_group + .process_message(backend, proposal_1) + .await + .unwrap(); match proposal_1.into_content() { ProcessedMessageContent::ProposalMessage(p) => bob_group.store_pending_proposal(*p), _ => unreachable!(), @@ -776,9 +807,13 @@ fn test_partial_proposal_commit(ciphersuite: Ciphersuite, backend: &impl OpenMls // Create second proposal in Alice's group let proposal_2 = alice_group .propose_self_update(backend, &alice_credential.signer, None) + .await .map(|(out, _)| MlsMessageIn::from(out)) .unwrap(); - let proposal_2 = bob_group.process_message(backend, proposal_2).unwrap(); + let proposal_2 = bob_group + .process_message(backend, proposal_2) + .await + .unwrap(); match proposal_2.into_content() { ProcessedMessageContent::ProposalMessage(p) => bob_group.store_pending_proposal(*p), _ => unreachable!(), @@ -795,17 +830,21 @@ fn test_partial_proposal_commit(ciphersuite: Ciphersuite, backend: &impl OpenMls alice_group.proposal_store.add(remaining_proposal); let (commit, _, _) = alice_group .commit_to_pending_proposals(backend, &alice_credential.signer) + .await .unwrap(); // Alice herself should be able to merge the commit alice_group .merge_pending_commit(backend) + .await .expect("Commits with partial proposals are not supported"); // Bob should be able to process the commit bob_group .process_message(backend, commit.into_protocol_message().unwrap()) + .await .expect("Commits with partial proposals are not supported"); bob_group .merge_pending_commit(backend) + .await .expect("Commits with partial proposals are not supported"); } diff --git a/openmls/src/group/tests/test_encoding.rs b/openmls/src/group/tests/test_encoding.rs index 35807702dc..2c5fc78fb8 100644 --- a/openmls/src/group/tests/test_encoding.rs +++ b/openmls/src/group/tests/test_encoding.rs @@ -9,7 +9,7 @@ use crate::{ }; /// Creates a simple test setup for various encoding tests. -fn create_encoding_test_setup(backend: &impl OpenMlsCryptoProvider) -> TestSetup { +async fn create_encoding_test_setup(backend: &impl OpenMlsCryptoProvider) -> TestSetup { // Create a test config for a single client supporting all possible // ciphersuites. let alice_config = TestClientConfig { @@ -47,13 +47,13 @@ fn create_encoding_test_setup(backend: &impl OpenMlsCryptoProvider) -> TestSetup }; // Initialize the test setup according to config. - setup(test_setup_config, backend) + setup(test_setup_config, backend).await } /// This test tests encoding and decoding of application messages. #[apply(backends)] -fn test_application_message_encoding(backend: &impl OpenMlsCryptoProvider) { - let test_setup = create_encoding_test_setup(backend); +async fn test_application_message_encoding(backend: &impl OpenMlsCryptoProvider) { + let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); let alice = test_clients .get("alice") @@ -94,8 +94,8 @@ fn test_application_message_encoding(backend: &impl OpenMlsCryptoProvider) { /// This test tests encoding and decoding of update proposals. #[apply(backends)] -fn test_update_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { - let test_setup = create_encoding_test_setup(backend); +async fn test_update_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { + let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); let alice = test_clients .get("alice") @@ -115,7 +115,8 @@ fn test_update_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { &credential_with_key_and_signer.signer, group_state.ciphersuite(), credential_with_key_and_signer.credential_with_key.clone(), - ); + ) + .await; let mut update: PublicMessage = group_state .create_update_proposal( @@ -147,8 +148,8 @@ fn test_update_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { /// This test tests encoding and decoding of add proposals. #[apply(backends)] -fn test_add_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { - let test_setup = create_encoding_test_setup(backend); +async fn test_add_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { + let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); let alice = test_clients .get("alice") @@ -168,7 +169,8 @@ fn test_add_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { &credential_with_key_and_signer.signer, group_state.ciphersuite(), credential_with_key_and_signer.credential_with_key.clone(), - ); + ) + .await; // Adds let mut add: PublicMessage = group_state @@ -197,8 +199,8 @@ fn test_add_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { /// This test tests encoding and decoding of remove proposals. #[apply(backends)] -fn test_remove_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { - let test_setup = create_encoding_test_setup(backend); +async fn test_remove_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { + let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); let alice = test_clients .get("alice") @@ -240,8 +242,8 @@ fn test_remove_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { /// This test tests encoding and decoding of commit messages. #[apply(backends)] -fn test_commit_encoding(backend: &impl OpenMlsCryptoProvider) { - let test_setup = create_encoding_test_setup(backend); +async fn test_commit_encoding(backend: &impl OpenMlsCryptoProvider) { + let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); let alice = test_clients .get("alice") @@ -263,7 +265,8 @@ fn test_commit_encoding(backend: &impl OpenMlsCryptoProvider) { alice_credential_with_key_and_signer .credential_with_key .clone(), - ); + ) + .await; // Create a few proposals to put into the commit @@ -319,6 +322,7 @@ fn test_commit_encoding(backend: &impl OpenMlsCryptoProvider) { backend, &alice_credential_with_key_and_signer.signer, ) + .await .expect("An unexpected error occurred."); let mut commit: PublicMessage = create_commit_result.commit.into(); commit @@ -340,8 +344,8 @@ fn test_commit_encoding(backend: &impl OpenMlsCryptoProvider) { } #[apply(backends)] -fn test_welcome_message_encoding(backend: &impl OpenMlsCryptoProvider) { - let test_setup = create_encoding_test_setup(backend); +async fn test_welcome_message_encoding(backend: &impl OpenMlsCryptoProvider) { + let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); let alice = test_clients .get("alice") @@ -389,10 +393,12 @@ fn test_welcome_message_encoding(backend: &impl OpenMlsCryptoProvider) { .build(); let create_commit_result = group_state .create_commit(params, backend, &credential_with_key_and_signer.signer) + .await .expect("An unexpected error occurred."); // Alice applies the commit group_state .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own commits"); // Welcome messages @@ -429,6 +435,7 @@ fn test_welcome_message_encoding(backend: &impl OpenMlsCryptoProvider) { backend, ResumptionPskStore::new(1024), ) + .await .is_ok()); } } diff --git a/openmls/src/group/tests/test_external_commit_validation.rs b/openmls/src/group/tests/test_external_commit_validation.rs index d2009e7ccf..a4dbce9dd4 100644 --- a/openmls/src/group/tests/test_external_commit_validation.rs +++ b/openmls/src/group/tests/test_external_commit_validation.rs @@ -33,13 +33,13 @@ use crate::{ // ValSem240: External Commit, inline Proposals: There MUST be at least one ExternalInit proposal. #[apply(ciphersuites_and_backends)] -fn test_valsem240(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem240(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ECValidationTestSetup { mut alice_group, bob_credential, public_message_commit, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Setup let public_message_commit_bad = { @@ -83,6 +83,7 @@ fn test_valsem240(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Negative case let err = alice_group .process_message(backend, ProtocolMessage::from(public_message_commit_bad)) + .await .expect_err("Could process message despite missing external init proposal."); assert_eq!( @@ -95,19 +96,20 @@ fn test_valsem240(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case alice_group .process_message(backend, ProtocolMessage::from(public_message_commit)) + .await .unwrap(); } // ValSem241: External Commit, inline Proposals: There MUST be at most one ExternalInit proposal. #[apply(ciphersuites_and_backends)] -fn test_valsem241(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem241(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let ECValidationTestSetup { mut alice_group, alice_credential: _, bob_credential, public_message_commit, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Setup let public_message_commit_bad = { @@ -146,6 +148,7 @@ fn test_valsem241(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Negative case let err = alice_group .process_message(backend, ProtocolMessage::from(public_message_commit_bad)) + .await .expect_err("Could process message despite second ext. init proposal in commit."); assert_eq!( @@ -158,19 +161,20 @@ fn test_valsem241(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case alice_group .process_message(backend, ProtocolMessage::from(public_message_commit)) + .await .expect("Unexpected error."); } // ValSem242: External Commit must only cover inline proposal in allowlist (ExternalInit, Remove, PreSharedKey) #[apply(ciphersuites_and_backends)] -fn test_valsem242(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem242(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let ECValidationTestSetup { mut alice_group, alice_credential, bob_credential, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Alice has to add Bob first, so that in the external commit, we can have // an Update proposal that comes from a leaf that's actually inside of the @@ -181,12 +185,14 @@ fn test_valsem242(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider Extensions::empty(), backend, bob_credential.clone(), - ); + ) + .await; alice_group .add_members(backend, &alice_credential.signer, &[bob_key_package]) + .await .unwrap(); - alice_group.merge_pending_commit(backend).unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); let verifiable_group_info = alice_group .export_group_info(backend, &alice_credential.signer, true) @@ -203,6 +209,7 @@ fn test_valsem242(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider &[], bob_credential.credential_with_key.clone(), ) + .await .unwrap(); let public_message_commit = { @@ -228,13 +235,15 @@ fn test_valsem242(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider "Charlie".into(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let charlie_key_package = generate_key_package( ciphersuite, Extensions::empty(), backend, charlie_credential, - ); + ) + .await; ProposalOrRef::Proposal(Proposal::Add(AddProposal { key_package: charlie_key_package, @@ -293,6 +302,7 @@ fn test_valsem242(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Negative case let err = alice_group .process_message(backend, public_message_commit_bad) + .await .unwrap_err(); assert_eq!( @@ -305,6 +315,7 @@ fn test_valsem242(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case alice_group .process_message(backend, public_message_commit.clone()) + .await .unwrap(); } } @@ -312,13 +323,13 @@ fn test_valsem242(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // ValSem243: External Commit, inline Remove Proposal: The identity of the // removed leaf are identical to the ones in the path KeyPackage. #[apply(ciphersuites_and_backends)] -fn test_valsem243(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem243(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ECValidationTestSetup { mut alice_group, alice_credential, bob_credential, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Alice has to add Bob first, so that Bob actually creates a remove // proposal to remove his former self. @@ -328,13 +339,15 @@ fn test_valsem243(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider Extensions::empty(), backend, bob_credential.clone(), - ); + ) + .await; alice_group .add_members(backend, &alice_credential.signer, &[bob_key_package]) + .await .unwrap(); - alice_group.merge_pending_commit(backend).unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); // Bob wants to commit externally. @@ -356,6 +369,7 @@ fn test_valsem243(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider &[], bob_credential.credential_with_key, ) + .await .unwrap(); // MlsMessageOut -> MlsMessageIn @@ -430,6 +444,7 @@ fn test_valsem243(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Negative case let err = alice_group .process_message(backend, ProtocolMessage::from(public_message_commit_bad)) + .await .expect_err( "Could process message despite the remove proposal targeting the wrong group member.", ); @@ -450,25 +465,27 @@ fn test_valsem243(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider alice_group.configuration(), &[], alice_credential.credential_with_key, - ); + ) + .await; assert!(alice_new_group.is_ok()); // Positive case alice_group .process_message(backend, ProtocolMessage::from(public_message_commit)) + .await .expect("Unexpected error."); } // ValSem244: External Commit must not include any proposals by reference #[apply(ciphersuites_and_backends)] -fn test_valsem244(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem244(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let ECValidationTestSetup { mut alice_group, bob_credential, public_message_commit, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Setup let public_message_commit_bad = { @@ -485,7 +502,8 @@ fn test_valsem244(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider Extensions::empty(), backend, bob_credential.clone(), - ); + ) + .await; let add_proposal = Proposal::Add(AddProposal { key_package: bob_key_package, @@ -521,6 +539,7 @@ fn test_valsem244(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // commit including an external init proposal by reference. let err = alice_group .process_message(backend, ProtocolMessage::from(public_message_commit_bad)) + .await .unwrap_err(); assert_eq!( @@ -533,19 +552,20 @@ fn test_valsem244(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case alice_group .process_message(backend, ProtocolMessage::from(public_message_commit)) + .await .unwrap(); } // ValSem245: External Commit: MUST contain a path. #[apply(ciphersuites_and_backends)] -fn test_valsem245(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem245(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let ECValidationTestSetup { mut alice_group, bob_credential, public_message_commit, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Setup let public_message_commit_bad = { @@ -579,6 +599,7 @@ fn test_valsem245(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Negative case let err = alice_group .process_message(backend, ProtocolMessage::from(public_message_commit_bad)) + .await .expect_err("Could process message despite missing path."); assert_eq!( @@ -589,19 +610,20 @@ fn test_valsem245(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case alice_group .process_message(backend, ProtocolMessage::from(public_message_commit)) + .await .unwrap(); } // ValSem246: External Commit: The signature of the PublicMessage MUST be verified with the credential of the KeyPackage in the included `path`. #[apply(ciphersuites_and_backends)] -fn test_valsem246(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem246(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let ECValidationTestSetup { mut alice_group, bob_credential, public_message_commit, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Setup let public_message_commit_bad = { @@ -616,7 +638,7 @@ fn test_valsem246(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // the path by generating a new credential for bob, putting it in the path // and then re-signing the message with his original credential. let bob_new_credential = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; // Generate KeyPackage let bob_new_key_package = generate_key_package( @@ -624,7 +646,8 @@ fn test_valsem246(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider Extensions::empty(), backend, bob_new_credential, - ); + ) + .await; if let Some(ref mut path) = commit_bad.path { path.set_leaf_node(bob_new_key_package.leaf_node().clone()) @@ -650,6 +673,7 @@ fn test_valsem246(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Negative case let err = alice_group .process_message(backend, ProtocolMessage::from(public_message_commit_bad)) + .await .expect_err("Could process message despite wrong signature."); // This shows that signature verification fails if the signature is not done @@ -701,19 +725,20 @@ fn test_valsem246(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // correct (which it only is, if alice is using the credential in the path). alice_group .process_message(backend, ProtocolMessage::from(public_message_commit)) + .await .expect("Unexpected error."); } // External Commit should work when group use ciphertext WireFormat #[apply(ciphersuites_and_backends)] -fn test_pure_ciphertest(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_pure_ciphertest(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PrivateMessage let ECValidationTestSetup { mut alice_group, alice_credential, bob_credential, .. - } = validation_test_setup(PURE_CIPHERTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_CIPHERTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Bob wants to commit externally. @@ -733,13 +758,17 @@ fn test_pure_ciphertest(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr &[], bob_credential.credential_with_key.clone(), ) + .await .expect("Error initializing group externally."); let mls_message_in: MlsMessageIn = message.into(); assert_eq!(mls_message_in.wire_format(), WireFormat::PublicMessage); // Would fail if handshake message processing did not distinguish external messages - assert!(alice_group.process_message(backend, mls_message_in).is_ok()); + assert!(alice_group + .process_message(backend, mls_message_in) + .await + .is_ok()); } mod utils { @@ -765,7 +794,7 @@ mod utils { } // Validation test setup - pub(super) fn validation_test_setup( + pub(super) async fn validation_test_setup( wire_format_policy: WireFormatPolicy, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -775,10 +804,10 @@ mod utils { "Alice".into(), ciphersuite.signature_algorithm(), backend, - ); + ).await; let bob_credential = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() @@ -793,6 +822,7 @@ mod utils { &mls_group_config, alice_credential.credential_with_key.clone(), ) + .await .unwrap(); // Bob wants to commit externally. @@ -814,6 +844,7 @@ mod utils { &[], bob_credential.credential_with_key.clone(), ) + .await .unwrap(); let public_message_commit = { diff --git a/openmls/src/group/tests/test_framing.rs b/openmls/src/group/tests/test_framing.rs index 4e50bbf7b6..d9bd3bbbde 100644 --- a/openmls/src/group/tests/test_framing.rs +++ b/openmls/src/group/tests/test_framing.rs @@ -26,7 +26,7 @@ use crate::{ }; #[apply(backends)] -fn padding(backend: &impl OpenMlsCryptoProvider) { +async fn padding(backend: &impl OpenMlsCryptoProvider) { // Create a test config for a single client supporting all possible // ciphersuites. let alice_config = TestClientConfig { @@ -53,7 +53,7 @@ fn padding(backend: &impl OpenMlsCryptoProvider) { }; // Initialize the test setup according to config. - let test_setup = setup(test_setup_config, backend); + let test_setup = setup(test_setup_config, backend).await; let test_clients = test_setup.clients.borrow(); let alice = test_clients @@ -99,7 +99,7 @@ fn padding(backend: &impl OpenMlsCryptoProvider) { /// Check that PrivateMessageContent's padding field is verified to be all-zero. #[apply(ciphersuites_and_backends)] -fn bad_padding(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn bad_padding(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let tests = { // { 2^i } ∪ { 2^i +- 1 } let padding_sizes = [ @@ -121,7 +121,8 @@ fn bad_padding(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { b"Alice".to_vec(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let sender = Sender::build_member(LeafNodeIndex::new(654)); diff --git a/openmls/src/group/tests/test_framing_validation.rs b/openmls/src/group/tests/test_framing_validation.rs index 99c3d04648..9daca17908 100644 --- a/openmls/src/group/tests/test_framing_validation.rs +++ b/openmls/src/group/tests/test_framing_validation.rs @@ -30,7 +30,7 @@ struct ValidationTestSetup { } // Validation test setup -fn validation_test_setup( +async fn validation_test_setup( wire_format_policy: WireFormatPolicy, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -39,10 +39,10 @@ fn validation_test_setup( // Generate credentials with keys let alice_credential = - generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend).await; let bob_credential = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; // Generate KeyPackages let alice_key_package = generate_key_package( @@ -50,14 +50,16 @@ fn validation_test_setup( Extensions::empty(), backend, alice_credential.clone(), - ); + ) + .await; let bob_key_package = generate_key_package( ciphersuite, Extensions::empty(), backend, bob_credential.clone(), - ); + ) + .await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() @@ -73,6 +75,7 @@ fn validation_test_setup( group_id, alice_credential.credential_with_key.clone(), ) + .await .expect("An unexpected error occurred."); // === Alice adds Bob & Bob joins === @@ -82,10 +85,12 @@ fn validation_test_setup( &alice_credential.signer, &[bob_key_package.clone()], ) + .await .expect("Could not add member."); alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); let bob_group = MlsGroup::new_from_welcome( @@ -94,6 +99,7 @@ fn validation_test_setup( welcome.into_welcome().expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("error creating bob's group from welcome"); ValidationTestSetup { @@ -108,7 +114,7 @@ fn validation_test_setup( // ValSem002 Group id #[apply(ciphersuites_and_backends)] -fn test_valsem002(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem002(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, mut bob_group, @@ -116,10 +122,11 @@ fn test_valsem002(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider _bob_credential: _, _alice_key_package: _, _bob_key_package: _, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let (message, _welcome, _group_info) = alice_group .self_update(backend, &_alice_credential.signer) + .await .expect("Could not self-update."); let serialized_message = message @@ -139,6 +146,7 @@ fn test_valsem002(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, message_in) + .await .expect_err("Could parse message despite wrong group ID."); assert_eq!( @@ -149,12 +157,13 @@ fn test_valsem002(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, ProtocolMessage::from(original_message)) + .await .expect("Unexpected error."); } // ValSem003 Epoch #[apply(ciphersuites_and_backends)] -fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, mut bob_group, @@ -162,20 +171,23 @@ fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider _bob_credential: _, _alice_key_package: _, _bob_key_package: _, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Alice needs to create a new message that Bob can process. let (message, _welcome, _group_info) = alice_group .self_update(backend, &_alice_credential.signer) + .await .expect("Could not self update."); - alice_group.merge_pending_commit(backend).unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); alice_group .merge_pending_commit(backend) + .await .expect("Could not merge commit."); let processed_message = bob_group .process_message(backend, message.into_protocol_message().unwrap()) + .await .expect("Could not process message."); if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = @@ -183,6 +195,7 @@ fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider { bob_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging commit."); } else { unreachable!("Expected StagedCommit."); @@ -191,6 +204,7 @@ fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Do a second Commit to increase the epoch number let (message, _welcome, _group_info) = alice_group .self_update(backend, &_alice_credential.signer) + .await .expect("Could not add member."); let current_epoch = alice_group.epoch(); @@ -207,6 +221,7 @@ fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider plaintext.set_epoch(current_epoch.as_u64() + 1); let err = bob_group .process_message(backend, plaintext.clone()) + .await .expect_err("Could parse message despite wrong epoch."); assert_eq!( err, @@ -217,6 +232,7 @@ fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider plaintext.set_epoch(current_epoch.as_u64() - 1); let err = bob_group .process_message(backend, plaintext) + .await .expect_err("Could parse message despite wrong epoch."); assert_eq!( err, @@ -226,6 +242,7 @@ fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case let processed_msg = bob_group .process_message(backend, original_message.clone()) + .await .unwrap(); if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = @@ -233,6 +250,7 @@ fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider { bob_group .merge_staged_commit(backend, *staged_commit) + .await .unwrap(); } else { unreachable!(); @@ -241,14 +259,14 @@ fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Processing a commit twice should fail i.e. an epoch can only be used once in a commit message let process_twice = bob_group.process_message(backend, original_message); assert_eq!( - process_twice.unwrap_err(), + process_twice.await.unwrap_err(), ProcessMessageError::ValidationError(ValidationError::WrongEpoch) ); } // ValSem004 Sender: Member: check the member exists #[apply(ciphersuites_and_backends)] -fn test_valsem004(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem004(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, mut bob_group, @@ -256,10 +274,11 @@ fn test_valsem004(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider _bob_credential: _, _alice_key_package: _, _bob_key_package: _, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let (message, _welcome, _group_info) = alice_group .self_update(backend, &_alice_credential.signer) + .await .expect("Could not self-update."); let serialized_message = message @@ -289,6 +308,7 @@ fn test_valsem004(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, message_in) + .await .expect_err("Could parse message despite wrong sender."); assert_eq!( @@ -299,12 +319,13 @@ fn test_valsem004(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, ProtocolMessage::from(original_message)) + .await .expect("Unexpected error."); } // ValSem005 Application messages must use ciphertext #[apply(ciphersuites_and_backends)] -fn test_valsem005(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem005(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, mut bob_group, @@ -312,10 +333,11 @@ fn test_valsem005(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider _bob_credential: _, _alice_key_package: _, _bob_key_package: _, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let (message, _welcome, _group_info) = alice_group .self_update(backend, &_alice_credential.signer) + .await .expect("Could not self-update."); let serialized_message = message @@ -344,6 +366,7 @@ fn test_valsem005(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, message_in) + .await .expect_err("Could parse message despite unencrypted application message."); assert_eq!( @@ -354,12 +377,13 @@ fn test_valsem005(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, ProtocolMessage::from(original_message)) + .await .expect("Unexpected error."); } // ValSem006 Ciphertext: decryption needs to work #[apply(ciphersuites_and_backends)] -fn test_valsem006(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem006(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, mut bob_group, @@ -367,7 +391,7 @@ fn test_valsem006(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider _bob_credential: _, _alice_key_package: _, _bob_key_package: _, - } = validation_test_setup(PURE_CIPHERTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_CIPHERTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let message = alice_group .create_message(backend, &_alice_credential.signer, &[1, 2, 3]) @@ -390,6 +414,7 @@ fn test_valsem006(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, message_in) + .await .expect_err("Could parse message despite garbled ciphertext."); assert_eq!( @@ -402,12 +427,13 @@ fn test_valsem006(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, ProtocolMessage::from(original_message)) + .await .expect("Unexpected error."); } // ValSem007 Membership tag presence #[apply(ciphersuites_and_backends)] -fn test_valsem007(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem007(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, mut bob_group, @@ -415,10 +441,11 @@ fn test_valsem007(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider _bob_credential: _, _alice_key_package: _, _bob_key_package: _, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let (message, _welcome, _group_info) = alice_group .self_update(backend, &_alice_credential.signer) + .await .expect("Could not self-update."); let serialized_message = message @@ -438,6 +465,7 @@ fn test_valsem007(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, message_in) + .await .expect_err("Could parse message despite missing membership tag."); assert_eq!( @@ -448,12 +476,13 @@ fn test_valsem007(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, ProtocolMessage::from(original_message)) + .await .expect("Unexpected error."); } // ValSem008 Membership tag verification #[apply(ciphersuites_and_backends)] -fn test_valsem008(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem008(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, mut bob_group, @@ -461,11 +490,12 @@ fn test_valsem008(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider _bob_credential: _, _alice_key_package: _, _bob_key_package: _, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Alice needs to create a new message that Bob can process. let (message, _welcome, _group_info) = alice_group .self_update(backend, &_alice_credential.signer) + .await .expect("Could not self-update."); let serialized_message = message @@ -488,6 +518,7 @@ fn test_valsem008(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, message_in) + .await .expect_err("Could process message despite wrong membership tag."); assert_eq!( @@ -498,12 +529,13 @@ fn test_valsem008(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, ProtocolMessage::from(original_message)) + .await .expect("Unexpected error."); } // ValSem009 Confirmation tag presence #[apply(ciphersuites_and_backends)] -fn test_valsem009(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem009(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, mut bob_group, @@ -511,10 +543,11 @@ fn test_valsem009(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider _bob_credential: _, _alice_key_package: _, _bob_key_package: _, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let (message, _welcome, _group_info) = alice_group .self_update(backend, &_alice_credential.signer) + .await .expect("Could not self-update."); let serialized_message = message @@ -543,6 +576,7 @@ fn test_valsem009(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, message_in) + .await .expect_err("Could parse message despite missing confirmation tag."); assert_eq!( @@ -553,12 +587,13 @@ fn test_valsem009(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, ProtocolMessage::from(original_message)) + .await .expect("Unexpected error."); } // ValSem010 Signature verification #[apply(ciphersuites_and_backends)] -fn test_valsem010(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem010(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, mut bob_group, @@ -566,11 +601,12 @@ fn test_valsem010(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider _bob_credential: _, _alice_key_package: _, _bob_key_package: _, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // Alice needs to create a new message that Bob can process. let (message, _welcome, _group_info) = alice_group .self_update(backend, &_alice_credential.signer) + .await .expect("Could not self update."); let serialized_message = message @@ -600,6 +636,7 @@ fn test_valsem010(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let err = bob_group .process_message(backend, message_in) + .await .expect_err("Could process message despite wrong signature."); assert_eq!(err, ProcessMessageError::InvalidSignature); @@ -607,5 +644,6 @@ fn test_valsem010(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, ProtocolMessage::from(original_message)) + .await .expect("Unexpected error."); } diff --git a/openmls/src/group/tests/test_group.rs b/openmls/src/group/tests/test_group.rs index b2d984217a..a778479274 100644 --- a/openmls/src/group/tests/test_group.rs +++ b/openmls/src/group/tests/test_group.rs @@ -16,7 +16,10 @@ use crate::{ }; #[apply(ciphersuites_and_backends)] -fn create_commit_optional_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn create_commit_optional_path( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { let group_aad = b"Alice's test group"; // Framing parameters let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); @@ -26,9 +29,10 @@ fn create_commit_optional_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsC b"Alice".to_vec(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let bob_credential_with_keys = - generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend).await; // Generate Bob's KeyPackage let bob_key_package = generate_key_package( @@ -36,7 +40,8 @@ fn create_commit_optional_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsC Extensions::empty(), backend, bob_credential_with_keys, - ); + ) + .await; // Alice creates a group let mut group_alice = CoreGroup::builder( @@ -45,6 +50,7 @@ fn create_commit_optional_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsC alice_credential_with_keys.credential_with_key, ) .build(backend, &alice_credential_with_keys.signer) + .await .expect("Error creating CoreGroup."); // Alice proposes to add Bob with forced self-update @@ -67,11 +73,14 @@ fn create_commit_optional_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsC .framing_parameters(framing_parameters) .proposal_store(&proposal_store) .build(); - let create_commit_result = match group_alice.create_commit( - params, /* No PSK fetcher */ - backend, - &alice_credential_with_keys.signer, - ) { + let create_commit_result = match group_alice + .create_commit( + params, /* No PSK fetcher */ + backend, + &alice_credential_with_keys.signer, + ) + .await + { Ok(c) => c, Err(e) => panic!("Error creating commit: {e:?}"), }; @@ -104,11 +113,13 @@ fn create_commit_optional_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsC .proposal_store(&proposal_store) .force_self_update(false) .build(); - let create_commit_result = - match group_alice.create_commit(params, backend, &alice_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; + let create_commit_result = match group_alice + .create_commit(params, backend, &alice_credential_with_keys.signer) + .await + { + Ok(c) => c, + Err(e) => panic!("Error creating commit: {e:?}"), + }; let commit = match create_commit_result.commit.content() { FramedContentBody::Commit(commit) => commit, _ => panic!(), @@ -118,12 +129,14 @@ fn create_commit_optional_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsC // Alice applies the Commit without the forced self-update group_alice .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging pending commit"); let ratchet_tree = group_alice.public_group().export_ratchet_tree(); let bob_private_key = backend .key_store() .read::(bob_key_package.hpke_init_key().as_slice()) + .await .unwrap(); let bob_key_package_bundle = KeyPackageBundle { key_package: bob_key_package, @@ -139,7 +152,9 @@ fn create_commit_optional_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsC bob_key_package_bundle, backend, ResumptionPskStore::new(1024), - ) { + ) + .await + { Ok(group) => group, Err(e) => panic!("Error creating group from Welcome: {e:?}"), }; @@ -159,6 +174,7 @@ fn create_commit_optional_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsC backend, &alice_credential_with_keys.signer, ) + .await .unwrap(); let alice_update_proposal = group_alice .create_update_proposal( @@ -184,11 +200,13 @@ fn create_commit_optional_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsC .proposal_store(&proposal_store) .force_self_update(false) .build(); - let create_commit_result = - match group_alice.create_commit(params, backend, &alice_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; + let create_commit_result = match group_alice + .create_commit(params, backend, &alice_credential_with_keys.signer) + .await + { + Ok(c) => c, + Err(e) => panic!("Error creating commit: {e:?}"), + }; let commit = match create_commit_result.commit.content() { FramedContentBody::Commit(commit) => commit, _ => panic!(), @@ -198,11 +216,12 @@ fn create_commit_optional_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsC // Apply UpdateProposal group_alice .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging pending commit"); } #[apply(ciphersuites_and_backends)] -fn basic_group_setup(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn basic_group_setup(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_aad = b"Alice's test group"; // Framing parameters let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); @@ -212,9 +231,10 @@ fn basic_group_setup(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi b"Alice".to_vec(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let bob_credential_with_keys = - generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend).await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -222,7 +242,8 @@ fn basic_group_setup(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi Extensions::empty(), backend, bob_credential_with_keys, - ); + ) + .await; // Alice creates a group let group_alice = CoreGroup::builder( @@ -231,6 +252,7 @@ fn basic_group_setup(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi alice_credential_with_keys.credential_with_key, ) .build(backend, &alice_credential_with_keys.signer) + .await .expect("Error creating CoreGroup."); // Alice adds Bob @@ -251,11 +273,14 @@ fn basic_group_setup(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi .framing_parameters(framing_parameters) .proposal_store(&proposal_store) .build(); - let _commit = match group_alice.create_commit( - params, /* PSK fetcher */ - backend, - &alice_credential_with_keys.signer, - ) { + let _commit = match group_alice + .create_commit( + params, /* PSK fetcher */ + backend, + &alice_credential_with_keys.signer, + ) + .await + { Ok(c) => c, Err(e) => panic!("Error creating commit: {e:?}"), }; @@ -274,7 +299,7 @@ fn basic_group_setup(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi /// - Charlie updates and commits /// - Charlie removes Bob #[apply(ciphersuites_and_backends)] -fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_aad = b"Alice's test group"; // Framing parameters let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); @@ -285,9 +310,10 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid b"Alice".to_vec(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let bob_credential_with_keys = - generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend).await; // Generate KeyPackages let bob_key_package_bundle = KeyPackageBundle::new( @@ -295,7 +321,8 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid &bob_credential_with_keys.signer, ciphersuite, bob_credential_with_keys.credential_with_key.clone(), - ); + ) + .await; let bob_key_package = bob_key_package_bundle.key_package(); // === Alice creates a group === @@ -305,6 +332,7 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid alice_credential_with_keys.credential_with_key.clone(), ) .build(backend, &alice_credential_with_keys.signer) + .await .expect("Error creating CoreGroup."); // === Alice adds Bob === @@ -328,6 +356,7 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid .build(); let create_commit_result = group_alice .create_commit(params, backend, &alice_credential_with_keys.signer) + .await .expect("Error creating commit"); let commit = match create_commit_result.commit.content() { FramedContentBody::Commit(commit) => commit, @@ -339,6 +368,7 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid group_alice .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own commits"); let ratchet_tree = group_alice.public_group().export_ratchet_tree(); @@ -350,7 +380,9 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid bob_key_package_bundle, backend, ResumptionPskStore::new(1024), - ) { + ) + .await + { Ok(group) => group, Err(e) => panic!("Error creating group from Welcome: {e:?}"), }; @@ -412,6 +444,7 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid backend, &alice_credential_with_keys.signer, ) + .await .unwrap(); let update_proposal_bob = group_bob @@ -437,11 +470,13 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid .proposal_store(&proposal_store) .force_self_update(false) .build(); - let create_commit_result = - match group_bob.create_commit(params, backend, &bob_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; + let create_commit_result = match group_bob + .create_commit(params, backend, &bob_credential_with_keys.signer) + .await + { + Ok(c) => c, + Err(e) => panic!("Error creating commit: {e:?}"), + }; // Check that there is a path let commit = match create_commit_result.commit.content() { @@ -454,13 +489,16 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid let staged_commit = group_alice .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("Error applying commit (Alice)"); group_alice .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); group_bob .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own commits"); // Make sure that both groups have the same public tree @@ -479,6 +517,7 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid backend, &alice_credential_with_keys.signer, ) + .await .unwrap(); let update_proposal_alice = group_alice @@ -504,11 +543,14 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid .proposal_store(&proposal_store) .force_self_update(false) .build(); - let create_commit_result = match group_alice.create_commit( - params, /* PSK fetcher */ - backend, - &alice_credential_with_keys.signer, - ) { + let create_commit_result = match group_alice + .create_commit( + params, /* PSK fetcher */ + backend, + &alice_credential_with_keys.signer, + ) + .await + { Ok(c) => c, Err(e) => panic!("Error creating commit: {e:?}"), }; @@ -518,12 +560,15 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid group_alice .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own commits"); let staged_commit = group_bob .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("Error applying commit (Bob)"); group_bob .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); // Make sure that both groups have the same public tree @@ -542,6 +587,7 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid backend, &bob_credential_with_keys.signer, ) + .await .unwrap(); let update_proposal_bob = group_bob @@ -567,17 +613,20 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid .proposal_store(&proposal_store) .force_self_update(false) .build(); - let create_commit_result = - match group_alice.create_commit(params, backend, &alice_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; + let create_commit_result = match group_alice + .create_commit(params, backend, &alice_credential_with_keys.signer) + .await + { + Ok(c) => c, + Err(e) => panic!("Error creating commit: {e:?}"), + }; // Check that there is a path assert!(commit.has_path()); group_alice .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own commits"); proposal_store.add( @@ -596,9 +645,11 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid &[bob_new_leaf_node], backend, ) + .await .expect("Error applying commit (Bob)"); group_bob .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); // Make sure that both groups have the same public tree @@ -612,14 +663,16 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid b"Charlie".to_vec(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let charlie_key_package_bundle = KeyPackageBundle::new( backend, &charlie_credential_with_keys.signer, ciphersuite, charlie_credential_with_keys.credential_with_key.clone(), - ); + ) + .await; let charlie_key_package = charlie_key_package_bundle.key_package().clone(); let add_charlie_proposal_bob = group_bob @@ -645,11 +698,13 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid .proposal_store(&proposal_store) .force_self_update(false) .build(); - let create_commit_result = - match group_bob.create_commit(params, backend, &bob_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; + let create_commit_result = match group_bob + .create_commit(params, backend, &bob_credential_with_keys.signer) + .await + { + Ok(c) => c, + Err(e) => panic!("Error creating commit: {e:?}"), + }; // Check there is no path since there are only Add Proposals and no forced // self-update @@ -663,12 +718,15 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid let staged_commit = group_alice .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("Error applying commit (Alice)"); group_alice .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); group_bob .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own commits"); let ratchet_tree = group_alice.public_group().export_ratchet_tree(); @@ -680,7 +738,9 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid charlie_key_package_bundle, backend, ResumptionPskStore::new(1024), - ) { + ) + .await + { Ok(group) => group, Err(e) => panic!("Error creating group from Welcome: {e:?}"), }; @@ -766,6 +826,7 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid backend, &charlie_credential_with_keys.signer, ) + .await .unwrap(); let update_proposal_charlie = group_charlie @@ -791,11 +852,13 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid .proposal_store(&proposal_store) .force_self_update(false) .build(); - let create_commit_result = - match group_charlie.create_commit(params, backend, &charlie_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; + let create_commit_result = match group_charlie + .create_commit(params, backend, &charlie_credential_with_keys.signer) + .await + { + Ok(c) => c, + Err(e) => panic!("Error creating commit: {e:?}"), + }; // Check that there is a new KeyPackageBundle let commit = match create_commit_result.commit.content() { @@ -806,18 +869,23 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid let staged_commit = group_alice .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("Error applying commit (Alice)"); group_alice .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); let staged_commit = group_bob .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("Error applying commit (Bob)"); group_bob .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); group_charlie .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own commits"); // Make sure that all groups have the same public tree @@ -854,11 +922,14 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid .proposal_store(&proposal_store) .force_self_update(false) .build(); - let create_commit_result = match group_charlie.create_commit( - params, /* PSK fetcher */ - backend, - &charlie_credential_with_keys.signer, - ) { + let create_commit_result = match group_charlie + .create_commit( + params, /* PSK fetcher */ + backend, + &charlie_credential_with_keys.signer, + ) + .await + { Ok(c) => c, Err(e) => panic!("Error creating commit: {e:?}"), }; @@ -868,16 +939,20 @@ fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvid let staged_commit = group_alice .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("Error applying commit (Alice)"); group_alice .merge_commit(backend, staged_commit) + .await .expect("error merging commit"); assert!(group_bob .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], backend) + .await .expect("Could not stage commit.") .self_removed()); group_charlie .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging own commits"); assert_ne!( diff --git a/openmls/src/group/tests/test_past_secrets.rs b/openmls/src/group/tests/test_past_secrets.rs index c3db44fb78..fbfb821a08 100644 --- a/openmls/src/group/tests/test_past_secrets.rs +++ b/openmls/src/group/tests/test_past_secrets.rs @@ -13,7 +13,10 @@ use crate::{ }; #[apply(ciphersuites_and_backends)] -fn test_past_secrets_in_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_past_secrets_in_group( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { // Test this for different parameters for max_epochs in (0..10usize).step_by(2) { let group_id = GroupId::from_slice(b"Test Group"); @@ -23,12 +26,12 @@ fn test_past_secrets_in_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr b"Alice".to_vec(), ciphersuite.signature_algorithm(), backend, - ); + ).await; let bob_credential_with_keys = generate_credential_with_key( b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend, - ); + ).await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -36,7 +39,8 @@ fn test_past_secrets_in_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr Extensions::empty(), backend, bob_credential_with_keys, - ); + ) + .await; // Define the MlsGroup configuration @@ -53,6 +57,7 @@ fn test_past_secrets_in_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr group_id.clone(), alice_credential_with_keys.credential_with_key.clone(), ) + .await .expect("An unexpected error occurred."); // Alice adds Bob @@ -62,10 +67,12 @@ fn test_past_secrets_in_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr &alice_credential_with_keys.signer, &[bob_key_package], ) + .await .expect("An unexpected error occurred."); alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); let mut bob_group = MlsGroup::new_from_welcome( @@ -74,6 +81,7 @@ fn test_past_secrets_in_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr welcome.into_welcome().expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // Generate application message for different epochs @@ -90,12 +98,14 @@ fn test_past_secrets_in_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr let (message, _welcome, _group_info) = alice_group .self_update(backend, &alice_credential_with_keys.signer) + .await .expect("An unexpected error occurred."); update_commits.push(message.clone()); alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); } @@ -104,6 +114,7 @@ fn test_past_secrets_in_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr for update_commit in update_commits { let bob_processed_message = bob_group .process_message(backend, update_commit.into_protocol_message().unwrap()) + .await .expect("An unexpected error occurred."); if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = @@ -111,6 +122,7 @@ fn test_past_secrets_in_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr { bob_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging commit."); } else { unreachable!("Expected a StagedCommit."); @@ -123,6 +135,7 @@ fn test_past_secrets_in_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr for application_message in application_messages.iter().take(max_epochs / 2) { let err = bob_group .process_message(backend, application_message.clone()) + .await .expect_err("An unexpected error occurred."); assert_eq!( err, @@ -136,6 +149,7 @@ fn test_past_secrets_in_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr for application_message in application_messages.iter().skip(max_epochs / 2) { let bob_processed_message = bob_group .process_message(backend, application_message.clone()) + .await .expect("An unexpected error occurred."); if let ProcessedMessageContent::ApplicationMessage(application_message) = diff --git a/openmls/src/group/tests/test_proposal_validation.rs b/openmls/src/group/tests/test_proposal_validation.rs index 291e866f51..8794b5a8dd 100644 --- a/openmls/src/group/tests/test_proposal_validation.rs +++ b/openmls/src/group/tests/test_proposal_validation.rs @@ -33,26 +33,27 @@ use crate::{ }; /// Helper function to generate and output CredentialWithKeyAndSigner and KeyPackage -fn generate_credential_with_key_and_key_package( +async fn generate_credential_with_key_and_key_package( identity: Vec, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) -> (CredentialWithKeyAndSigner, KeyPackage) { let credential_with_key_and_signer = - generate_credential_with_key(identity, ciphersuite.signature_algorithm(), backend); + generate_credential_with_key(identity, ciphersuite.signature_algorithm(), backend).await; let key_package = generate_key_package( ciphersuite, Extensions::empty(), backend, credential_with_key_and_signer.clone(), - ); + ) + .await; (credential_with_key_and_signer, key_package) } /// Helper function to create a group and try to add `members` to it. -fn create_group_with_members( +async fn create_group_with_members( ciphersuite: Ciphersuite, alice_credential_with_key_and_signer: &CredentialWithKeyAndSigner, member_key_packages: &[KeyPackage], @@ -69,6 +70,7 @@ fn create_group_with_members( .credential_with_key .clone(), ) + .await .expect("An unexpected error occurred."); alice_group @@ -77,6 +79,7 @@ fn create_group_with_members( &alice_credential_with_key_and_signer.signer, member_key_packages, ) + .await .map(|(msg, welcome, _group_info)| { ( msg.into(), @@ -93,7 +96,7 @@ struct ProposalValidationTestSetup { } // Creates a standalone group -fn new_test_group( +async fn new_test_group( identity: &str, wire_format_policy: WireFormatPolicy, ciphersuite: Ciphersuite, @@ -103,7 +106,7 @@ fn new_test_group( // Generate credentials with keys let credential_with_key_and_signer = - generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend).await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() @@ -119,30 +122,32 @@ fn new_test_group( group_id, credential_with_key_and_signer.credential_with_key.clone(), ) + .await .unwrap(), credential_with_key_and_signer, ) } // Validation test setup -fn validation_test_setup( +async fn validation_test_setup( wire_format_policy: WireFormatPolicy, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) -> ProposalValidationTestSetup { // === Alice creates a group === let (mut alice_group, alice_credential_with_key_and_signer) = - new_test_group("Alice", wire_format_policy, ciphersuite, backend); + new_test_group("Alice", wire_format_policy, ciphersuite, backend).await; let bob_credential_with_key_and_signer = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; let bob_key_package = generate_key_package( ciphersuite, Extensions::empty(), backend, bob_credential_with_key_and_signer.clone(), - ); + ) + .await; let (_message, welcome, _group_info) = alice_group .add_members( @@ -150,9 +155,10 @@ fn validation_test_setup( &alice_credential_with_key_and_signer.signer, &[bob_key_package], ) + .await .unwrap(); - alice_group.merge_pending_commit(backend).unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() @@ -166,6 +172,7 @@ fn validation_test_setup( welcome.into_welcome().unwrap(), Some(alice_group.export_ratchet_tree().into()), ) + .await .unwrap(); ProposalValidationTestSetup { @@ -231,26 +238,27 @@ enum KeyUniqueness { /// Add Proposal: /// Signature public key in proposals must be unique among proposals #[apply(ciphersuites_and_backends)] -fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { for bob_and_charlie_share_keys in [ KeyUniqueness::NegativeSameKey, KeyUniqueness::PositiveDifferentKey, ] { // 0. Initialize Alice let (alice_credential_with_keys, _) = - generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend); + generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend).await; // 1. Initialize Bob and Charlie let bob_credential_with_keys = generate_credential_with_key( b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend, - ); + ).await; let mut charlie_credential_with_keys = generate_credential_with_key( b"Charlie".to_vec(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; match bob_and_charlie_share_keys { KeyUniqueness::NegativeSameKey => { @@ -275,13 +283,15 @@ fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide Extensions::empty(), backend, bob_credential_with_keys.clone(), - ); + ) + .await; let charlie_key_package = generate_key_package( ciphersuite, Extensions::empty(), backend, charlie_credential_with_keys.clone(), - ); + ) + .await; // 1. Alice creates a group and tries to add Bob and Charlie to it let res = create_group_with_members( @@ -289,17 +299,18 @@ fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide &alice_credential_with_keys, &[bob_key_package, charlie_key_package], backend, - ); + ) + .await; match bob_and_charlie_share_keys { KeyUniqueness::NegativeSameKey => { let err = res.expect_err("was able to add users with the same signature key!"); - assert_eq!( + assert!(matches!( err, AddMembersError::CreateCommitError(CreateCommitError::ProposalValidationError( ProposalValidationError::DuplicateSignatureKey )) - ); + )); } KeyUniqueness::PositiveDifferentKey => { let _ = res.expect("failed to add users with different signature keypairs!"); @@ -316,13 +327,13 @@ fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide alice_credential_with_key_and_signer, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // We now have alice create a commit with an add proposal. Then we // artificially add another add proposal with a different identity, // different hpke public key, but the same signature public key. let (charlie_credential_with_key, charlie_key_package) = - generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend); + generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend).await; // Create the Commit with Add proposal. let serialized_update = alice_group @@ -331,6 +342,7 @@ fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide &alice_credential_with_key_and_signer.signer, &[charlie_key_package], ) + .await .expect("Error creating self-update") .tls_serialize_detached() .expect("Could not serialize message."); @@ -360,6 +372,7 @@ fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .signature_key, }, ) + .await .unwrap(); let second_add_proposal = Proposal::Add(AddProposal { @@ -380,6 +393,7 @@ fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // Have bob process the resulting plaintext let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process message despite modified public key in path."); assert_eq!( @@ -396,6 +410,7 @@ fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // Positive case bob_group .process_message(backend, original_update_plaintext) + .await .expect("Unexpected error."); } @@ -403,18 +418,18 @@ fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide /// Add Proposal: /// HPKE init key in proposals must be unique among proposals #[apply(ciphersuites_and_backends)] -fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { for bob_and_charlie_share_keys in [ KeyUniqueness::NegativeSameKey, KeyUniqueness::PositiveDifferentKey, ] { // 0. Initialize Alice, Bob, and Charlie let (alice_credential_with_key, _) = - generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend); + generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend).await; let (bob_credential_with_key, mut bob_key_package) = - generate_credential_with_key_and_key_package("Bob".into(), ciphersuite, backend); + generate_credential_with_key_and_key_package("Bob".into(), ciphersuite, backend).await; let (_charlie_credential_with_key, charlie_key_package) = - generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend); + generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend).await; match bob_and_charlie_share_keys { KeyUniqueness::NegativeSameKey => { @@ -432,6 +447,7 @@ fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider Extensions::empty(), charlie_key_package.hpke_init_key().as_slice().to_vec(), ) + .await .unwrap(); } KeyUniqueness::PositiveDifferentKey => { @@ -447,17 +463,18 @@ fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider &alice_credential_with_key, &[bob_key_package, charlie_key_package], backend, - ); + ) + .await; match bob_and_charlie_share_keys { KeyUniqueness::NegativeSameKey => { let err = res.expect_err("was able to add users with the same HPKE init key!"); - assert_eq!( + assert!(matches!( err, AddMembersError::CreateCommitError(CreateCommitError::ProposalValidationError( ProposalValidationError::DuplicateInitKey )) - ); + )); } KeyUniqueness::PositiveDifferentKey => { let _ = res.expect("failed to add users with different HPKE init keys!"); @@ -474,13 +491,13 @@ fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider alice_credential_with_key_and_signer, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // We now have alice create a commit with an add proposal. Then we // artificially add another add proposal with a different identity, // different signature key, but the same hpke public key. let (_charlie_credential_with_key, charlie_key_package) = - generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend); + generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend).await; // Create the Commit with Add proposal. let serialized_update = alice_group @@ -489,6 +506,7 @@ fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider &alice_credential_with_key_and_signer.signer, &[charlie_key_package.clone()], ) + .await .expect("Error creating self-update") .tls_serialize_detached() .expect("Could not serialize message."); @@ -505,7 +523,7 @@ fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // a different signature key, different identity, but the same hpke init // key. let (dave_credential_with_key_and_signer, mut dave_key_package) = - generate_credential_with_key_and_key_package("Dave".into(), ciphersuite, backend); + generate_credential_with_key_and_key_package("Dave".into(), ciphersuite, backend).await; // Change the init key and re-sign. dave_key_package.set_init_key(charlie_key_package.hpke_init_key().clone()); let dave_key_package = dave_key_package.resign( @@ -532,6 +550,7 @@ fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Have bob process the resulting plaintext let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process message despite modified encryption key in path."); assert_eq!( @@ -548,6 +567,7 @@ fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, original_update_plaintext) + .await .expect("Unexpected error."); } @@ -556,7 +576,7 @@ fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider /// Signature public key in proposals must be unique among existing group /// members #[apply(ciphersuites_and_backends)] -fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { for alice_and_bob_share_keys in [ KeyUniqueness::NegativeSameKey, KeyUniqueness::PositiveDifferentKey, @@ -564,8 +584,11 @@ fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide ] { // 0. Initialize Alice and Bob let new_kp = || { - openmls_basic_credential::SignatureKeyPair::new(ciphersuite.signature_algorithm()) - .unwrap() + openmls_basic_credential::SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap() }; let shared_signature_keypair = new_kp(); let [alice_credential_with_key, bob_credential_with_key, target_credential_with_key] = @@ -599,13 +622,13 @@ fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide Extensions::empty(), backend, bob_credential_with_key.clone(), - ); + ).await; let target_key_package = generate_key_package( ciphersuite, Extensions::empty(), backend, target_credential_with_key.clone(), - ); + ).await; // 1. Alice creates a group and tries to add Bob to it let mut alice_group = MlsGroup::new_with_group_id( @@ -617,6 +640,7 @@ fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide GroupId::from_slice(b"Alice's Friends"), alice_credential_with_key.credential_with_key.clone(), ) + .await .unwrap(); match alice_and_bob_share_keys { @@ -627,13 +651,14 @@ fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide &alice_credential_with_key.signer, &[bob_key_package, target_key_package], ) + .await .expect_err("was able to add user with same signature key as a group member!"); - assert_eq!( + assert!(matches!( err, AddMembersError::CreateCommitError(CreateCommitError::ProposalValidationError( ProposalValidationError::DuplicateSignatureKey )) - ); + )); } KeyUniqueness::PositiveDifferentKey => { alice_group @@ -642,6 +667,7 @@ fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide &alice_credential_with_key.signer, &[bob_key_package, target_key_package], ) + .await .expect("failed to add user with different signature keypair!"); } KeyUniqueness::PositiveSameKeyWithRemove => { @@ -651,8 +677,9 @@ fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide &alice_credential_with_key.signer, &[bob_key_package.clone()], ) + .await .unwrap(); - alice_group.merge_pending_commit(backend).unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); let bob_index = alice_group .members() .find_map(|member| { @@ -668,6 +695,7 @@ fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .unwrap(); alice_group .add_members(backend, &alice_credential_with_key.signer, &[target_key_package]) + .await .expect( "failed to add a user with the same identity as someone in the group (with a remove proposal)!", ); @@ -811,16 +839,16 @@ fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide /// ValSem104: /// Add Proposal: Init key and encryption key must be different #[apply(ciphersuites_and_backends)] -fn test_valsem103_valsem104(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem103_valsem104(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { for alice_and_bob_share_keys in [ KeyUniqueness::NegativeSameKey, KeyUniqueness::PositiveDifferentKey, ] { // 0. Initialize Alice and Bob let (alice_credential_with_key, _) = - generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend); + generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend).await; let (bob_credential_with_key, mut bob_key_package) = - generate_credential_with_key_and_key_package("Bob".into(), ciphersuite, backend); + generate_credential_with_key_and_key_package("Bob".into(), ciphersuite, backend).await; match alice_and_bob_share_keys { KeyUniqueness::NegativeSameKey => { @@ -851,18 +879,19 @@ fn test_valsem103_valsem104(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp &alice_credential_with_key, &[bob_key_package], backend, - ); + ) + .await; match alice_and_bob_share_keys { KeyUniqueness::NegativeSameKey => { let err = res.expect_err("was able to add user with colliding init and encryption keys!"); - assert_eq!( + assert!(matches!( err, AddMembersError::CreateCommitError(CreateCommitError::ProposalValidationError( ProposalValidationError::InitEncryptionKeyCollision )) - ); + )); } KeyUniqueness::PositiveDifferentKey => { let _ = res.expect("failed to add user with different HPKE init key!"); @@ -878,7 +907,7 @@ fn test_valsem103_valsem104(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp alice_credential_with_key_and_signer, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // We now have alice create a commit. Then we artificially add an Add // proposal with a leaf that has the same encryption key as an existing leaf. @@ -886,6 +915,7 @@ fn test_valsem103_valsem104(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp // Create the Commit. let serialized_update = alice_group .self_update(backend, &alice_credential_with_key_and_signer.signer) + .await .expect("Error creating self-update") .tls_serialize_detached() .expect("Could not serialize message."); @@ -908,7 +938,7 @@ fn test_valsem103_valsem104(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp // Generate fresh key material for Dave. let (dave_credential_with_key, _) = - generate_credential_with_key_and_key_package("Dave".into(), ciphersuite, backend); + generate_credential_with_key_and_key_package("Dave".into(), ciphersuite, backend).await; // Insert Bob's public key into Dave's KPB and resign. let dave_key_package = KeyPackage::new_from_encryption_key( @@ -924,6 +954,7 @@ fn test_valsem103_valsem104(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp Extensions::empty(), bob_encryption_key, ) + .await .unwrap(); // Use the resulting KP to create an Add proposal. @@ -947,6 +978,7 @@ fn test_valsem103_valsem104(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp // Have bob process the resulting plaintext let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process message despite modified public key in path."); assert_eq!( @@ -963,6 +995,7 @@ fn test_valsem103_valsem104(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp // Positive case bob_group .process_message(backend, original_update_plaintext) + .await .expect("Unexpected error."); } @@ -989,7 +1022,7 @@ enum ProposalInclusion { /// Add Proposal: /// Ciphersuite & protocol version must match the group #[apply(ciphersuites_and_backends)] -fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let _ = pretty_env_logger::try_init(); // Ciphersuite & protocol version validation includes checking the @@ -1026,10 +1059,10 @@ fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider alice_credential_with_key_and_signer, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let (charlie_credential_with_key, mut charlie_key_package) = - generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend); + generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend).await; let kpi = KeyPackageIn::from(charlie_key_package.clone()); kpi.validate(backend.crypto(), ProtocolVersion::Mls10) @@ -1077,7 +1110,7 @@ fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider "Charlie".into(), ciphersuite, backend, - ); + ).await; // Let's just pick a ciphersuite that's not the one we're testing right now. let wrong_ciphersuite = match ciphersuite { @@ -1128,10 +1161,12 @@ fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider ) .unwrap(); - let result = alice_group.commit_to_pending_proposals( - backend, - &alice_credential_with_key_and_signer.signer, - ); + let result = alice_group + .commit_to_pending_proposals( + backend, + &alice_credential_with_key_and_signer.signer, + ) + .await; // The error types differ, so we have to check the error inside the `match`. match key_package_version { @@ -1139,26 +1174,39 @@ fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider result.unwrap(); } _ => { - matches!( - result.unwrap_err(), - CommitToPendingProposalsError::CreateCommitError(_) - ); + assert!(matches!( + result.expect_err( + "no error when committing add with key package with insufficient capabilities", + ), + CommitToPendingProposalsError::CreateCommitError( + _ + ) + )) } } } ProposalInclusion::ByValue => { - let result = alice_group.add_members( - backend, - &alice_credential_with_key_and_signer.signer, - &[test_kp_2.clone()], - ); + let result = alice_group + .add_members( + backend, + &alice_credential_with_key_and_signer.signer, + &[test_kp_2.clone()], + ) + .await; match key_package_version { KeyPackageTestVersion::ValidTestCase => { result.unwrap(); } _ => { - matches!(result.unwrap_err(), AddMembersError::CreateCommitError(_)); + assert!(matches!( + result.expect_err( + "no error when committing add with key package with insufficient capabilities", + ), + AddMembersError::CreateCommitError( + _ + ) + )) } } } @@ -1172,6 +1220,7 @@ fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Create the Commit. let serialized_update = alice_group .self_update(backend, &alice_credential_with_key_and_signer.signer) + .await .unwrap() .tls_serialize_detached() .unwrap(); @@ -1225,6 +1274,7 @@ fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Have bob process the resulting plaintext let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process message despite injected add proposal."); match key_package_version { @@ -1308,6 +1358,7 @@ fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, original_update_plaintext) + .await .unwrap(); } @@ -1319,7 +1370,7 @@ fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider /// Remove Proposal: /// Removed member must be unique among proposals #[apply(ciphersuites_and_backends)] -fn test_valsem107(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem107(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Helper function to unwrap a commit with a single proposal from an mls message. fn unwrap_specific_commit(commit_ref_remove: MlsMessageOut) -> Commit { let serialized_message = commit_ref_remove.tls_serialize_detached().unwrap(); @@ -1347,7 +1398,7 @@ fn test_valsem107(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider alice_credential_with_key_and_signer, bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // We first try to make Alice create a commit with two remove proposals for // Bob. @@ -1386,6 +1437,7 @@ fn test_valsem107(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // that contains only one remove proposal. let (commit_ref_remove, _welcome, _group_info) = alice_group .commit_to_pending_proposals(backend, &alice_credential_with_key_and_signer.signer) + .await .expect("error while trying to commit to colliding remove proposals"); // Clear commit to try another way of committing two identical removes. @@ -1398,6 +1450,7 @@ fn test_valsem107(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider &alice_credential_with_key_and_signer.signer, &[bob_leaf_index, bob_leaf_index], ) + .await .expect("error while trying to remove the same member twice"); // Check commit with referenced remove proposals. @@ -1466,7 +1519,7 @@ fn test_valsem107(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider /// Remove Proposal: /// Removed member must be an existing group member #[apply(ciphersuites_and_backends)] -fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Before we can test creation or reception of (invalid) proposals, we set // up a new group with Alice and Bob. let ProposalValidationTestSetup { @@ -1474,7 +1527,7 @@ fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider alice_credential_with_key_and_signer, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // We first try to make Alice create a commit with a proposal targeting a // non-existing group member. @@ -1494,6 +1547,7 @@ fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider .expect_err("Successfully created remove proposal for leaf not in the tree"); let _ = alice_group .commit_to_pending_proposals(backend, &alice_credential_with_key_and_signer.signer) + .await .expect("No error while committing empty proposals"); // FIXME: #1098 This shouldn't be necessary. Something is broken in the state logic. alice_group.clear_pending_commit(); @@ -1519,14 +1573,15 @@ fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider &alice_credential_with_key_and_signer.signer, &[fake_leaf_index], ) + .await .expect_err("no error while trying to remove non-group-member"); - assert_eq!( + assert!(matches!( err, RemoveMembersError::CreateCommitError(CreateCommitError::ProposalValidationError( ProposalValidationError::UnknownMemberRemoval )) - ); + )); // We now have alice create a commit. Then we artificially add an invalid // remove proposal targeting a member that is not part of the group. @@ -1534,6 +1589,7 @@ fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Create the Commit. let serialized_update = alice_group .self_update(backend, &alice_credential_with_key_and_signer.signer) + .await .expect("Error creating self-update") .tls_serialize_detached() .expect("Could not serialize message."); @@ -1567,6 +1623,7 @@ fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Have bob process the resulting plaintext let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process message despite modified public key in path."); assert_eq!( @@ -1583,6 +1640,7 @@ fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, original_update_plaintext) + .await .expect("Unexpected error."); } @@ -1590,7 +1648,7 @@ fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider /// Update Proposal: /// Encryption key must be unique among existing members #[apply(ciphersuites_and_backends)] -fn test_valsem110(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem110(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Before we can test creation or reception of (invalid) proposals, we set // up a new group with Alice and Bob. let ProposalValidationTestSetup { @@ -1598,7 +1656,7 @@ fn test_valsem110(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider alice_credential_with_key_and_signer, mut bob_group, bob_credential_with_key_and_signer, - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // We can't test this by having Alice propose an update herself, so we have // to have Bob propose the update. This is due to the commit logic filtering @@ -1639,12 +1697,14 @@ fn test_valsem110(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider &bob_credential_with_key_and_signer.signer, Some(update_leaf_node.clone()), ) + .await .map(|(out, _)| MlsMessageIn::from(out)) .expect("error while creating remove proposal"); // Have Alice process this proposal. if let ProcessedMessageContent::ProposalMessage(proposal) = alice_group .process_message(backend, update_proposal) + .await .expect("error processing proposal") .into_content() { @@ -1656,16 +1716,17 @@ fn test_valsem110(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // This should fail, since the hpke keys collide. let err = alice_group .commit_to_pending_proposals(backend, &alice_credential_with_key_and_signer.signer) + .await .expect_err("no error while trying to commit to update proposal with differing identity"); - assert_eq!( + assert!(matches!( err, CommitToPendingProposalsError::CreateCommitError( CreateCommitError::ProposalValidationError( ProposalValidationError::DuplicateEncryptionKey ) ) - ); + )); // Clear commit to see if Bob will process a commit containing two colliding // keys. @@ -1678,6 +1739,7 @@ fn test_valsem110(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Create the Commit. let serialized_update = alice_group .self_update(backend, &alice_credential_with_key_and_signer.signer) + .await .expect("Error creating self-update") .tls_serialize_detached() .expect("Could not serialize message."); @@ -1710,14 +1772,16 @@ fn test_valsem110(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let leaf_keypair = alice_group .group() .read_epoch_keypairs(backend) + .await .into_iter() .find(|keypair| keypair.public_key() == &alice_encryption_key) .unwrap(); - leaf_keypair.write_to_key_store(backend).unwrap(); + leaf_keypair.write_to_key_store(backend).await.unwrap(); // Have bob process the resulting plaintext let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process message despite modified public key in path."); assert_eq!( @@ -1730,7 +1794,7 @@ fn test_valsem110(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider /// Update Proposal: /// The sender of a full Commit must not include own update proposals #[apply(ciphersuites_and_backends)] -fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Before we can test creation or reception of (invalid) proposals, we set // up a new group with Alice and Bob. let ProposalValidationTestSetup { @@ -1738,7 +1802,7 @@ fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider alice_credential_with_key_and_signer, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // We can't test this by having Alice propose an update herself. This is due // to the commit logic filtering out own proposals and just including a path @@ -1754,7 +1818,8 @@ fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider Extensions::empty(), backend, alice_credential_with_key_and_signer.clone(), - ); + ) + .await; let update_proposal = Proposal::Update(UpdateProposal { leaf_node: update_kp.leaf_node().clone(), @@ -1764,6 +1829,7 @@ fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // proposals, just a path. let commit = alice_group .self_update(backend, &alice_credential_with_key_and_signer.signer) + .await .expect("Error creating self-update"); // Check that there's no proposal in it. @@ -1812,6 +1878,7 @@ fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Have bob process the resulting plaintext let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process message despite modified public key in path."); assert_eq!( @@ -1839,6 +1906,7 @@ fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider let commit = alice_group .self_update(backend, &alice_credential_with_key_and_signer.signer) + .await .expect("Error creating self-update"); let serialized_update = commit @@ -1872,6 +1940,7 @@ fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Have bob process the resulting plaintext let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could process message despite modified public key in path."); assert_eq!( @@ -1888,6 +1957,7 @@ fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, original_update_plaintext) + .await .expect("Unexpected error."); } @@ -1895,7 +1965,7 @@ fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider /// Update Proposal: /// The sender of a standalone update proposal must be of type member #[apply(ciphersuites_and_backends)] -fn test_valsem112(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem112(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Before we can test creation or reception of (invalid) proposals, we set // up a new group with Alice and Bob. let ProposalValidationTestSetup { @@ -1903,7 +1973,7 @@ fn test_valsem112(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider alice_credential_with_key_and_signer, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; // This can really only be tested by the receiver, as there is no way to // make a client create a proposal with a different sender type than @@ -1913,6 +1983,7 @@ fn test_valsem112(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // manually. let commit = alice_group .propose_self_update(backend, &alice_credential_with_key_and_signer.signer, None) + .await .expect("Error creating self-update"); // Check that the sender type is indeed `member`. @@ -1938,6 +2009,7 @@ fn test_valsem112(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Have bob process the resulting plaintext let err = bob_group .process_message(backend, update_message_in) + .await .expect_err("Could parse message despite modified public key in path."); assert_eq!( @@ -1952,19 +2024,20 @@ fn test_valsem112(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider // Positive case bob_group .process_message(backend, ProtocolMessage::from(original_plaintext)) + .await .expect("Unexpected error."); } // --- PreSharedKey Proposals --- #[apply(ciphersuites_and_backends)] -fn test_valsem401_valsem402(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_valsem401_valsem402(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ProposalValidationTestSetup { mut alice_group, alice_credential_with_key_and_signer, mut bob_group, .. - } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend); + } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let alice_backend = OpenMlsRustCrypto::default(); let bob_backend = OpenMlsRustCrypto::default(); @@ -2051,9 +2124,11 @@ fn test_valsem401_valsem402(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp for psk_id in psk_ids { psk_id .write_to_key_store(&alice_backend, ciphersuite, b"irrelevant") + .await .unwrap(); psk_id .write_to_key_store(&bob_backend, ciphersuite, b"irrelevant") + .await .unwrap(); let (psk_proposal, _) = alice_group @@ -2072,6 +2147,7 @@ fn test_valsem401_valsem402(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp &alice_backend, &alice_credential_with_key_and_signer.signer, ) + .await .unwrap(); alice_group.clear_pending_proposals(); @@ -2080,6 +2156,7 @@ fn test_valsem401_valsem402(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp for psk_proposal in proposals.into_iter() { let processed_message = bob_group .process_message(&bob_backend, psk_proposal.into_protocol_message().unwrap()) + .await .unwrap(); match processed_message.into_content() { @@ -2094,6 +2171,7 @@ fn test_valsem401_valsem402(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp expected_error, bob_group .process_message(&bob_backend, commit.into_protocol_message().unwrap()) + .await .unwrap_err(), ); diff --git a/openmls/src/group/tests/test_remove_operation.rs b/openmls/src/group/tests/test_remove_operation.rs index 2f4425636c..c744142475 100644 --- a/openmls/src/group/tests/test_remove_operation.rs +++ b/openmls/src/group/tests/test_remove_operation.rs @@ -11,7 +11,10 @@ use openmls_rust_crypto::OpenMlsRustCrypto; // Tests the different variants of the RemoveOperation enum. #[apply(ciphersuites_and_backends)] -fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_remove_operation_variants( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { let _ = backend; let alice_backend = OpenMlsRustCrypto::default(); let bob_backend = OpenMlsRustCrypto::default(); @@ -32,19 +35,22 @@ fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenM "Alice".into(), ciphersuite.signature_algorithm(), &alice_backend, - ); + ) + .await; let bob_credential_with_key_and_signer = generate_credential_with_key( "Bob".into(), ciphersuite.signature_algorithm(), &bob_backend, - ); + ) + .await; let charlie_credential_with_key_and_signer = generate_credential_with_key( "Charlie".into(), ciphersuite.signature_algorithm(), &charlie_backend, - ); + ) + .await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -52,13 +58,15 @@ fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenM Extensions::empty(), &bob_backend, bob_credential_with_key_and_signer.clone(), - ); + ) + .await; let charlie_key_package = generate_key_package( ciphersuite, Extensions::empty(), &charlie_backend, charlie_credential_with_key_and_signer, - ); + ) + .await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfigBuilder::new() @@ -73,6 +81,7 @@ fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenM group_id, alice_credential_with_key_and_signer.credential_with_key, ) + .await .expect("An unexpected error occurred."); // === Alice adds Bob & Charlie === @@ -83,9 +92,11 @@ fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenM &alice_credential_with_key_and_signer.signer, &[bob_key_package, charlie_key_package], ) + .await .expect("An unexpected error occurred."); alice_group .merge_pending_commit(&alice_backend) + .await .expect("error merging pending commit"); let welcome = welcome.into_welcome().expect("Unexpected message type."); @@ -96,6 +107,7 @@ fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenM welcome.clone(), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); let mut charlie_group = MlsGroup::new_from_welcome( @@ -104,6 +116,7 @@ fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenM welcome, Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // === Remove operation === @@ -120,6 +133,7 @@ fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenM &alice_credential_with_key_and_signer.signer, &[bob_index], ) + .await .expect("Could not remove members."), // Bob leaves TestCase::Leave => { @@ -135,6 +149,7 @@ fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenM &charlie_backend, message.clone().into_protocol_message().unwrap(), ) + .await .expect("Could not process message."); match processed_message.into_content() { @@ -151,6 +166,7 @@ fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenM &alice_backend, &alice_credential_with_key_and_signer.signer, ) + .await .expect("An unexpected error occurred.") } }; @@ -197,6 +213,7 @@ fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenM &bob_backend, message.clone().into_protocol_message().unwrap(), ) + .await .expect("Could not process message."); match bob_processed_message.into_content() { @@ -248,6 +265,7 @@ fn test_remove_operation_variants(ciphersuite: Ciphersuite, backend: &impl OpenM let charlie_processed_message = charlie_group .process_message(&charlie_backend, message.into_protocol_message().unwrap()) + .await .expect("Could not process message."); match charlie_processed_message.into_content() { diff --git a/openmls/src/group/tests/test_wire_format_policy.rs b/openmls/src/group/tests/test_wire_format_policy.rs index bb334d256e..cc9db46cf3 100644 --- a/openmls/src/group/tests/test_wire_format_policy.rs +++ b/openmls/src/group/tests/test_wire_format_policy.rs @@ -16,7 +16,7 @@ use super::utils::{ }; // Creates a group with one member -fn create_group( +async fn create_group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, wire_format_policy: WireFormatPolicy, @@ -25,7 +25,7 @@ fn create_group( // Generate credentials with keys let credential_with_key_and_signer = - generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend).await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() @@ -42,13 +42,14 @@ fn create_group( group_id, credential_with_key_and_signer.credential_with_key.clone(), ) + .await .expect("An unexpected error occurred."), credential_with_key_and_signer, ) } // Takes an existing group, adds a new member and sends a message from the second member to the first one, returns that message -fn receive_message( +async fn receive_message( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, alice_group: &mut MlsGroup, @@ -56,7 +57,7 @@ fn receive_message( ) -> MlsMessageIn { // Generate credentials with keys let bob_credential_with_key_and_signer = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend); + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -64,14 +65,17 @@ fn receive_message( Extensions::empty(), backend, bob_credential_with_key_and_signer.clone(), - ); + ) + .await; let (_message, welcome, _group_info) = alice_group .add_members(backend, alice_signer, &[bob_key_package]) + .await .expect("Could not add member."); alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); let mls_group_config = MlsGroupConfig::builder() @@ -85,35 +89,39 @@ fn receive_message( welcome.into_welcome().expect("Unexpected message type."), None, ) + .await .expect("error creating bob's group from welcome"); let (message, _welcome, _group_info) = bob_group .self_update(backend, &bob_credential_with_key_and_signer.signer) + .await .expect("An unexpected error occurred."); message.into() } // Test positive cases with all valid (pure & mixed) policies #[apply(ciphersuites_and_backends)] -fn test_wire_policy_positive(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_wire_policy_positive(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { for wire_format_policy in WIRE_FORMAT_POLICIES.iter() { let (mut alice_group, alice_credential_with_key_and_signer) = - create_group(ciphersuite, backend, *wire_format_policy); + create_group(ciphersuite, backend, *wire_format_policy).await; let message = receive_message( ciphersuite, backend, &mut alice_group, &alice_credential_with_key_and_signer.signer, - ); + ) + .await; alice_group .process_message(backend, message) + .await .expect("An unexpected error occurred."); } } // Test negative cases with only icompatible policies #[apply(ciphersuites_and_backends)] -fn test_wire_policy_negative(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_wire_policy_negative(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // All combinations that are not part of WIRE_FORMAT_POLICIES let incompatible_policies = vec![ WireFormatPolicy::new( @@ -127,15 +135,17 @@ fn test_wire_policy_negative(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry ]; for wire_format_policy in incompatible_policies.into_iter() { let (mut alice_group, alice_credential_with_key_and_signer) = - create_group(ciphersuite, backend, wire_format_policy); + create_group(ciphersuite, backend, wire_format_policy).await; let message = receive_message( ciphersuite, backend, &mut alice_group, &alice_credential_with_key_and_signer.signer, - ); + ) + .await; let err = alice_group .process_message(backend, message) + .await .expect_err("An unexpected error occurred."); assert_eq!(err, ProcessMessageError::IncompatibleWireFormat); } diff --git a/openmls/src/group/tests/utils.rs b/openmls/src/group/tests/utils.rs index 68b8691dc3..cefe0fea03 100644 --- a/openmls/src/group/tests/utils.rs +++ b/openmls/src/group/tests/utils.rs @@ -80,7 +80,10 @@ pub(crate) struct TestSetup { const KEY_PACKAGE_COUNT: usize = 10; /// The setup function creates a set of groups and clients. -pub(crate) fn setup(config: TestSetupConfig, backend: &impl OpenMlsCryptoProvider) -> TestSetup { +pub(crate) async fn setup( + config: TestSetupConfig, + backend: &impl OpenMlsCryptoProvider, +) -> TestSetup { let mut test_clients: HashMap<&'static str, RefCell> = HashMap::new(); let mut key_store: HashMap<(&'static str, Ciphersuite), Vec> = HashMap::new(); // Initialize the clients for which we have configurations. @@ -96,7 +99,8 @@ pub(crate) fn setup(config: TestSetupConfig, backend: &impl OpenMlsCryptoProvide client.name.as_bytes().to_vec(), ciphersuite.signature_algorithm(), backend, - ); + ) + .await; // Create a number of key packages. let mut key_packages = Vec::new(); for _ in 0..KEY_PACKAGE_COUNT { @@ -105,7 +109,8 @@ pub(crate) fn setup(config: TestSetupConfig, backend: &impl OpenMlsCryptoProvide &credentia_with_key_and_signer.signer, ciphersuite, credentia_with_key_and_signer.credential_with_key.clone(), - ); + ) + .await; key_packages.push(key_package_bundle.key_package().clone()); key_package_bundles.push(key_package_bundle); } @@ -146,6 +151,7 @@ pub(crate) fn setup(config: TestSetupConfig, backend: &impl OpenMlsCryptoProvide ) .with_config(group_config.config) .build(backend, &credential_with_key_and_signer.signer) + .await .expect("Error creating new CoreGroup"); let mut proposal_list = Vec::new(); let group_aad = b""; @@ -203,6 +209,7 @@ pub(crate) fn setup(config: TestSetupConfig, backend: &impl OpenMlsCryptoProvide .build(); let create_commit_result = core_group .create_commit(params, backend, &credential_with_key_and_signer.signer) + .await .expect("An unexpected error occurred."); let welcome = create_commit_result .welcome_option @@ -214,6 +221,7 @@ pub(crate) fn setup(config: TestSetupConfig, backend: &impl OpenMlsCryptoProvide create_commit_result.staged_commit, &mut proposal_store, ) + .await .expect("Error merging commit."); // Distribute the Welcome message to the other members. @@ -263,7 +271,9 @@ pub(crate) fn setup(config: TestSetupConfig, backend: &impl OpenMlsCryptoProvide key_package_bundle, backend, ResumptionPskStore::new(1024), - ) { + ) + .await + { Ok(group) => group, Err(err) => panic!("Error creating new group from Welcome: {err:?}"), }; @@ -299,7 +309,7 @@ fn test_random() { } #[apply(backends)] -fn test_setup(backend: &impl OpenMlsCryptoProvider) { +async fn test_setup(backend: &impl OpenMlsCryptoProvider) { let test_client_config_a = TestClientConfig { name: "TestClientConfigA", ciphersuites: vec![Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519], @@ -328,15 +338,19 @@ pub(crate) struct CredentialWithKeyAndSigner { } // Helper function to generate a CredentialWithKeyAndSigner -pub(crate) fn generate_credential_with_key( +pub(crate) async fn generate_credential_with_key( identity: Vec, signature_scheme: SignatureScheme, backend: &impl OpenMlsCryptoProvider, ) -> CredentialWithKeyAndSigner { let (credential, signer) = { let credential = Credential::new(identity, CredentialType::Basic).unwrap(); - let signature_keys = SignatureKeyPair::new(signature_scheme).unwrap(); - signature_keys.store(backend.key_store()).unwrap(); + let signature_keys = SignatureKeyPair::new( + signature_scheme, + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); + signature_keys.store(backend.key_store()).await.unwrap(); (credential, signature_keys) }; @@ -353,7 +367,7 @@ pub(crate) fn generate_credential_with_key( } // Helper function to generate a KeyPackageBundle -pub(crate) fn generate_key_package( +pub(crate) async fn generate_key_package( ciphersuite: Ciphersuite, extensions: Extensions, backend: &impl OpenMlsCryptoProvider, @@ -370,6 +384,7 @@ pub(crate) fn generate_key_package( &credential_with_keys.signer, credential_with_keys.credential_with_key, ) + .await .unwrap() } diff --git a/openmls/src/key_packages/mod.rs b/openmls/src/key_packages/mod.rs index aab7c05cc7..6294c0e75c 100644 --- a/openmls/src/key_packages/mod.rs +++ b/openmls/src/key_packages/mod.rs @@ -246,7 +246,8 @@ impl KeyPackage { .map_err(LibraryError::unexpected_crypto_error)?; let init_key = backend .crypto() - .derive_hpke_keypair(config.ciphersuite.hpke_config(), ikm.as_slice()); + .derive_hpke_keypair(config.ciphersuite.hpke_config(), ikm.as_slice()) + .map_err(LibraryError::unexpected_crypto_error)?; let (key_package, encryption_keypair) = Self::new_from_keys( config, backend, @@ -316,16 +317,18 @@ impl KeyPackage { } /// Delete this key package and its private key from the key store. - pub fn delete( + pub async fn delete( &self, backend: &impl OpenMlsCryptoProvider, ) -> Result<(), KeyStore::Error> { backend .key_store() - .delete::(self.hash_ref(backend.crypto()).unwrap().as_slice())?; + .delete::(self.hash_ref(backend.crypto()).unwrap().as_slice()) + .await?; backend .key_store() .delete::(self.hpke_init_key().as_slice()) + .await } /// Get a reference to the extensions of this key package. @@ -391,7 +394,7 @@ impl KeyPackage { #[allow(clippy::too_many_arguments)] impl KeyPackage { /// Generate a new key package with a given init key - pub fn new_from_init_key( + pub async fn new_from_init_key( config: CryptoConfig, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -421,11 +424,13 @@ impl KeyPackage { key_package.hash_ref(backend.crypto())?.as_slice(), &key_package, ) + .await .map_err(KeyPackageNewError::KeyStoreError)?; // Store the encryption key pair in the key store. encryption_key_pair .write_to_key_store(backend) + .await .map_err(KeyPackageNewError::KeyStoreError)?; Ok(key_package) @@ -435,7 +440,7 @@ impl KeyPackage { /// provided `encryption_key`. #[cfg(test)] #[allow(clippy::too_many_arguments)] - pub(crate) fn new_from_encryption_key( + pub(crate) async fn new_from_encryption_key( config: CryptoConfig, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -449,13 +454,15 @@ impl KeyPackage { let ikm = Secret::random(config.ciphersuite, backend, config.version).unwrap(); let init_key = backend .crypto() - .derive_hpke_keypair(config.ciphersuite.hpke_config(), ikm.as_slice()); + .derive_hpke_keypair(config.ciphersuite.hpke_config(), ikm.as_slice()) + .map_err(LibraryError::unexpected_crypto_error)?; // Store the private part of the init_key into the key store. // The key is the public key. backend .key_store() - .store::(&init_key.public, &init_key.private) + .store::(&init_key.public, &init_key.private.into()) + .await .map_err(KeyPackageNewError::KeyStoreError)?; // We don't need the private key here. It's stored in the key store for @@ -489,6 +496,7 @@ impl KeyPackage { key_package.hash_ref(backend.crypto())?.as_slice(), &key_package, ) + .await .map_err(KeyPackageNewError::KeyStoreError)?; Ok(key_package) @@ -612,7 +620,7 @@ impl KeyPackageBuilder { } /// Finalize and build the key package. - pub fn build( + pub async fn build( self, config: CryptoConfig, backend: &impl OpenMlsCryptoProvider, @@ -642,18 +650,24 @@ impl KeyPackageBuilder { key_package.hash_ref(backend.crypto())?.as_slice(), &key_package, ) + .await .map_err(KeyPackageNewError::KeyStoreError)?; // Store the encryption key pair in the key store. encryption_keypair .write_to_key_store(backend) + .await .map_err(KeyPackageNewError::KeyStoreError)?; // Store the private part of the init_key into the key store. // The key is the public key. backend .key_store() - .store::(key_package.hpke_init_key().as_slice(), &init_private_key) + .store::( + key_package.hpke_init_key().as_slice(), + &init_private_key.into(), + ) + .await .map_err(KeyPackageNewError::KeyStoreError)?; Ok(key_package) @@ -672,7 +686,7 @@ pub(crate) struct KeyPackageBundle { // Public `KeyPackageBundle` functions. impl KeyPackageBundle { /// Get a reference to the public part of this bundle, i.e. the [`KeyPackage`]. - pub(crate) fn key_package(&self) -> &KeyPackage { + pub fn key_package(&self) -> &KeyPackage { &self.key_package } @@ -684,7 +698,7 @@ impl KeyPackageBundle { #[cfg(test)] impl KeyPackageBundle { - pub(crate) fn new( + pub(crate) async fn new( backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, ciphersuite: Ciphersuite, @@ -700,10 +714,12 @@ impl KeyPackageBundle { signer, credential_with_key, ) + .await .unwrap(); let private_key = backend .key_store() .read::(key_package.hpke_init_key().as_slice()) + .await .unwrap(); Self { key_package, diff --git a/openmls/src/key_packages/test_key_packages.rs b/openmls/src/key_packages/test_key_packages.rs index 16162bf7e2..1211fd13d5 100644 --- a/openmls/src/key_packages/test_key_packages.rs +++ b/openmls/src/key_packages/test_key_packages.rs @@ -1,17 +1,22 @@ use crate::test_utils::*; use openmls_basic_credential::SignatureKeyPair; use openmls_rust_crypto::OpenMlsRustCrypto; +use openmls_traits::random::OpenMlsRand; use tls_codec::Deserialize; use crate::{extensions::*, key_packages::*}; /// Helper function to generate key packages -pub(crate) fn key_package( +pub(crate) async fn key_package( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) -> (KeyPackage, Credential, SignatureKeyPair) { let credential = Credential::new(b"Sasha".to_vec(), CredentialType::Basic).unwrap(); - let signer = SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); + let signer = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); // Generate a valid KeyPackage. let key_package = KeyPackage::builder() @@ -27,14 +32,15 @@ pub(crate) fn key_package( signature_key: signer.to_public_vec().into(), }, ) + .await .expect("An unexpected error occurred."); (key_package, credential, signer) } #[apply(ciphersuites_and_backends)] -fn generate_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - let (key_package, _credential, _signature_keys) = key_package(ciphersuite, backend); +async fn generate_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { + let (key_package, _credential, _signature_keys) = key_package(ciphersuite, backend).await; let kpi = KeyPackageIn::from(key_package); assert!(kpi @@ -43,8 +49,8 @@ fn generate_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr } #[apply(ciphersuites_and_backends)] -fn serialization(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - let (key_package, _, _) = key_package(ciphersuite, backend); +async fn serialization(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { + let (key_package, _, _) = key_package(ciphersuite, backend).await; let encoded = key_package .tls_serialize_detached() @@ -58,10 +64,14 @@ fn serialization(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) } #[apply(ciphersuites_and_backends)] -fn application_id_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn application_id_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let credential = Credential::new(b"Sasha".to_vec(), CredentialType::Basic) .expect("An unexpected error occurred."); - let signature_keys = SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); + let signature_keys = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); // Generate a valid KeyPackage. let id = b"application id" as &[u8]; @@ -81,6 +91,7 @@ fn application_id_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp credential, }, ) + .await .expect("An unexpected error occurred."); let kpi = KeyPackageIn::from(key_package.clone()); @@ -103,8 +114,8 @@ fn application_id_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp /// - The protocol version is correct /// - The init key is not equal to the encryption key #[apply(ciphersuites_and_backends)] -fn key_package_validation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - let (key_package_orig, _, _) = key_package(ciphersuite, backend); +async fn key_package_validation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { + let (key_package_orig, _, _) = key_package(ciphersuite, backend).await; // === Protocol version === diff --git a/openmls/src/messages/mod.rs b/openmls/src/messages/mod.rs index c2b44992b3..a406613f39 100644 --- a/openmls/src/messages/mod.rs +++ b/openmls/src/messages/mod.rs @@ -302,7 +302,8 @@ impl PathSecret { .map_err(LibraryError::unexpected_crypto_error)?; let HpkeKeyPair { public, private } = backend .crypto() - .derive_hpke_keypair(ciphersuite.hpke_config(), node_secret.as_slice()); + .derive_hpke_keypair(ciphersuite.hpke_config(), node_secret.as_slice()) + .map_err(LibraryError::unexpected_crypto_error)?; Ok((HpkePublicKey::from(public), private).into()) } @@ -459,6 +460,7 @@ impl GroupSecrets { #[cfg(test)] impl GroupSecrets { + #[allow(dead_code)] pub fn random_encoded( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/messages/tests/test_codec.rs b/openmls/src/messages/tests/test_codec.rs index 46099229c5..4e4eaacd86 100644 --- a/openmls/src/messages/tests/test_codec.rs +++ b/openmls/src/messages/tests/test_codec.rs @@ -12,7 +12,7 @@ use crate::{ /// Test the encoding for PreSharedKeyProposal, that also covers some of the /// other PSK-related structs #[apply(backends)] -fn test_pre_shared_key_proposal_codec(backend: &impl OpenMlsCryptoProvider) { +async fn test_pre_shared_key_proposal_codec(backend: &impl OpenMlsCryptoProvider) { // External let psk = PreSharedKeyId { psk: Psk::External(ExternalPsk::new(vec![1, 2, 3])), @@ -80,7 +80,7 @@ fn test_pre_shared_key_proposal_codec(backend: &impl OpenMlsCryptoProvider) { /// Test the encoding for ReInitProposal, that also covers some of the /// other PSK-related structs #[apply(ciphersuites_and_backends)] -fn test_reinit_proposal_codec(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_reinit_proposal_codec(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let orig = ReInitProposal { group_id: GroupId::random(backend), version: ProtocolVersion::default(), diff --git a/openmls/src/messages/tests/test_export_group_info.rs b/openmls/src/messages/tests/test_export_group_info.rs index 9bf2bc34a7..dc46c15f72 100644 --- a/openmls/src/messages/tests/test_export_group_info.rs +++ b/openmls/src/messages/tests/test_export_group_info.rs @@ -12,9 +12,9 @@ use crate::{ /// Tests the creation of an [UnverifiedGroupInfo] and verifies it was correctly signed. #[apply(ciphersuites_and_backends)] -fn export_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn export_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Alice creates a group - let (group_alice, _, signer, pk) = setup_alice_group(ciphersuite, backend); + let (group_alice, _, signer, pk) = setup_alice_group(ciphersuite, backend).await; let group_info: GroupInfo = group_alice .export_group_info(backend, &signer, true) diff --git a/openmls/src/messages/tests/test_proposals.rs b/openmls/src/messages/tests/test_proposals.rs index df1b1cd69d..581552316d 100644 --- a/openmls/src/messages/tests/test_proposals.rs +++ b/openmls/src/messages/tests/test_proposals.rs @@ -14,7 +14,7 @@ use crate::{ /// This test encodes and decodes the `ProposalOrRef` struct and makes sure the /// decoded values are the same as the original #[apply(ciphersuites_and_backends)] -fn proposals_codec(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn proposals_codec(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Proposal let remove_proposal = RemoveProposal { diff --git a/openmls/src/messages/tests/test_welcome.rs b/openmls/src/messages/tests/test_welcome.rs index 4a892fe377..2180e364c4 100644 --- a/openmls/src/messages/tests/test_welcome.rs +++ b/openmls/src/messages/tests/test_welcome.rs @@ -30,12 +30,17 @@ use crate::{ versions::ProtocolVersion, }; +use openmls_traits::random::OpenMlsRand; + /// This test detects if the decryption of the encrypted group secrets fails due to a change in /// the encrypted group info. As the group info is part of the decryption context of the encrypted /// group info, it is not possible to generate a matching encrypted group context with different /// parameters. #[apply(ciphersuites_and_backends)] -fn test_welcome_context_mismatch(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_welcome_context_mismatch( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { let _ = pretty_env_logger::try_init(); // We need a ciphersuite that is different from the current one to create @@ -53,9 +58,9 @@ fn test_welcome_context_mismatch(ciphersuite: Ciphersuite, backend: &impl OpenMl .build(); let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_signature_key) = - crate::group::test_core_group::setup_client("Alice", ciphersuite, backend); + crate::group::test_core_group::setup_client("Alice", ciphersuite, backend).await; let (_bob_credential, bob_kpb, _bob_signer, _bob_signature_key) = - crate::group::test_core_group::setup_client("Bob", ciphersuite, backend); + crate::group::test_core_group::setup_client("Bob", ciphersuite, backend).await; let bob_kp = bob_kpb.key_package(); let bob_private_key = bob_kpb.private_key(); @@ -68,14 +73,17 @@ fn test_welcome_context_mismatch(ciphersuite: Ciphersuite, backend: &impl OpenMl group_id, alice_credential_with_key, ) + .await .expect("An unexpected error occurred."); let (_queued_message, welcome, _group_info) = alice_group .add_members(backend, &alice_signer, &[bob_kp.clone()]) + .await .expect("Could not add member to group."); alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); let mut welcome = welcome.into_welcome().expect("Unexpected message type."); @@ -104,9 +112,13 @@ fn test_welcome_context_mismatch(ciphersuite: Ciphersuite, backend: &impl OpenMl let psk_secret = { let resumption_psk_store = ResumptionPskStore::new(1024); - let psks = load_psks(backend.key_store(), &resumption_psk_store, &[]).unwrap(); + let psks = load_psks(backend.key_store(), &resumption_psk_store, &[]) + .await + .unwrap(); - PskSecret::new(backend, ciphersuite, psks).unwrap() + PskSecret::new(backend, ciphersuite, psks) + .await + .expect("Could not create PskSecret.") }; // Create key schedule @@ -147,6 +159,7 @@ fn test_welcome_context_mismatch(ciphersuite: Ciphersuite, backend: &impl OpenMl backend, bob_kpb.key_package().leaf_node().encryption_key(), ) + .await .unwrap(); // Bob tries to join the group @@ -156,12 +169,13 @@ fn test_welcome_context_mismatch(ciphersuite: Ciphersuite, backend: &impl OpenMl welcome, Some(alice_group.export_ratchet_tree().into()), ) + .await .expect_err("Created a group from an invalid Welcome."); - assert_eq!( + assert!(matches!( err, WelcomeError::GroupSecrets(GroupSecretsError::DecryptionFailed) - ); + )); // === Process the original Welcome === @@ -173,13 +187,18 @@ fn test_welcome_context_mismatch(ciphersuite: Ciphersuite, backend: &impl OpenMl bob_kp.hash_ref(backend.crypto()).unwrap().as_slice(), bob_kp, ) + .await .unwrap(); backend .key_store() .store::(bob_kp.hpke_init_key().as_slice(), bob_private_key) + .await .unwrap(); - encryption_keypair.write_to_key_store(backend).unwrap(); + encryption_keypair + .write_to_key_store(backend) + .await + .unwrap(); let _group = MlsGroup::new_from_welcome( backend, @@ -187,11 +206,12 @@ fn test_welcome_context_mismatch(ciphersuite: Ciphersuite, backend: &impl OpenMl original_welcome, Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from a valid Welcome."); } #[apply(ciphersuites_and_backends)] -fn test_welcome_msg(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_welcome_msg(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { test_welcome_message(ciphersuite, backend); } @@ -218,7 +238,11 @@ fn test_welcome_message(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr }; // We need a signer - let signer = SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); + let signer = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); let group_info = group_info_tbs .sign(&signer) @@ -229,12 +253,15 @@ fn test_welcome_message(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr let welcome_nonce = AeadNonce::random(backend); // Generate receiver key pair. - let receiver_key_pair = backend.crypto().derive_hpke_keypair( - ciphersuite.hpke_config(), - Secret::random(ciphersuite, backend, None) - .expect("Not enough randomness.") - .as_slice(), - ); + let receiver_key_pair = backend + .crypto() + .derive_hpke_keypair( + ciphersuite.hpke_config(), + Secret::random(ciphersuite, backend, None) + .expect("Not enough randomness.") + .as_slice(), + ) + .unwrap(); let hpke_context = b"group info welcome test info"; let group_secrets = b"these should be the group secrets"; let new_member = KeyPackageRef::from_slice(&[0u8; 16]); diff --git a/openmls/src/schedule/kat_key_schedule.rs b/openmls/src/schedule/kat_key_schedule.rs index e4e4eb500f..2498000fa8 100644 --- a/openmls/src/schedule/kat_key_schedule.rs +++ b/openmls/src/schedule/kat_key_schedule.rs @@ -13,7 +13,7 @@ use tls_codec::Serialize as TlsSerializeTrait; use super::{errors::KsTestVectorError, CommitSecret}; #[cfg(test)] -use crate::test_utils::{read, write}; +use crate::test_utils::read; use crate::{ciphersuite::*, extensions::Extensions, group::*, schedule::*, test_utils::*}; #[derive(Serialize, Deserialize, Debug, Clone, Default)] @@ -133,7 +133,8 @@ fn generate( // Calculate external HPKE key pair let external_key_pair = epoch_secrets .external_secret() - .derive_external_keypair(crypto.crypto(), ciphersuite); + .derive_external_keypair(crypto.crypto(), ciphersuite) + .expect("Cannot generate ext HPKE KeyPair"); ( confirmed_transcript_hash, @@ -238,19 +239,19 @@ pub fn generate_test_vector( } } -#[test] -fn write_test_vectors() { - const NUM_EPOCHS: u64 = 2; - let mut tests = Vec::new(); - let backend = OpenMlsRustCrypto::default(); - for &ciphersuite in backend.crypto().supported_ciphersuites().iter() { - tests.push(generate_test_vector(NUM_EPOCHS, ciphersuite, &backend)); - } - write("test_vectors/key-schedule-new.json", &tests); -} +// #[test] +// fn write_test_vectors() { +// const NUM_EPOCHS: u64 = 2; +// let mut tests = Vec::new(); +// let backend = OpenMlsRustCrypto::default(); +// for &ciphersuite in backend.crypto().supported_ciphersuites().iter() { +// tests.push(generate_test_vector(NUM_EPOCHS, ciphersuite, &backend)); +// } +// write("test_vectors/key-schedule-new.json", &tests); +// } #[apply(backends)] -fn read_test_vectors_key_schedule(backend: &impl OpenMlsCryptoProvider) { +async fn read_test_vectors_key_schedule(backend: &impl OpenMlsCryptoProvider) { let _ = pretty_env_logger::try_init(); let tests: Vec = read("test_vectors/key-schedule.json"); @@ -437,7 +438,8 @@ pub fn run_test_vector( // Calculate external HPKE key pair let external_key_pair = epoch_secrets .external_secret() - .derive_external_keypair(backend.crypto(), ciphersuite); + .derive_external_keypair(backend.crypto(), ciphersuite) + .expect("Cannot derive HPKE Keypair"); if hex_to_bytes(&epoch.external_pub) != external_key_pair.public { log::error!(" External public key mismatch"); log::debug!( diff --git a/openmls/src/schedule/kat_psk_secret.rs b/openmls/src/schedule/kat_psk_secret.rs index 90a4f36f79..0021be5814 100644 --- a/openmls/src/schedule/kat_psk_secret.rs +++ b/openmls/src/schedule/kat_psk_secret.rs @@ -59,7 +59,10 @@ struct TestElement { psk_secret: Vec, } -fn run_test_vector(test: TestElement, backend: &impl OpenMlsCryptoProvider) -> Result<(), String> { +async fn run_test_vector( + test: TestElement, + backend: &impl OpenMlsCryptoProvider, +) -> Result<(), String> { let ciphersuite = Ciphersuite::try_from(test.cipher_suite).unwrap(); // Skip unsupported ciphersuites. if !backend @@ -71,29 +74,29 @@ fn run_test_vector(test: TestElement, backend: &impl OpenMlsCryptoProvider) -> R return Ok(()); } - let psk_ids = test - .psks - .iter() - .map(|psk| { - let external_psk = ExternalPsk::new(psk.psk_id.clone()); - let psk_type = Psk::External(external_psk); + let mut psk_ids = vec![]; + for psk in test.psks.into_iter() { + let external_psk = ExternalPsk::new(psk.psk_id.clone()); + let psk_type = Psk::External(external_psk); - let psk_id = PreSharedKeyId::new_with_nonce(psk_type, psk.psk_nonce.clone()); + let psk_id = PreSharedKeyId::new_with_nonce(psk_type, psk.psk_nonce.clone()); - psk_id - .write_to_key_store(backend, ciphersuite, &psk.psk) - .unwrap(); - psk_id - }) - .collect::>(); + psk_id + .write_to_key_store(backend, ciphersuite, &psk.psk) + .await + .unwrap(); + psk_ids.push(psk_id); + } // Prepare the PskSecret let psk_secret = { let resumption_psk_store = ResumptionPskStore::new(1024); - let psks = load_psks(backend.key_store(), &resumption_psk_store, &psk_ids).unwrap(); + let psks = load_psks(backend.key_store(), &resumption_psk_store, &psk_ids) + .await + .unwrap(); - PskSecret::new(backend, ciphersuite, psks).unwrap() + PskSecret::new(backend, ciphersuite, psks).await.unwrap() }; if psk_secret.secret().as_slice() == test.psk_secret { @@ -104,14 +107,14 @@ fn run_test_vector(test: TestElement, backend: &impl OpenMlsCryptoProvider) -> R } #[apply(backends)] -fn read_test_vectors_ps(backend: &impl OpenMlsCryptoProvider) { +async fn read_test_vectors_ps(backend: &impl OpenMlsCryptoProvider) { let _ = pretty_env_logger::try_init(); log::debug!("Reading test vectors ..."); let tests: Vec = read("test_vectors/psk_secret.json"); for test_vector in tests { - match run_test_vector(test_vector, backend) { + match run_test_vector(test_vector, backend).await { Ok(_) => {} Err(e) => panic!("Error while checking PSK secret test vector.\n{e:?}"), } diff --git a/openmls/src/schedule/mod.rs b/openmls/src/schedule/mod.rs index 7fde19fc1c..c27d8dbcc9 100644 --- a/openmls/src/schedule/mod.rs +++ b/openmls/src/schedule/mod.rs @@ -766,7 +766,7 @@ impl ExternalSecret { &self, crypto: &impl OpenMlsCrypto, ciphersuite: Ciphersuite, - ) -> HpkeKeyPair { + ) -> Result { crypto.derive_hpke_keypair(ciphersuite.hpke_config(), self.secret.as_slice()) } diff --git a/openmls/src/schedule/psk.rs b/openmls/src/schedule/psk.rs index a42f372431..813f5bae64 100644 --- a/openmls/src/schedule/psk.rs +++ b/openmls/src/schedule/psk.rs @@ -279,7 +279,7 @@ impl PreSharedKeyId { /// Save this `PreSharedKeyId` in the keystore. /// /// Note: The nonce is not saved as it must be unique for each time it's being applied. - pub fn write_to_key_store( + pub async fn write_to_key_store( &self, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, @@ -296,6 +296,7 @@ impl PreSharedKeyId { backend .key_store() .store(&keystore_id, &psk_bundle) + .await .map_err(|_| PskError::KeyStore) } @@ -401,7 +402,7 @@ impl PskSecret { /// psk_secret_[i] = KDF.Extract(psk_input[i-1], psk_secret_[i-1]) /// psk_secret = psk_secret[n] /// ``` - pub(crate) fn new( + pub(crate) async fn new( backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, psks: Vec<(&PreSharedKeyId, Secret)>, @@ -468,7 +469,7 @@ impl From for PskSecret { } } -pub(crate) fn load_psks<'p>( +pub(crate) async fn load_psks<'p>( key_store: &impl OpenMlsKeyStore, resumption_psk_store: &ResumptionPskStore, psk_ids: &'p [PreSharedKeyId], @@ -487,7 +488,8 @@ pub(crate) fn load_psks<'p>( } } Psk::External(_) => { - if let Some(psk_bundle) = key_store.read::(&psk_id.keystore_id()?) { + if let Some(psk_bundle) = key_store.read::(&psk_id.keystore_id()?).await + { psk_bundles.push((psk_id, psk_bundle.secret)); } else { return Err(PskError::KeyNotFound); diff --git a/openmls/src/schedule/unit_tests.rs b/openmls/src/schedule/unit_tests.rs index 487326cf0a..fccc6e457a 100644 --- a/openmls/src/schedule/unit_tests.rs +++ b/openmls/src/schedule/unit_tests.rs @@ -12,7 +12,7 @@ use crate::{ }; #[apply(ciphersuites_and_backends)] -fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Create a new PSK secret from multiple PSKs. let prng = backend.rand(); @@ -40,14 +40,17 @@ fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { { psk_id .write_to_key_store(backend, ciphersuite, secret.as_slice()) + .await .unwrap(); } let _psk_secret = { let resumption_psk_store = ResumptionPskStore::new(1024); - let psks = load_psks(backend.key_store(), &resumption_psk_store, &psk_ids).unwrap(); + let psks = load_psks(backend.key_store(), &resumption_psk_store, &psk_ids) + .await + .unwrap(); - PskSecret::new(backend, ciphersuite, psks).unwrap() + PskSecret::new(backend, ciphersuite, psks).await.unwrap() }; } diff --git a/openmls/src/test_utils/mod.rs b/openmls/src/test_utils/mod.rs index d6a7164deb..11dff0786c 100644 --- a/openmls/src/test_utils/mod.rs +++ b/openmls/src/test_utils/mod.rs @@ -28,18 +28,18 @@ use crate::{ pub mod test_framework; -pub(crate) 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(crate) 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(crate) fn read(file_name: &str) -> T { let file = match File::open(file_name) { @@ -96,20 +96,26 @@ pub(crate) struct GroupCandidate { } #[cfg(test)] -pub(crate) fn generate_group_candidate( +pub(crate) async fn generate_group_candidate( identity: &[u8], ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, use_store: bool, ) -> GroupCandidate { + use openmls_traits::random::OpenMlsRand; + let credential_with_key_and_signer = { let credential = Credential::new(identity.to_vec(), CredentialType::Basic).unwrap(); - let signature_keypair = SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); + let signature_keypair = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); // Store if there is a key store. if use_store { - signature_keypair.store(backend.key_store()).unwrap(); + signature_keypair.store(backend.key_store()).await.unwrap(); } let signature_pkey = OpenMlsSignaturePublicKey::new( @@ -138,17 +144,20 @@ pub(crate) fn generate_group_candidate( &credential_with_key_and_signer.signer, credential_with_key_and_signer.credential_with_key.clone(), ) + .await .unwrap(); let encryption_keypair = EncryptionKeyPair::read_from_key_store( backend, key_package.leaf_node().encryption_key(), ) + .await .unwrap(); let init_keypair = { let private = backend .key_store() .read::(key_package.hpke_init_key().as_slice()) + .await .unwrap(); HpkeKeyPair { @@ -200,52 +209,20 @@ pub(crate) fn generate_group_candidate( // === Define backend per platform === -// For now we only use Evercrypt on specific platforms and only if the feature was enabled - -#[cfg(all( - target_arch = "x86_64", - not(target_os = "macos"), - not(target_family = "wasm"), - feature = "evercrypt", -))] -pub use openmls_evercrypt::OpenMlsEvercrypt; // This backend is currently used on all platforms pub use openmls_rust_crypto::OpenMlsRustCrypto; // === Backends === -#[cfg(any( - not(target_arch = "x86_64"), - target_os = "macos", - target_family = "wasm", - not(feature = "evercrypt") -))] -#[template] -#[export] -#[rstest(backend, - case::rust_crypto(&OpenMlsRustCrypto::default()), - ) -] -#[allow(non_snake_case)] -pub fn backends(backend: &impl OpenMlsCryptoProvider) {} - -// For now we only use Evercrypt on specific platforms and only if the feature was enabled - -#[cfg(all( - target_arch = "x86_64", - not(target_os = "macos"), - not(target_family = "wasm"), - feature = "evercrypt", -))] #[template] #[export] #[rstest(backend, case::rust_crypto(&OpenMlsRustCrypto::default()), - case::evercrypt(&openmls_evercrypt::OpenMlsEvercrypt::default()), ) ] +#[tokio::test] #[allow(non_snake_case)] -pub fn backends(backend: &impl OpenMlsCryptoProvider) {} +pub async fn backends(backend: &impl OpenMlsCryptoProvider) {} // === Ciphersuites === @@ -261,50 +238,32 @@ pub fn backends(backend: &impl OpenMlsCryptoProvider) {} case::MLS_128_DHKEMP256_AES128GCM_SHA256_P256( Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 ), + case::MLS_256_DHKEMP384_AES256GCM_SHA384_P384( + Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 + ), case::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519( Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 ) )] #[allow(non_snake_case)] -pub fn ciphersuites(ciphersuite: Ciphersuite) {} +#[tokio::test] +pub async fn ciphersuites(ciphersuite: Ciphersuite) {} // === Ciphersuites & backends === -#[cfg(any( - not(target_arch = "x86_64"), - target_os = "macos", - target_family = "wasm", - not(feature = "evercrypt"), -))] #[template] #[export] #[rstest(ciphersuite, backend, case::rust_crypto_MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519(Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, &OpenMlsRustCrypto::default()), case::rust_crypto_MLS_128_DHKEMP256_AES128GCM_SHA256_P256(Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, &OpenMlsRustCrypto::default()), + case::rust_crypto_MLS_256_DHKEMP384_AES256GCM_SHA384_P384(Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, &OpenMlsRustCrypto::default()), case::rust_crypto_MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519(Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, &OpenMlsRustCrypto::default()), ) ] +#[tokio::test] #[allow(non_snake_case)] -pub fn ciphersuites_and_backends(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) {} - -// For now we only use Evercrypt on specific platforms and only if the feature was enabled - -#[cfg(all( - target_arch = "x86_64", - not(target_os = "macos"), - not(target_family = "wasm"), - feature = "evercrypt", -))] -#[template] -#[export] -#[rstest(ciphersuite, backend, - case::rust_crypto_MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519(Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, &OpenMlsRustCrypto::default()), - case::rust_crypto_MLS_128_DHKEMP256_AES128GCM_SHA256_P256(Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, &OpenMlsRustCrypto::default()), - case::rust_crypto_MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519(Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, &OpenMlsRustCrypto::default()), - case::evercrypt_MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519(Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, &openmls_evercrypt::OpenMlsEvercrypt::default()), - case::evercrypt_MLS_128_DHKEMP256_AES128GCM_SHA256_P256(Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, &openmls_evercrypt::OpenMlsEvercrypt::default()), - case::evercrypt_MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519(Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, &openmls_evercrypt::OpenMlsEvercrypt::default()), - ) -] -#[allow(non_snake_case)] -pub fn ciphersuites_and_backends(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) {} +pub async fn ciphersuites_and_backends( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { +} diff --git a/openmls/src/test_utils/test_framework/client.rs b/openmls/src/test_utils/test_framework/client.rs index 74f479cb47..2b615c252d 100644 --- a/openmls/src/test_utils/test_framework/client.rs +++ b/openmls/src/test_utils/test_framework/client.rs @@ -49,7 +49,7 @@ impl Client { /// Generate a fresh key package and return it. /// The first ciphersuite determines the /// credential used to generate the `KeyPackage`. - pub fn get_fresh_key_package( + pub async fn get_fresh_key_package( &self, ciphersuite: Ciphersuite, ) -> Result { @@ -60,8 +60,8 @@ impl Client { let keys = SignatureKeyPair::read( self.crypto.key_store(), credential_with_key.signature_key.as_slice(), - ciphersuite.signature_algorithm(), ) + .await .unwrap(); let key_package = KeyPackage::builder() @@ -74,6 +74,7 @@ impl Client { &keys, credential_with_key.clone(), ) + .await .unwrap(); Ok(key_package) @@ -82,7 +83,7 @@ impl Client { /// Create a group with the given [MlsGroupConfig] and [Ciphersuite], and return the created [GroupId]. /// /// Returns an error if the client doesn't support the `ciphersuite`. - pub fn create_group( + pub async fn create_group( &self, mls_group_config: MlsGroupConfig, ciphersuite: Ciphersuite, @@ -94,8 +95,8 @@ impl Client { let signer = SignatureKeyPair::read( self.crypto.key_store(), credential_with_key.signature_key.as_slice(), - ciphersuite.signature_algorithm(), ) + .await .unwrap(); let group_state = MlsGroup::new( @@ -103,7 +104,9 @@ impl Client { &signer, &mls_group_config, credential_with_key.clone(), - )?; + ) + .await?; + let group_id = group_state.group_id().clone(); self.groups .write() @@ -116,14 +119,15 @@ impl Client { /// is created with the given `MlsGroupConfig`. Throws an error if no /// `KeyPackage` exists matching the `Welcome`, if the client doesn't /// support the ciphersuite, or if an error occurs processing the `Welcome`. - pub fn join_group( + pub async fn join_group( &self, mls_group_config: MlsGroupConfig, welcome: Welcome, ratchet_tree: Option, ) -> Result<(), ClientError> { let new_group: MlsGroup = - MlsGroup::new_from_welcome(&self.crypto, &mls_group_config, welcome, ratchet_tree)?; + MlsGroup::new_from_welcome(&self.crypto, &mls_group_config, welcome, ratchet_tree) + .await?; self.groups .write() .expect("An unexpected error occurred.") @@ -134,7 +138,7 @@ impl Client { /// Have the client process the given messages. Returns an error if an error /// occurs during message processing or if no group exists for one of the /// messages. - pub fn receive_messages_for_group( + pub async fn receive_messages_for_group( &self, message: &ProtocolMessage, sender_id: &[u8], @@ -145,14 +149,16 @@ impl Client { .get_mut(group_id) .ok_or(ClientError::NoMatchingGroup)?; if sender_id == self.identity && message.content_type() == ContentType::Commit { - group_state.merge_pending_commit(&self.crypto)? + group_state.merge_pending_commit(&self.crypto).await? } else { if message.content_type() == ContentType::Commit { // Clear any potential pending commits. group_state.clear_pending_commit(); } // Process the message. - let processed_message = group_state.process_message(&self.crypto, message.clone())?; + let processed_message = group_state + .process_message(&self.crypto, message.clone()) + .await?; match processed_message.into_content() { ProcessedMessageContent::ApplicationMessage(_) => {} @@ -163,11 +169,15 @@ impl Client { group_state.store_pending_proposal(*staged_proposal); } ProcessedMessageContent::StagedCommitMessage(staged_commit) => { - group_state.merge_staged_commit(&self.crypto, *staged_commit)?; + group_state + .merge_staged_commit(&self.crypto, *staged_commit) + .await?; } } } + drop(group_states); + Ok(()) } @@ -187,7 +197,7 @@ impl Client { /// update their leaf with. Returns an error if no group with the given /// group id can be found or if an error occurs while creating the update. #[allow(clippy::type_complexity)] - pub fn self_update( + pub async fn self_update( &self, action_type: ActionType, group_id: &GroupId, @@ -200,17 +210,15 @@ impl Client { // Get the signature public key to read the signer from the // key store. let signature_pk = group.own_leaf().unwrap().signature_key(); - let signer = SignatureKeyPair::read( - self.crypto.key_store(), - signature_pk.as_slice(), - group.ciphersuite().signature_algorithm(), - ) - .unwrap(); + let signer = SignatureKeyPair::read(self.crypto.key_store(), signature_pk.as_slice()) + .await + .unwrap(); let (msg, welcome_option, group_info) = match action_type { - ActionType::Commit => group.self_update(&self.crypto, &signer)?, + ActionType::Commit => group.self_update(&self.crypto, &signer).await?, ActionType::Proposal => ( group .propose_self_update(&self.crypto, &signer, leaf_node) + .await .map(|(out, _)| out)?, None, None, @@ -229,7 +237,7 @@ impl Client { /// given group id can be found or if an error occurs while performing the /// add operation. #[allow(clippy::type_complexity)] - pub fn add_members( + pub async fn add_members( &self, action_type: ActionType, group_id: &GroupId, @@ -242,16 +250,14 @@ impl Client { // Get the signature public key to read the signer from the // key store. let signature_pk = group.own_leaf().unwrap().signature_key(); - let signer = SignatureKeyPair::read( - self.crypto.key_store(), - signature_pk.as_slice(), - group.ciphersuite().signature_algorithm(), - ) - .unwrap(); + let signer = SignatureKeyPair::read(self.crypto.key_store(), signature_pk.as_slice()) + .await + .unwrap(); let action_results = match action_type { ActionType::Commit => { - let (messages, welcome_message, group_info) = - group.add_members(&self.crypto, &signer, key_packages)?; + let (messages, welcome_message, group_info) = group + .add_members(&self.crypto, &signer, key_packages) + .await?; ( vec![messages], Some( @@ -282,7 +288,7 @@ impl Client { /// given group id can be found or if an error occurs while performing the /// remove operation. #[allow(clippy::type_complexity)] - pub fn remove_members( + pub async fn remove_members( &self, action_type: ActionType, group_id: &GroupId, @@ -295,16 +301,13 @@ impl Client { // Get the signature public key to read the signer from the // key store. let signature_pk = group.own_leaf().unwrap().signature_key(); - let signer = SignatureKeyPair::read( - self.crypto.key_store(), - signature_pk.as_slice(), - group.ciphersuite().signature_algorithm(), - ) - .unwrap(); + let signer = SignatureKeyPair::read(self.crypto.key_store(), signature_pk.as_slice()) + .await + .unwrap(); let action_results = match action_type { ActionType::Commit => { let (message, welcome_option, group_info) = - group.remove_members(&self.crypto, &signer, targets)?; + group.remove_members(&self.crypto, &signer, targets).await?; ( vec![message], welcome_option.map(|w| w.into_welcome().expect("Unexpected message type.")), diff --git a/openmls/src/test_utils/test_framework/mod.rs b/openmls/src/test_utils/test_framework/mod.rs index 2fad637288..42b8a38c92 100644 --- a/openmls/src/test_utils/test_framework/mod.rs +++ b/openmls/src/test_utils/test_framework/mod.rs @@ -37,6 +37,7 @@ use openmls_rust_crypto::OpenMlsRustCrypto; use openmls_traits::{ crypto::OpenMlsCrypto, key_store::OpenMlsKeyStore, + random::OpenMlsRand, types::{Ciphersuite, HpkeKeyPair, SignatureScheme}, OpenMlsCryptoProvider, }; @@ -135,7 +136,11 @@ impl MlsGroupTestSetup { /// `MlsGroupConfig` and the given number of clients. For lifetime /// reasons, `create_clients` has to be called in addition with the same /// number of clients. - pub fn new(default_mgc: MlsGroupConfig, number_of_clients: usize, use_codec: CodecUse) -> Self { + pub async fn new( + default_mgc: MlsGroupConfig, + number_of_clients: usize, + use_codec: CodecUse, + ) -> Self { let mut clients = HashMap::new(); for i in 0..number_of_clients { let identity = i.to_be_bytes().to_vec(); @@ -144,9 +149,12 @@ impl MlsGroupTestSetup { let mut credentials = HashMap::new(); for ciphersuite in crypto.crypto().supported_ciphersuites().iter() { let credential = Credential::new(identity.clone(), CredentialType::Basic).unwrap(); - let signature_keys = - SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); - signature_keys.store(crypto.key_store()).unwrap(); + let signature_keys = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *crypto.rand().borrow_rand().unwrap(), + ) + .unwrap(); + signature_keys.store(crypto.key_store()).await.unwrap(); let signature_key = OpenMlsSignaturePublicKey::new( signature_keys.public().into(), signature_keys.signature_scheme(), @@ -184,12 +192,12 @@ impl MlsGroupTestSetup { /// to a group. The `KeyPackageBundle` will be fetched automatically when /// delivering the `Welcome` via `deliver_welcome`. This function throws an /// error if the client does not support the given ciphersuite. - pub fn get_fresh_key_package( + pub async fn get_fresh_key_package( &self, client: &Client, ciphersuite: Ciphersuite, ) -> Result { - let key_package = client.get_fresh_key_package(ciphersuite)?; + let key_package = client.get_fresh_key_package(ciphersuite).await?; self.waiting_for_welcome .write() .expect("An unexpected error occurred.") @@ -241,7 +249,7 @@ impl MlsGroupTestSetup { /// distribute the commit adding the members to the group. This function /// will throw an error if no key package was previously created for the /// client by `get_fresh_key_package`. - pub fn deliver_welcome(&self, welcome: Welcome, group: &Group) -> Result<(), SetupError> { + pub async fn deliver_welcome(&self, welcome: Welcome, group: &Group) -> Result<(), SetupError> { // Serialize and de-serialize the Welcome if the bit was set. let welcome = match self.use_codec { CodecUse::SerializedMessages => { @@ -267,11 +275,13 @@ impl MlsGroupTestSetup { .expect("An unexpected error occurred.") .read() .expect("An unexpected error occurred."); - client.join_group( - group.group_config.clone(), - welcome.clone(), - Some(group.public_tree.clone().into()), - )?; + client + .join_group( + group.group_config.clone(), + welcome.clone(), + Some(group.public_tree.clone().into()), + ) + .await?; } Ok(()) } @@ -279,7 +289,7 @@ impl MlsGroupTestSetup { /// Distribute a set of messages sent by the sender with identity /// `sender_id` to their intended recipients in group `Group`. This function /// also verifies that all members of that group agree on the public tree. - pub fn distribute_to_members( + pub async fn distribute_to_members( &self, // We need the sender to know a group member that we know can not have // been removed from the group. @@ -301,28 +311,23 @@ impl MlsGroupTestSetup { .expect("Unexptected message type."); let clients = self.clients.read().expect("An unexpected error occurred."); // Distribute message to all members, except to the sender in the case of application messages - let results: Result, _> = group - .members - .par_iter() - .filter_map(|(_index, member_id)| { - if message.content_type() == ContentType::Application && member_id == sender_id { - None - } else { - Some(member_id) - } - }) - .map(|member_id| { - let member = clients - .get(member_id) - .expect("An unexpected error occurred.") - .read() - .expect("An unexpected error occurred."); - member.receive_messages_for_group(&message, sender_id) - }) - .collect(); + for member_id in group.members().filter_map(|(_index, member_id)| { + if message.content_type() == ContentType::Application && member_id == sender_id { + None + } else { + Some(member_id) + } + }) { + let member = clients + .get(&member_id) + .expect("An unexpected error occurred.") + .read() + .expect("An unexpected error occurred."); + member + .receive_messages_for_group(&message, sender_id) + .await?; + } - // Check if we received an error - results?; // Get the current tree and figure out who's still in the group. let sender = clients .get(sender_id) @@ -352,48 +357,42 @@ impl MlsGroupTestSetup { /// each group member encrypt an application message and delivers all of /// these messages to all other members. This function panics if any of the /// above tests fail. - pub fn check_group_states(&self, group: &mut Group) { + pub async fn check_group_states(&self, group: &mut Group) { let clients = self.clients.read().expect("An unexpected error occurred."); - let messages = group - .members - .par_iter() - .filter_map(|(_, m_id)| { - let m = clients - .get(m_id) - .expect("An unexpected error occurred.") - .read() - .expect("An unexpected error occurred."); - let mut group_states = m.groups.write().expect("An unexpected error occurred."); - // Some group members may not have received their welcome messages yet. - if let Some(group_state) = group_states.get_mut(&group.group_id) { - assert_eq!(group_state.export_ratchet_tree(), group.public_tree); - assert_eq!( - group_state - .export_secret(&m.crypto, "test", &[], 32) - .expect("An unexpected error occurred."), - group.exporter_secret - ); - // Get the signature public key to read the signer from the - // key store. - let signature_pk = group_state.own_leaf().unwrap().signature_key(); - let signer = SignatureKeyPair::read( - m.crypto.key_store(), - signature_pk.as_slice(), - group_state.ciphersuite().signature_algorithm(), - ) + let mut messages: Vec<(Vec, MlsMessageOut)> = Vec::new(); + for (_, m_id) in group.members() { + let m = clients + .get(&m_id) + .expect("An unexpected error occurred.") + .read() + .expect("An unexpected error occurred."); + let mut group_states = m.groups.write().expect("An unexpected error occurred."); + // Some group members may not have received their welcome messages yet. + if let Some(group_state) = group_states.get_mut(&group.group_id) { + assert_eq!(group_state.export_ratchet_tree(), group.public_tree); + assert_eq!( + group_state + .export_secret(&m.crypto, "test", &[], 32) + .expect("An unexpected error occurred."), + group.exporter_secret + ); + // Get the signature public key to read the signer from the + // key store. + let signature_pk = group_state.own_leaf().unwrap().signature_key(); + let signer = SignatureKeyPair::read(m.crypto.key_store(), signature_pk.as_slice()) + .await .unwrap(); - let message = group_state - .create_message(&m.crypto, &signer, "Hello World!".as_bytes()) - .expect("Error composing message while checking group states."); - Some((m_id.to_vec(), message)) - } else { - None - } - }) - .collect::, MlsMessageOut)>>(); + let message = group_state + .create_message(&m.crypto, &signer, "Hello World!".as_bytes()) + .expect("Error composing message while checking group states."); + messages.push((m_id.to_vec(), message)); + } + } + drop(clients); for (sender_id, message) in messages { self.distribute_to_members(&sender_id, group, &message.into()) + .await .expect("Error sending messages to clients while checking group states."); } } @@ -441,7 +440,7 @@ impl MlsGroupTestSetup { /// does not support the given ciphersuite. TODO #310: Fix to always work /// reliably, probably by introducing a mapping from ciphersuite to the set /// of client ids supporting it. - pub fn create_group(&self, ciphersuite: Ciphersuite) -> Result { + pub async fn create_group(&self, ciphersuite: Ciphersuite) -> Result { // Pick a random group creator. let clients = self.clients.read().expect("An unexpected error occurred."); let group_creator_id = ((OsRng.next_u32() as usize) % clients.len()) @@ -453,7 +452,9 @@ impl MlsGroupTestSetup { .read() .expect("An unexpected error occurred."); let mut groups = self.groups.write().expect("An unexpected error occurred."); - let group_id = group_creator.create_group(self.default_mgc.clone(), ciphersuite)?; + let group_id = group_creator + .create_group(self.default_mgc.clone(), ciphersuite) + .await?; let creator_groups = group_creator .groups .read() @@ -477,13 +478,13 @@ impl MlsGroupTestSetup { } /// Create a random group of size `group_size` and return the `GroupId` - pub fn create_random_group( + pub async fn create_random_group( &self, target_group_size: usize, ciphersuite: Ciphersuite, ) -> Result { // Create the initial group. - let group_id = self.create_group(ciphersuite)?; + let group_id = self.create_group(ciphersuite).await?; let mut groups = self.groups.write().expect("An unexpected error occurred."); let group = groups @@ -500,7 +501,8 @@ impl MlsGroupTestSetup { // Add between 1 and 5 new members. let number_of_adds = ((OsRng.next_u32() as usize) % 5 % new_members.len()) + 1; let members_to_add = new_members.drain(0..number_of_adds).collect(); - self.add_clients(ActionType::Commit, group, &adder_id.1, members_to_add)?; + self.add_clients(ActionType::Commit, group, &adder_id.1, members_to_add) + .await?; } Ok(group_id) } @@ -508,7 +510,7 @@ impl MlsGroupTestSetup { /// Have the client with identity `client_id` either propose or commit /// (depending on `action_type`) a self update in group `group`. Will throw /// an error if the client is not actually a member of group `group`. - pub fn self_update( + pub async fn self_update( &self, action_type: ActionType, group: &mut Group, @@ -521,11 +523,13 @@ impl MlsGroupTestSetup { .ok_or(SetupError::UnknownClientId)? .read() .expect("An unexpected error occurred."); - let (messages, welcome_option, _) = - client.self_update(action_type, &group.group_id, leaf_node)?; - self.distribute_to_members(&client.identity, group, &messages.into())?; + let (messages, welcome_option, _) = client + .self_update(action_type, &group.group_id, leaf_node) + .await?; + self.distribute_to_members(&client.identity, group, &messages.into()) + .await?; if let Some(welcome) = welcome_option { - self.deliver_welcome(welcome, group)?; + self.deliver_welcome(welcome, group).await?; } Ok(()) } @@ -536,7 +540,7 @@ impl MlsGroupTestSetup { /// * the `adder` is not part of the group /// * the `addee` is already part of the group /// * the `addee` doesn't support the group's ciphersuite. - pub fn add_clients( + pub async fn add_clients( &self, action_type: ActionType, group: &mut Group, @@ -563,16 +567,20 @@ impl MlsGroupTestSetup { .ok_or(SetupError::UnknownClientId)? .read() .expect("An unexpected error occurred."); - let key_package = self.get_fresh_key_package(&addee, group.ciphersuite)?; + let key_package = self + .get_fresh_key_package(&addee, group.ciphersuite) + .await?; key_packages.push(key_package); } - let (messages, welcome_option, _) = - adder.add_members(action_type, &group.group_id, &key_packages)?; + let (messages, welcome_option, _) = adder + .add_members(action_type, &group.group_id, &key_packages) + .await?; for message in messages { - self.distribute_to_members(adder_id, group, &message.into())?; + self.distribute_to_members(adder_id, group, &message.into()) + .await?; } if let Some(welcome) = welcome_option { - self.deliver_welcome(welcome, group)?; + self.deliver_welcome(welcome, group).await?; } Ok(()) } @@ -581,7 +589,7 @@ impl MlsGroupTestSetup { /// removal the `target_members` from the Group `group`. If the `remover` or /// one of the `target_members` is not part of the group, it returns an /// error. - pub fn remove_clients( + pub async fn remove_clients( &self, action_type: ActionType, group: &mut Group, @@ -594,13 +602,15 @@ impl MlsGroupTestSetup { .ok_or(SetupError::UnknownClientId)? .read() .expect("An unexpected error occurred."); - let (messages, welcome_option, _) = - remover.remove_members(action_type, &group.group_id, target_members)?; + let (messages, welcome_option, _) = remover + .remove_members(action_type, &group.group_id, target_members) + .await?; for message in messages { - self.distribute_to_members(remover_id, group, &message.into())?; + self.distribute_to_members(remover_id, group, &message.into()) + .await?; } if let Some(welcome) = welcome_option { - self.deliver_welcome(welcome, group)?; + self.deliver_welcome(welcome, group).await?; } Ok(()) } @@ -608,7 +618,7 @@ impl MlsGroupTestSetup { /// This function picks a random member of group `group` and has them /// perform a random commit- or proposal action. TODO #133: This won't work /// yet due to the missing proposal validation. - pub fn perform_random_operation(&self, group: &mut Group) -> Result<(), SetupError> { + pub async fn perform_random_operation(&self, group: &mut Group) -> Result<(), SetupError> { // Who's going to do it? let member_id = group.random_group_member(); println!("Member performing the operation: {member_id:?}"); @@ -625,7 +635,8 @@ impl MlsGroupTestSetup { match operation_type { 0 => { println!("Performing a self-update with action type: {action_type:?}"); - self.self_update(action_type, group, &member_id.1, None)?; + self.self_update(action_type, group, &member_id.1, None) + .await?; } 1 => { // If it's a single-member group, don't remove anyone. @@ -682,7 +693,8 @@ impl MlsGroupTestSetup { group, &member_id.1, &target_member_leaf_indices, - )? + ) + .await? }; } 2 => { @@ -700,7 +712,8 @@ impl MlsGroupTestSetup { .expect("An unexpected error occurred."); println!("{action_type:?}: Adding new clients: {new_member_ids:?}"); // Have the adder add them to the group. - self.add_clients(action_type, group, &member_id.1, new_member_ids)?; + self.add_clients(action_type, group, &member_id.1, new_member_ids) + .await?; } } _ => return Err(SetupError::Unknown), diff --git a/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs b/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs index 7be3601f1a..74a69b827a 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs @@ -84,7 +84,9 @@ use std::convert::TryFrom; use itertools::izip; use openmls_basic_credential::SignatureKeyPair; use openmls_rust_crypto::OpenMlsRustCrypto; -use openmls_traits::{signatures::Signer, types::SignatureScheme, OpenMlsCryptoProvider}; +use openmls_traits::{ + random::OpenMlsRand, signatures::Signer, types::SignatureScheme, OpenMlsCryptoProvider, +}; use serde::{self, Deserialize, Serialize}; use thiserror::Error; @@ -139,15 +141,19 @@ pub struct EncryptionTestVector { leaves: Vec, } -fn generate_credential( +async fn generate_credential( identity: Vec, credential_type: CredentialType, signature_algorithm: SignatureScheme, backend: &impl OpenMlsCryptoProvider, ) -> (CredentialWithKey, SignatureKeyPair) { let credential = Credential::new(identity, credential_type).unwrap(); - let signature_keys = SignatureKeyPair::new(signature_algorithm).unwrap(); - signature_keys.store(backend.key_store()).unwrap(); + let signature_keys = SignatureKeyPair::new( + signature_algorithm, + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); + signature_keys.store(backend.key_store()).await.unwrap(); ( CredentialWithKey { @@ -159,7 +165,7 @@ fn generate_credential( } #[cfg(any(feature = "test-utils", test))] -fn group( +async fn group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) -> (CoreGroup, CredentialWithKey, SignatureKeyPair) { @@ -170,7 +176,8 @@ fn group( CredentialType::Basic, ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let group = CoreGroup::builder( GroupId::random(backend), @@ -178,13 +185,14 @@ fn group( credential_with_key.clone(), ) .build(backend, &signer) + .await .unwrap(); (group, credential_with_key, signer) } #[cfg(any(feature = "test-utils", test))] -fn receiver_group( +async fn receiver_group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, group_id: GroupId, @@ -196,7 +204,8 @@ fn receiver_group( CredentialType::Basic, ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let group = CoreGroup::builder( group_id, @@ -204,6 +213,7 @@ fn receiver_group( credential_with_key.clone(), ) .build(backend, &signer) + .await .unwrap(); (group, credential_with_key, signer) @@ -321,13 +331,11 @@ fn build_application_messages( } #[cfg(any(feature = "test-utils", test))] -pub fn generate_test_vector( +pub async fn generate_test_vector( n_generations: u32, n_leaves: u32, ciphersuite: Ciphersuite, ) -> EncryptionTestVector { - use openmls_traits::random::OpenMlsRand; - use crate::binary_tree::array_representation::TreeSize; let ciphersuite_name = ciphersuite; @@ -357,7 +365,7 @@ pub fn generate_test_vector( nonce: bytes_to_hex(sender_data_nonce.as_slice()), }; - let (mut group, _, signer) = group(ciphersuite, &crypto); + let (mut group, _, signer) = group(ciphersuite, &crypto).await; *group.message_secrets_test_mut().sender_data_secret_mut() = SenderDataSecret::from_slice( sender_data_secret_bytes, ProtocolVersion::default(), @@ -453,32 +461,32 @@ pub fn generate_test_vector( } } -#[test] -fn write_test_vectors() { - let _ = pretty_env_logger::try_init(); - use openmls_traits::crypto::OpenMlsCrypto; - let mut tests = Vec::new(); - const NUM_LEAVES: u32 = 10; - const NUM_GENERATIONS: u32 = 15; - - log::debug!("Generating new test vectors ..."); - - for &ciphersuite in OpenMlsRustCrypto::default() - .crypto() - .supported_ciphersuites() - .iter() - { - for n_leaves in 1u32..NUM_LEAVES { - let test = generate_test_vector(NUM_GENERATIONS, n_leaves, ciphersuite); - tests.push(test); - } - } - - write("test_vectors/kat_encryption_openmls-new.json", &tests); -} +// #[tokio::test] +// async fn write_test_vectors() { +// let _ = pretty_env_logger::try_init(); +// use openmls_traits::crypto::OpenMlsCrypto; +// let mut tests = Vec::new(); +// const NUM_LEAVES: u32 = 10; +// const NUM_GENERATIONS: u32 = 15; + +// log::debug!("Generating new test vectors ..."); + +// for &ciphersuite in OpenMlsRustCrypto::default() +// .crypto() +// .supported_ciphersuites() +// .iter() +// { +// for n_leaves in 1u32..NUM_LEAVES { +// let test = generate_test_vector(NUM_GENERATIONS, n_leaves, ciphersuite).await; +// tests.push(test); +// } +// } + +// write("test_vectors/kat_encryption_openmls-new.json", &tests); +// } #[cfg(any(feature = "test-utils", test))] -pub fn run_test_vector( +pub async fn run_test_vector( test_vector: EncryptionTestVector, backend: &impl OpenMlsCryptoProvider, ) -> Result<(), EncTestVectorError> { @@ -612,7 +620,8 @@ pub fn run_test_vector( ciphersuite, backend, mls_ciphertext_application.group_id().clone(), - ); + ) + .await; *group.message_secrets_test_mut().sender_data_secret_mut() = SenderDataSecret::from_slice( hex_to_bytes(&test_vector.sender_data_secret).as_slice(), @@ -766,7 +775,8 @@ pub fn run_test_vector( ciphersuite, backend, mls_ciphertext_handshake.group_id().clone(), - ); + ) + .await; *group.message_secrets_test_mut().sender_data_secret_mut() = SenderDataSecret::from_slice( &hex_to_bytes(&test_vector.sender_data_secret), @@ -819,14 +829,14 @@ pub fn run_test_vector( } #[apply(backends)] -fn read_test_vectors_encryption(backend: &impl OpenMlsCryptoProvider) { +async fn read_test_vectors_encryption(backend: &impl OpenMlsCryptoProvider) { let _ = pretty_env_logger::try_init(); log::debug!("Reading test vectors ..."); let tests: Vec = read("test_vectors/kat_encryption_openmls.json"); for test_vector in tests { - match run_test_vector(test_vector, backend) { + match run_test_vector(test_vector, backend).await { Ok(_) => {} Err(e) => panic!("Error while checking encryption test vector.\n{e:?}"), } @@ -844,7 +854,9 @@ fn read_test_vectors_encryption(backend: &impl OpenMlsCryptoProvider) { ]; for &tv_file in tv_files.iter() { let tv: EncryptionTestVector = read(tv_file); - run_test_vector(tv, backend).expect("Error while checking key schedule test vector."); + run_test_vector(tv, backend) + .await + .expect("Error while checking key schedule test vector."); } log::trace!("Finished test vector verification"); diff --git a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs index 6b05951544..749ca730b8 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs @@ -63,6 +63,7 @@ use openmls_basic_credential::SignatureKeyPair; use openmls_rust_crypto::OpenMlsRustCrypto; +use openmls_traits::random::OpenMlsRand; use openmls_traits::{types::SignatureScheme, OpenMlsCryptoProvider}; use serde::{self, Deserialize, Serialize}; @@ -105,15 +106,19 @@ pub struct MessageProtectionTest { application_priv: String, } -fn generate_credential( +async fn generate_credential( identity: Vec, credential_type: CredentialType, signature_algorithm: SignatureScheme, backend: &impl OpenMlsCryptoProvider, ) -> (CredentialWithKey, SignatureKeyPair) { let credential = Credential::new(identity, credential_type).unwrap(); - let signature_keys = SignatureKeyPair::new(signature_algorithm).unwrap(); - signature_keys.store(backend.key_store()).unwrap(); + let signature_keys = SignatureKeyPair::new( + signature_algorithm, + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); + signature_keys.store(backend.key_store()).await.unwrap(); ( CredentialWithKey { @@ -125,7 +130,7 @@ fn generate_credential( } #[cfg(any(feature = "test-utils", test))] -fn group( +async fn group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) -> (CoreGroup, CredentialWithKey, SignatureKeyPair) { @@ -136,7 +141,8 @@ fn group( CredentialType::Basic, ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let group = CoreGroup::builder( GroupId::random(backend), @@ -144,13 +150,14 @@ fn group( credential_with_key.clone(), ) .build(backend, &signer) + .await .unwrap(); (group, credential_with_key, signer) } #[cfg(any(feature = "test-utils", test))] -fn receiver_group( +async fn receiver_group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, group_id: GroupId, @@ -162,7 +169,8 @@ fn receiver_group( CredentialType::Basic, ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let group = CoreGroup::builder( group_id, @@ -170,13 +178,14 @@ fn receiver_group( credential_with_key.clone(), ) .build(backend, &signer) + .await .unwrap(); (group, credential_with_key, signer) } #[cfg(test)] -pub fn run_test_vector( +pub async fn run_test_vector( test: MessageProtectionTest, backend: &impl OpenMlsCryptoProvider, ) -> Result<(), String> { @@ -222,11 +231,17 @@ pub fn run_test_vector( private.append(&mut hex_to_bytes(&test.signature_pub)); private } - Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 => hex_to_bytes(&test.signature_priv), + Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 + | Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => { + hex_to_bytes(&test.signature_priv) + } _ => unimplemented!(), }; - let random_own_signature_key = - SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); + let random_own_signature_key = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); let random_own_signature_key = random_own_signature_key.public(); let signer = SignatureKeyPair::from_raw( ciphersuite.signature_algorithm(), @@ -235,7 +250,7 @@ pub fn run_test_vector( ); // Make the group think it has two members. - fn setup_group( + async fn setup_group( backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, test: &MessageProtectionTest, @@ -260,13 +275,17 @@ pub fn run_test_vector( private.append(&mut hex_to_bytes(&test.signature_pub)); private } - Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 => { + Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 + | Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => { hex_to_bytes(&test.signature_priv) } _ => unimplemented!(), }; - let random_own_signature_key = - SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); + let random_own_signature_key = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); let random_own_signature_key = random_own_signature_key.public(); let signer = SignatureKeyPair::from_raw( ciphersuite.signature_algorithm(), @@ -283,10 +302,15 @@ pub fn run_test_vector( }, ) .build(backend, &signer) + .await .unwrap(); let credential = Credential::new("Fake user".into(), CredentialType::Basic).unwrap(); - let signature_keys = SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); + let signature_keys = SignatureKeyPair::new( + ciphersuite.signature_algorithm(), + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); let bob_key_package_bundle = KeyPackageBundle::new( backend, &signature_keys, @@ -295,7 +319,8 @@ pub fn run_test_vector( credential, signature_key: hex_to_bytes(&test.signature_pub).into(), }, - ); + ) + .await; let bob_key_package = bob_key_package_bundle.key_package(); let framing_parameters = FramingParameters::new(&[], WireFormat::PublicMessage); let bob_add_proposal = group @@ -319,10 +344,12 @@ pub fn run_test_vector( let create_commit_result = group .create_commit(params, backend, &signer) + .await .expect("Error creating Commit"); group .merge_commit(backend, create_commit_result.staged_commit) + .await .expect("error merging pending commit"); // Inject the test values into the group @@ -374,7 +401,7 @@ pub fn run_test_vector( let proposal_priv = MlsMessageIn::tls_deserialize_exact(hex_to_bytes(&test.proposal_priv)).unwrap(); - fn test_proposal_pub( + async fn test_proposal_pub( mut group: CoreGroup, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, @@ -408,14 +435,15 @@ pub fn run_test_vector( } test_proposal_pub( - setup_group(backend, ciphersuite, &test, false), + setup_group(backend, ciphersuite, &test, false).await, backend, ciphersuite, proposal.clone(), proposal_pub, - ); + ) + .await; - fn test_proposal_priv( + async fn test_proposal_priv( mut group: CoreGroup, backend: &impl OpenMlsCryptoProvider, proposal: ProposalIn, @@ -434,6 +462,7 @@ pub fn run_test_vector( &proposal_store, &[], ) + .await .unwrap(); // check that proposal == processed_message @@ -445,12 +474,12 @@ pub fn run_test_vector( } } - let group = setup_group(backend, ciphersuite, &test, false); - test_proposal_priv(group, backend, proposal.clone(), proposal_priv); + let group = setup_group(backend, ciphersuite, &test, false).await; + test_proposal_priv(group, backend, proposal.clone(), proposal_priv).await; // Wrap `proposal` into a `PrivateMessage`. - let group = setup_group(backend, ciphersuite, &test, false); - let mut sender_group = setup_group(backend, ciphersuite, &test, true); + let group = setup_group(backend, ciphersuite, &test, false).await; + let mut sender_group = setup_group(backend, ciphersuite, &test, true).await; let proposal_authenticated_content = AuthenticatedContent::member_proposal( FramingParameters::new(&[], WireFormat::PrivateMessage), sender_index, @@ -470,11 +499,12 @@ pub fn run_test_vector( backend, proposal.clone(), my_proposal_priv_out.into(), - ); + ) + .await; // Wrap `proposal` into a `PublicMessage`. - let group = setup_group(backend, ciphersuite, &test, false); - let sender_group = setup_group(backend, ciphersuite, &test, true); + let group = setup_group(backend, ciphersuite, &test, false).await; + let sender_group = setup_group(backend, ciphersuite, &test, true).await; let proposal_authenticated_content = AuthenticatedContent::member_proposal( FramingParameters::new(&[], WireFormat::PublicMessage), sender_index, @@ -499,7 +529,8 @@ pub fn run_test_vector( ciphersuite, proposal, my_proposal_pub_out.into(), - ); + ) + .await; } // Commit @@ -546,7 +577,7 @@ pub fn run_test_vector( } test_commit_pub( - setup_group(backend, ciphersuite, &test, false), + setup_group(backend, ciphersuite, &test, false).await, backend, ciphersuite, commit.clone(), @@ -589,7 +620,7 @@ pub fn run_test_vector( } test_commit_priv( - setup_group(backend, ciphersuite, &test, false), + setup_group(backend, ciphersuite, &test, false).await, backend, ciphersuite, commit.clone(), @@ -597,8 +628,8 @@ pub fn run_test_vector( ); // Wrap `commit` into a `PrivateMessage`. - let group = setup_group(backend, ciphersuite, &test, false); - let mut sender_group = setup_group(backend, ciphersuite, &test, true); + let group = setup_group(backend, ciphersuite, &test, false).await; + let mut sender_group = setup_group(backend, ciphersuite, &test, true).await; let mut commit_authenticated_content = AuthenticatedContent::commit( FramingParameters::new(&[], WireFormat::PrivateMessage), Sender::Member(sender_index), @@ -625,8 +656,8 @@ pub fn run_test_vector( ); // Wrap `commit` into a `PublicMessage`. - let group = setup_group(backend, ciphersuite, &test, false); - let sender_group = setup_group(backend, ciphersuite, &test, true); + let group = setup_group(backend, ciphersuite, &test, false).await; + let sender_group = setup_group(backend, ciphersuite, &test, true).await; let mut commit_authenticated_content = AuthenticatedContent::commit( FramingParameters::new(&[], WireFormat::PublicMessage), Sender::Member(sender_index), @@ -663,7 +694,7 @@ pub fn run_test_vector( let application_priv = MlsMessageIn::tls_deserialize_exact(hex_to_bytes(&test.application_priv)).unwrap(); - fn test_application_priv( + async fn test_application_priv( mut group: CoreGroup, backend: &impl OpenMlsCryptoProvider, application: Vec, @@ -682,6 +713,7 @@ pub fn run_test_vector( &proposal_store, &[], ) + .await .unwrap(); match processed_message.into_content() { ProcessedMessageContent::ApplicationMessage(a) => { @@ -692,14 +724,15 @@ pub fn run_test_vector( } test_application_priv( - setup_group(backend, ciphersuite, &test, false), + setup_group(backend, ciphersuite, &test, false).await, backend, application.clone(), application_priv, - ); + ) + .await; // Wrap `application` into a `PrivateMessage`. - let mut sender_group = setup_group(backend, ciphersuite, &test, true); + let mut sender_group = setup_group(backend, ciphersuite, &test, true).await; let private_message = sender_group .create_application_message(&[], &application, 0, backend, &signer) .unwrap(); @@ -707,11 +740,12 @@ pub fn run_test_vector( MlsMessageOut::from_private_message(private_message, sender_group.version()); test_application_priv( - setup_group(backend, ciphersuite, &test, false), + setup_group(backend, ciphersuite, &test, false).await, backend, application.clone(), my_application_priv_out.into(), - ); + ) + .await; } log::trace!("Finished test verification"); @@ -720,14 +754,14 @@ pub fn run_test_vector( } #[apply(backends)] -fn read_test_vectors_mp(backend: &impl OpenMlsCryptoProvider) { +async fn read_test_vectors_mp(backend: &impl OpenMlsCryptoProvider) { let _ = pretty_env_logger::try_init(); log::debug!("Reading test vectors ..."); let tests: Vec = read("test_vectors/message-protection.json"); for test_vector in tests { - match run_test_vector(test_vector, backend) { + match run_test_vector(test_vector, backend).await { Ok(_) => {} Err(e) => panic!("Error while checking message protection test vector.\n{e:?}"), } diff --git a/openmls/src/tree/tests_and_kats/kats/secret_tree.rs b/openmls/src/tree/tests_and_kats/kats/secret_tree.rs index cd9414e965..6b1eec26d8 100644 --- a/openmls/src/tree/tests_and_kats/kats/secret_tree.rs +++ b/openmls/src/tree/tests_and_kats/kats/secret_tree.rs @@ -191,7 +191,7 @@ pub fn run_test_vector( } #[apply(backends)] -fn read_test_vectors_st(backend: &impl OpenMlsCryptoProvider) { +async fn read_test_vectors_st(backend: &impl OpenMlsCryptoProvider) { let _ = pretty_env_logger::try_init(); log::debug!("Reading test vectors ..."); diff --git a/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs b/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs index 871b40770c..9f3c9f60b5 100644 --- a/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs +++ b/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs @@ -12,7 +12,7 @@ use std::collections::HashMap; // This tests the boundaries of the generations from a SecretTree #[apply(ciphersuites_and_backends)] -fn test_boundaries(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_boundaries(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let configuration = &SenderRatchetConfiguration::default(); let encryption_secret = EncryptionSecret::random(ciphersuite, backend); let mut secret_tree = SecretTree::new( @@ -157,7 +157,7 @@ fn test_boundaries(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // This tests if the generation gets incremented correctly and that the returned // values are unique. #[apply(ciphersuites_and_backends)] -fn increment_generation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn increment_generation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { const SIZE: usize = 100; const MAX_GENERATIONS: usize = 10; @@ -224,7 +224,7 @@ fn increment_generation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr } #[apply(ciphersuites_and_backends)] -fn secret_tree(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn secret_tree(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let leaf_index = 0u32; let generation = 0; let n_leaves = 10u32; diff --git a/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs b/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs index c60aac059d..bc7de1d6d8 100644 --- a/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs +++ b/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs @@ -7,7 +7,7 @@ use crate::{ // Test the maximum forward ratcheting #[apply(ciphersuites_and_backends)] -fn test_max_forward_distance(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_max_forward_distance(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let configuration = &SenderRatchetConfiguration::default(); let secret = Secret::random(ciphersuite, backend, ProtocolVersion::Mls10) .expect("Not enough randomness."); @@ -45,7 +45,7 @@ fn test_max_forward_distance(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry // Test out-of-order generations #[apply(ciphersuites_and_backends)] -fn test_out_of_order_generations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_out_of_order_generations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let configuration = &SenderRatchetConfiguration::default(); let secret = Secret::random(ciphersuite, backend, ProtocolVersion::Mls10) .expect("Not enough randomness."); @@ -83,7 +83,7 @@ fn test_out_of_order_generations(ciphersuite: Ciphersuite, backend: &impl OpenMl // Test forward secrecy #[apply(ciphersuites_and_backends)] -fn test_forward_secrecy(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_forward_secrecy(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Encryption Ratchets are forward-secret by default, since they don't store // any keys. Thus, we can only test FS on Decryption Ratchets. let configuration = &SenderRatchetConfiguration::default(); diff --git a/openmls/src/treesync/mod.rs b/openmls/src/treesync/mod.rs index cd985c0094..ee874bb7e8 100644 --- a/openmls/src/treesync/mod.rs +++ b/openmls/src/treesync/mod.rs @@ -724,12 +724,12 @@ mod test { } #[apply(ciphersuites_and_backends)] - fn test_ratchet_tree_trailing_blank_nodes( + async fn test_ratchet_tree_trailing_blank_nodes( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { let (key_package, _, _) = - crate::key_packages::test_key_packages::key_package(ciphersuite, backend); + crate::key_packages::test_key_packages::key_package(ciphersuite, backend).await; let node_in = NodeIn::from(Node::LeafNode(LeafNode::from(key_package))); let tests = [ (vec![], false), diff --git a/openmls/src/treesync/node/encryption_keys.rs b/openmls/src/treesync/node/encryption_keys.rs index 552bde7d29..c8918c1006 100644 --- a/openmls/src/treesync/node/encryption_keys.rs +++ b/openmls/src/treesync/node/encryption_keys.rs @@ -36,16 +36,6 @@ impl EncryptionKey { self.key.as_slice() } - /// Helper function to prefix the given (serialized) [`EncryptionKey`] with - /// the `ENCRYPTION_KEY_LABEL`. - /// - /// Returns the resulting bytes. - fn to_bytes_with_prefix(&self) -> Vec { - let mut key_store_index = ENCRYPTION_KEY_LABEL.to_vec(); - key_store_index.extend_from_slice(self.as_slice()); - key_store_index - } - /// Encrypt to this HPKE public key. pub(crate) fn encrypt( &self, @@ -126,6 +116,7 @@ impl EncryptionPrivateKey { #[cfg(test)] impl EncryptionPrivateKey { + #[allow(dead_code)] pub(crate) fn key(&self) -> &HpkePrivateKey { &self.key } @@ -144,21 +135,20 @@ pub(crate) struct EncryptionKeyPair { private_key: EncryptionPrivateKey, } -const ENCRYPTION_KEY_LABEL: &[u8; 19] = b"leaf_encryption_key"; - impl EncryptionKeyPair { /// Write the [`EncryptionKeyPair`] to the key store of the `backend`. This /// function is meant to store standalone keypairs, not ones that are /// already in use with an MLS group. /// /// Returns a key store error if access to the key store fails. - pub(crate) fn write_to_key_store( + pub(crate) async fn write_to_key_store( &self, backend: &impl OpenMlsCryptoProvider, ) -> Result<(), KeyStore::Error> { backend .key_store() - .store(&self.public_key().to_bytes_with_prefix(), self) + .store(&self.public_key().as_slice(), self) + .await } /// Read the [`EncryptionKeyPair`] from the key store of the `backend`. This @@ -166,13 +156,11 @@ impl EncryptionKeyPair { /// already in use with an MLS group. /// /// Returns `None` if the keypair cannot be read from the store. - pub(crate) fn read_from_key_store( + pub(crate) async fn read_from_key_store( backend: &impl OpenMlsCryptoProvider, encryption_key: &EncryptionKey, ) -> Option { - backend - .key_store() - .read(&encryption_key.to_bytes_with_prefix()) + backend.key_store().read(&encryption_key.as_slice()).await } /// Delete the [`EncryptionKeyPair`] from the key store of the `backend`. @@ -180,13 +168,14 @@ impl EncryptionKeyPair { /// already in use with an MLS group. /// /// Returns a key store error if access to the key store fails. - pub(crate) fn delete_from_key_store( + pub(crate) async fn delete_from_key_store( &self, backend: &impl OpenMlsCryptoProvider, ) -> Result<(), KeyStore::Error> { backend .key_store() - .delete::(&self.public_key().to_bytes_with_prefix()) + .delete::(&self.public_key().as_slice()) + .await } pub(crate) fn public_key(&self) -> &EncryptionKey { @@ -206,6 +195,7 @@ impl EncryptionKeyPair { Ok(backend .crypto() .derive_hpke_keypair(config.ciphersuite.hpke_config(), ikm.as_slice()) + .map_err(LibraryError::unexpected_crypto_error)? .into()) } } @@ -214,11 +204,13 @@ impl EncryptionKeyPair { pub mod test_utils { use super::*; - pub fn read_keys_from_key_store( + pub async fn read_keys_from_key_store( backend: &impl OpenMlsCryptoProvider, encryption_key: &EncryptionKey, ) -> HpkeKeyPair { - let keys = EncryptionKeyPair::read_from_key_store(backend, encryption_key).unwrap(); + let keys = EncryptionKeyPair::read_from_key_store(backend, encryption_key) + .await + .unwrap(); HpkeKeyPair { private: keys.private_key.key, @@ -226,13 +218,13 @@ pub mod test_utils { } } - pub fn write_keys_from_key_store( + pub async fn write_keys_from_key_store( backend: &impl OpenMlsCryptoProvider, encryption_key: HpkeKeyPair, ) { let keypair = EncryptionKeyPair::from(encryption_key); - keypair.write_to_key_store(backend).unwrap(); + keypair.write_to_key_store(backend).await.unwrap(); } } diff --git a/openmls/src/treesync/node/leaf_node.rs b/openmls/src/treesync/node/leaf_node.rs index ab9d79aa4f..0900b1ac15 100644 --- a/openmls/src/treesync/node/leaf_node.rs +++ b/openmls/src/treesync/node/leaf_node.rs @@ -179,7 +179,7 @@ impl LeafNode { /// This function can be used when generating an update. In most other cases /// a leaf node should be generated as part of a new [`KeyPackage`]. #[cfg(test)] - pub(crate) fn updated( + pub(crate) async fn updated( &self, config: CryptoConfig, tree_info_tbs: TreeInfoTbs, @@ -198,6 +198,7 @@ impl LeafNode { backend, signer, ) + .await } /// Generate a fresh leaf node. @@ -208,7 +209,7 @@ impl LeafNode { /// This function can be used when generating an update. In most other cases /// a leaf node should be generated as part of a new [`KeyPackage`]. #[cfg(test)] - pub(crate) fn generate_update( + pub(crate) async fn generate_update( config: CryptoConfig, credential_with_key: CredentialWithKey, capabilities: Capabilities, @@ -234,6 +235,7 @@ impl LeafNode { // Store the encryption key pair in the key store. encryption_key_pair .write_to_key_store(backend) + .await .map_err(LeafNodeGenerationError::KeyStoreError)?; Ok(leaf_node) diff --git a/openmls/src/treesync/node/leaf_node/capabilities.rs b/openmls/src/treesync/node/leaf_node/capabilities.rs index 6f909d5651..d33b1f61d0 100644 --- a/openmls/src/treesync/node/leaf_node/capabilities.rs +++ b/openmls/src/treesync/node/leaf_node/capabilities.rs @@ -201,6 +201,7 @@ pub(super) fn default_ciphersuites() -> Vec { vec![ Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, + Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, ] } diff --git a/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs b/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs index 1c9baf02ae..f2abfcf473 100644 --- a/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs +++ b/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs @@ -48,7 +48,10 @@ struct TestElement { tree_after: Vec, } -fn run_test_vector(test: TestElement, backend: &impl OpenMlsCryptoProvider) -> Result<(), String> { +async fn run_test_vector( + test: TestElement, + backend: &impl OpenMlsCryptoProvider, +) -> Result<(), String> { let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519; let group_id = GroupId::random(backend); @@ -120,14 +123,14 @@ fn run_test_vector(test: TestElement, backend: &impl OpenMlsCryptoProvider) -> R } #[apply(backends)] -fn read_test_vectors_tree_operations(backend: &impl OpenMlsCryptoProvider) { +async fn read_test_vectors_tree_operations(backend: &impl OpenMlsCryptoProvider) { let _ = pretty_env_logger::try_init(); log::debug!("Reading test vectors ..."); let tests: Vec = read("test_vectors/tree-operations.json"); for test_vector in tests { - match run_test_vector(test_vector, backend) { + match run_test_vector(test_vector, backend).await { Ok(_) => {} Err(e) => panic!("Error while checking tree operations test vector.\n{e:?}"), } diff --git a/openmls/src/treesync/tests_and_kats/kats/kat_tree_validation.rs b/openmls/src/treesync/tests_and_kats/kats/kat_tree_validation.rs index 39a6d83bf8..5ca6f56711 100644 --- a/openmls/src/treesync/tests_and_kats/kats/kat_tree_validation.rs +++ b/openmls/src/treesync/tests_and_kats/kats/kat_tree_validation.rs @@ -98,8 +98,11 @@ fn run_test_vector(test: TestElement, backend: &impl OpenMlsCryptoProvider) -> R } let group_id = &GroupId::from_slice(test.group_id.as_slice()); - let ratchet_tree = RatchetTreeIn::tls_deserialize_exact(test.tree) - .unwrap() + let Ok(ratchet_tree_in) = RatchetTreeIn::tls_deserialize_exact(test.tree) else { + // ! Some trees are malformed in the test vectors, ignore them + return Ok(()) + }; + let ratchet_tree = ratchet_tree_in .into_verified(ciphersuite, backend.crypto(), group_id) .unwrap(); @@ -131,7 +134,7 @@ fn run_test_vector(test: TestElement, backend: &impl OpenMlsCryptoProvider) -> R } #[apply(backends)] -fn read_test_vectors_tree_validation(backend: &impl OpenMlsCryptoProvider) { +async fn read_test_vectors_tree_validation(backend: &impl OpenMlsCryptoProvider) { let _ = pretty_env_logger::try_init(); log::debug!("Reading test vectors ..."); diff --git a/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs b/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs index ecb8a5ea94..4514af9632 100644 --- a/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs +++ b/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs @@ -110,10 +110,8 @@ pub fn run_test_vector(test: TreeKemTest, backend: &impl OpenMlsCryptoProvider) .leaf(LeafNodeIndex::new(leaf_private_test.index)) .unwrap(); let signature_key = own_leaf.signature_key(); - let mut private_key = leaf_private_test.signature_priv.clone(); - if ciphersuite != Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 { - private_key.append(&mut signature_key.as_slice().to_vec()); - } + let private_key = leaf_private_test.signature_priv.clone(); + let signature_keypair = SignatureKeyPair::from_raw( ciphersuite.signature_algorithm(), private_key, diff --git a/openmls/src/treesync/tests_and_kats/tests.rs b/openmls/src/treesync/tests_and_kats/tests.rs index a00269d598..14e8cc50f8 100644 --- a/openmls/src/treesync/tests_and_kats/tests.rs +++ b/openmls/src/treesync/tests_and_kats/tests.rs @@ -16,7 +16,7 @@ mod test_unmerged_leaves; /// Pathological example taken from ... /// https://github.com/mlswg/mls-protocol/issues/690#issue-1244086547. #[apply(ciphersuites_and_backends)] -fn that_commit_secret_is_derived_from_end_of_update_path_not_root( +async fn that_commit_secret_is_derived_from_end_of_update_path_not_root( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -36,13 +36,13 @@ fn that_commit_secret_is_derived_from_end_of_update_path_not_root( backend: OpenMlsRustCrypto, } - fn create_member( + async fn create_member( ciphersuite: Ciphersuite, backend: OpenMlsRustCrypto, name: Vec, ) -> Member { let credential_with_key_and_signer = - generate_credential_with_key(name.clone(), ciphersuite.signature_algorithm(), &backend); + generate_credential_with_key(name.clone(), ciphersuite.signature_algorithm(), &backend).await; let key_package = KeyPackage::builder() .build( CryptoConfig::with_default_version(ciphersuite), @@ -50,6 +50,7 @@ fn that_commit_secret_is_derived_from_end_of_update_path_not_root( &credential_with_key_and_signer.signer, credential_with_key_and_signer.credential_with_key.clone(), ) + .await .unwrap(); Member { @@ -82,10 +83,10 @@ fn that_commit_secret_is_derived_from_end_of_update_path_not_root( .unwrap() } - let alice = create_member(ciphersuite, OpenMlsRustCrypto::default(), "alice".into()); - let bob = create_member(ciphersuite, OpenMlsRustCrypto::default(), "bob".into()); - let charlie = create_member(ciphersuite, OpenMlsRustCrypto::default(), "charlie".into()); - let dave = create_member(ciphersuite, OpenMlsRustCrypto::default(), "dave".into()); + let alice = create_member(ciphersuite, OpenMlsRustCrypto::default(), "alice".into()).await; + let bob = create_member(ciphersuite, OpenMlsRustCrypto::default(), "bob".into()).await; + let charlie = create_member(ciphersuite, OpenMlsRustCrypto::default(), "charlie".into()).await; + let dave = create_member(ciphersuite, OpenMlsRustCrypto::default(), "dave".into()).await; // `A` creates a group with `B`, `C`, and `D` ... let mut alice_group = MlsGroup::new( @@ -97,6 +98,7 @@ fn that_commit_secret_is_derived_from_end_of_update_path_not_root( .credential_with_key .clone(), ) + .await .unwrap(); alice_group.print_ratchet_tree("Alice (after new)"); @@ -106,9 +108,13 @@ fn that_commit_secret_is_derived_from_end_of_update_path_not_root( &alice.credential_with_key_and_signer.signer, &[bob.key_package, charlie.key_package, dave.key_package], ) + .await .expect("Adding members failed."); - alice_group.merge_pending_commit(&alice.backend).unwrap(); + alice_group + .merge_pending_commit(&alice.backend) + .await + .unwrap(); alice_group.print_ratchet_tree("Alice (after add_members)"); // --------------------------------------------------------------------------------------------- @@ -121,6 +127,7 @@ fn that_commit_secret_is_derived_from_end_of_update_path_not_root( welcome.into_welcome().unwrap(), None, ) + .await .expect("Joining the group failed.") }; charlie_group.print_ratchet_tree("Charlie (after new)"); @@ -133,10 +140,12 @@ fn that_commit_secret_is_derived_from_end_of_update_path_not_root( &charlie.credential_with_key_and_signer.signer, &[alice, bob], ) + .await .expect("Removal of members failed."); charlie_group .merge_pending_commit(&charlie.backend) + .await .unwrap(); charlie_group.print_ratchet_tree("Charlie (after remove)"); diff --git a/openmls/src/treesync/tests_and_kats/tests/test_diff.rs b/openmls/src/treesync/tests_and_kats/tests/test_diff.rs index 02698d7e81..a49cd09cb9 100644 --- a/openmls/src/treesync/tests_and_kats/tests/test_diff.rs +++ b/openmls/src/treesync/tests_and_kats/tests/test_diff.rs @@ -11,23 +11,28 @@ use crate::{ // Verifies that when we add a leaf to a tree with blank leaf nodes, the leaf will be added at the leftmost free leaf index #[apply(ciphersuites_and_backends)] -fn test_free_leaf_computation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_free_leaf_computation( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { let (c_0, sk_0) = new_credential( backend, b"leaf0", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; - let kpb_0 = KeyPackageBundle::new(backend, &sk_0, ciphersuite, c_0); + let kpb_0 = KeyPackageBundle::new(backend, &sk_0, ciphersuite, c_0).await; let (c_3, sk_3) = new_credential( backend, b"leaf3", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); - let kpb_3 = KeyPackageBundle::new(backend, &sk_3, ciphersuite, c_3); + ) + .await; + let kpb_3 = KeyPackageBundle::new(backend, &sk_3, ciphersuite, c_3).await; // Build a rudimentary tree with two populated and two empty leaf nodes. let ratchet_tree = RatchetTree::trimmed(vec![ @@ -51,8 +56,9 @@ fn test_free_leaf_computation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr b"leaf2", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); - let kpb_2 = KeyPackageBundle::new(backend, &signer_2, ciphersuite, c_2); + ) + .await; + let kpb_2 = KeyPackageBundle::new(backend, &signer_2, ciphersuite, c_2).await; let mut diff = tree.empty_diff(); let free_leaf_index = diff.free_leaf_index(); diff --git a/openmls/test_vectors/crypto-basics.json b/openmls/test_vectors/crypto-basics.json index 876be5e808..b405b3a5d6 100644 --- a/openmls/test_vectors/crypto-basics.json +++ b/openmls/test_vectors/crypto-basics.json @@ -3,301 +3,301 @@ "cipher_suite": 1, "derive_secret": { "label": "DeriveSecret", - "out": "e0d9f5de2914b2e018dd6efeb17dbb1d412e9f3687e6dbd1a1604c9b06dc817d", - "secret": "328f5dde49dd58c97511c651be7ebe3abb2cc124d0721ae999ae2a81a8d3d867" + "out": "3b08c195a246c4ad469c1d11c10e62890d8fa6b684494ff925409efdb1ff0464", + "secret": "1a9ce178a53f8752d2513c27efe9c85133f6c0a97f7b35ac200695024a77228e" }, "derive_tree_secret": { "generation": 2694881440, "label": "DeriveTreeSecret", "length": 32, - "out": "fb6a4bb14ab71b87ef4d681a1e2b1cf60f43db4d3453f5450d78c99038314560", - "secret": "647b34c8054c1dd471f534520829d11562f6281ca06cf6ebf763285573084d69" + "out": "8461f3ccc603eae52149a23a4134d29c880a1ad1ba70441e5d586e3521ec7b25", + "secret": "5133c6f8bad297f5d3beacdf477f0c45ec51b02de659d305220c5f9385c6eb43" }, "encrypt_with_label": { - "ciphertext": "4a22e124c9fd1d643aa24ea5b3f619b7a057b76577e58c6981e0499ba1a0dd093d6268335145e0ce337adfb7e539c836", - "context": "4d361cb2467d026b21012a099c0ee2503a1dd66706fc3c567a40a1582c19e7ca", - "kem_output": "bc19b7998ffd548b67d14a1ebac651b307b0dff359e4c599ddafb0691d58cf56", + "ciphertext": "15c80ea2bc37db221baa530ef5aea88650f0ce0f262803d6f78f3a1392f7ccd960eff94ca081ee54efa4c3acfa0eb591", + "context": "26347dd7f218d1de8673d6a66646ce06ac5fd3aa8d5c33f65d86aeefdcf4a31e", + "kem_output": "0a144e8fbf2d6dcf6fe9d2e2b8aeca5461ff5b0ea9c0ede1040c3dc7ed1dfd1c", "label": "EncryptWithLabel", - "plaintext": "4643fe152285ff61d8345ff0d0b36c648a52141d1b3c6431f83d40660657243b", - "priv": "6ac910db28ccafe3e1819672b17be638cc087474d2e437ccf259871f552cdba7", - "pub": "dedd07d9cf60e32523ced9bb80e496e4c4bf50efa381d7225e288764c3af691c" + "plaintext": "8f55dd30f03d64335c22b53ea7670bb1becf49b04021f706368fe93eeb358f46", + "priv": "fb1ade7939987ff12a9d620772b1f9f7caeba26f8a3ecea9617d9402cd862444", + "pub": "ecea6564da58d6c6cff6c733bd4ae0815b1f60bb911b73e4ef1d06263ec4ce58" }, "expand_with_label": { - "context": "b980b868d7f7299bb4746308d1137a8b6dd8adc285904109e85744bb82e7ce61", + "context": "2ff8c1f9d9c1248f82e372ddb5791c771695e01882abca6a64097bd2f04c971f", "label": "ExpandWithLabel", "length": 16, - "out": "b9bd30befa385f8ee1aca89dec70f45c", - "secret": "70ba3d1ba25577f3ab1f657896c81f9017f001dd16adf103c5f3c4a64d1566df" + "out": "c1e8eb360391526c0c64039f13e0c5b1", + "secret": "1499360a561335f4ef51d0a1b0d586900dc8007ae405b1ab79bf4207bb3d67e4" }, "ref_hash": { "label": "RefHash", - "out": "2b4e7d394c697423980c61d328c092f53a2f281003a56c3c2e6d0247d04631b4", - "value": "d6118d082d81be525739f3386c1276bf6c93dee043a51bdc90f8acee4aa3a559" + "out": "e8027fffc5f9bb469f29172538dc0f3a78f14f323495bbd2217eba7a77fb242a", + "value": "40312db83f651883c05ab26fa12c6af61930015c81947cfd0f129e6d99210bb2" }, "sign_with_label": { - "content": "ec0050f527ca7e9e22d050d1ff2914025b66572a0b53d0bad66860be54b4c067", + "content": "cd289cc7ba2869f64f3c32ffd133f500d17abace919a5ffe7faa974200d81932", "label": "SignWithLabel", - "priv": "0e0ebe3af8b15d3f774257223ccb07e75241dea117c514138344460db07e779f", - "pub": "d412afae39be053c8606cea6e6d5961a73ef55eedc1c67b417d3cb4af8e05cea", - "signature": "b2a730f27bd610715d635901eea817ed251d584e72f8deac46c643e78f7e331e6d16d54b0c0d84c23d43b28897d677c4afb078b94330d063c6b7c6741ad85d09" + "priv": "a2f640dd5005fcad6adb8e9bd8b60d70946bb802e1e788307929fdac81e1ec74", + "pub": "85600e54e5c2919ccbd0742126e5d837cf7a2ba50d75a69b3f35dcfe4a50ffe2", + "signature": "996bd223ddb4d55a2b57d85cb2944f21facc95696053ddf66d590060fdc719f4a26c6212ce605414e0d5e66a55921dd99d11122218c35bc23408b0076e8bc40b" } }, { "cipher_suite": 2, "derive_secret": { "label": "DeriveSecret", - "out": "e65d898ec930298203ae8a443ddd2768dea8d7cc016b3874fa454f8e42e098be", - "secret": "383da60ed10ea443b46c829a86c1cf49fba185b70745007d10aa79b21d9aa358" + "out": "1ecafd3d40cb32cae416e09bc01da56357d00cb094f74e3c69c2969216e50afc", + "secret": "7165576616048aa145d20f7c1d460e2d9d8bbde882bc3bb0750d50e369809f99" }, "derive_tree_secret": { "generation": 2694881440, "label": "DeriveTreeSecret", "length": 32, - "out": "65834175eb8233c2a54a7e7f6202bd8bb45e2f68040116fbcc53e039d4b44318", - "secret": "1444c5fd64e2d1f8c91017b93f14f6e343cffa634439dec3fc1c70abcd6c0155" + "out": "298ab27d2e621d9fc079126d9ffce5259fa0d58697267b40bfadf805b01d0d3c", + "secret": "62db8a06300e98dbfd2c831e18544873cec3a2c1ba852fcee8423ff3c08e397e" }, "encrypt_with_label": { - "ciphertext": "a47705e4d102760e57749279995d5ec0bfcea8bade7d1153c8a1f3b8b68d5a47b16eae519d73914e60c276012d635365", - "context": "4b15254f2f4d600e24d46effe473b67dd9f8f5d78f6600b07bf64909e6ec7f1c", - "kem_output": "0489258cf131f7b718c85f54b5003f07e7283c47db27e7b3b799995ade9c4dacf446989655063bbf1c48cd7d999964e1c368e1bb1651290a9aca3e24e6c25a2fa1", + "ciphertext": "98b7d4edc79f4a90f65434050f75e31a3bc6501c584d41abd6ed5fe3c2db9de22d25ef40c45cce7d0fcc881d7d27af2f", + "context": "e27e3b0104990cca866751732c82787af4dcf265c893f77e31bcfd679370e24e", + "kem_output": "041fae8a8173cad50c0cc6d55f148ff8edda63083b3673cf6dce6a0ae6ee3f0b61505470309dade87ac5ccdb581da3e9ddf6726949d5a92b65dcad6c8679c7313e", "label": "EncryptWithLabel", - "plaintext": "d410024bdcf15e9e881b5707bf23abbb007d0b991399c12d6c66761f8570e394", - "priv": "31e72362eb6630d63253a73a117f2ccbbab2cba38b50fd0ff368fa9a3c8de858", - "pub": "04cb85d6a7593ac2424d3587c68fd0360b91f332d6f415b5ced3382f49cd3d1a05544d67e11425701fcea971e4559365197e022f40f4ff6cc15fbc00a341d1a897" + "plaintext": "38a6b327573639d654b5b729336cf74d01728cf4fa9af81a0ef1814ffc1d492f", + "priv": "ff21771424dadd640e05c67983aafe19b4d8df50783a0c2decc17d0c7ca4cc17", + "pub": "047b27b0be346d14d7b4df30296a030deeba088746da7cfda43d0ec739df3ce90d3c96d5f302e41f935ac9020651285c7bcf073172d375c5abcfc9e491b3491f88" }, "expand_with_label": { - "context": "beb566191d50bdaab9258c254e1e09d9ca9020f8bbfecbb6a4b0ce54c96ec7b1", + "context": "453c420d63ecb1a8da89d3569770c42bad572864f3709d1e9dd19a0961cbf5e1", "label": "ExpandWithLabel", "length": 16, - "out": "85d6bef41aed564c04b4cbc461d895ae", - "secret": "4cd65a6504a1a39942c02df8d533545dd352323b9bce7a2a379cda6084ca9030" + "out": "5710680c556304f4aec67aab4abbc1b1", + "secret": "194d4e81c3f9fcfbc40e2cf3f5b104d0f51e71b5a7f1e4ddd70cc8d4a3620e5f" }, "ref_hash": { "label": "RefHash", - "out": "8bfe9351d68b21b13d635500eff9b1766a7ced82d9d4c3920232bf5085f68e49", - "value": "5626c6b2a65b958a0cc8395cf74a2075413043f01e417a7998c5012bc6b20aec" + "out": "8f508c2f89d2797b6882ee487dc2832b2b6d59b27681293c2c2c3f1f0e6cd54f", + "value": "feb64672017685dc26d0b7fc41cd96d8bef40af09002eb5aa9a52580c80f0b2e" }, "sign_with_label": { - "content": "fdff04d170e04260eed6becd221826676ff20439c51df2963b90e9bfe74bfa69", + "content": "a0dda617ce4685d764c762b11186b6d60ff8de85ff01eca2413bd4ecfd3b3a57", "label": "SignWithLabel", - "priv": "0b3c5891b905326f835e4be6932068dce75abc2346a254f77040a1f28876c18e", - "pub": "04ab9e59681a34073af1b22c5038fdfe1f19ecc48cc9a1896a6a905456ed9522e9a36783a17ffc1bb0a1679a93e42af6721dd66be1b424ab9d9369ea4589d8fbc8", - "signature": "304502204f000c0478b54d1eaba9c302fd7c3475fe7bddf35183fbf6953c4669b4e829bd022100f844f46654e3b92f066b8c88cf0db8925f5d5a25c08d9103145baabbf8e3ebd5" + "priv": "207c472d3efaf6737a6f5ae14a3c33a139034865364a128bca5475c85cc02fe0", + "pub": "04448971fc06de011d780cf68fd27e2570322d04079f529c3deb48a2015fdd828162c570264e051b5856e8111171fb0341907173aefa665682c2549af982a31483", + "signature": "304402206042e397e1bb78951709790e3446b13bf17f9c641aaed92fb1768b5bc99dd98402202465153d91dd79e808286088768d8ed150381f3675498d6e150ae713be43387b" } }, { "cipher_suite": 3, "derive_secret": { "label": "DeriveSecret", - "out": "5d619508cd791107a0f38151ca080a38baae7f2fe847eb7323cf78d835aa62ab", - "secret": "e2bc389300bd77ea6bd373e9cd68615f6405a853f37aa07fbeef38423caf7d13" + "out": "aad859818ca5f2a9896d4d3ee2dccc0cefcd69b666bdb16b52f1de15fb1a5567", + "secret": "cae460c779ebaa3e81c061a371486dff1ed1ff273bea369cc0fc46550b83c407" }, "derive_tree_secret": { "generation": 2694881440, "label": "DeriveTreeSecret", "length": 32, - "out": "67caff3d9312b347f139528a354e1f38ee32853e755b59acf3793d06e29b6ae0", - "secret": "86cfecf2fc19fbd0a10b41467136f53e1d54bfffbc025fc96a4be5970d5c89e5" + "out": "2095d6a81ab87095d1df26f6bdf012ec06f197e418381c1795a7b758603c936d", + "secret": "c994e257b53f726087ddd7121876f558f1fbd6f807e5ff010830d618d7bab6f2" }, "encrypt_with_label": { - "ciphertext": "473e122e018bc7252ced7a852d11ecba495393eba9cf1260e7d822d4f1292d24afa9151223fbc4dfc978806a3fe43195", - "context": "9ea32688f2faa4efa60a1a05fe5a67b0e5b8c4e63f36991a0f0a98b10692fc93", - "kem_output": "96fa4aaa16df47a682a7cb0ee3ef234fe48f68fbfe2007ce5757d7cb3bac397e", + "ciphertext": "40dd09ad4c5dc29d373f814bf054c9359cb75a468bc4d2c8bbcffb072a73105c4d9416ebd4fafeb62e59a9dea55da3cd", + "context": "0d6a5cf9ee88b1f8c79d8512477d9bfc5496c207c8173f8dcac0368b4dba7407", + "kem_output": "f26e9e5a94396a90f85a5f72eedf3dacfb1b7f4164e0573edeb9c6c912e1cb49", "label": "EncryptWithLabel", - "plaintext": "70d161b2599580a2a1d1ecbbd239509eedca2b16dd36ae011f4def1e6bfa657f", - "priv": "6a28e493e6a0765012261d280444324d212cbbbb9253473ebce48f0208dd59c4", - "pub": "4b07a5fc9ba1da95c9eeec1bdcbaa6955ce8f05f8fc152f8c3a83609ddf08c31" + "plaintext": "1dd4c1904996ce7d42cee7de68881459fa7a345da59a02040ade37103505baf6", + "priv": "9d122ad4638fcb301b6eb5f4073414afb44bb34d37b4ddee9975b2941d700edb", + "pub": "7a5544b59f5940bf093c921469a00a170a7c92ba56c173d74db32713608d8a40" }, "expand_with_label": { - "context": "d3728953499a90a3773fcd951312386f25039c748aa15494f1904a445e76bf65", + "context": "2e07148f4340c62a55e7608c20d73fddf1f3b8dafb2c7ef24eceb70e136c0d8c", "label": "ExpandWithLabel", "length": 32, - "out": "9a0cd4efc11e361f58609a54244ab9a08d9465e4e484e057823bace7ecf4561d", - "secret": "5328cb2e307d35d4449cfc781af397c62b78c058d6d7f4a093753994b0ae245a" + "out": "1df5ba7996a34f75d717916a094a14083c03a75e80f0330a8095f5f11cfe1e1f", + "secret": "55aa3ae5242564782567ce097beafe19510230660008b2cc064a78387fa16f36" }, "ref_hash": { "label": "RefHash", - "out": "c53da1bbdd8355f44e5e4b46ccf56bf467ead2074fa9fbdb0c49a0dbe30e62a1", - "value": "6d2e94599f40a46373f432086b4544560d20eae4f535c90e3c060514bd8e8206" + "out": "f11019703c8b630060839b12a475fd39c6a30f8a866790ff46a35f9c65e1df3c", + "value": "4f0c86f9c82fba0a896bd7eecf79a29856e98a7e4f13b9f841ae285d70ed8b68" }, "sign_with_label": { - "content": "bc6fe6ed2dd45699b7e8ef463db770bc32e38a187cb34ededdfd23cf220dba5b", + "content": "df308cf2dbf471edf2c29d30e3daf161b5b87d350ee3b2c715c298ec3d10d432", "label": "SignWithLabel", - "priv": "92a4b7c40021c83383c020a3809077baabebf23005148554ef38123024c7d107", - "pub": "45628736352a0cdd824dfdacedf7591bfcfebe27eb285dad571e90728c29be7e", - "signature": "fb379dfd9069561b1a9db646a8727045df7e604047141feca997918a931942b58e6f60f81ee63a27e74f6cda90c39dc8ada92cc5f27732dc085058a840832202" + "priv": "4e312160ee4981358db479aa877412847abc7f7054b5605511256c395404d054", + "pub": "18275f892ee0ca6f4687ff26c990776387502646ff658c3f572b324faecb05c5", + "signature": "4f56851c2c47f5115a61ff0ab6121b4a4732d4e94805fc7135a5132f87d5ca5f1dc7408816c1ea4f25887725cf5914b48c427a52cabcfeb746a2b8a12e821f08" } }, { "cipher_suite": 4, "derive_secret": { "label": "DeriveSecret", - "out": "6a1270efcf4ce09ec8a5aa58946b6fbe6e9ea4050d727ec3a46f1f2ea726ab8ec70e5de23392b0b040ff7abf4d1ba1fc09b193a4f400c2b14b5b5f6464863376", - "secret": "848bb8c3643bc9e044f36d3d839bb4ad6c16397ed1a052cf13cc5e83197de8196375de78652838768052bee283fdfeb665a98413defb598aa07669bcd494bbba" + "out": "ffed9b67c5b8388af9ba51bfa983400dc6cee5c1d79b4cc45a400611075139b842642f1a8d4d92ed3aa9f7497d8a3ce5516552230a883c57dd2bb277ef1e571c", + "secret": "92c5ae58c8aeed565d772847a41f4901a999572ae53740b56a2a3df94c62e57c60f1107c88c70d8aa902049d5f33d36cc4d1f7c233b1406947deb6c1fb243007" }, "derive_tree_secret": { "generation": 2694881440, "label": "DeriveTreeSecret", "length": 64, - "out": "487a8cd1b775545ab54dce219d36ab1c69d4c6de67b9cf84325a0728453b7d91b683f01291413ae4df9406cac118c64eaa58e66cb8c28c9fca8c4f9fdc9f433d", - "secret": "35ba080a1169b6308dd998c6aa842dbbd4b705ad8476b8d80221bc565425c867214f2195d280b8cf52a66c162a23bd811a892383509d136c2e23e39b1c32a2f4" + "out": "dd738960f713469ad0599222c8caa031bbe9438491e79d901a6f7151e2bf8c57948cc27dbc5561a54e600e469f51037d0e1609116bce8b6520e21ee6135d5791", + "secret": "44ecd457f6280bcb1ce1a525aeb3d6b32eb24a81628cdd07c7c663d7790cbbbc1805ec90bb28df27c0567b6ccdc39eabb0b1d3c66033c1355aac4397ea7459ac" }, "encrypt_with_label": { - "ciphertext": "078a400533d45c6bbad0a6b0cf024943411f8277a1714ee16362cb5b8c9fdb6d90c3872567d06a39b5d4f86b69fabc1ae5d8ea54d86b1bf46215239af7ac1fbb12b898def897452238694aaa372c841d", - "context": "3fe6bb2547377ff649e5c0a07fe13ac395615ba5a9c3b44c4b954fb6fa026c8d79997f2637a996cf37fd782a9570f2613ae6ba9080c38823d7a4783d06ccd35d", - "kem_output": "2c67dbd418c7cc78c29404fdd0c1738ef108037f30d22ee918a439e365eccae22d9c9b27afc1c9e6b3b3b15b2a8c51079eaadc40391d07d5", + "ciphertext": "194059f738cabf5fb568373030024fd73b19cb7bb45b61173639e85309879287e8327c8afd09d784018770bd4994c0b050df3e608430151419fc6886c57f847c8f68e001b39f2346dae17108d1fa36aa", + "context": "a73f953ea54f3785af7ee8c8928e5d71baf489e425d1068b1407d4a6efff3c5ed0f8f7d60f58af1856d7990fe9e04bd0b827bda913aa22f10fba0e5f2b1c8c50", + "kem_output": "c487db6afe43e4537890be46ef126bd804510fbc8f53bb3e60c39e439d8cfce804575ebea1b1c298d9fb31e84677b5bc3270a74feb051209", "label": "EncryptWithLabel", - "plaintext": "df16add8a19a9a8a1d27f81c0d374f31c719855a39b1b81c54c6810d74da697634c758072a0e6f484e45468c13871a4af5dea8c6390dd264e6f25e2eed067b9b", - "priv": "5c52966da281cba56d447b6a0a881de2001d277a1e503cb15df3c2af6d9e2a9f16dc00020c789fab799e19da23b0b57ce5a0b519ee4d7117", - "pub": "52ce9d5d97ec3d356f49b9e20d3d96ee5ff3ade87471a82ee722b25d1cb19e3cc10b81ad24a2d2509b2cfebda41e1e8dd4f45c661c0dd70f" + "plaintext": "d1e409fcb7b955e2b368314dc6a34816142448627958f2ce1a103bc2b38ce2a2c1b05678606ae44b2300c959da77b1287228cd9c10e62e8c665db83665a857b4", + "priv": "f98a74e98ed6f217513be360d6d36378fcbbf548f3e4292cf35078f3cc23855fb370fb0ddce879fa864e5a20fd43ab6eed8d8f606b705b92", + "pub": "ab23487f3028c6a5dc5f12eb9bb65a2f93d913962ecc60f6505b6ba82db7d292b1af2106404c829192a14854fff94780cfa325995afc22f7" }, "expand_with_label": { - "context": "37bd7750bf5a63e4d0e3f4c9166831c2631816404b127fb59112fef8ae74838c685c2c30dc7caa3564088de4c03a9e698fc21aa3e209911abe948ca21f8c5f53", + "context": "5837a1b11e1feaba3d403c151e78d233bb4a914b6f7b207607c2c824288cbcc729922d551e829d1727e6875b81b1aad65b7b8398040c09bdc1228d2fab9263f2", "label": "ExpandWithLabel", "length": 32, - "out": "d101ba73840b134dc8f2e01a840db87ade2ff1630aee110d1a7b992de49295f4", - "secret": "263a846250954f55b43e8b5d4e9a552115b4bbd875710ef24b04b1447a151a28029bc0bcb440b8cbe52af99e045f7b67f8f7def5823a5aaec496625a4bf80952" + "out": "0422601b4c3a04ec67e1196eb1f0549d53f0639509309d84f88c265ebc383d18", + "secret": "cf0fb7f2b30b491a94cc2ce32cfd8c46ff0725c7c82376dd4f2ca25f4242ac87fdf02b59482c646d026975a7f7076111875f0b377dc5ca437799735e78d13464" }, "ref_hash": { "label": "RefHash", - "out": "f259df17acad88657913e46c0211c865f091441853fc117d4074f7b1fe51a07d1ce09e1c581ea26d3aab236bbcbcc382f6bdaebe87b720f49e11954d4006ad50", - "value": "4c52582847e5c8aab49f314ab671a3165699724e5f332ddfd2020db911c4e873cc6ce3eb68e3c7c8548c329902f530913f362dec9d2006e629873c1b097d44d5" + "out": "4040acc515e1e0881ab971ea6a6ad0916f756c6edcdfcdd9656d87e942dc1c756b9b6f28aa2659d5342b2244afe4c545fa890529d041dfd4b9256cc9e535ccef", + "value": "cfb75a56c741a7373cfdf7b575f1686db3f9ab05b5fb54f1279488f5ca5145086540477cecc2e79aef5ec4a5942e6b06513121b91f46786e7d8b935ea9ae0783" }, "sign_with_label": { - "content": "e03307808451c3f89fc4511b182d3a7f3fd1aff9a5d8b491017aed07f2762cea16ad79f4a80941d65e08d2b2c4a748bad1e0bee424ec0fe246272ea1ffe26acd", + "content": "0b1aee9793837d23fcdd58cdfd3bdd72666deb49dab282b2e1891ce4e7665d3544787a5eb9663bdbc4397bb111ad182dc5a305e9720687812fda4dc988461621", "label": "SignWithLabel", - "priv": "7fd6ea534477235e177bd6801f40b5f6584d08eadf6a75549b50a1d17d132f0ec4e5532c09fe5d936036347e5f2cf6d5291c6a8e6e1be88325", - "pub": "7a252877e07e4569937feeb424753c6c0ba76d1b19288b765be39375692f65198b9700fa0c6f8ba73261021e8b91d36e05173ad7a530e84180", - "signature": "cc9c90e4b628bba89f2237f237065213983e2facd1e4c9587f43766852a251ebddce49c8952dc6c76f926891a252db375583bd11ddfac800007295f3165fc57ce489172b12d4c5b105846a0fb7ea003258f085bc767674e6db63893ef190aa2fb2f259a7cdb91a9292007c2ccd6f2a5b2a00" + "priv": "e6e78b6be2d3a2ef0be99f62c2d3c57a3f392bf53084f276c9675ca1890e46dbdd6922cafff540c7dae1af237ba8272659e1c64cfc8c141aad", + "pub": "7ed080c31aa1c4ba05aea3bd75efe01825a5f219fb7e1ea67b1c171f01c19339873b7bc29614903888ba49f954c9f0c6f0b6051a1ed4ab5880", + "signature": "0b363568911eb4c34d89a01007bab0585d06cc24937cea982d330429802d4d91fae03766f8f0cae6c3067a369ea9b9e87c8015e91a424dcd80377df71eaf4b5ad2b98cea049ba3e29faf66eee0a9289f8ad5f9b38d52ce9c653f2b86002803e1c918bb5ba511ee2784c5ced7df3e59bf0200" } }, { "cipher_suite": 5, "derive_secret": { "label": "DeriveSecret", - "out": "41126440e44c61f78782d4e25dca18559f4402eb5efe9285616517a26ee665a95aabe3a2f4e407731f91593345493dd8bc20c5b3243f2ccb2c9c0e16c2417dd6", - "secret": "e5b9d32c6e4c67b8ba7fb1b6817439c8bb571d969a910abe4b229e80332557f71d1dbf4887aed8dadf9eee4fd0de43d0e9eee9ba644395310bb990a3ec953bd3" + "out": "53b38636e19310b7a4b7c03b2e908307aea080763c223504ed21b7821bdcaad7da9ca68d5196eed9337b2f36b5e23a40404c8a1adf5c92c9bf82d4bdcbbb1243", + "secret": "3efe299abc9a73a264098c9beecd6923d8b07bb842537eb21f16e90c4a094512b46f3c1cbef3b86018b2d038f8ba6e874ea3972cebcd21a4ba4eb9474cccabb8" }, "derive_tree_secret": { "generation": 2694881440, "label": "DeriveTreeSecret", "length": 64, - "out": "d8430b2012755f83f375918890722e0f4eff91c5ea37f7ee1c52410f5639d4dc0efe5463a25c6248c1ff0f6ee39c83cd2c14d337f00a7606c36a23754b0733c1", - "secret": "c49f17a532c5df3f424400d01211e8952008c28a17c0c16785674789f5c32d264379e43d3706aa94c9a3282ee264e199e359137b8f7108c8672e8099cf4ad61c" + "out": "6b9050104fff086270280532ae5403112071605b27b898359c25c67ff811ce68e7c484e9c2fc6bf7fcf3f4203cd6ca6b5ad4d623a82f65307d8e16a345aa5bff", + "secret": "2d837ce0a4b3277564a9b5895b6cc1795e1c5374e0738a1da67ed4edb32eabc7d8825e8bf3391e3ec8ea7212e68147fccdac6ee5f915b75f091e122d9036efba" }, "encrypt_with_label": { - "ciphertext": "30fd228ab426f9b6d95df7cf35b427aadb01dd950edabe3a706ded33a4638478d855580b786dcf0ae4de449106dcaf8fb953c95a6d91f7f071925e03555f5911c61db03ceeb85c5c62d5f06d659a3972", - "context": "a5c9314086799ebdbdb22e9b85bafbacc797610d68f62ce98cacc62832f7a3fee0bfa531b1365fd98e037045ac927e4a523a6f196e628316bca5b347ac3dd84e", - "kem_output": "040151ca5b4f678c9c2da16bcdcf995bb6bf8cbc17bd70d05fca52bb4e353e494cac7f22a9a6419a4b2ca67ee5d9019749c7d861e2acec85483942ae26a3d5d20408620082a052d16da181523de54b1ac78b7254c4826d2b85558621f9dce32f1e53863f2ca56308f92ed8f1010b5d2adc68b3f539147e3d816b872a0ef3fd3a9ffb9f1768", + "ciphertext": "28205aeb628df17121ea4627d2da4a98b632322be1da7c5abdf47d7fe747f13c575a5d1f8f2812cd9dfd7386edc6ea1fdb9767e5e8b49f09a0808bd3264f960b3ef496a27651760be88360045aaadcf9", + "context": "64ea1f98161c229460d04c46d78c0861e3f0189597102e4c1ee2a6776d3e6b54bbcb1a43fc24ed9dba742d553dfc583f2ca24c696644a19975cc10b0e9992b0f", + "kem_output": "04009e3cec5e8cbb05ed3aa5b9d71a4afbd855082852bf1d46c9c65f9229f7cd4005031cff7b642c7bbefb351271f2280d1fb01f10796a3ee25b3c9b902dfa74ff2a0d017618797aff7c24686625b93ec12d4994d25cffc7d64542578e96f02b4ea0665ac2c5649137b1beea44569fbd691aeb14c290eb6f6245c149c60d702be2518080c3", "label": "EncryptWithLabel", - "plaintext": "074a862742d5655d90383a0ce1c45dca9319931505eec3f23523529bad63b2d7366ba04792695192d2422f781542e0b483b11bbc1084be98be11aca6d8361ee6", - "priv": "0022f80418b591278404c9aaeb43c75b1b6a9be0a1e7ae19f81c15ac0a291f70fbcfa1522494c6087100e5bc6853013ac8a7b81864bb8beb2ef18e495af3ffc1f856", - "pub": "0401ec074e6fdb2d7697c1009a5577ef9a4b4a2fa273548ce2180d26636d7351e67893584d08c427f174c3977271df9da0ffa6e63660c6ecccbbe5e0ea1ecae7978a3301091922c1e0c11e2406dc6c3773677af73fb0d0d0452fcb229957a4b7b13193961cd9d688779684641e054cac88390a7ce2cc2828977998f0afe72874fb904be08d" + "plaintext": "d9f4f8a07e52230c13b55e5d930230421812ed5602d2bbc1b3c6eab80ac453fb6b36bd66054e11369f421d5db19703be5525bb0c30ba295a5cfe443cdbed99be", + "priv": "0140731573ff1b849df07d9077d49242b2c1a8e9dc845fc38fc75c708be74bfa9f7c3f3f4294d6f4f309e9ad23fbaf8d308a483c4e6cc52483c192f7e0cb62b8f7d5", + "pub": "0400d9488728d90e262fc67d8d4efbd76005dad0b29f695b075dbd1dcac0d792aafd4b550b3c92406e8efd97a9b5dc84b6534b4749febef436fbd974986649da38fae5017db4eadbee6600599cbb8d5be2c77b97f6c784fc5883d1c9207ab7157694e63189abec4b5198684089228700658093922c45a8d1d90d657f069282a94274531e1e" }, "expand_with_label": { - "context": "699d502e2a1d80a9d895a27a44127e10b8483b0109659b131bb22703fed87de2ace70912263ca1bace88e6903baab9af2a5eb06a7628f58c3a2833690c4ac235", + "context": "5d40fba2326929a96e6b25731a26736b268b0cfc9c70a5fdb6797d3bc024ecb0c98ee22bbd77c743b9d295b5011b5febdbd48c73a299aacbc4d4555403fde017", "label": "ExpandWithLabel", "length": 32, - "out": "8860311a19e3a72d4f657f37ca943b45a464f17388a6b2865b28bba24ab6ac77", - "secret": "12407d874c88b2d3e2e29066bdc578e972e91782bb18f8c006e477588cfbaa0923355bc607de62ff3c6810e40d55a2979d28eb96cfe6b133bdd38e7150401c6e" + "out": "fe1ad7af92705b6eeed83698913e13a43f229f4c22869d301d12aaccdb691375", + "secret": "6d58f215bcd087e3d759cca1079ab0caa7f7f46cd877ac616fdcc1d7001beda434dfa1bc94db58da6cbc50f866aee57c9b8c03853335d10898a4d89d2c5362c1" }, "ref_hash": { "label": "RefHash", - "out": "ab2478aaba701a41a2d032d94e6c945e007ec16d1dcf524340d2643684a2b36eabbd9ba0e28ac82e5efb7e86c2255ea8ee2693222750057dee628490880209fe", - "value": "56056cacfdcf386eb1216603c061a86e126d1e91c42cd5d4e0d6540e8d1a10896ca946615ee936bf46aa3353e50b9b5f5207d8f674fbd4c8955b541773d92100" + "out": "e41c93df994c4a6195b08e6155022aa8b0f85aa795fd90e789f20709fb6ca5b9a348a461e41d155f829e31beb2fee95046fcd25ff8aad476bd237b4a6d62e6ae", + "value": "d033d0d1de66630c0eb70bffa7ad36a170fe00fe11fe1e595910c3ce7d2ab1ec21b6a6c1815d6bf6731248908ddcaaf15cbda9cb8cfc9be6a8b5746b12d2910d" }, "sign_with_label": { - "content": "80ac3db935e15c3942ce24be175176687b8803865de853f4f7a27fbb48b238fcc4b2e3de50e8494e6fd1f01ab9ecfe2f50f22fb4c015e85b3f2c79ae68183c0a", + "content": "3ccae9578cddec157c33b42712316cd2b360fa1020c21aa763bff71c6116cf93ce9db1ee95028f85c6711675f340f36e1382cfedf59e5b8ae6d85b659cda883d", "label": "SignWithLabel", - "priv": "00aa62829efcd3d5f2c0e3bf50871c520f130dcdc1ac16fcd537785e55dbbf5278f10b00e1806f8c19f43a7c8a2bc10c26d6277ff132714871020ceef42f08fd89dd", - "pub": "0401f319f60fc5aac086f5214d85104f733e4d2c11eaed3706c47bdd419bf227e81a6801cbbf43fbe6540483ce039bb47e5ac0b13c1475e9e20257e5fa6b3d7609dac60026ab395cbce351bfb2d44b93221dd3f007e76f13fe17b1eeb571d27236b36beffe2c2158eefdfa339ce194854c257ad1fd7418e71fada336921e57342585684003", - "signature": "3081880242009073c22d3a6dbfc70eb8baa67fc82126d59976b2153ad92bc7f0c32bd440e2da53ac98a1e0d473f84ec02c442ddf2e60f58b0d5b4693529708aed920e5dd8445fe0242017bc259941e4fb3d9fe1b928c67a5991ea1c2c3d4550a5f8335945ea0d021ac5c41bbc39e380bb7e7b34c1e88866a1cdd1c794a66ef68a3970609b287debc99b578" + "priv": "0075aac2317c4cd22c101bee131cf4eae76960435356436c46ea76cc821f66e0c59db66f8f6524cddc621019079b57ec5f105def6c08dcfaa0122480a088a5c44b5e", + "pub": "040049f6755dc251dea75df263286c9617cb676434c1167e14bd48222a15e10daa02b9251c432f31c59511e9d9a6132dc2e3a5ee0d001925f4cbf6c9aff007e38c61f601501a72177aa3d8836ee7adbce0ad5fedd59d578b1eb5d85dfadcae518a8a3d1549c625f47db54040da9325ad5e1273d5df18e99c2a6d03d974f404862bf2102054", + "signature": "308186024160b6fe7407a52fb499a54e7a4b8a095c38c9f7f52dd18c3342692027592252fda87b3476d7f433a79c9426b05d66b1d58291e625912483e04581fba7cd64c63a920241760fe9615de08c694e7a443a33f2df2ee25f55527500616434ff5ba19778f3afac9d5b300a46cfb53cb317e7eb4f10679b09828edf62f529317a8c0d612103b1c5" } }, { "cipher_suite": 6, "derive_secret": { "label": "DeriveSecret", - "out": "075e1f3cca76d6d7f238d85080b6043987063a707496538424fb33594ed3bb4df8f6ce8875dfdc22d08c2c4ed34f879956424ddb836afb1379a3a8f901c7b365", - "secret": "31a8c904e314bc54ceb6727ebdbd751545e91c211ceecdddaa3505790f84cffe0b74f7c75b52c776d3df7dc552e4289bedaf9204561763a14fda87f62083e803" + "out": "c8413f81c9f94350ff784f043f98ce8e49f6549eb14301c287e8d8f6d4449776da1006e706145a3e8cf222802bdaee170b540d8f5d7513e2440736d3c4f1d894", + "secret": "082065745545cd8112e5385fa15321ad34e55c97971ec6ef4510720054d03ddfa610f04656996980256b171d25565fb7d34899c1e98db4f69614dfaf72e8838c" }, "derive_tree_secret": { "generation": 2694881440, "label": "DeriveTreeSecret", "length": 64, - "out": "bd0311c5b0841a2c810481dd611692043efa3329de4802ebfecc3417515d84524a753e00217b9d0c68ad29997c8705fb2fff3b783080713233876f14eea5e8f8", - "secret": "200a013240ed9dce73369d3822e609f8e59ffb782dbfb9ccb9fb39f39ef0ca9e643725fdf8e7ba51c8353be5b64542d81cad977b66ecf560926281d52555f587" + "out": "96245ee6e051ebeb58dbcc791e76ee88c09b83cb44bcb1c9d304499a5be7ed5b4d4480760708ed46c0876cd796abb8c05e7b249249d7ca71e41d60da7013d253", + "secret": "141aad224cbe429c4dfd382e587c74d9577fcd428d0739e0670c7d3996d2182e55f4dd3f10902ef3524427db47af375e596727b57519f95a00d20426c35b2659" }, "encrypt_with_label": { - "ciphertext": "e61cb21790696d293ab97ba35d4e5f6c6602ffe00ecc0e83c4708b9107b8c83938b39ea21378128dbb824ef0c47eef7190e5a7ea1f83f60e16f92c9b06b177bf958fcb817d350511447e7f95994dff3b", - "context": "143c5fb494e0e886cdd4a860328a077fd8e4f622b48a59cdc753d4e6673b30a0c3310ab1a8d122cccb102df21e1ed448d7dad219e8d788fb74699e3fd36bebbc", - "kem_output": "afdac08d1cd6fc3fca3a81a8187155cc43e72e139b4ed88f0cf3c1a18b858d38669adb37a65b848688aedffd487762a149166766ef2f9f8e", + "ciphertext": "1cf0b7459dd15bec64bf97584990ee9848dc5474a38557ba07ef17f5b9503be2adb47ec68df6ac4d9b11e6e6df0d64a60127d7caa109a894b58d7c3e9fba61b3ea2345fb65ae0413e9797580c6ab4a1a", + "context": "edade7087f93e8c34b5949bfb7baeb71f32a0c39d1ccec077625381716bf551275b4ac51019ff290211d81160acf090a89000e3a72eb471a670bd8cbd7a34808", + "kem_output": "d79688d9adb95186b5d475f5745f1e38905ce89fab7c6d890cf6fa257ed24681116e6dc6f3b86aafc47be6c4fd14614b5f05ffb0e64c29be", "label": "EncryptWithLabel", - "plaintext": "094783e161d3e3b1b4cd62ba32354f170dc008ef3ba3718529c77ac09784c65da7366309efea023cf1ed6880adc2dd76d4af56a889db26840401cf7936d4e310", - "priv": "6bda208f3b4c61e2fe56339c5a6980a6fae15f9a32db65cc73118246ddaa93ccdbef2988dea62618b01dfb4de448310c0278e4e081a196dc", - "pub": "8b1eef40f8b29e35a7db840d32e8caeffbf87f319d4d65138d9df248e3038193308a90408299df15b6754f2a9317377ffe7de84172e84efb" + "plaintext": "749869e8f80f186e02881c59cf538ffc6de6d2a6c0e90e6b0362b14f37bd510a82b769ab1544d980c37f294ed68fc0cf7934c058921274908bf2f13f7a18166a", + "priv": "34c86cc64b825321869ee81bcb38bd7baf01a1491fb52f671451b32ed7e89847f3968783a3e351b582b4520179b1a0d710c47b1442ac07f8", + "pub": "0193786e5197b8ee594a0d2f9554907216e2923367113406c8664a7985df1f1e818e22224eb51a66eae888b527f3cd1ace7296f8e9109ed6" }, "expand_with_label": { - "context": "8b66b6d32c1754ebe55f99593a76eb0e0744f935913869cee10848518352833c4a73641e2dad7cfe113f5890cc7d82d85021aca597ab83c15522322ec3e862c2", + "context": "20303270ec0b655674f8d8f7d736c27c55f0de1a6b8b098a2e73ca7010d43293e33c8d52887d1918e2b4bac75a633770a8677fbaaec8d6cc4bd00d3bc67dbedc", "label": "ExpandWithLabel", "length": 32, - "out": "de35a6d2a08119f72351b143eae9f4c88f0dff01c192a8e024f996d0e3bf7da6", - "secret": "dd119da3cb6893787cd150d5e798a25ae127002c3a8a509d47a99a2d331c3da6be904d562f756d249184ec58a19ab517f9d510e6ec022e3e731040642a5c3e19" + "out": "d57df8c0a0a746d0423f7cce96c90137013daed93c3d40967e18be44f535107c", + "secret": "ecdee9131293e97eccd92c133d0b81bd5c311ea513195275f6ba711810dd19a687096c2021c270976595ca1095932bb837c0e98191e749dccb7fec4941d6d29b" }, "ref_hash": { "label": "RefHash", - "out": "561837755ffcc0b93b5430fcbac378c2382bf442ed3a50af6d3cba1c8ba8ecde7e5d62486fdb48f3145f5f907c38cd3a565a298fd62004d6ba7c842a5c12c264", - "value": "8bd70fe2f606f93b40229312311a312aa28954bee0924eebc186dd0bf71be6c6497a2f2edc76fc0b340b17e82d50bd05d3161a5dfedd3168f2e91370cd229542" + "out": "f7991c51bf10ad1e65529d49d6e61f45c0495730584842ebef42ad8e287e0a1af797b1b19fbb10fc60723ff92b18bb1aa1d883df258415cb33e434ce550b59f8", + "value": "fd2cf8bba40e11a2a1853b7c311034dbef4ceff58352a71f55c722911cb3b6e8959601b05f11e1e9c20b7b56cf3d49df688c16a41b4f123142c073068007fd0f" }, "sign_with_label": { - "content": "d58d36e6cfb287e068eb10032156c3b295aab3fefa507c40bc08aa1ed68e6c6283861751bee15d0a07f3b492171d54988dcf3e22c963fd9a85695d59636186f4", + "content": "975cf2f26ea067f9d7bcb034d6fe7a28016f0a84e60e6178a1b7cb1f6d6381da17bb1c2681bf51a7d351cb6e5e40a0379ec2de285cb94964bb18a9373153a25e", "label": "SignWithLabel", - "priv": "ec1d03ca52c7bba8d1ee2dc4fe662b2ad3bf64113b81abfe2ce9f6337d40b1e6f4a8b727eee93fa293d6872b5f467f80674d85eaf0359f4e5d", - "pub": "beb589cd79a9663487bbe02b98d3163f0a82e26b288a1fcc4af1fb84000229df4e9a92bbb494af8dec05f5a4cddd611d3d6fd193b0e05d4b00", - "signature": "a55830307056a2dc01b0deb894f4333d13b0783199e1d8ac5c44acc59a4f0ba0a32473a806f1fd3e6c6b1fb99c9f1ce18344d3b6a889d95800f64fcf251a1eaee0be1b570c284338bb47292189eb4f8332d0465ce4b0a2f69ffb809ff700e6aeaa0df8cde6ff0ebcc9d66181a721ad822600" + "priv": "e1c591839b257097e473c5b4185fdfbb627ec85283fc0834302545000f8feb3d7a3d3105e8b1ae7af00c6fac145ee9329920254ac349c722c1", + "pub": "2ea45851d68149bc5837eeec4db1d91e43e36ce3f4e3c2bd70e4a365f6915eec182173c5aed688cda8c266cd929d7b53bc9639f6c3592e7500", + "signature": "a03644bf9dadd9c96977aeef5b891bd857318ccc3899bf05c525186c1a7b70e0bffc98a250b4c561b893670a897a3d6f97105debfbed421c80a9356b21a54b84d580fa86c52952e4791ef58ab54671a1f2ee25721c21925da57937c3b2d683b5b36b63de253abec596406b8485df91622d00" } }, { "cipher_suite": 7, "derive_secret": { "label": "DeriveSecret", - "out": "0744a41b6e7f7959be6930bb285b681911dc6ac6dd9ce173040f172412f816bf7be8f9d53a9680c53ea6fc2057d8911a", - "secret": "dbd8e286875faa0fd717c8e33f0b95d9932b504e3dbe1cc6fcf7d9ef8554e47c8c162784e1afe5b2a6d3877a050bc914" + "out": "ad64a2748a2894e725825fbb97d8e9e11b30c7df09056b825a9328d7dd95a35436c326929b964afb181c747c1e95c3da", + "secret": "ee9426e58f77d9e2c16a18a8758097143645c523a1e3050c7e61c54b6703c721d3747961660a163230a03515f0ca0746" }, "derive_tree_secret": { "generation": 2694881440, "label": "DeriveTreeSecret", "length": 48, - "out": "3fb980d1d7ced928f1e53dfe245dcd51ecd8bfab67456ee4355f548286cdaf1ea405bba213ee6cdfd8cbd0166fc355ee", - "secret": "abdb9509f92026e1d0d4e7e288fedf30a55cad0811dd883bdc6b21bd8be153f67895ce15b7b612108846a064b70b0eb1" + "out": "db3fd2753dc762875a927ed98e18f7cc68e143939a552252c2bebc1d42f78fb4413c12c2913c49e9dc0c9a85d913b8cf", + "secret": "428ded6823d274edd5fee63ec1a9a5e18cf364f5a4fdf3efb21e927af2b8915d0ef28e48673f96054aae87cc59dd78b4" }, "encrypt_with_label": { - "ciphertext": "d0968a4dc38875c1b3b686eefdd2917164d88eae76339da4548fd751b6bfc160781403e6cccb2100c42ecc3a36fde4db5d87ead8a9f1102c46d4000c6fae9306", - "context": "6a3d8f8bfe72c28836d163e1a3f3d36107448a909e67ed3941cf5b2925dbcdf333720fe1536fdb9d0c8a830826719583", - "kem_output": "048561878a28a07e36d973a3e3261f80bcf34b0e69d5e827a4c40527cbbb8e3d1e8c911ffc3c866c5e035a75ccbedc29b86aa7a9876e307714bf66c9e8c825264cd6f40a73d546375253d44394cb3db290c7a65a84878fd7080539416fb6efd40a", + "ciphertext": "f9caffe1b567c1860b70de5676c19c81b5927d9af22b3a0361cd936865b519d35f06e8f81dd85cb79cb6b43abdf944c2663f79f82677c71979ce7fc457e56d47", + "context": "fb91549fe16bc4540788e61d4bc7f9704cc7af3657cc96ad2940500ab9fb749bbc63065477b336bb4b1c80d212536030", + "kem_output": "04e7f79fb8cb58a43ad5897c5696d5ec6b2b5e8b84fd65fa24e5ef98aaccadcd95df930f04462ec378e501f60c79baeaf199f4f752cee808efd55e9a4ce32010f1624314eae209cde57a4a6c9c10b3a26c681e5555419994490d8829cf9c3bc96c", "label": "EncryptWithLabel", - "plaintext": "35bbfa9b5477ae9c529ac1fea119c465930620af724f37710905a9506694c02c89389a1b50baa78ed5c9398f8558a076", - "priv": "ed15c85aa80d2d6fdedb78f6d9e773c1e944f7d5e9bff5b9d760acaadf6a9bdafa701e0b40e9337f76893c7712c37dda", - "pub": "0455618cafbd6e62639d22bedc5f3e213d53a2a0beb28b3b38d3f32a8937e18f72699486982e35679984021eab014309042edd78da049268b3a3be84ff0946fdaace60fd829119f53eef793e06a80db0fb83d0ec069da1246f45201d51fea9c7f0" + "plaintext": "fd137dab4ff57f2e9064a0fe2b2d8c72dd0f162d8ca3550e3e0561d5ee45a1e37494fc1f2547dd9f38977fc01ada7e50", + "priv": "cc20f4f626228151e2f75d623198087e14cf8fecb5eb7e780bd6a177d31fafa295e6ea24e601d52c7fc4e7a9fb8907ff", + "pub": "046b377162761a08807035865b8e6122beb118cb640df3468e30fe4249daaeb60e1ee9eff15bc9b95cc08839b35ff48b6848b12de26e96d671cab4926235739bd972ad08cd36574c22e884f3e9c761c3a28a9c8e140b887bb7460b8b657b57b28b" }, "expand_with_label": { - "context": "23d05c463d6701d188072962701083301c3ef454b268b1e4eb9693ebd427fd50e9bef0c1bdaff9467835978d3c237ceb", + "context": "2588d6b1ca306a9fc348a8b1b4de45c7111c4fb86ee362086ecf3f8f18f0b869df492fc5d30af6dd2d53e7b1eddf7d2f", "label": "ExpandWithLabel", "length": 32, - "out": "3d4ad83eb65e7e629fb0e0ef21477e0dfc61224bbb0adb8e5ced44e3a4ee8b4f", - "secret": "39366855d77e931e2ebd9fa2d6df2fd6156c8042f38bd6919182ec8534bd2326c7b9db824dbb17c1954bce27978dce94" + "out": "a2b96cd5edc41d0d56ce7a2282ce52aa792c7990483a52cbb044ee3f1f14f159", + "secret": "39bb10928726fffb2b9b61fa4b07fed7ef5e554021a83c9d3af3aac9a8abab3db0bae67df71e8b3dd7ab3cf17dc783ce" }, "ref_hash": { "label": "RefHash", - "out": "080d4d99a9ff1c9238d7edeccae89eafc92a7b85f78ce29c3bc27dea0d49c9ca72e75d214545b7fd6f69870e01fa49de", - "value": "c003fcd16cebd94030f7ae9c2c98b82a4e0d032e951731b51d3f99f0558f83fb32955e9f3a50049b9c5bcca21ad7748f" + "out": "d614f284ccf7854f081964581fb0fbe315a5e12cc3f10b929adfa1e94f06b35dbb28f4b4a8195d97c43ede073a8cabb4", + "value": "bd9dfd5d309a2ff67a5a28ba0f75b365b52376626c372e115d30ebf3540858f83d0f70406e588d0a6c3e39d460f62074" }, "sign_with_label": { - "content": "18266690ae3e66920d3b2cfd3d6626a47066abefb721b782e67a85908b008220ff9def32a4d8bba51d9c76831b67c2be", + "content": "80e54bf4518194eb9034dade8753ed06b4763a2f7b1df80320b00d8e7c3d1f6181f56fbf663b272592ae6d90e30816fd", "label": "SignWithLabel", - "priv": "5685954a124d64cd5714a647c872d49f1a0adac91aa622da959a9b81c63d02df88f3bdcbe6818cc79b3ca39c087e3ee8", - "pub": "0447d89f2b579291353a05731837bc29540d86eb3ebf54c6827d88c862939cdfbcd3a2dd4d46aca013e0cc0196260caff5681b4f033806e9908b4a40ab9263aae6be03262fb184b8ec02570c4e25c4307a53b1196f54cb6eca4215576a64247393", - "signature": "3066023100bbc9fce5b27ceeea12c0b56dc3ad0faee9f8f23052a813559f94f1991f4e8bdb5c50e038ec33f65e2387a3463acb0bb20231009039d5d4c1c13e49fb16a6ac09374e3401e4e0640f46fb10d04cb414894ce4e1f7980c4c2f250670e24897c37fb467f7" + "priv": "3fa2573fe417aba24c2bf0df290c6cd4425ad12cd95ce81871b15aee5ae34f1c49531b9ca392f3fc6443c7917381b77e", + "pub": "04fe2469e05e1fae9874891e3c05907e16eba323ed97680187ab8321504b78c3159a6747fb6dcbc600e7e95cfaf1c623be2a4e4a4855d7bd9a3104c6238397f526de1e61b62e48cac51a5a413ee435b6d15158100649c69d0abbaa4200f806334c", + "signature": "3066023100c0847e70396150fdbe01fd2b192166ccfdfa398dd256855d6485c243235b32276b40ca285a1aa8adf7257732e8f25f47023100b2aa141a67e034d333b29690d705668350c637220836c29042a146383a07b2438ffa732afc272351cb3bddaf6a26afcb" } } ] diff --git a/openmls/test_vectors/message-protection.json b/openmls/test_vectors/message-protection.json index 3c832917cc..5cb6f7ea43 100644 --- a/openmls/test_vectors/message-protection.json +++ b/openmls/test_vectors/message-protection.json @@ -1,142 +1,142 @@ [ { "cipher_suite": 1, - "group_id": "5a8907a17f990969e70c0efa26fcd52f3ecdd130f657bf402a3aa84781004678", + "group_id": "42e4c3a73738d838cb4f9dc550cb81406206943f9e6870ee150f2000ae8aa780", "epoch": 1184274, - "tree_hash": "688dcdeb241c4a9c0e4019469f57005938697ed7d19e04f1de0db986154499e8", - "confirmed_transcript_hash": "2e808291b263828ca44175c31533c0db59d827c46575bb19f10a0bdd1fcf726f", - "signature_priv": "e553dbad012b0a69bcdb5d1c061c10539126391b2fd2793d0a50108e7bb6e73f", - "signature_pub": "ef631376d66858817229dfd84fa5a85b45f95a14e33891ef1d13f6d266f8c490", - "encryption_secret": "6d687acc4452ec6667ef8379bb13785a1c14cab359a6f72cc14053b7a6521a12", - "sender_data_secret": "602041804dda39a7ef4a132efff51a1d24fb4a623b8776bf70486aadd7130584", - "membership_key": "933ff010b6a0b8216b5756055048c4c237072e505374eb47a32604fb5c5576aa", + "tree_hash": "a257a326c7b632ce7ccdea8d3a3b5c3c2daa53a21029b3673ee05e9a3cea5934", + "confirmed_transcript_hash": "59b10f7a137e853c4ef7ce43d2fe0a481bb80652f648c5efab11c3141a1ba60c", + "signature_priv": "3091b9149866552f1bc8452aacd58b0a0b2ef87f2bb606d03ef6c06da0ca5cfc", + "signature_pub": "62087f5b047e5292d5d29ded0977442788633c196a607988238b8c4374b4b4be", + "encryption_secret": "4db33574f514024e61e2a15e71527182f62561d84b4d8230501b623d848df998", + "sender_data_secret": "b21dfbbf69da2fec93299d8bd0795ad0aec22b42d83ff10a2e5e3f997672b8b3", + "membership_key": "1f338b39337d019ab52c797bc836875998387e88fe711287df2258bc8f9967fb", "proposal": "000300000002", - "proposal_priv": "00010002205a8907a17f990969e70c0efa26fcd52f3ecdd130f657bf402a3aa84781004678000000000012121202001c1664f86d2b0c3388c126cbc1cd68e4e57b3b338f411dd80f3d48315b40581aab93ebe19bc5cfad82f4248c979ed682197ccedac1d33944a3021ae26c326f56313bba5f19e668c0fc4bc850dfed88afaea2f4fbf752d5f9904cb4d3f7b270ef00d0514b227218c69ee03ea8dfb9bb31e7e9b6c3b714f8", - "proposal_pub": "00010001205a8907a17f990969e70c0efa26fcd52f3ecdd130f657bf402a3aa84781004678000000000012121201000000010002000300000002404070ae3e5311de1c3b7cd6f722d572dfd62c4b3e5fec2cfcad78e4b204708785d5cae9a8d6a9e8ee89d65d1a25662d6da83800ff88889337fb982860981ed19c02209d17928d902665c3e2fd990e4813d17b669ede449ca2d88f27a72ff0c48fdd72", - "commit": "040100070000", - "commit_priv": "00010002205a8907a17f990969e70c0efa26fcd52f3ecdd130f657bf402a3aa84781004678000000000012121203001cd6f782cdb858a3c734310def9c53474999bf87f41269f61436012a5e4079b404e3d1e02b3164a18068ebe21840c3962e0d160817b97d25d02b8543dcc7a9ecb495efd2c049a8f0eb0b09046b0708012b9eb08b28462f19c68b166b3c05d71b7b839c2a7423c3e2cc2c9a393232f3b61c5f4c1ce5a9e58f586cfac17b86d2940669117886129945029f7b7a44bca970afc680d15dda992e", - "commit_pub": "00010001205a8907a17f990969e70c0efa26fcd52f3ecdd130f657bf402a3aa847810046780000000000121212010000000100030401000700004040bdd68f2aa86397eb282eb0c0440f362e44c50c6bbab0c64bc5e31dfff7604644eb5eb66f93ec6002a15d949b1ef1cf889edebf4c863bb27cac222387c0c62d0420b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad20907a444131db5f423279a6c9d798d890315e9f360a43d48831642e381b5c93f2", - "application": "0d6e0abdcb3c37d45a5ede7ffc8cbc1b3209a2b8b4e591d555ad5f6ab6d3be413b0d63a55cbef3993c30", - "application_priv": "00010002205a8907a17f990969e70c0efa26fcd52f3ecdd130f657bf402a3aa84781004678000000000012121201001ca5d58e70f6462ad35376df82a1af59935de9c8a1dceb799ce47b7ce2407d9d4844402ed24213dbebfed007553c944fcbf5d00c664fc4d442ad67a04cf1cbbfb0988b19d9022b3098a86ec73ceabba9d468705afaedf744039efa815d9a068d68d0cff795b3ef9fe34446a4acd8093bea3a65b2eff8469aafee9750a6b59d9bdfa16f388a31135029bdaa70acafdbf24881c71f43dff013a2b14bde" + "proposal_priv": "000100022042e4c3a73738d838cb4f9dc550cb81406206943f9e6870ee150f2000ae8aa780000000000012121202001cf8f3a79aa9f3cbc60ea1e886e46dd6dcbcde71e80f7e46a68b88edeb4058b7761be40499be70b7a0b5f4e2b151b20250153fe6af4599853b1b351ad6c68a7d7490cd58301a3ed08b2bfc254feee2f14f63df0bafa7994b6957dc449384b06b6cd24bd228d8870d5ba8a346fcca8dcaacadd95959443c", + "proposal_pub": "000100012042e4c3a73738d838cb4f9dc550cb81406206943f9e6870ee150f2000ae8aa7800000000000121212010000000100020003000000024040932e43773e992f7b0c8ec9d82f72c9395ad62120f19a764e50ec150ca48ba94dbb139a1f9c6901aab353d67f97f68210027818f634ac9cf8a738fe35b7404906201be6bf2e5e22e38a6f908d534a04ae231c3f6de72750d74bbf626a2c0bfcf1ec", + "commit": "404601000401205ff3e28b8183cba127d0b74459b5d8a81286c299ba02d68c7d62f6101c3782dc20e0fbf69e4e80c4b2b8d43ffac9c0863a25300d0a6880aa9287d3916c467ae3ab00", + "commit_priv": "000100022042e4c3a73738d838cb4f9dc550cb81406206943f9e6870ee150f2000ae8aa780000000000012121203001c97a2cc4f511ed694db9bd5ca4aeb06b5eb5fb92765f41feeece83a4040bc6c0e40ad742111fe68c17b0848c047f6909e0675dce492f4e2635fda105d20aac769b1b3916267a396235cbf955e1843072f62466f6c328f535bf7ecc326c224ae1b24c574ba543c0d1dc27328dfe10b221a43c29a0ea76e3e475980995e92cdf0c7b21bde1fcbb6bc5cc92b920b4b8bced424ccd518d2b7e738fe4c582040ef866cbe8afe72e76a8bae2e19e20a5c901d12baf43c2f01d98813a775ec5c6fdba27c8ccd1cc024dc95e4138bf8dd0135998c598240a0c87d5fe225b2", + "commit_pub": "000100012042e4c3a73738d838cb4f9dc550cb81406206943f9e6870ee150f2000ae8aa780000000000012121201000000010003404601000401205ff3e28b8183cba127d0b74459b5d8a81286c299ba02d68c7d62f6101c3782dc20e0fbf69e4e80c4b2b8d43ffac9c0863a25300d0a6880aa9287d3916c467ae3ab0040403fe4fb435c134950173cf4244a899e3a3fedb6d77dde1279b8a1b03a507109661d82e6e08f9a476ebad8983ce83a1218dbf4df0b538e88d3d8ffb0cbc5fffb0120b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad2096f30e3de73f8f49037897e7e2ff6964857d7e662c6823cea2b5c4b78c3b03a2", + "application": "a1ab266714fdb6d121f4c7f248271fb824a3e61dd3f91835e68fc8789f17f754a86233781fb59d23811b", + "application_priv": "000100022042e4c3a73738d838cb4f9dc550cb81406206943f9e6870ee150f2000ae8aa780000000000012121201001cf18ad2e0ad4462390d72714d68b2a845ba336c3e8d398d21334f8d0f407dab47ce77c5fff3107b2f4e4bf99936c0ec680364333403663b7a955708a73d91e921a4fb3ffd50292a51ec31d4ec684231eb6f765cfbd5cca5b78fbe42bbf1b04dd1682fc87678b3a4f1599770839b171caff15e5e264957f3d16c31ba3ca16416f4665115244291536ddb438528cdb8502de654be9ca093915d36b1ab" }, { "cipher_suite": 2, - "group_id": "37364bfbdd89950971d7c4c3ccd2d371e468ad5f4602b1526812c0e35b8dca14", + "group_id": "e0b4d1bc9c0a23d09a816899c0fe01b06e8a1d72eacf0367c1f0f09a12730f36", "epoch": 1184274, - "tree_hash": "99a2d8e3ed65d5e54b5382ef923ccf2054b5e8830ad8802add5ffafebbe677db", - "confirmed_transcript_hash": "c4dc9fbacae550f576afad0ec291d1df03634eb6527806563ef3d590fb4d6fd9", - "signature_priv": "71b377000a5c747cf21c97416074c13c6879e840f054f0d5ef6c43b07ebd48b4", - "signature_pub": "040dcd96f9e3910db0c93934588054da946f1604969e431c98a3a59dc0fb48e767399fdb21bbb86302b7f637c73e34d745f30bf40037b22baa033382fc2e30f72b", - "encryption_secret": "07572271a69e75191802bc08dc2c04a441c2d1aea887f3b34bef80a8d083b654", - "sender_data_secret": "9d7f4486674da5b3878ada7084a7e1dab41c1d3a96c6f7663c2c5cff1b17be9f", - "membership_key": "d69fb554c41a46f3fdca0b25b7eb617b653bc5a5d0fca9eaf8170077d6d4e187", + "tree_hash": "eef060eca7674ec7f6fcaac9121425b96da8fd1aebd4332cc64943164079ea7d", + "confirmed_transcript_hash": "e6ef10bc2d749d8837e3fcf26f52e4a0180e03047a25c01b29a26821f3c6801e", + "signature_priv": "a95ae08cb5156432ae2e3f04cef1fc30b2cf5aa6bb0dc2abec68dd18ea0dd0cf", + "signature_pub": "04e3426cca272cb55ffc026e1ce9fc58641cc8455c5e3aa4cb1e553a0581344b667efc72414c9013ea74cb0da9a38c771a80cac3597a9698650ee3bf0443929d7e", + "encryption_secret": "ea4ab83fbd97640be9661375271b0b3bdaf9a131ebb97385b88877cdc7b05685", + "sender_data_secret": "b884593d3fe0371a60324e3fc1e24a9400895fc4217eac1bc48ee63f82db76b8", + "membership_key": "437af9098e9fe51afe4bb2e3fedd6fd83b6042156005dcfdaf189744f3c569d8", "proposal": "000300000002", - "proposal_priv": "000100022037364bfbdd89950971d7c4c3ccd2d371e468ad5f4602b1526812c0e35b8dca14000000000012121202001c14826b439bf9379ee332314d678f1488ffa821a223e71d16c9149df3405fab65162480db2c86df8f87ddc625d606d5c3f8a90564dab9ece786443635b567e7a8d83dbc0ddcd8c2606d81947ac9001c6b0787c122a76105bf725b33ee3c4e7b3c8c563dbca5202d4908d6c24f5d06ca02c587abe99369c1d6aaa130106d", - "proposal_pub": "000100012037364bfbdd89950971d7c4c3ccd2d371e468ad5f4602b1526812c0e35b8dca140000000000121212010000000100020003000000024047304502207eca4d9c2241fa01134bac296edc6011cf16d9d9524b6929542f4958aad49181022100b1fc415c44c122b5758a7953c8ec2c37d0294a270e33d4dbfb2234c40c60f277201f01e7054a697f3bed5c2ce9c96792b28d2ee6662f5ba32dcfe515d48a85af88", - "commit": "040100070000", - "commit_priv": "000100022037364bfbdd89950971d7c4c3ccd2d371e468ad5f4602b1526812c0e35b8dca14000000000012121203001c6bf6ffc71b566224ee9017f4ee549024d2a4359863d6bf199566ebc4408025d913738e09dfb8807a55a2d2d0dcdd8e6ea3cfcb37b0c4c2169012d5b9e1edec371c3ee2ea73d194a5715ec8721ca28874af665d0c0525b3e2146105604bb9bbb40dc084eacf8cf03c365487258d07b6320a21c29421b00114870c2ae25c9db8c5f098f0528ef5ccc45053a164138f3d021def9b8520cee3b2317d03c45e37", - "commit_pub": "000100012037364bfbdd89950971d7c4c3ccd2d371e468ad5f4602b1526812c0e35b8dca140000000000121212010000000100030401000700004046304402203514c617518dc5ba1b1104089a76e6a37dab46908a1b6c5c5c8fb81470ad7883022027ec2cda6b4f2f037ae90208880fb0c5f7f8a6c204f767d8d9f29c9e2583f03e20b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad20ffd27896b6d706b11d4049e461166b8c2b5a9fb36fd2c8c5ef5de5baec1be82f", - "application": "def3439e362440d427aa0629071d17f8ee89998dd99171d7e085eb741cb4678ea321ade1a550bb2440f5", - "application_priv": "000100022037364bfbdd89950971d7c4c3ccd2d371e468ad5f4602b1526812c0e35b8dca14000000000012121201001ceb35c6995eee3cce73dbb5a044aab743c2013d6be8b72d9ed69c7c984085b01fb903ba0565a9c44c8a8adeb5630b0bd1b79ebb7e76a123cae3e3ae1129738145c754175da230496893691552249f5716f36055f44e8668377d903a421822661f7599aecc8a289785feec6ec6f124a8e2f509567f0bc204e15065229dbe7f764f14dcabafd0989af0ab133d7a1d5041f73bc277ba4623fb2301699c6ff07138569d7b45" + "proposal_priv": "0001000220e0b4d1bc9c0a23d09a816899c0fe01b06e8a1d72eacf0367c1f0f09a12730f36000000000012121202001c9b3e51c35eb7d5e9af63d8979e8f3dcd87505fa84ac78302e2995cc04060cef258b9b4dd6c198e9358745711ecee01b0334dec1e89bbda05f191adfada2b8dfa999bdff963f814f01294eac45a787910552017b2fea83ca4663ed64ae379a916426d9d61951c6e74089d8c01c7fa74fd801cbe11d90661cd2b9fa9d59c3e", + "proposal_pub": "0001000120e0b4d1bc9c0a23d09a816899c0fe01b06e8a1d72eacf0367c1f0f09a12730f360000000000121212010000000100020003000000024046304402203f0dcad38c6a291b51977ec59d3752432b6187877927ece1a905a187a4e0ed8b022051df5e37ba11dd99ae75116183473c6f4082a682bbe324361bc8656c982e613e20b50e0bce4d54bdc381aa6e72f4454049233c76a8b26210cf277f30986271fb94", + "commit": "404601000401208785123b1533bdf9a4865fdddd6e223b4d629cd35e1b725472c6f3e7a0139ed120bc761ba5cc518fa1a77157f88b7e2b54e12d81f0268d772ac857191159529db900", + "commit_priv": "0001000220e0b4d1bc9c0a23d09a816899c0fe01b06e8a1d72eacf0367c1f0f09a12730f36000000000012121203001c099e2e5218664fb5a71605a0bfb7658692c7c6873ff855ac28362dab40c3bb9fa5be21a2f67e4186c3290f27304e66b71cc7a5c15727afe8a594fd2753063f268e1ffbe659bafc32bedf6429899461c6d74fbe4e08c765aedf4f90721ae3690d815199b842597c2a85b55c510bda667f08e475d3d242ac3f2a724bca52a776a7f3c25a924bda2e591efb5bef07fc6277c28d04cff81a7b31bb3af65ac3848334150f4551f6201838732b15cc68cc0e216fc56853902e6db7165bc02d9d99da7074b79c6e1a1de307f2fa42d4da30284cafeab3963d4be30f0f3915c371b6227576", + "commit_pub": "0001000120e0b4d1bc9c0a23d09a816899c0fe01b06e8a1d72eacf0367c1f0f09a12730f36000000000012121201000000010003404601000401208785123b1533bdf9a4865fdddd6e223b4d629cd35e1b725472c6f3e7a0139ed120bc761ba5cc518fa1a77157f88b7e2b54e12d81f0268d772ac857191159529db90040483046022100f34cdab7ba0aac6b624c7f3071d01f81d21959bd5f1f2affd2cd3ccf7cb9fdc4022100c8213303ee2aae421bb154703531ad874fc22e90d8a9118d91680225cdea807220b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad2046f571e58675e310ad13cf8a8b8d01a0da9350f654df6e7cc8a24d9d4f06c109", + "application": "4010f2812003eda38e18ace515ce9edac4d53ec4c2c2beb2e28d5a9fe19766f8a1242a5d9e12a05fc62f", + "application_priv": "0001000220e0b4d1bc9c0a23d09a816899c0fe01b06e8a1d72eacf0367c1f0f09a12730f36000000000012121201001ce3aa03e3a175dfa88af8a2a8d6007e0aeb0ea10fdc734132ad6bf8ee4084f252be09e4c23df5b6dfcbe3cfe9274cdb8f8cd603d594a4f7ba17395b99e18224bcb60f90ebc4d72bbf843efe45a1a97d8997290756ccfdc570e2b5800bf19791b5f4185ed4965a669395c946504bf1bb075780e47b43682862ba3a8d015cda88a2dce5bbaccf98eeeb3b676c99e938a11fb4743962e5d52dfac944bd82d0d423c1bd44" }, { "cipher_suite": 3, - "group_id": "fbcca1a34671d4dc88fc139bd810ea5a2a50d5449c3014db975220e9f9c5734d", + "group_id": "fdbada2ddce02e7493673caab793308bdeb5cdb0558f151f647ab6e2ca0c63cf", "epoch": 1184274, - "tree_hash": "54e44cec46502343557ae97120a389d151ca54b03a3cef035684ab49671bff5e", - "confirmed_transcript_hash": "717cb87b2ee666db3aae76e71a0b31516f948f74415e82c73aeba509099d1303", - "signature_priv": "1f3958b072cb3325d884894ffadbe070494e9a6094aac5aad75c7c56219cd2f6", - "signature_pub": "c448773f7cfcc054e93f10135bba16e95fbb7b2ce37022baedeade50a585114f", - "encryption_secret": "773facd2ae1dd529657198b0d2e562fc84d40078d92c35071b071f4cc44dcb3c", - "sender_data_secret": "25a853739197e0a022af69c60d0d2c5db6724ebdd1eb4c58f83c8c3de58c0364", - "membership_key": "c87452dcd264bbc5d6a2f20cc979dd889f1b711e8fd2a92700a2f2c81d036540", + "tree_hash": "5ae6ee5f282786b753d55f15938248f6212c69b2f677319e5fcdb2ce0bfbf48a", + "confirmed_transcript_hash": "0ba35f0d36007bc44df41db074b9d98a1314e3c690cb345ceeae152fbe5b6afe", + "signature_priv": "d48bc8fcb54ad6e48eb55e773577030224af3a68308cd4a2a2c3f939a774c1a0", + "signature_pub": "b1d7f2e45d6c94fa8bff3b63308bbba38024c5b70c22234834ee24294b6e0a28", + "encryption_secret": "ce02c4dd80a8ed57503e3a50ec11b926a549d65e840417c0635427a838b3cf29", + "sender_data_secret": "85fc31fac878dbe2850e30a8fa88bdc63dacd890756aade03433791b50a510f5", + "membership_key": "82f73c829938c5d1a19e19de2b2c109214455ee6671cf918af426f364162678e", "proposal": "000300000002", - "proposal_priv": "0001000220fbcca1a34671d4dc88fc139bd810ea5a2a50d5449c3014db975220e9f9c5734d000000000012121202001cc9e84d4304b288025bee9a9dfae5823c4e6a5ac0bc112899deb4f606405860c561ffec5aea1382459747983c4a9a932d21433cbe38bb21ed017e96ce80246f8447c998dc857f0e595d8611534868cfbc9fbc72d95c881d9b6fa74b1693d0d6340865f943f39d77f5efc6b381c38f5fa57cd18979577a", - "proposal_pub": "0001000120fbcca1a34671d4dc88fc139bd810ea5a2a50d5449c3014db975220e9f9c5734d00000000001212120100000001000200030000000240406c5100e0aa378ada9f9a790f41db8946c1fb0396f041e68d1990538c1e1155ece4a6f92cf1e1234f4b36b79cb8f3f8f7b8625c9619c4f43b29876ee5d89e160a20f47f88ff1e8720b43dd6414f2645d11f21e7ada9ceaa7b666acad977593dbcd0", - "commit": "040100070000", - "commit_priv": "0001000220fbcca1a34671d4dc88fc139bd810ea5a2a50d5449c3014db975220e9f9c5734d000000000012121203001cecb0360688f64b4c1bc131a994497d20d21700933c482b6dfa06573d4079c1426d6ef8b25bcc4214b9410ed85b7f6b601c2ea0f5f3d3bd0aef1fcb011fa56327236b62a85e1105005bb40fdbb73cdeaf0f9507e0f4ca31acd76cb5b081e203fcdc4e7870bece8594324fad1bbf9359dfc336e372698215592372ed41c850d63f85b38a2db413ebb7790d45cea263ee5153cfd3407514ba", - "commit_pub": "0001000120fbcca1a34671d4dc88fc139bd810ea5a2a50d5449c3014db975220e9f9c5734d00000000001212120100000001000304010007000040407134c95d1e12787133af2477e2bb28778ce9cbbf013f9d50a77fc19b36fc2afb268da5f4d035815ff1cfdf352fc7f21645b339d95e71ee193a4e1d26df4e350b20b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad20b2c174e2b4de372d04487b0c54bb9677166c153041806980d2cfc09dd049ecf8", - "application": "aa29d4e00f011cbbec454de081d629da0ff30dc414974624679a103a97bbee1adba50e1371c9cb248781", - "application_priv": "0001000220fbcca1a34671d4dc88fc139bd810ea5a2a50d5449c3014db975220e9f9c5734d000000000012121201001c17b6e204d7df5fadde1954f4026d7a0ca0b3ca112d1851c614dc7a79407dddf63958ac584ba37ad565e18037ba03b78ff2bacefdff5ab38e632883c4157039c7af713c2c1147781e6721d39aa7045d2baff230499e230759d88fbc201b064f8db1520547b83298b4e222dcec1987db68ebe0bc655e297ebf48b13b487c49972d2066be6aa767a5e58a1cf3c4a0db095fee86284be40011196f3aab" + "proposal_priv": "0001000220fdbada2ddce02e7493673caab793308bdeb5cdb0558f151f647ab6e2ca0c63cf000000000012121202001cc15e1b90a18448376191078a6b66567941ce2472f110def1215eb1114058f9bf6e2c2dd19ff11b450ce94d9990ca3dbaf94edf8873c0f7cbf8feeefd362ac674b32d4c1c6ca646b8282fec7aaa51dcdd402345792aa77d98eaa97da5bf13969e44845f33114c10e1edd765d1c738059757570366687b", + "proposal_pub": "0001000120fdbada2ddce02e7493673caab793308bdeb5cdb0558f151f647ab6e2ca0c63cf00000000001212120100000001000200030000000240402aef68e55e2092800105a0a963c84afb2039093f5431f9c59fce19d3f2bca20689a617deb8fa59085f21b609649b0373e62c0c099e9bfc66f939e623a851d3022048451581d2796c68ca95ddbf06fe4cc69f5b1360f12dce5947e095ab26c259a3", + "commit": "4046010004012017e89d9ff20be622654e2b82b1e55d5b51c9e01d3b02cf0fa179c14e8697c11620d3ad3e549ffe3fe157d4d2e6627b8d35be6b5b8f37ffb10ed358bfb47c6c829500", + "commit_priv": "0001000220fdbada2ddce02e7493673caab793308bdeb5cdb0558f151f647ab6e2ca0c63cf000000000012121203001c71db9299c28f6e4feec4ddd63660f8ee186769ba51f2721953c48b9440bc32b3c5b162267da077973a9966c1c1bf6406d4326d9b0649bffeceec9ebe4dcf8603aa291254ce1954471cb01670b8e8e2f111ed54f8c818b348bd0300eb174602b484899f2fa95bbebdae6807de23355e2a988cf30cdeb2327759b2274205f485ff15b668b00a4f36ead53e21091be1133c2dd9a635bd6a942509921e33647e864a26af80aa5e54e15e695d2156209c2da2a7b8fe1938632192fce5e75736249c2fb6a7c2af46b29bfb5a83699d568f6b5614bf4edca36dc290f71d", + "commit_pub": "0001000120fdbada2ddce02e7493673caab793308bdeb5cdb0558f151f647ab6e2ca0c63cf0000000000121212010000000100034046010004012017e89d9ff20be622654e2b82b1e55d5b51c9e01d3b02cf0fa179c14e8697c11620d3ad3e549ffe3fe157d4d2e6627b8d35be6b5b8f37ffb10ed358bfb47c6c82950040402ad9df6c6e068cfb07cd8a6e49455f256ca630341744128cdc2bd180c8dd76b8e2b3affc5e8c0b3a70d55e349e46adf95e2641299b6c1ac78336e69ec243860520b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad20aa0af8115702fcd155306e3f0091dbc40c772a97547ee9398157106ad10e9bf1", + "application": "725b6517ff1e5ed74224de23777a9b5f4a2dd74fe4a816d19ea71baa069d74c0975b7f10e61b7c3ef339", + "application_priv": "0001000220fdbada2ddce02e7493673caab793308bdeb5cdb0558f151f647ab6e2ca0c63cf000000000012121201001c22d286c12043af1e8093bd213bf707b9b675ee98a171d0eeb206de91407d9d099e17b2aa1de37065098feb8efa598721128ec5bada3a6a07e06123eb61ced1dd265a0e7468f05f39b549ede80606f4795425de91acaee0b0bd840d0eabd284ae30adf04a5bf97027180836adee1203636c2cfd36f477747e9f0549dc99f5b3ef3a3db4f4def426625757365cf871061383a6dbd516127a11f83cc4" }, { "cipher_suite": 4, - "group_id": "dffdaf7e904ca23539e92631b02078d30fbecfbcb7903df52fc27cd6ff4637fd4d754e0c2b46fe77e991fb901592936de17f29f2ec1afb03856531810421598e", + "group_id": "79aceba7efc2fdbeb7dba9087d0c2317ef9a1150439b439979ca76564507822ba5aa20f035c4a866c4437b68401d62d8748940c3924ec07b03a10f27a599c362", "epoch": 1184274, - "tree_hash": "9c79bca5a5a270a547464c5047dc36eb9cd9bd4880c096a45a111b0c675575cf5f009f412b64bb68e4fde7e86ceca9afe88892fbed4b4cf5d3ea1862e88e0ce2", - "confirmed_transcript_hash": "a275e1f47eac7da44458981e46b34645b497dc9d1b6555388780b48430fab0c6c50a5292aedfcfdf635bb0958a86553eec8cdf99a43704e91ae76d8e2b6fe58a", - "signature_priv": "ca78c50894171e67e7c5be237fa8bec9fd9c7129fe7f6b85a2a25d08d131bd1cf4aaf6d2d78a4fde03a92fcb584c6c203f172dcba2d1ba9b2d", - "signature_pub": "2c80acb6826ffb0f83a8723530202c04693e792d6349e3b37238180b8267c3174d5f44e45a3e49722789efdda0bc43a5e3136faeb3ddbcc580", - "encryption_secret": "a4ad0c0f7dccf227ba967ebe06f0fbd6e409cc80c32cc6069d132bf38649363720b4880ee21087478c4f0bbe773d41d0097128c9523c6d01c7ad5a8f822c2702", - "sender_data_secret": "6159020777e0688e263d890c3dd86932b31d7d7c0a3169b6eb20e77fccdc3f1d00c20a9ef0391ac073681cc2c73df754f924cf2149a4f59ac4850ad1856c6f0f", - "membership_key": "4012edc5e096bea3a54ca4b27bfe3e302498cfd4026326d0699c1a919dd291731c33c59b58884b1f1e48201b2998f0f6a3761646a686bb0de9b54a2ff0171887", + "tree_hash": "644b7525c3a4693e3598988f4cfec0df372eeec860ed82d50356cdf66213041fd723d9e65b2cd7ba5a7551165addaf0902a2ece733cd8a3480466d5917243a71", + "confirmed_transcript_hash": "fbe89de69aee2b43a93921e0809e3d11b305fb985d40f7346fc96197ce0f4c5c7c19460512c72e95ecda8d9158514bc6022f90275c9261119eacc68b5e2cb8ff", + "signature_priv": "75fd38581c7eaada442fd0a89b71aa912ede2c77518ad8ac83dd3f5e6a994faf958c200ab68e4e84a4a5ffb8c3fece436101fbf161585b9710", + "signature_pub": "d82d0cfbe1ca129087fc5e21307106b8455961ccb0fa52d4085ac5f0bd38bd01fd6aaafcfb91989a237be03c411b7c707c77e32047213b1d80", + "encryption_secret": "299566ab0a25d7d246bc35366b23b4d5af3818682139d91123efb2e7ac44441396d9487c74a5a0754b6442cf794dd2c06c08870170ea78214e71b5e1c546a122", + "sender_data_secret": "3f3d0a2186d16c03f98f8e200d68f7626278a68b6c908b8972def6b088432d7b5ea9d85856b78a9c871687b2d81295bc561cf166ff1480b99eb63b745905d145", + "membership_key": "6c07d3a6c6d9db5c68f491352cd869cfed2cfb3d7e5044fce7e7dc91c0c812bf148c0f82c9e7de850a1daa9c6cb90f9b99a000358e163b97f34fef157f70a0f8", "proposal": "000300000002", - "proposal_priv": "000100024040dffdaf7e904ca23539e92631b02078d30fbecfbcb7903df52fc27cd6ff4637fd4d754e0c2b46fe77e991fb901592936de17f29f2ec1afb03856531810421598e000000000012121202001c118506c0e03f77c1c47f2b1f28b87d66e94900af805b825119154ab2408a3b1196642ad82d111a278360dde958e3d39123f6e26911cc3b23fab6870cdeca08ae160bd15ce9ef9626d2c4c3b3b1ddf2fa2b316a49b5c67e98b1752c451c73971f17b2ea2e1ff9d713a74fe3ce34604b3992445b696487b68f510b1c1dceb8b81f0e9c920b7c4d7766b3be5d77bcbd5de2146f0df9b46a3859fb2da22abca84d68486f78740779f560", - "proposal_pub": "000100014040dffdaf7e904ca23539e92631b02078d30fbecfbcb7903df52fc27cd6ff4637fd4d754e0c2b46fe77e991fb901592936de17f29f2ec1afb03856531810421598e000000000012121201000000010002000300000002407279d6d9458acb7128aea4ce4dd1bb0eb208e977dbe5ae4bc5e581194ec1a64217969905d7fec930382b0c0750d7929d78229f6eeff7f742e4009df344bc587fe30e4fcca3c77e48721119410e9b7910b887dd08dfc94bfc0ddb4cdc530092a9d638f872792abaf14cb8414ae2f92007f8100040408de56a4c84c1b3b023631ff73873e79f3e64aa6b4aac9e88ed9db33029cbb6eb0062a286d3d8121df429cd46407a9964fcbdb726cea18f119c9af26d8d8d1b04", - "commit": "040100070000", - "commit_priv": "000100024040dffdaf7e904ca23539e92631b02078d30fbecfbcb7903df52fc27cd6ff4637fd4d754e0c2b46fe77e991fb901592936de17f29f2ec1afb03856531810421598e000000000012121203001cf4136b4a8d83736ffa8a9b145236caa5c5afe40b659f2bfb5b56c02940ccae1b456b9583ee3b95e058079adf71124be86ddbdd89c7cc0290bc2ed320fc0609069ed763b4ca51c0de9e42272a6e5e0b8fdd30dad369cf35857d30d1d3cdcf2b9b422ed65dd9a807eb3270218c2d0f26b1e24815a70ad4bebc5e38a5d59078948654b9829e3d4aab6c252cd07b7f1cafdd79792b191e21dbecb277191600c278f2b488609a4f523f861fba80ec20165a5f6b405187bc70c84c27345559dd74107610f9ebeac15238c2c4a651531c3a47e7f06a3ad582de76994515612c997c52307b5af7b49e5e386ba355", - "commit_pub": "000100014040dffdaf7e904ca23539e92631b02078d30fbecfbcb7903df52fc27cd6ff4637fd4d754e0c2b46fe77e991fb901592936de17f29f2ec1afb03856531810421598e0000000000121212010000000100030401000700004072ecac4d18aeeb2fae6543fb1e90dbc56602b287e3c4d0ceb25108664537a439f01cba34b92f4d547c9ef8586b10627af26b945f526cfe791d8042a7a6e72d06fefd397d9cdbaa65a3e40541a598d56143d9b18ff27e914609cce278587eb4c574cedd969a3939dc1b9a16b550ff8fedba0f004040b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d47404066c78dc7d2fc5e3ba70ff69eaea7e22c5dfa6f6a214bb81bb5b8220ed84306352977bcb919b01b575d2ddf39a0b092a33c24b69acd29eecf20b9b89a969c0aae", - "application": "708fba8e930a7b84be5f5a0337317b662dfe4bc5ea5a2433673af85eb6b470696f364b8e61d0e1ec2a1c", - "application_priv": "000100024040dffdaf7e904ca23539e92631b02078d30fbecfbcb7903df52fc27cd6ff4637fd4d754e0c2b46fe77e991fb901592936de17f29f2ec1afb03856531810421598e000000000012121201001c19cf9006f582451178217024ca073a5e39f8cc28cd528d5149dfa2da40afb139ea1a379905511a5376bcd6409267b90c3cbe25fa80bbf309f46004762315131f368f7506ddbc77e4c1b0576bc1d18c4486bc5062711dbf358931b92db5eb5efaa6a19945e988da687fd7dfed37948f98e472639d4777815aa1be9bd1ec1a274dcb38cb409f9953e8b0695ec959072e1ca71548bbd25effe31bba06b05b4cc3dd41ab912be2117aafbca5c541ecd1789d3c4f34dc5699c748ebc2f70534837785edbca825a3c31bd25604972b5e" + "proposal_priv": "00010002404079aceba7efc2fdbeb7dba9087d0c2317ef9a1150439b439979ca76564507822ba5aa20f035c4a866c4437b68401d62d8748940c3924ec07b03a10f27a599c362000000000012121202001c4eae29575d125c662ee7387e5ef91247e9663d23239e79e6a66032e7408adfb433942a7cb5f14122b0274399a4ca571940f013e3cb4d26950e13211c92fcd0cf819ec01f144e3da9d407bfcb623904980e24a9b310df0fe1a72cade435b9711a87f7227c5f283179c01b43646485a370c74fc4ba0bd77166b6bde82803876714c420d8665ef10339bc7dff2809c897ef6c56c3b975cc793618c9ab54fde6a500f021856986bcfb8c", + "proposal_pub": "00010001404079aceba7efc2fdbeb7dba9087d0c2317ef9a1150439b439979ca76564507822ba5aa20f035c4a866c4437b68401d62d8748940c3924ec07b03a10f27a599c3620000000000121212010000000100020003000000024072f2b2c084ce2ca92b1dc5bea63f0b1ebc3bf360e40119b18f1e9b39fd2dcf3f6add1d7bb92fd7ebbc08822e1406fad0deadc720706a4d9a668045c138eda32f4570bae9ebc5aa9cb4e2d24f2884d9b39b233328dd89a2d76fd073b95fb0d44e5d943c1d01bb8607d92054981a75edd2253400404039c8ed1865af67bd3ea9056479cc670bc010e0ecd58175f3fba6762d95b486b83b262442e0e76e1f32655445c7b83a531a7b395082a6d6dc2d58a731d6c46e8e", + "commit": "40880100040140400f21ffbd22707e1411a09e26e524e9b551ff58b16e63049011cd6b18e1195581f1b2f3cc846ecc732c540124104a020a51311aa1ff75c2dbd752c08d2d874ce34040a44d40f39327ff9c3b7b60bc94224f3245353514b37715458883398667df5bc563c441650ca83affac2ff152c7fad1f699262b9b3fb254f7b6f40ffe8089a97400", + "commit_priv": "00010002404079aceba7efc2fdbeb7dba9087d0c2317ef9a1150439b439979ca76564507822ba5aa20f035c4a866c4437b68401d62d8748940c3924ec07b03a10f27a599c362000000000012121203001cdfb59e66ac31671929477ef87e1d0b8a6672d6830c8d70d219b4925141517eddcc1daa6ff0d54b6f49cb141e89c5a2e7d6eee6883ed5ed0b1c2b371b14fc7aef7f0c0ebf7016a6ac65de558fe8782d53d251b31153fb993fceb91beb19e450b8dfe1f33242a61ac5aa1109ca11290282209cb49be2da2d3441331007da27bac3f49d381b6a347e67dc704dcddcd3509da2c341dbfee3ec4e077f68d264fce799cbcc408ffa00ee1aa352be71e261da2054dd29c27b7e8777fcd4ab051b20736a402d1e5d259aea8ed57fb98661b3215cd0db48696df7ae1df502575fe20994216a2214bb69d73e077b726b03feadf07c1fa3cdf623d3b1a4b7215321b3fd711697f7da293ba129ddb2ea65b1591712b3aa6626fd595b2e670f85c273ec516aa0052d215c7e8962c6ec6d9d2c682965fcadda725888df78fe09d6fad36c666ef596d12e53a515ff7b927ae2ff11bd95ecb8236e98501f58529ca9864d6549c849b7c970ebda7f0a0baf026b5ad5c70f", + "commit_pub": "00010001404079aceba7efc2fdbeb7dba9087d0c2317ef9a1150439b439979ca76564507822ba5aa20f035c4a866c4437b68401d62d8748940c3924ec07b03a10f27a599c36200000000001212120100000001000340880100040140400f21ffbd22707e1411a09e26e524e9b551ff58b16e63049011cd6b18e1195581f1b2f3cc846ecc732c540124104a020a51311aa1ff75c2dbd752c08d2d874ce34040a44d40f39327ff9c3b7b60bc94224f3245353514b37715458883398667df5bc563c441650ca83affac2ff152c7fad1f699262b9b3fb254f7b6f40ffe8089a974004072de713163fd7af85b8d5f9ed5f0253fbbef29c5fec2d9c4b860de8068e5aae0cd15374226863f514d2d96a373d6edc247021023845778fea080fd6a1f8ff0d079a961ea48bd0d31d02ba2c9eb357aa5937146b4b2bfef80f03318e4dd3592f273e513127b967d5cfb7ed1e0ae6b6457a128004040b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d4740409ebd123a98b0cea3f591199e0ff2a256533eaddfe3dd66144abd4fe8c4718351718bb30fd2248c696f49621892160d2d52deb3994f18026792afb2a62a936b88", + "application": "04c6e8e549e5e1e5ec7aff322272d4b6cfafe5171cbe601ab8d2d03fa6299cee38c1f06f1518fd1ede30", + "application_priv": "00010002404079aceba7efc2fdbeb7dba9087d0c2317ef9a1150439b439979ca76564507822ba5aa20f035c4a866c4437b68401d62d8748940c3924ec07b03a10f27a599c362000000000012121201001c404d04dc5a3b315b5b3908750cebe651ff602f8bdfffa15b267f9c9240af3efb6032639bda6e1120e4f3d884c1b7048bdcbbb87c45c92ad93a174efa5a7bae84a9cba934f40c42456d13e6c9ec0b5b4b330e8ee5a89507931d7e94d89c3da73ad57dafa38e5aa0ea9e2a11a4fa79568373e4f9cc4bfad141848bd86d3515f4ea12b75fd166b87d70fd00dab18b6bc2482c21ce864e2ce44b377c436a71159bc504e8ecd7147003cd744d16551a2a574b1ce09738c897f14f7254a71814efb378efa96605ca059539d2db8344d0" }, { "cipher_suite": 5, - "group_id": "30fc0cf0acac94efcefaf85a0d1eef750b6302800a64906a6b705654c0e1b7b7bc517f0aa5e2d8e0f7c02b82db57bcbb14398cfe73113e1a92320de1882fec12", + "group_id": "06d7dc76a63f418e028f3897fb34fa5eeeddab2190070d67f77f36a6dc0116f3c0d7854c675d0a98940447d3449708106ae55942eb4fbdb16e6582b998e00dbb", "epoch": 1184274, - "tree_hash": "02f84cd14b282fb977eb76f436de3f839cdeb32606acd6ba90320c1093c559bf19cdb3252b4c284e91a5dbb120339611fa252f685e12ab2b73f0c23d942b71eb", - "confirmed_transcript_hash": "64d06926664607b48534386d7cdf546a8cba892f4a8711c761c0393f6297cd5e0d6596bb632d25e55b6f959f3461b8ec26c434ea08bf7d087de0e80fdbd25b5b", - "signature_priv": "01dbce589a8042c1f791e03726f41875b3d8fbe1ffb1af6e60c60252a780f9ff3d3f10025f7e481c603a029e72db53fbaa8569ec4f8a8ececb491387815128929311", - "signature_pub": "04017680b2b6cfe27e893fe8a33269738aeb28af61e6120b7865d3fadd58b7b0943a9a2a81969baa532de317d66b696789f8529b40d2ba8539ddc5a24e035338f26400004b12cf804f9f932f0d9dd308f0aa04568e76c18bc6e9dabbdc16742b5d5c1d079639fc6f0067d3eff244d9bf08a0b03c57db4c0d793aa17e9ea916349f51cac029", - "encryption_secret": "f707a9bd1b21266393a030058235a1f39c7f9d4f4e593d7261394af8082896c599aa379c2bb6ccf108c12a113eaeeffaefa2b231ad08fb5028abdee56e762fff", - "sender_data_secret": "6b0eccbe40aa20164ea4f292b71bf5e7acf354cf6deec7408ed151a3637ee725850de0701c74d4ea18147a73f67d00fa6ecef4e89d5d713a99885d8233f52bc5", - "membership_key": "c1ead38157d69b08d05b16ebab6b584ee2fe1cd46a5d3c8046ace7469491047ac67e8432d1ae1d47b76c317181ffa0111bad10fe4175cf7dc067858ca151bc59", + "tree_hash": "59e530256271b91e4777df9e34c1d9857f6d91df100c501b91e1ace690b62d2f628919f3ea895c6e495037c7d2b068bacc0e9c4cefa001125b86035f488495a5", + "confirmed_transcript_hash": "175683d8d7da0a26ce09a4ef129cf369848c6c6cd1e18175afd66d6ca26e8d23c3ea648b896487defb28078fed61c27f799f2943acaefd0924e67ad0fb965d21", + "signature_priv": "0beee7d4e812a02538473225803aca13f8dea26718f188f2e1de8357a0037df621230cf4593885f282b858ac301e54c0643f5d07b6e85f237baa13b574000cd821", + "signature_pub": "0400bd899c19f74c9200f2e7d1b88a681ad78df3bdec43bce09f08cafd7405b41709b618c9af3343d5adc2bd893be18b17825ca6ebe5deb647350274979dd8208cc863016b2cc8a367596139b384252184771083881d66557ca469945ac8d0d3af08c048ccffb51d44ed70efaf8cb8e238912a7751ed74a5e33bddc7cef0d6ec37422a9df2", + "encryption_secret": "96a6068bdb5e3cc957e96864206152be593c8437b17ca369de4ed63ac801dd432a601a69732f7c234505dcaffb737e9b8041239605ef253614c6857383e73ea2", + "sender_data_secret": "294caf7baafff902437743b87bf83b6ce1c35ca81b39e07c950731d57a0ec1576b9ec995364001b73ff9f11d4b40c479a9609024dcba0eeed2900f4e2ac1b496", + "membership_key": "91b4d653b8397027a9da0589846b1a00622ebce84cae044e882a553f141297cb12637731ebdbd97971bdd9dd459a9168ac3f2c1b81f29d44e710afdbca9bd037", "proposal": "000300000002", - "proposal_priv": "00010002404030fc0cf0acac94efcefaf85a0d1eef750b6302800a64906a6b705654c0e1b7b7bc517f0aa5e2d8e0f7c02b82db57bcbb14398cfe73113e1a92320de1882fec12000000000012121202001c3ea9f7cd8057433fb6d7c39c6d9aaa1af9d446b33dc93cf8791aff6140a283c0c703f04f62f86e0c66cd99f365aef98ce09439a2a0780f3622ebc7cb0c24e4a513b68e820b52742e664f44da34b7a9449f1c9449e29a336a18503bb88524d655911651d6b1c70e8c88f2e0e573203035541e57e1710f56ccbe100ea42acf49436292b3c03a56334e0e5f82335de01203b144d646ad6433989280270bbafce01d5dfaaa233f83733232bbd08ca5adc41ade9a1ced20cd49c91eb945609814656b", - "proposal_pub": "00010001404030fc0cf0acac94efcefaf85a0d1eef750b6302800a64906a6b705654c0e1b7b7bc517f0aa5e2d8e0f7c02b82db57bcbb14398cfe73113e1a92320de1882fec120000000000121212010000000100020003000000024089308186024159f7313ccac559043e46cef42c1f6bb2283aece9b3cc1ede60d0693d51a8c2c9307d90c96c25aaaebc3fd49e7f3b2fd2fce463156c2e0d762c7df76035daac7776024169e6a089538f315364b61c0a3359965c6461ff098274269784c2f612c603ff20a7c8928db22d06129a27c71c65d358c3d7789d5fa7fa1be277a846b7ef7ff75c5d40403c2c05b3e098f438ca226640c5e66d02d757c2035dcd7dc25949b9af6ad3df464ef49e1c396fddb32d7c32995e353992b984365ff0e60388ebe5438b84906d9e", - "commit": "040100070000", - "commit_priv": "00010002404030fc0cf0acac94efcefaf85a0d1eef750b6302800a64906a6b705654c0e1b7b7bc517f0aa5e2d8e0f7c02b82db57bcbb14398cfe73113e1a92320de1882fec12000000000012121203001cd43f4e21a3c74333cc37e50fdee1b24b0cdab75fb409b1ed207d247440e4942a1da4003228667e5c42ba6b05f2aa2df8e09f05793f9e2caa8f9e411185d0439563509eceaa05dd17a611b0c7926f572d0fd38d192c011457fedf0873aaf33143079557b53d31e2e20b2cb61247200bdbb5e5368bd79869d80565477c659a7baa2332216b126877df7fdd43bcb5ae7baacbff67741081bfcfe0df879bf5804effd24003d13de143d0dd89018173d417dfc1321abc47812372f5e1166ea65fd4e28e4b6001f7ed3907d360188ca8ad261714f7e6330e42763f6e32a7bc3df3c72aad7b898904042d966656c35433487cc0206f2679769c6c964a5160cc724a2f72f06f", - "commit_pub": "00010001404030fc0cf0acac94efcefaf85a0d1eef750b6302800a64906a6b705654c0e1b7b7bc517f0aa5e2d8e0f7c02b82db57bcbb14398cfe73113e1a92320de1882fec12000000000012121201000000010003040100070000408b3081880242010fb0fa551b4ab871dbea2480e43c1cfa93595838aaa1644226da77bf0652bd9fa2d5c534d227f6d7f0e008e99a3694807d42b676e9dce44886bf64fca41e123c64024200d6f7186e795e1bb8105b1db14a0ee6894cc0f17c5fe124a792553a10fe784b7f75e1aacd2b66547aae64d8e1df3705312076614886ccd08052b282c8158ee1d1f64040b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d4740402d2e552a102b0f07b59fd5b405ba7c2e7f4658412d38f8cac610506f314980837806fbe51362d78d9bdcad46a48dd3c59abc34962cd1dbf27bd0c55c70ecede2", - "application": "7385b6b2e0f3aecc6978167c5bb373e1cadf7b3344e7740ae940ded147005bebc380df2c3c2259d634f9", - "application_priv": "00010002404030fc0cf0acac94efcefaf85a0d1eef750b6302800a64906a6b705654c0e1b7b7bc517f0aa5e2d8e0f7c02b82db57bcbb14398cfe73113e1a92320de1882fec12000000000012121201001cf3adcfbda02be66ca0f567355360d64cead4a23b30bd963f62e57e2a40c8386561a29fbe49cbbce8d0ee9458ed3e65d74726d17de93c1c7a88f9f8a1a17b3c4a06ed958ebfbcfb4939a506b93c52767740749a3355e132d034032c2f370a2e6b8fb7475b8a0001b4b2a2b42592c7a2f72c19c126594512df98245ecc1a112b7fac801133367fd73b19875c40bfaa576038eee6ac92b9e45f1ec225a6bbe23b95c1c4601a2337a854bc520d56f1a59d5ff54dcb4f8aef367e71119ba92dd953af8ed02a706d1ffc8b2a06928298ee7f0e12b848bfcdbff7288bb7819bec30ed9e569d81c0edf1" + "proposal_priv": "00010002404006d7dc76a63f418e028f3897fb34fa5eeeddab2190070d67f77f36a6dc0116f3c0d7854c675d0a98940447d3449708106ae55942eb4fbdb16e6582b998e00dbb000000000012121202001cb687027f36c530034a181574a6c29d30afd5b6414d967c242b50c62c40a330910d301b99ade6c0b9605fb044a22329b8ed17f4ee992187f1be91f10b5c2a91f4e52edd6da127b35ed1e211d18529a11fbdaadf6274cf591aaac6cf5094c3f0743ad10004c9610f803969ec0e9d10ff4c7effe1b7c2050684e7a19e756e27c22f35494a7de8d7d1386e8004340409e97b4871f5e09ed6968054c9348425e8b428839dd93b6caded910aac2e1efb4e38798256ad4f33d8c3eaaf1dcb994b7832d856", + "proposal_pub": "00010001404006d7dc76a63f418e028f3897fb34fa5eeeddab2190070d67f77f36a6dc0116f3c0d7854c675d0a98940447d3449708106ae55942eb4fbdb16e6582b998e00dbb000000000012121201000000010002000300000002408a30818702410ba0d868b9225b75459e0b17a1e3e850eb9caaf312a92a85b1b144cb9b5fd88e09c98480b0c5ba765b7499cfb5079b18f9671404e08a7af009f58ee3754a0438c1024201ab76d80427da4b491a1289b1bcc4763c7cf5e695f20a2a3b2608c481d2a4cb6c865318e1ed0747098041c52bbdfc4e1597136dcab2703fe14875629bc2061a145c4040331599d99b1a0a12d4858fda021b5bf317cbb9171d82cf54a9c939e8bf4796ed34789d135f424a39474c0a63d0ac5a94693e2bd5864d31f10d4700d05b7fe91d", + "commit": "40880100040140401d03b01592ec9cf5bf6f13b127fd83c4cc5e8a1e833d2a10abc08dea7920a57141c629839e6ea2e8a42c9907da63adbe1195381cd43ea7166b0a698c2be88b36404037226d4d2cf1ff02f71ffae949e5c546197ae80180736db45bfc0f565e988d925dac2bdd4acb1daa7f7a9939f564f9eedecf9d04523da477eff77709986e4cc100", + "commit_priv": "00010002404006d7dc76a63f418e028f3897fb34fa5eeeddab2190070d67f77f36a6dc0116f3c0d7854c675d0a98940447d3449708106ae55942eb4fbdb16e6582b998e00dbb000000000012121203001ce0f113892b9d6b655f1dbf8ce9ebe346de19b4e094ad884fcdb07851416860c14d6b045fd6f2e941261afac2f2599bfaf8a8215901ba81ef52ab9f58448b08b94aa7b4a35fdfc377c34359d63dea85b9751c3d6aff9215dfded23717eb27eee429d24da72afdc9664546aa4f345bec15d298b8f967e26b0d10e16bd017ecd6428ee6cab4949145cdf4ab98edcc98b6996f2ee44308917ab5b954b22f078415c8c682865275b5f7be49db752cb906954f36d75f05c393cdc85cc04ecf1d97466edf8602f534329ff0e6d643ec7560a95050229979e24d374bddcb723a2b81ad3d3f5f7dc7ba668068536196f957d36f70c6a06acd5cd4ae3cee04cab49c070084aa27fa2c5f065d08900e5417c1e8b498a6edfd50db510310643420a51099f353f4c44530bd9f2f5dbc50b8acb7cb7b678a7500eaf85839651aa382aef550826f1c8e99088ed108f0dde64592b7e5b44e4a563e561393aa69804870fb57ec6b2d2739d0f3f0f3354892a758f385e351646335605028566cc5a4e989877fd7034693c4f1af3631", + "commit_pub": "00010001404006d7dc76a63f418e028f3897fb34fa5eeeddab2190070d67f77f36a6dc0116f3c0d7854c675d0a98940447d3449708106ae55942eb4fbdb16e6582b998e00dbb00000000001212120100000001000340880100040140401d03b01592ec9cf5bf6f13b127fd83c4cc5e8a1e833d2a10abc08dea7920a57141c629839e6ea2e8a42c9907da63adbe1195381cd43ea7166b0a698c2be88b36404037226d4d2cf1ff02f71ffae949e5c546197ae80180736db45bfc0f565e988d925dac2bdd4acb1daa7f7a9939f564f9eedecf9d04523da477eff77709986e4cc100408a3081870241171109926aef64a5397152a83138545f0766c98e3b66506901a8189c50fe2a7ef1cff56e26cf0fac9698193b72d2a688c5ffbb908cdbb68fcb8f44c40017cbd10d02420177991d4444ce9c0e0d0ebde5915a51333df17af71c34dedc388ebb783edfb920618e6a80a6730d8e324125ec4cbe0b29b44b46e1e193682b01af988953d023b3294040b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d474040eaeb7535cad483bb9d3f9d32ce51502382ac6006bb50576278efd611ecf0f5e46136165d2fea1b0ad7ec9941e5f82c418e6c9e35c751c6cead0e2254e68d34ae", + "application": "790a2cf292162a82b0380170120121d04450bbefcf39a7096387737d6d326173042de3d11a0982c321a5", + "application_priv": "00010002404006d7dc76a63f418e028f3897fb34fa5eeeddab2190070d67f77f36a6dc0116f3c0d7854c675d0a98940447d3449708106ae55942eb4fbdb16e6582b998e00dbb000000000012121201001cc62179225aede4d46fe94b5738ed8c939cf812bc666d46f84addebb440c863aa47d154e06efe6da8cf25de9754c4223bea0b66b1b1b2d664776d74074974add73361918a8d28ddfadd7a44c00085bbe28f90513d7dcfb33ce80a5395f083e3b77c8330563c6d3ad0608cbac536314ea73e570ad1650a506217f71a944f0f250e03879d30d657ccb04e122b03a1ff4078958b16f64c5c835181c02dd8ab9b76b6529db4852d99770a5db75a6b4521ae9cc9ef24541245f47a7df2d6680a005e5a4386f74db4f98a1c45fbeaa9c74176ce0883cc2ce57b31ecaf7d581e6c6ec329936863cb9a49" }, { "cipher_suite": 6, - "group_id": "be2d07c0589730471d5418b8fe241cfcd8f6c7f30b3b5602bc87511d5a61e53753c3f95a8af2dfc3d289210be8f93db59174b8cdcf193a0bd90cb5ba4e688707", + "group_id": "de0215cb6a2ce86be0ed8f3821d8cd8f55f25392e95af652280a950541a7397b35d589ca21d2fc0ebd122e9112dcff3563a8dfd6c986cde072140b164d27e0f8", "epoch": 1184274, - "tree_hash": "30ff1dfff2eb2137bc0e8582f397ee877d66410282174958f4a0f8848b1d253ff2d16b221819d5b9eb0644d40fe66b08f05f43ca24b6eaa930b6491697a904fe", - "confirmed_transcript_hash": "5448b37e449af287652aec4910788c7ffbed00e66742dc68679f75e2938d8b6772a24516c80c2ef3ab0959586fec9def3bb619848b722fb8f6a199b293200a34", - "signature_priv": "3596601c67ffac254c34cabdcdefdbb6b1696a0e34abb376779a0120a2df513778707e6e6edd4a48ff182c2eceaf307058cfb8c43e07854e76", - "signature_pub": "4939043de6170ef4387b8035281ed330e8bf0997d2020fcb0ad71fda8d0066a24c47d721d2b305c3294a4194b3fd79caf55cea53c35b748d80", - "encryption_secret": "07442b8042eb4ac3bebca86917f3d70f94fca57a68576d5f271f10acca58857ed0cedb41da75993071384eb847937af189c258b8c6b092e4c499574788fdb2b9", - "sender_data_secret": "a2fede70b2ac0efc5209e137e100a72744204d82f0453528cb21d515b062c744eefb050efee55634cbad42abef3715c4da54bb0648657fa3114d4b17a825558b", - "membership_key": "b47efd88fa257669cc36ed3200ba4df6035dc5148f713c45eb61f3db2702e60ae87c67310184ec8238eab6e373bfd332614db2ea60ec23e34773adbafd294a55", + "tree_hash": "6071a6b389944ee8f41bc87f71f59632f4d3bb6904e4ad6735928a1e1b90dcb9ff59003d92da7a3a80637758116598056389c21dcdaa19a99a05c5b94e341bc9", + "confirmed_transcript_hash": "34f4eb8fbc77abd139cac88c03a6063484ad5afa0e4f9ca34e7ddea7e071b29c0ad4185b22657ac2aab2965682ae7c3a31ede7aec472e431a1063d03e2ed907f", + "signature_priv": "d47fea7274c6dd6172907d0ef4fb625fa3a9adc038aa089b8780783d2b986be7c21390e3ceca12a879ad8998d16ffb3fdcfabffc5f3ad278aa", + "signature_pub": "ac06934afc423e39be6d71ef3c7f10c436f2ebe9f558023805ed9e2a40442203340a8b6cbd864df1ee9e084e9898625ec5d2ec37ccddfa8f80", + "encryption_secret": "bcc9f3d6f4ae418eb1f804c1d1455452c59b65118dc09cf959c45bd4f8239a5a709f9da4f5f2df82fd7da499f7d59cfddbb527aa84911c4738b0fda40c037ef9", + "sender_data_secret": "75654a7cb351b6b0e58b92df101dd51fe20d1364d5950cef091f0f74bed5dca5a1e11d76154abb99b92e867782e8f286bbcc3c0ee57905852a6db21eec6f011c", + "membership_key": "c2b121ec0db354c57c61090a265100ce221225adc21461e04f518207c3fe000b68c7f282d494e1794463e6c076f4a8dd77c85583029118482bac639d3df78d76", "proposal": "000300000002", - "proposal_priv": "000100024040be2d07c0589730471d5418b8fe241cfcd8f6c7f30b3b5602bc87511d5a61e53753c3f95a8af2dfc3d289210be8f93db59174b8cdcf193a0bd90cb5ba4e688707000000000012121202001c4dc1bf5f062d2ab8a17c2963a4a93d6d590953c4307e52f0ef8fde13408ac97f02d38b121712192e95eb7efb76afcbcb375ab8c49687a9491cb67035e833ca2bd7656676ad6268d812f5bbb0f86eb3d21d38582d23aaa6f25b565e19bc4152245d2850f68049c01f6d30ffdf0c1664ef5526521704b0185350b77713204e7fafb8c0107829326621733f155e5c5a5a08f33b5e282a7b04244b881fc2af447c7e5b99b591abaa3fcc", - "proposal_pub": "000100014040be2d07c0589730471d5418b8fe241cfcd8f6c7f30b3b5602bc87511d5a61e53753c3f95a8af2dfc3d289210be8f93db59174b8cdcf193a0bd90cb5ba4e6887070000000000121212010000000100020003000000024072f8484103444d9ff35dd6fade06b8ae9f2d446596c705b766f63562561898c6cfe28cfff3ddea803b679967f78eb6e7b190121208f4056c1400b86c023bf7cab105ed266669a62ec8c0e75c5c097b48ad7bc89b99167dcb2ef2e078d6db89ba0deee9d08b929dda0b31752271b99c935a34004040da8bf7aeabd303ee2e3eeb31487ece80ce22951b9d2f96dc8d4bd3b436fec1cc4d7f6776da3538aa877999ebdb16ed812eb2923867c8fbce43037b0a57cb8187", - "commit": "040100070000", - "commit_priv": "000100024040be2d07c0589730471d5418b8fe241cfcd8f6c7f30b3b5602bc87511d5a61e53753c3f95a8af2dfc3d289210be8f93db59174b8cdcf193a0bd90cb5ba4e688707000000000012121203001c0f4c9ebb2c7918a80d6c093c82bd60a4d384005f4e8367bec8bccaf640ccdf3ff5be7866577b3e898780436afdc4877258e72be8166242ca8e0a951797884dec52ed0fe747ab403a219d35f04d69ccf1ac0f74cbbbdbd3dad3751b6dab55a9999c977e04a2b7f888651db70a5b6d4e79ef72de4a6624801b7fb1ce5aa004c83478c3d5264217dfdce76e7282f5e1d5c3a100133537142fdb83e601c6cb4761249a6c95774ccdcced769c5bced5ee79ddff301220f487bcdca335b7253ec84c6e1822d36f601ed338b55be6caf6be4103ba8b1a753514caefbfa07b4b59762c8250eff6f735691344e8db", - "commit_pub": "000100014040be2d07c0589730471d5418b8fe241cfcd8f6c7f30b3b5602bc87511d5a61e53753c3f95a8af2dfc3d289210be8f93db59174b8cdcf193a0bd90cb5ba4e68870700000000001212120100000001000304010007000040722997cc456ee7aa4d3c97fd94d23e22992aa8d806261f856e243dd93b0b6ad8b9a0933a8c7f6a54777e088d8a34fee362ad5bb89fadbeaf90008420fa1d208901b1221f2eaae2fe0a2bd8c8767326b6282667c4138f682e657075825548cd20cc53701e80ef759491ba8ed245e8a017272f004040b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d4740404767007cd5ac60953e160a349ed4a7e6fe6cc30ab7b44c6149ef25cae0e7cbabe118d68a6b09d3f88b14e3fb78c4fa8ed44eec300cc4d26e6926d9b7d1dfce00", - "application": "8c39850852eb4f7648ee82de8bf313e1bf41bb38332f8d9915eb0e029b40a985cace8a00b29d08591efd", - "application_priv": "000100024040be2d07c0589730471d5418b8fe241cfcd8f6c7f30b3b5602bc87511d5a61e53753c3f95a8af2dfc3d289210be8f93db59174b8cdcf193a0bd90cb5ba4e688707000000000012121201001c7ea99bb9d9a03bba835afe136269617e7bb339869feb6fef183b535f40af7263af8abce985108975d4844e3d1bb37fff8b0247aa761156630d9695e028d3371223255a1c17798090ef8132da8cbc7fb338b2429b16372a37b69b0e257cac673fa2874e8a8d4fe8a424672f271dd8517819b7e51e1d5cac0b529b8de69bb84c8144e2b56fad755ea8b92dd44d7d3e6d90a0a30689050e31d0264771a8d1f04bb8e0c5d2c07af944196ade023b0a9161b50e4a50fec43f0f2796b0c658993a8a63fedabb0c1641f11d4b5ba4ad5f" + "proposal_priv": "000100024040de0215cb6a2ce86be0ed8f3821d8cd8f55f25392e95af652280a950541a7397b35d589ca21d2fc0ebd122e9112dcff3563a8dfd6c986cde072140b164d27e0f8000000000012121202001c85b71d3268547ccdf76dce7e3090decf5c5cc47c3986af15463f9f01408a9f613bb459dd515aac16b63ac5a1aa6b0a003d673aa7c34f5662a2605ac69d25db4dee562b8bb6cb6660f6b2d5d6007fe5fba7f928f7496b51fee1d44e971ae92951653e9a5081abba7f348c7588aafdf0b63b8fc509b1a8bd48ccc8d914c226304bd8cd61c2afff788b2d62ab66352806e0445af7f669b142ab18fc71582a5312a6c25b65cc2bb7e81a", + "proposal_pub": "000100014040de0215cb6a2ce86be0ed8f3821d8cd8f55f25392e95af652280a950541a7397b35d589ca21d2fc0ebd122e9112dcff3563a8dfd6c986cde072140b164d27e0f80000000000121212010000000100020003000000024072414666b084038fde6af02cae3245809fab67f607d3b68df8dc49882419e0275f46dc73a667a7359a02346a04d74e45fb2550ee329ce6698e00ec3b4a37c14dc506bf02f0357028043721cbd4c499c8b09034c2fab6fc043efa78da88402f2d62cde8328b1fd2b5f074441f076b6c827310004040cbde10fd5c1cb9914342d083d928d6d998b9d4117172e9fab76abf46807f64701953f8f3670a9298bd67a3390e3c786398fa94cf17d2e91351c898b9a976c093", + "commit": "4088010004014040fc76848bdf95a48ee26a8beca0ece18de5e0440f4848e3dc62392e3b311e963a8bebb5cff1e6b6211b0605dc446f460b995656f5bfe1915f693d632e82a293ec4040cdce799060485b09200b4ab02e06e236d3fd5801cc91e1470966463007406c332fdc7c84b09f303a7818f2ebde8c4696c11b64f274c1dd03fb3dfca9ab631f4e00", + "commit_priv": "000100024040de0215cb6a2ce86be0ed8f3821d8cd8f55f25392e95af652280a950541a7397b35d589ca21d2fc0ebd122e9112dcff3563a8dfd6c986cde072140b164d27e0f8000000000012121203001c88b4dd76435bc83765fb42d2d03fe2cea60202ba241ce9a0329c0b144151bd325b50a834d383961c9853d5090c4c3f78a974a9b0a0a9423f40dc5f7b0587377a9ad5210d8ab92150ddf49dfeca4c0acfb1bb9e47a9d295d5245f0592a82d9e80f2f2852af8cfc4a9191bb5f654dd33eb67de876cf6dcb30cd3aed0f8a7cab3b42025d324e669bbf310accbf595f0ba5039f1b40cb35e3659d6c066dcc69246716efd1400957f9d54f2d07c1bd8636b18810603a261c14e62f3a9539056fae14f0e1cfb9cab90dc057c2eee92b43e41c83dcbee491ccbac82e9f87ae0193c798c652f0d63ef8a9b2efd5d502412f52a94409e7cc31994a445030040f111a84a9036b9dd7919fb3d7530518e91fa46a3fd027032938a7324745daa6ce199980622f9ea9a0a09531a41da0b9a311419ab4c9fdbf0dc42c31e721cd1e1b1a969ee99f1f823be54f964276cb55ae02288c345c1e1401e57da9c4544cd20c9dacef8e850bbf626d75e70037871c57da3e167", + "commit_pub": "000100014040de0215cb6a2ce86be0ed8f3821d8cd8f55f25392e95af652280a950541a7397b35d589ca21d2fc0ebd122e9112dcff3563a8dfd6c986cde072140b164d27e0f80000000000121212010000000100034088010004014040fc76848bdf95a48ee26a8beca0ece18de5e0440f4848e3dc62392e3b311e963a8bebb5cff1e6b6211b0605dc446f460b995656f5bfe1915f693d632e82a293ec4040cdce799060485b09200b4ab02e06e236d3fd5801cc91e1470966463007406c332fdc7c84b09f303a7818f2ebde8c4696c11b64f274c1dd03fb3dfca9ab631f4e004072c2349e24d8201c95d17d28ffeb079b8067e1fbb6d32447f86ec1a37d6974403225f0cf1dc58e04cb427b3d4f6e4e07b3022ef35017fd615100747c233975e41a2305d3578ae06eaabe2148bc855bea89cb20dcd06bae6132d9a18368e8b7cc96ceac64cfa5811b7645ea4a032b4ca9993c004040b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d474040f48ec717a3dc0c656245c33828f70e9be31b8ef1e5fd9fe548ea4ea3f875fa0bbdef0d724333fb1e9e25abe67a40d134e88c63e96497e76994f75c9195533b62", + "application": "170a35c0d98ce0db033548da15dcf2b1886903f5fcc38ed5217b2e2053de122fec8aa19e1df67d16604a", + "application_priv": "000100024040de0215cb6a2ce86be0ed8f3821d8cd8f55f25392e95af652280a950541a7397b35d589ca21d2fc0ebd122e9112dcff3563a8dfd6c986cde072140b164d27e0f8000000000012121201001c8773ff6b24c5040928f456d51f11aecf5442a237d263b3d1eb7cc08640af132e5f2cd4ffcf040566bcdec15bdb2be8d5b039c9128a2407138c8cffbdd17539eb2227152000d11e43c173a869a570c6236786f5b08566f17486ba12e71e9726ed1914d363b61b394ea92d4020a1e3fe02e8e9522a5db857165abef43d0c8a69489da22c4cda5e95939fb54d76f3f4814b5a2a8a070542c17cf17acf99473d80cdce130d67fea9c16b5555c83fd9f845881409a4440366254f4d515fdb8e6c10703d22c9f0bf978391046499036f" }, { "cipher_suite": 7, - "group_id": "594c2e7a7855dd32f1298d9aae5f8401a6497e3f2118cfa7f67d8415c29615b6a3c5eb3b3b3fa65ed99413b6d12fed2c", + "group_id": "80f486b08d22f42dc899d0d09ded745238129fefed0587d946af22dfb0dbe0cfff07b324ca059d50ae638c5cb0a7d0e9", "epoch": 1184274, - "tree_hash": "21e861df27eabe98ea5f21501d4279892641b3aba80a5def4afe545a394dbfa26d531111b9cf3a8e0f0b7c91e6d9fea0", - "confirmed_transcript_hash": "df3915ec244b9eee166717229fd1a25ee17c03038da620cc08c06646cb9db020f715252a1d37f113aa1cc49e3b72994b", - "signature_priv": "33fb86780c17190821f0877985a9fefcec9f2b05811d14a093fa3074a3cb86bb4fc803289743c1893b204170c174c9a1", - "signature_pub": "04d8c3e11dc2a99eabe55433e77b26635533e75594bbdbe249be5f3ab238273f4540a238b9ff7ab4f74b54fca19c2c287fa48dc11b725efe05bf17b95050a14873b0af5d441bc715273b30164a0a661bce7d8aa77b5aa184e118b359cd19e98271", - "encryption_secret": "45ddf20911304fdb57181e5a418bd2b1b49b3255dc8b696f81293ad39f816a40f01d2fef0e4dcd40affe07b6103d6bc4", - "sender_data_secret": "a7d1d4c28736fcfd37a99bf087b1fbbe369165022e89160b7bffc61c311435ccb87e1e8340b1b320764088f249a114f4", - "membership_key": "cc8ecff0fbfe3b066c215bdf2319eb434b1611d1fecdc695590f690b774b23d59088f8947f21382408ca9efdc3fb92af", + "tree_hash": "801301618b8e2a4ed32c36f81eb44b98545031c9c2ca22c03248e2ab0afbf91bc9af6009ccc22aba3b4bbd2cbdfce690", + "confirmed_transcript_hash": "190d1fa607662cbc066fd0179b5c9c6c6c041c97b7b55fe91c3b8e0161c2e7e6221cc6af243700cf9a51f224b4cc18f5", + "signature_priv": "5525caacfdf8bf46c469f4293df21df31829cb9953e5e8c6e8bbb769ee5f56763fcfced38b361dc033346b3e2217464e", + "signature_pub": "048a843a13a21a73e7c27b679ccb90d18f36af84cf442aa389c25f13f235b44e7db52e3f02a2359b7502a5f3db2ac28bee8c56419b7d65648364a60971616b5bb48b958c2b89ffe4609af62d51c6531766f216c4ab518e0adc096cd2f27b332016", + "encryption_secret": "17c352d0dc64886b206c3bf6c9337846cbeba5a49d6c41ddcd35e9cf23c6bd3cc71893b825c84ed2ef0a47c7aee92309", + "sender_data_secret": "db07bb890083a19e4bfa008d5bd9b0b4c299669648bc75af7a9ce0f66e7efe5ebb61ddb4f19db1119f0b7b4f2373c154", + "membership_key": "36cd667f286c76823186a16b37a6b37eb7152ec49a8fec866627f00fbc2d003560643f4d458dbbee4693c378fd538d54", "proposal": "000300000002", - "proposal_priv": "0001000230594c2e7a7855dd32f1298d9aae5f8401a6497e3f2118cfa7f67d8415c29615b6a3c5eb3b3b3fa65ed99413b6d12fed2c000000000012121202001c23b26180fa5c4012d4fdbce3f5072956725aaeffb581f01bbb0d1a56407fb98ec73531c7d8fa7e2f4d276e25367a25f2ff7246d165bcd671f16a02805f609859da1656f5845e2adc98071306010b0ba853b8c841081cdf1cebd4c3acb5c2a3a91fef07416262e9d042840736401bff8a652a2b5e06b07db94954b30e1921d2104a4bfc56cb60cf14c6f8ebfbb3b05cfae550ad45bf10f6db70ab1327d9", - "proposal_pub": "0001000130594c2e7a7855dd32f1298d9aae5f8401a6497e3f2118cfa7f67d8415c29615b6a3c5eb3b3b3fa65ed99413b6d12fed2c00000000001212120100000001000200030000000240683066023100dbe072c4d40843ec0401d82fb103a7267c55df4595310bac787c3eada933624efe40f9eca9e977382d348a26fcc46809023100902c01eef87cc8989b2529209c4fda881b96f9fcb6087f35fa9637da499a0bd120a6f2cb12d18491cb6d5da78b13582a30a0e562b16777201ee0da9c0bc93c08300bcc107178af5c0fb1f8af53c45cacade5be05228def351b136574cd80d1fa05", - "commit": "040100070000", - "commit_priv": "0001000230594c2e7a7855dd32f1298d9aae5f8401a6497e3f2118cfa7f67d8415c29615b6a3c5eb3b3b3fa65ed99413b6d12fed2c000000000012121203001c266c147f27da1d27f952e727af243a2e13dd18aebf2a6e70bae8d44540b0cbc2cf8bd31a50f092ac269427617668ebdbc890ecd89ad9af631a00ce0e18a9613308ecdc9709603e73097666fb505c549ce5f6679f3882692053c0920b35699ed10d17c206af6fe467c23f6b0abb2dd6bc70e05255c707646bd0d5fb795943da2915502e3d280072fcf4ca4a5a79e94f7b293e344745bfd04c0c796339dd7b678252d20d6623f7ec1005ac0dd9a768339d6b4b2ceeb73991fbaae86f4de150bd8b1205933a8505370eebb4af96f52c", - "commit_pub": "0001000130594c2e7a7855dd32f1298d9aae5f8401a6497e3f2118cfa7f67d8415c29615b6a3c5eb3b3b3fa65ed99413b6d12fed2c0000000000121212010000000100030401000700004066306402307c6b32e79e01313d2caf597090569499ccc8946c8ed97b3ef4660ad2a632f909651e21bbb6e040bba2e57b27a5f12a5f02305e33196301604a1b0be4dab3347c63bacab049249f07e515fc649a517437e7656c1c52edf0458303e22b1f12d838bf9f306c1f2ee938fad2e24bd91298474382ca218c75db3d83e114b3d4367776d14d3551289e75e8209cd4b792302840234adc303aeeb2362c0581972ce8c59af15fe3cbe8422243cca69dd9b594616c9695c91647c00359a1042b9dc3dae2f2a38641b8", - "application": "a1c699770328b8039914bfc2ed6350b4b2ab191de52d9a83e33160b5f72eb9149973edf78e723222af36", - "application_priv": "0001000230594c2e7a7855dd32f1298d9aae5f8401a6497e3f2118cfa7f67d8415c29615b6a3c5eb3b3b3fa65ed99413b6d12fed2c000000000012121201001c88034a51f5bf020db37c81f5d3499a61c6aca5476324903a39559ce140a484232ae71d99385ec86f3857f476a760e52d9c428d0103a8a44a2c867edbeb609e18ed9f4f684a789be8f3969ccca3bef642e09e76e1baf747b415d809f084c571bbcfe8f134f5e3108a448766f8c56ea6ce1f82b5c92c9b1f5ec6813b4621c01caf7f96b2c13d95a4ad0303daf321216ab7ee9c488da006c9e05a21b22ef78a8c5919e5313e907c7a56c665d9e1cb4b1a48c1e8d37f84098e1aca83aaab7068f0602c95" + "proposal_priv": "000100023080f486b08d22f42dc899d0d09ded745238129fefed0587d946af22dfb0dbe0cfff07b324ca059d50ae638c5cb0a7d0e9000000000012121202001cd98d935033c4b5f6400870e8f6fe09413257e394f19397866c061f3b4080112bbb973a6249f98b8323bafcf77de4881f98daa292d570991ab54e4872de2a050058443c99c3fbf094d8292c79314760220f185e1e547bd62dbf3e7b477b423d37e2dc6ff6300d4b596bc6e2f47564811d247a00ca2c50f72f2fbfae248129c9223b2c9b95e78eb8e95a96fef020013cbcbd3f64231c9f02861b8d21e9b4df", + "proposal_pub": "000100013080f486b08d22f42dc899d0d09ded745238129fefed0587d946af22dfb0dbe0cfff07b324ca059d50ae638c5cb0a7d0e9000000000012121201000000010002000300000002406730650230211bf540e4de0bd6aee90f1156b80b7361c3b5fc9ba34d5a2a5ff239f0fdf948c73d08067ba4d49c1ca33d516b8aa5fc02310088429f2eab811854d5807b3bab4d507043e795d419f1150a273b9f234c118f588cfd13aaf735792f828ac6b861742b7f303b83c1d8fff040e4d767291ae7a0fa3f8e94272cf4fcf25045c9bf0167291cb2c938706462a1e5ed49c8a9d7503cdd92", + "commit": "406601000401308925ac0a958285db9dcac4b8c483c9b4f251de6fef730371a19ab42cce445cdf6e0de9873809dc165f08d04be63f4ecb30e46aafa71f31d343d994b7106a30575740b10687463328f3a11eecd8ef9d8b67140cabbba7b65d4b0d31a6c60885e16600", + "commit_priv": "000100023080f486b08d22f42dc899d0d09ded745238129fefed0587d946af22dfb0dbe0cfff07b324ca059d50ae638c5cb0a7d0e9000000000012121203001cbdc24924e0700952380843c3a9c0bd6b23c482352a9a9453ec324ec34114122b3b892f9c70cc75c3d17e1712ceaf9d84fc6ad6d7c00d3776451764e413250b831b8524e7ef0b7c156028e79d7e2eda2a9f2cfa3c8b78e4deb0c7db466a8fb737ece544af4431c6973f47f18e11a1e3c7c6bcf24ccf88a3f1550b8fd595f8cc74c82539dfd0e8a703d031ac36c28fe7b116fe5e6aa994828bcda1f62beabcc30b78694ed8f9eda7d1b26ca31dbe72a30a2fb2f363cd9ca9246f55c93a19ebf0af179e8d28c735d3f81fa43165685f1996912caa81fa842a5595e2d363fdab1c715889f519169b4bdf6cbc6281158403153ca30e12dced70e66d02933c850c60aed47c844905ec3e99c69532e4cdea2ca7be073d82350c7511a887a374f31c611248983dc31bd4609c2a0b7ff1b6590288a0f7", + "commit_pub": "000100013080f486b08d22f42dc899d0d09ded745238129fefed0587d946af22dfb0dbe0cfff07b324ca059d50ae638c5cb0a7d0e9000000000012121201000000010003406601000401308925ac0a958285db9dcac4b8c483c9b4f251de6fef730371a19ab42cce445cdf6e0de9873809dc165f08d04be63f4ecb30e46aafa71f31d343d994b7106a30575740b10687463328f3a11eecd8ef9d8b67140cabbba7b65d4b0d31a6c60885e1660040673065023100b3e904ef38842006c2bcd842b96e19b8b9555acf9bcc41e66e25badc254d8bc14c8d5e6f4a5ec83e5ac2bbf84345e26b02303b844194e8d6a8ef1562de33e6a2c0eeb2074a77c6e94b2d0b8ad42d8673dcbe8b1417144fdb89b6b0b19860bc00d446306c1f2ee938fad2e24bd91298474382ca218c75db3d83e114b3d4367776d14d3551289e75e8209cd4b792302840234adc305dc48f171627f5d1eb1fe873c165679348110a8415ed00d774b86db84f62ad01b2229e6bcb7da0580003d27ba5fefb34", + "application": "334360921f1209bd93357c184cd257de0d4799fbeb53a1c6f963cb0f40af432d4c17114a8d0c6f474e69", + "application_priv": "000100023080f486b08d22f42dc899d0d09ded745238129fefed0587d946af22dfb0dbe0cfff07b324ca059d50ae638c5cb0a7d0e9000000000012121201001cd0223b73a6e92917fc3ac4d99f24a4d34070cf75d643e4ffbcb03cfa40a349baf2b5a257f6945cfc63c7c15b7e0de6302a7c707c41337e584bd2e72d61162d529eb92877cd6aae0b365f835254f25a0670be8b50a89bdda359151dfe9b9f059642e9b0951a3c90083735191d75f28df629e8e71386722c0c3756bd53c2d9706601eb8a4889d7ce79ca3bd17583ef80bc000313563f3b51ec2d2e57c9f19915fc710ddb02eccb9479c2d52c8579d009c130e30a83dc959766a3837c38541b9629dc" } -] +] \ No newline at end of file diff --git a/openmls/test_vectors/psk_secret.json b/openmls/test_vectors/psk_secret.json index 7d17033523..4e16be3276 100644 --- a/openmls/test_vectors/psk_secret.json +++ b/openmls/test_vectors/psk_secret.json @@ -1,4 +1,9 @@ [ + { + "cipher_suite": 1, + "psks": [], + "psk_secret": "0000000000000000000000000000000000000000000000000000000000000000" + }, { "cipher_suite": 1, "psks": [ @@ -334,6 +339,11 @@ ], "psk_secret": "55cc191e0d3a186eff36f73c343e6d0b9e33a66296ffd29acda8896b98ea974a" }, + { + "cipher_suite": 2, + "psks": [], + "psk_secret": "0000000000000000000000000000000000000000000000000000000000000000" + }, { "cipher_suite": 2, "psks": [ @@ -669,6 +679,11 @@ ], "psk_secret": "1d51c729bea246d460fb24fd9437e55d61fbb828a4ad51beef5df116d71d12b0" }, + { + "cipher_suite": 3, + "psks": [], + "psk_secret": "0000000000000000000000000000000000000000000000000000000000000000" + }, { "cipher_suite": 3, "psks": [ @@ -1004,6 +1019,11 @@ ], "psk_secret": "1c96a8f7010c9860bd628da9128c93c1820dbea4dcc7090a5782f8e3aee8ee8b" }, + { + "cipher_suite": 4, + "psks": [], + "psk_secret": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, { "cipher_suite": 4, "psks": [ @@ -1339,6 +1359,11 @@ ], "psk_secret": "d8b543df7d0028beb24a54f9ca9be9b58e1b9dd5c362b16378574c377b89bfd838f16e71142f8bfbc6e1bee02f2ea4a612c7051a01d28f840d48adc802796299" }, + { + "cipher_suite": 5, + "psks": [], + "psk_secret": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, { "cipher_suite": 5, "psks": [ @@ -1674,6 +1699,11 @@ ], "psk_secret": "bad37129b9b76990b360771f96b7c4139e249beef34e26d0cb748058b08224a9263075b9573c0bf001b25ffd7f827ebbc0bb9adb7d820d0c690bb38cbcce51a3" }, + { + "cipher_suite": 6, + "psks": [], + "psk_secret": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, { "cipher_suite": 6, "psks": [ @@ -2009,6 +2039,11 @@ ], "psk_secret": "1810f7160d5df535d026fdde8d90d853d3147a001f7f1d0d06c2a1d8d208bae8b270f07ff57276f51c089e4e5c95e1dc30eb43126c144eec73866edc5d669aa5" }, + { + "cipher_suite": 7, + "psks": [], + "psk_secret": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, { "cipher_suite": 7, "psks": [ diff --git a/openmls/test_vectors/transcript-hashes.json b/openmls/test_vectors/transcript-hashes.json index d62194dbc4..a1512433e7 100644 --- a/openmls/test_vectors/transcript-hashes.json +++ b/openmls/test_vectors/transcript-hashes.json @@ -1,2402 +1,58 @@ [ { "cipher_suite": 1, - "confirmation_key": "7d6929bdcb9c7f4820103c4ebabb9879c790f91eac6d514a5ec2670fec8270fa", - "authenticated_content": "0001100bf49c67eae4a61e4b35c946a953608bd3e9a6e0fb8e5b1c01000000073067188d11f7919c93dc55d3a9730908e8c0f6962dce59a4c72274f95dc6eefbcd20b46d14093b3d09c7bc9e75375a7850030000404020348655084d039c2e38bdd2d3db015f41abcaa110765f84b20a3d4ec0a2ea27cce4e994053cccbd6864f555e06a3559e32c680ef2bc16946137f6357adc230720cd208fc4e8846e3ea03bbc082e61212f37d22de8ebba9666be511fd55bff0dd5", - "interim_transcript_hash_before": "f61280c7398f09cffb7b3251afd7f69bdafc0e57b5b3f91337c6dbc8ec34f61c", - "confirmed_transcript_hash_after": "dc5beb0e4c0a440038e54412074066eb9d3405e877877eeb64c6a901824cdf57", - "interim_transcript_hash_after": "7e5c11e017a0a80d374ff963ef4abe060e120d4abb364a17f1952504995d4045" - }, - { - "cipher_suite": 1, - "confirmation_key": "9176989a81695a0dcc099ffaf08f7954fba80cf0ff47883ba2cbe0b68ad2b549", - "authenticated_content": "00011016ef66ce212e200f1f6dc3a4dba11f2659fcd4164d0ce0c8010000000730766e10c03cdb1467cfeb6bba8d7aa2e9edcfe83105ca14519f059704b0d40bfeb77b1ae5f67f6a807d6dc6c3cb0bd66a03000040400b4527cd5504ac209f5a19ec04150adc627ff232a594ffefd8ee25ea3be4f9ec9e0547403faac8631ef5299f7f7a8331d157c60da238f02cffb5a5da9f4b660720cf21e5d22877e4a3fb8f9c88a86af0100b614f29acd39e7a420d970665d30257", - "interim_transcript_hash_before": "467cad81a8d94f582950c9643b6a42b183b5cb15b3a670b24fbd038384f02367", - "confirmed_transcript_hash_after": "a39453ef0e7dc459ac6c863436997507271eabc9230df5c02ecce72b89db3042", - "interim_transcript_hash_after": "96732c8679b3cc12ce38e44fbfd4818ee0fc13497345fcef2d1409f687b0fc2d" - }, - { - "cipher_suite": 1, - "confirmation_key": "a8bc5bf1bb433cf176a8456312b10f4daf32dc4186ef9e40ec98cf2a48b3b326", - "authenticated_content": "00011051cb7b114ca54528fd2f4f46c50a1fa6084f938d19517f8f010000000730e21a6f97a322661d4d5fe38bdf545aa4dac86716bab8b139b14320560d7a2282b2ef2570ce9a58a21731513f15fabb490300004040760c8886eb352690f923e9c0febac6532dfc8b3addf890e1c1b451ca3da1d1a7268c62e0c532e0e0052811b76b38f52745ff2a772b5188e422c0de742a419d08208518ce380ca060a676347b3108dffed0896186fdbb6b9e46baf6dd934cac8131", - "interim_transcript_hash_before": "343c338ad2c2ca9ace944db4807efdff9fcf649465a85511c2334e6bb5cf55f7", - "confirmed_transcript_hash_after": "5a263bdbcee4299615a85aab6d519a9e79e027e8c7dc5ad329a5944023f6f984", - "interim_transcript_hash_after": "5e9f6224fab6d1eca77859ef884bdbe1842741e0145bbf7b88fee8b33eb1cdf1" - }, - { - "cipher_suite": 1, - "confirmation_key": "e98a4127a3b3e23852dcf1705f8f0113275a2c49b153d465cf8da0075d13006c", - "authenticated_content": "000110accdda5f4519f00dbf81ebfb589b4bf7275e7ad00802cd4f0100000007305408e50918534f1264bcbc63858e73a62549548bfa48bda8eeb82b3a794f24cc6ddefe591f330efa2090c492f25186d70300004040be54ec088991947d18c772afd3aa338a1b29e7e89c38b33eeeeec31a2177f020b0ff506c9454bba9b095c132caa0c974adaf2e528fe4751de4ad7e01859ac60020fc3de17d5564b745942921abaf1d57ed3ade894fa64d4d342ccf67ef431506bd", - "interim_transcript_hash_before": "dc509b31b95516cc3f558ca7c4e84b014b0a63580fc5194338d3b80d542c0e36", - "confirmed_transcript_hash_after": "74638eb4a9f0241fe7dee39177f08027dc8ebdde9e044a9f9ef23b79e77d53bd", - "interim_transcript_hash_after": "655b8bba503e510004f1bffe80637fada015ab745fd1c3255d1c297638eaaeef" - }, - { - "cipher_suite": 1, - "confirmation_key": "645a9ac313d6a389dac3396f57b64201e3d54360cb282fc5c3afaac6ce09c057", - "authenticated_content": "0001103e27c8439e1bf4f76060d3a5e3c42db574f76e4c47da9a8801000000073000a13ec66cd7a967a426b9e65f7be8376281c4686f471c2da3386716fb8d1b86a7827487186ff92171acb7cdbfa877d303000040406b0a96112e15f73e76838c52d437fe0867ccf5d60bc423a8cd2c6cd2f7012cbcfc5bc0edfa31709183fca1763980552b7d073523ffbfbc388c63d767d88e3403206ad883222c01754c8e0f73beb74aed260cda0aa924914af26f15101413ab912b", - "interim_transcript_hash_before": "41a2cc3c9cd0c51b76ac118270db5ffe9f37d88fa5bfba4edf8813c5315e88cc", - "confirmed_transcript_hash_after": "2d0afb777634a35566de48ddb96a81cec35900910e991a5577289333e172d584", - "interim_transcript_hash_after": "dac0fae2bcaaf46ef1bd342f9e83e315ad23530619b318d8cd04b7711eefdfc9" - }, - { - "cipher_suite": 1, - "confirmation_key": "208e329992b680db806e1596e04462ac404f1bdd29d3e116cdbdd7949b7fbcaa", - "authenticated_content": "000110b699d65c8273e20c6fe553447dc2ef493330c8c77822cd80010000000730a15b0004c3407d9ebc80e9773269e57486721731057fe620feb7ad2574c223bae5f1d4b2deddb7b6c84f6bfc609019530300004040cf040ba6f7b6cbe29012b0af818bd161de1bf4a08dbd651da6d5296135e5cdfb48714ab2b07eafffe3b958ebba4f34ea104708d95352c99e0e269b8d7715e10d202c57ba2f760336bc7ff40d2bbfc9debefb3ff33240f2b7abcf6d50a689cff7e9", - "interim_transcript_hash_before": "83ccd365c1fa216ea8f8db22f5bd1883b9a1fac01f6eb1bf2ef48e79363a23e0", - "confirmed_transcript_hash_after": "b7afe77926ae97ee31c19e8ea7beb39e3c5c294d3346abde857eaa1fcccaed30", - "interim_transcript_hash_after": "82d366105163cc1e492951ccd9bfd20510a0550da0940493ba3f0412f8acd981" - }, - { - "cipher_suite": 1, - "confirmation_key": "91b0a7400f378e02e030d555ce73e9ce69aaffdcaf884c030a47369aeada86b6", - "authenticated_content": "0001109b9b6319f5c3003f233b63d3aeba8932bac3038f73e114d0010000000730f1362e7c3914931a3e6b3090ffd2260add18e5e93bbd1a7a547ab40e9e11c29ed15dd31ffc130e1b1380692e1d4dfb9d030000404035a3eacd18f9a0deed258e7578dc0139846066642ca51e38d40d0c64f4ba348f08405024f8a9f1367ad02878081c006caf988751aaf94eaffe1b64c3521d8505205efca17b3f80a18b353b6fefe503f31966a305ff80787849367d31dc63801d25", - "interim_transcript_hash_before": "19b2add76fe4bf9c81d785ed580fa0d68185d266adefd9571cad094c2f9ebfc0", - "confirmed_transcript_hash_after": "c96131cd11294e30a5f2db0083d473831bdd26ba9c18f7d7877ed1c05fb50431", - "interim_transcript_hash_after": "1a68bd53b1e939ec9bd210cf31ab47baabc855641b8bdbb9aeb956b5f29e0be1" - }, - { - "cipher_suite": 1, - "confirmation_key": "5eac69716ff224d2f7110bab4729735084928ca0cdc6fd34a4f1fa44c72efe73", - "authenticated_content": "00011010d6062783934ca93f7cdb35813edc15fff7eb6bdd3368ca010000000730aed4b89cbcc17e0c41488edd8d4ab56f12e26b7b5ac14fe169d77ac4f30446748eff5e5788a7d0e89b4b0d4ef0b24de10300004040dfb4e43c12cdc8075eadbb034c343690f3131b28fac177b1d336e3ba373fc0e1bc51dd7c1bcf25726ec957609c2b194d48021288b20a399fdf96d2f4b77d33002060f16f83ee0fe95aad39c404f082915c1ea2c036e00b68bcefc9ea6ffd82542d", - "interim_transcript_hash_before": "91a4e2d3cd0f1da124d73e9676c20a2839700dcb32e9f3c2f89934be54f10d38", - "confirmed_transcript_hash_after": "b2fa06f0a8dc12b407263dd97999c78ac358db932867f46226a2196a6aa31f17", - "interim_transcript_hash_after": "cbabd22500e1369e0bcc7ca5ada1d5d6f217bca648dd28c6efd97b9bbc24d07a" - }, - { - "cipher_suite": 1, - "confirmation_key": "00fc499011ede07327db08ac4b1ec3afd46241aa89a4044a923bc1b62c55add7", - "authenticated_content": "000110d6b222507c3624ab5857eabf2c590f57983dafe99de511da010000000730f5a9c85a77aac3ad65c318aa8bbe44261b071a58226e9b1f059bdbf3ce19a759c88390f2d81e5581b84eb5186514b7a90300004040924baed55eab1da39e6b1509150e56e485b51c07e96eb7c625f4281f42d2a6d8a10378e3a08083688cc76da32eb695e8d01fd9f34b66ee86f7ee5204a4ed280e20f3e64989601ecc1153dd6aa264986b94878e53d2f6c4f89af6a8817cf8059deb", - "interim_transcript_hash_before": "87783e37fb56510146df4ca5538504b21bcb43fe0591bf197431e42002a8548d", - "confirmed_transcript_hash_after": "90a8ef1435fa7c2cb237341cae23dc33c13ed4f801e4fd2ac2ebbbadf0c2a44d", - "interim_transcript_hash_after": "97a039e110836ae9a96dd47b980cb331cfb3f8c675dc59554c8481c2c0d4c9fb" - }, - { - "cipher_suite": 1, - "confirmation_key": "d1d8b8b5336cc1eade0edaf4cf29d7af3eefdf8c13e4e75ee1272696c85017db", - "authenticated_content": "000110878fb792476e30cc2509f1c7a26aebc6e5b3c898fe9cc4fd010000000730cb4c9b3ef2516c440a60ae4b1698c9f5efff91699125ea845f064343885a1e1b2ff21facd507bc3dd36a1c349cc71e3d03000040401394a8156ce06a113fc3ae5b7dbe17b8bed7806ff0723d5378cbe271274ab333f67b1965ddbdf0d8fac3c0e6c29fae8a1936bc54fb9f3495cb09edf3c24d9d0d2050e52f10fff04ec9b7d0642d7ea6d56c3a202150b0bcac6f74389ce4ba29f8df", - "interim_transcript_hash_before": "bf78eae0636496a87a013b5d8c8937bc938cb6714a6451540635ddf108512a98", - "confirmed_transcript_hash_after": "6aa056179fbfcfbcff0f9b54634af5ab4ee0ce6a3e21516b840b51dce4f036ea", - "interim_transcript_hash_after": "8cc7610fd270d532e85d2a99520b368db136ebe25dc5628092c3ad623dc687b9" - }, - { - "cipher_suite": 1, - "confirmation_key": "e66256e215718487c1cee18322fbab389b3f984f208811ba9ae3412283744f3d", - "authenticated_content": "0001106b25bab44e52fe0bef1daa04bbf53da34f6f119fc02fbe1d01000000073007ac8bd42f936aec027b259daa5ea7ef805a79d843937d1dacc00f40e8d8441efeef6482b81a28f8d38a7a9f232fd3390300004040ac21a9d5d873d9ab1f835d62be664b37b9cb5a83b3d649646a863eda7b1e5c61c16f66823d9686c945c18c78e77f527edc3bc36195a69ca6d3238c41fb401e0a201fe50f5272146fe49537eb45d5e3056e216f8e574db78d12395e25a075652b74", - "interim_transcript_hash_before": "86bc36d40352e1e9d3431b2786b571abd24f4d607019e1b123ead7c1832ab2ae", - "confirmed_transcript_hash_after": "ad78f8f9c2e250f4bc1a3a05b74d6f9e962355c8aca626e584c2785a5421a084", - "interim_transcript_hash_after": "a154ac1e0ee30eebbd3c7fd2efbc3ca0730693efd0c7ea3b43aecbfa35bca2ab" - }, - { - "cipher_suite": 1, - "confirmation_key": "8a4eddfbcb120afea83670d962087cc0175e42476925bd5bc34dccc632a32718", - "authenticated_content": "00011076898b183b44b48976437b57c460cb95dcf785db549281080100000007303116506ee6853c54fbcd1d82713dfdb09c5ed4a4278f8ecd8497dbe5246a7161fd8c13086d7d493864a3e5a8296b5dc503000040409e155665caefe0fd27909d64fa7ebf711360c6a9d3e84c3f9780837fb09d2de8fd013bb805492b3734403eafc996f128f16ad163079604756bca2ac5f129e60f205dad6bc1c6d5e86de69abb3802439a2f233a35b5ccb546f3820daac5733db16d", - "interim_transcript_hash_before": "7b25e059f80402e1dc93acf08e02074052bbb7084d4bed6795b2610781461196", - "confirmed_transcript_hash_after": "f903121e1e9401d705b6dfa325aaeeeea71c9f52ddac866f298cc0061cee9b63", - "interim_transcript_hash_after": "acabab9ff8e8e1a657b306d2c47daa3ab761e51ae1a213dc15a1166e047222af" - }, - { - "cipher_suite": 1, - "confirmation_key": "675e942aa2090daa3c0b341386dd0439190bea1f6ae6f3e5eb8acd54d5eb25f4", - "authenticated_content": "0001104936b9439446488454ccf24b560aa02d8b00f6715f8effa7010000000730a4e45373e9d07ab62cdbb94d609d4573d094a7b7e0dc87e963dcbcfdad2c15df41cbdeb7c353a75bd2fa0780938dad6c0300004040a81f22e1da7568adff8b13f05f611ad84a78f950c0a5d782dcd1d9e0c8853fc6d387915f244214e4e545ab2373f5832fe2cd799be11691a4b7e50f7dbedd94032094d7c86af28b0ff4034d8f00ba5056b82c60b22beda6f3a45fa21882f9f2cd71", - "interim_transcript_hash_before": "82b74a5ec63bb288695c8faeed93f8a08444ef2af779296b831564b007c91629", - "confirmed_transcript_hash_after": "e51c503e1225ccdde2f74e596c5b021fec64591acf2eb7ceab39bfc8068f675c", - "interim_transcript_hash_after": "a893bb20b468de80026ac0beeaecab59523e41b58def74225269e85601a08b30" - }, - { - "cipher_suite": 1, - "confirmation_key": "5a23e5a92fe44a480a117754c0d5078f3effa00a62bccd2b13ae2b94ee395adb", - "authenticated_content": "000110a8120e86eb2a25391f031fcc282f3126be62bfc0ae45de9b010000000730963995fb27b38d11bc04d95aaac8ee5efffd2b508f8a21979955b009843ff3578f2f78ee00a2946979efd45052e0a0d40300004040c2e1d8c04a57b8771df74c00839b5d93a3a88cdcc713855557def4c836d5ab5fb72bf8202a02df4159cc95d946676f41140929313b06cc55c4531fc22ddaa50320aa00c48fc235cc02073d816656815467cde36e21261b89072af49e9568e49cfe", - "interim_transcript_hash_before": "bc61d5eb49d57e7c29190805637451fd145e66068ac7da76f1cb6d8fe67f9fd7", - "confirmed_transcript_hash_after": "69f4bd6d602d2b0a66a11395242319a21239f012772b0129817238df3b3fd9e4", - "interim_transcript_hash_after": "ec3a42bec3ad4ac61399d703daff3a9d5c31584ff49ebe87335c89c5d3163475" - }, - { - "cipher_suite": 1, - "confirmation_key": "c8cea0d4b6b0d843f17c08ea07d78ba25aec9296b65bb1f737bc4e7d7b2bf486", - "authenticated_content": "000110a1857fe3f5b5e83f6624b2f1e1f61417e143ac95526c205b010000000730725d40e514edab571b56dc9b01e92805395c27d28eebb00e08158ef0ff8b963e090f41438d90d828f6ffc7a56aaa986a03000040407d37c16ce822daf33be8cc0e2cc19df4788174e352ff15f4a217b1e01dad9eed4f605db68f2dee178956b793668bd107d3c8d8bc2942fa9f24ba447e1a4e3a0e20af18f85e051fc68a268acc4911fc32d756b2ce812d3a8ebad998862f41e753b8", - "interim_transcript_hash_before": "d2ee60dfb5ddf09b2aa21bd18c554a957b63ed16c4133053bb55e074b9bc0dcb", - "confirmed_transcript_hash_after": "077b76a68f0c5d55b0bc984219412d9f9d904b9f88af2d2722614ac3026f2563", - "interim_transcript_hash_after": "7e4ae1c965bbf245d65de8b5100a9c87d5960b614e60db37e0d5211e3ba56b03" - }, - { - "cipher_suite": 1, - "confirmation_key": "b1d610777a6e33978c61c776030fa87d5d2d7d177effc178ac29b790068f1eb8", - "authenticated_content": "000110b3f18b667e9384e933278f3bd200e6c48fe8b08c3d4d2405010000000730f8c291f81b23f48d894789a246e0b2c9cd6593bdcadc62115725602647ff3552342aa675ec9a9665adc0f836ce2f10c003000040400c82d3b2f4998f57777d0f66a605e4a374d3b4432c75a024011335a90f7c9286aaa877cf5c0ffc2a13bbab5fa1c7cbae3a99a0edbf60e7d1638849a3944dc40720d2ce147e8a7bb37c39cee8d7815d4f53b2f5ed8c1e82026196fa0a160738a0a4", - "interim_transcript_hash_before": "599916aaadfc0b201374cf93aacb8bf5f644d1d914a31056915cd256927faeb8", - "confirmed_transcript_hash_after": "fa1cd8dcc04a2076e3b718da31f1700a9c43b6822437eed678afd0e92c6e2e75", - "interim_transcript_hash_after": "8f2fd8e2cd4c43f4eb4d4bb42889a354668491f74164ed776cc00c7d0c77cd2d" - }, - { - "cipher_suite": 1, - "confirmation_key": "22df5128bf264762a5e1a756f5ce2d087351831822f2c9f48b381d13f1a2bcb4", - "authenticated_content": "0001100c569a3e710bd4495a33728b7ee4535e04fb9b46e087127c0100000007306e12f9765082169752551d09bdee50644e9828fb2a9bdd406b67682f8c539126f0182416badfa20202cf13f2301253d0030000404055108fe626dde19bd67e1cb23878ff3229bba379312c403de47f62c3e4ada9f4e68ada5af4b885f7ac506eefb9359832e801f798cb45d8c5c76384a228a4650520b98053297e1d3736fde67bca09d84a0807b9530c4a58a078e8ebc1c57f742a74", - "interim_transcript_hash_before": "d6987b5e29700f17b23d3456e76126a3aca6bc75b72e257e4171272303a7fa63", - "confirmed_transcript_hash_after": "031ab4ff45520d3ad73d8469f648c2d7c1f04d3d5d2883e53c66c04280ecf0ab", - "interim_transcript_hash_after": "3d78dc18387f823525fd70503b43c7254838f25b5ce0b1adecc2f901e9aa9fdb" - }, - { - "cipher_suite": 1, - "confirmation_key": "f7751e6a007a9faab0de25466be6b884ebf0110aaab959eaa4e03d396c7725ae", - "authenticated_content": "0001100f6095d44dc868f9b45e5ee6249f75f70fe27e1a2d918fa5010000000730618ce94245f7b8b21d99943190d63e659bf9c6dda54652da9bb6c1446395e44e5cbd8b5739d00ec5775c624eae9a9a610300004040ca1483c2985d81447338c86306b71306cf54ef79113c1faaba188c5e2b6d9a64e55cdadf0c5c86f36d8116bd26c4e4b6bdbc21889011c5b4393f594a8549420920a9da64b1947a87053b952a2b6f50ea9b9ce885857f3e1fe54fced6823fcb1466", - "interim_transcript_hash_before": "3e77744165653ad01d830e06c6b6e3d66aec2d00f721e539e03083fb4632a9d0", - "confirmed_transcript_hash_after": "835deb3fea8878b6ae742964f8e8568f42bddb8144fb2f6de967a9a1b2af1cd9", - "interim_transcript_hash_after": "c2ed007c73addb239417c817ca6ec59c4d3b34d709887b340472fc11d9f11733" - }, - { - "cipher_suite": 1, - "confirmation_key": "12195612725629e3196d7385eccde56b2979353abdde877ee24fd2321316032b", - "authenticated_content": "000110893b501ac6b1b7c0f16991368953ef5518d18fed4aa84c57010000000730e7e0d1ee619c4e34537346063557be43d9b2d72824c6f940ddb0cd5511874cc981a91e741b10667ce9634d15e480e4e8030000404075a4061a3188937c54db92c0c7f13d0a22f592db462f97f9d409df1b080708ecc63dac2444856259a7f2092bd7c6f109c543ce00ae03bda598671c98c562520120044ca497210239f6a1ed30cceb2f0e59d0ea3e373aa1819dea22a69ac7bcf5f3", - "interim_transcript_hash_before": "960d2fbc651b79d39e0d0902c0ec5634e6189a1a1f96f139fa3483cf721b1c94", - "confirmed_transcript_hash_after": "d0a16a244d4a03f972ac644058c78eafd3d429e94638945c6c88958dddc172bb", - "interim_transcript_hash_after": "f9b36dfa89c83193bb5c890a4a40ee826d44b722dc5f34bd23c141152cf0183a" - }, - { - "cipher_suite": 1, - "confirmation_key": "8bfa3089dba93f613a46706abae47bc107066b582d71d02824406998566127b6", - "authenticated_content": "000110cb38956c685100cae7a0d05794522e681ebc69a807045a8c01000000073075f5f69637b6cd4dec0a0734a28e3b1c43398a04fffcdf020b28503a7b33493ec7420248d7daaefc797891f39559ff800300004040c1cddab2afac7dbcbcd58731628277168dff349a25da1dd72f8c71b9156f51116ce0cdbeae1c393c12e4b7be229eb07373c574edb92147a22d82ae76f101050b20e820c7c2fb56c988835a4b078b33573ce5cb20a7436fb3f716dd053550fc2171", - "interim_transcript_hash_before": "67a2afe21fbddda5fb1dbc399436cf91473a0ece0c45a407d36da09c3a53d87b", - "confirmed_transcript_hash_after": "936bb92343e409dd498534d381b7d406e53d42e2da9f3b66cb6c672f3a7311db", - "interim_transcript_hash_after": "c98e1cd86dcce1395470704288f700849641d5ca6bbcf2450e15de44cfbfe1ab" - }, - { - "cipher_suite": 1, - "confirmation_key": "20dbef12588c20ffdd28cf2e47d7a146d6b6399ca8a0414639163d55edb6021d", - "authenticated_content": "0001105a31a043f6f307e6a4469d630c41e5b278ae7599d3cf9477010000000730085d78a193ced1b3b0eb5954401627437904777d39ab84c63054f2907bdebeacab60b93f79b6f8e2e0fcc53a44904c2003000040407cd27adbbf3831280b4a5482570f87e0d2ce69837047d00d182f7ef12e654f4ff49c09d267f1962b54d27a90d09e7c47015f70623fcb877fa6b396d7daa7e606208694e6cc5341916bc9c19afc4af3d0b9dd78adce0d6bf26612e55d2f262db3bf", - "interim_transcript_hash_before": "23cd902ce263e4c8c3aecb4eaddadd8374da2a304dcfa9c4aa341c762df35b9e", - "confirmed_transcript_hash_after": "2ff8ea6b904bfa3698024e58b240ed11c209f87b85be244056f81c23bc59e905", - "interim_transcript_hash_after": "509a93896165f4621027feaa5d561c04ab90590fdca6d0960855e9b7791ac10b" - }, - { - "cipher_suite": 1, - "confirmation_key": "41c7785247871cfb1b633b323a243e5e7a9a139f218a268b8503d99c04c9893d", - "authenticated_content": "0001101ae3e600da7cbd2a1060010f1467a20661124ae43c80badf010000000730166379a60d1ec0b6a6c9a15355f357bf2fba58e30d45cf1480af985e141bef1ba6a990abadd2359e0c5238c07fb9318b0300004040f178310620a45de0eeb554e0d21ac9798a850a69338cc94d3739e700ce9b31411c1f86d83e7575b8e5d4b154502b778d75bed01d5a1b116255c74ce16f11470d20c7bf955eb2987eef2283dc0653241fab008745b3291a942386114da756b1d51f", - "interim_transcript_hash_before": "12a81fc0e57c98fc4264a51a103e28afd32a27886d60d46cad4969206b2f191d", - "confirmed_transcript_hash_after": "50799bd8cff09bf9b020a114a32ed6e5ec24c910b4aee144f0f080d551f8382a", - "interim_transcript_hash_after": "c71728e5d670f8b121d7a78c2e6a1d4aa235febe58ec5a4c945517af81aaf412" - }, - { - "cipher_suite": 1, - "confirmation_key": "64d1e502fb16da83f34950cd98aab91bdaab1e4a228d16dd9264e4d2ada8c7eb", - "authenticated_content": "0001106726619a511cf1b22a4667cf662b3e045042772adb584caf0100000007305a1de3094d8a3864b4cf781d2c865b637c25bb8f0d1c7a76f235149368fa7a8d3283cbf84426c753886d5141f8a15712030000404050101cf945992bd5966a0cb8b47a1191d13f1730a4ce5cf0a901665b3d96ab177e853a5359c68b7156b207a038ee369a9cb7a17fe676bdd7d8da0323f9443e0f20aff3234598b649948ae415fac05d2dd67efe2fd0f171443d317575297ef6daf2", - "interim_transcript_hash_before": "3c8ade43b45c56e1e0700eb6a80f05a9c3f86b3ed92091149420feb102378244", - "confirmed_transcript_hash_after": "948696b3c5d498a156f6bec94acfd5f1b9ff86d252df4b5255a4645a1eae5ef3", - "interim_transcript_hash_after": "141942863214ab8b07424312aee027ba611bf3c9a6f017535fd0349967e4c300" - }, - { - "cipher_suite": 1, - "confirmation_key": "dbef7dd7e07fb79eedabd0b7be74972c64b2cff4da0fcf3f36d59fc106888498", - "authenticated_content": "0001104df223a71a43c9241b35f5c6ab2102115bf172e44e106ffd010000000730de86312660f56d24fc4432990e51d6caec5258389a2746a75a37102d075d2bc16d623c6b8b67f7a51bf1b5e477d788ab0300004040711c10d7bbc6e816f6244f6efa5dfc28d8b1b63bd52f5ca9bebf4510eb1fb7f9284c6ec87d73033855affb0489893fbd5ce844ee7429e04f2c51ae9bf08ae80e20ba1827630def7b7e5f870b2f035aac73651e3cddfebb1cb3eee66e7006cab22b", - "interim_transcript_hash_before": "ed2973305857a01ff60020e303f729c5ea15e63a6ca616e1f98888ec4789987b", - "confirmed_transcript_hash_after": "ba2a9c25a9bb875e3902c315626481f8fd9e5e795fcf3865520701ae20409ab2", - "interim_transcript_hash_after": "2cc8087f75e57a7a100edbdb948dafc4cff8512d600677451b67cb7de20cdcff" - }, - { - "cipher_suite": 1, - "confirmation_key": "fbcf9ad0a027b9f415eb268adafa713b6de04fc41953cabf9dbca37f56d98dc1", - "authenticated_content": "00011067c19d7c14c8bcb653587d20deb6c7fbfa29948772e270f1010000000730859c4a70f4b7e61622258f39006049e5d88b42810fe132907349fbd53bdd3b164235e87db977d3d8060cd4c5a1d101770300004040118d13603698fc5d292ab7b55a05bca118f1cc6052ddb88a7f7a3a009308ecb47e55ef5ecf33b45463b5d71dc51c3f17f4c776f1ffe0700f24a55e3ecd41200c201f6a8b7a02b2c622f713b05fb765cc8b1222a02c2212f1bac818e00d5f77cfd9", - "interim_transcript_hash_before": "adc314e137e5452a1ef27a572232abf04af024e34c64b99a76661f7e9e9f98a6", - "confirmed_transcript_hash_after": "043c494c767e0f5238b23080b87b9fa1ec7d8024c6670ffb2478bdbb53ae1522", - "interim_transcript_hash_after": "8262f37d4aed5d97d51372b37c0c6cd10604645052f10ffbe673343543e3a64a" - }, - { - "cipher_suite": 1, - "confirmation_key": "7a1d20e33b1b1792f1a284ffd8d43189b9827e3ae71f74d387ba74c86275cf98", - "authenticated_content": "000110702ec4216228ccad66f5de4bcea532a2859f375119054f61010000000730f592b9a6f895057d8345f688bf4331c849d404a19f6b806b1bc6e97e72fd3db8206d0c71f40ea775885375b8475e347b030000404053d90aec075a92676f06207d0230cf0bbeb2a37445576957096d7eb4e8e365addc5c8a589945680fabf223e3b811a16f2d33f1b3532a728098c8563e9872ca0d208fb8de09bd67b8c231e1996f14e5e76ff0c4e26c4be4a44f83a990603c11af68", - "interim_transcript_hash_before": "ce01a709bd00cba35f50c10e7b736c602adf3b77ecf00af3141c82a008342551", - "confirmed_transcript_hash_after": "18060264c9d380e4b3e2a769a0f9ab01ee30468f63ca2dc4f1261776f35c8b4a", - "interim_transcript_hash_after": "520a824a9b36b4124c31cec4932cf880ef1ef485e28aac79ff41bcb6b1422c24" - }, - { - "cipher_suite": 1, - "confirmation_key": "0d1d33d9bfe0c2f57d5649708c2162e6f7aefeb3d64ac624ac512ef977e575ec", - "authenticated_content": "0001109c7a50201ee02375c82b70ca1e2940c5b6acf69fa4459f5a010000000730cc8a5978687c320a270bc657e2aacb07f45b4530a64e5dda12787f83c926c06a78dbbb297765550df27f1a1ebe9409550300004040f2d39049904bb8f02b4083063e9e99e0a8988bae8442256fc00b97286e2389c0b85f845656e0161be20ded4fc908dca9d2e9a2d41de50f5af7d7db8dfba7a7082019c83e527d217ebcde31ce44e0d3d8da3b44004fba8cce152726bdd7e4704329", - "interim_transcript_hash_before": "7f62df20885524422261ee0365d5da3b4c8e7ff599df1dff8243d1137b5c71aa", - "confirmed_transcript_hash_after": "504f69fd203ffe2da7c089c45341324129982fa7951a46bf66eb29326aabe183", - "interim_transcript_hash_after": "de7ff71fe2a390a254e129bebf74056daddc2ecba72fd9575026dc6960f8ddca" - }, - { - "cipher_suite": 1, - "confirmation_key": "31da21b5c1fc12b9ef7d92327ea4f77641d75bf9f33aac061f43f3a191ed5f31", - "authenticated_content": "000110efbe3a80f5848bbe785bc344c0a0a96853c188294818742f01000000073004f71f459bbfbfa4d848ea1b9b131b82ded90a745ee81754d8d67d10cc373066bff87088e278ee4dbd0b2f5dfc876e3e0300004040d8a5835a418b58092d8cade24df9c40c1f5e4b626687ed2b4cb75b3446d0aa919b104417ea297bf520bc86dcc63fef43fc23c21fe619b2b499b3cd6633490c0320708d1e098d0d34b1c1781d09fbecf2e5b4041f6c1f1d823595974f4a27168b09", - "interim_transcript_hash_before": "999e20ee7b3f65e8fe22505bcf129d8ace4f356536aa54a670b9cc3ba68faa43", - "confirmed_transcript_hash_after": "2914163c737dc65b27a97699ce43b79db9e81c37051e4dfee280fed0f0afb3af", - "interim_transcript_hash_after": "2c3aa8b55bdd89b356be5c2062579697826451ce6ea32261cf9b3abf641477f7" - }, - { - "cipher_suite": 1, - "confirmation_key": "cd5d82925c47549a5d48a31d0c4344d4235ae3b69f86c156134c72729bc6e53c", - "authenticated_content": "0001105cc6f217f61a3b052a8aac5d3e608edeaaf79c4b280e5406010000000730126e639c3c68f087048c63494f3ee5e9db435c13e4727f442626666a99fcfd05dc23b698bff58372d19237de309e27b90300004040466dde2ae08696e1ca6e0b0159b748f691a89a8597651000972f4a843018c2708ce196a3ece77d5313e8e961913f8483ae0ddad032bee23cd8300aa02246df0a208d650ff7795b21fbc1773bb392b6f2657ec2c488e3a8006f5eb723f14d7cab37", - "interim_transcript_hash_before": "828d2bf053d163e2b318f7dccf43757c285cd7471cfe4adb66b9c22be799629a", - "confirmed_transcript_hash_after": "29636d465c7e3edcf8450e24dcd1b445352f014e5de6921960fc33d8f0c77cd4", - "interim_transcript_hash_after": "e23482a0377cb13da44cc7d79c9fb6b48be73585c2579a3f8236ebd4ac598f8c" - }, - { - "cipher_suite": 1, - "confirmation_key": "a980c9dde83bce56bd79f613bcaf7219eedb8bf6f0ff24f5391263f5e1bfff0f", - "authenticated_content": "000110d51b0c1af0b85378628929c030ba07bb58308b81195c378f010000000730d495250c319f1fe0b21d672029290f72afcab54f6400c08c17e12f75df7aacd08c80515490615dabdc10696ba6f7a31d030000404070378aeb77d83bb3c66ef2b55ecd4e27042df891dfbe5952aace33d576dfbaa4f1f9e92c7bae675ebf5ab3c0df3532dd62d5ea4f5bd641bda04a80019bb89b0e20e6351c8ac1bd4f2250256e3db17bde2c77e6241a3e97ec874bb6eb51f7154a61", - "interim_transcript_hash_before": "943dcae9e8b35d25c2403f480651857ab84b33b8dddb4094a7d12b6100411bc9", - "confirmed_transcript_hash_after": "ec5ccc442e290d7dc12987bf3f1ab8935f79c6b2335d0764a3d8942e05375584", - "interim_transcript_hash_after": "387abf91769560a306eb7e4cb4b3ce5f352943918e1155a69faca31bd2b3b71f" - }, - { - "cipher_suite": 1, - "confirmation_key": "95ec443934acdcbf9a14d0462eb3c627d82f125cfdf95d51b5eb13a68dece309", - "authenticated_content": "000110710a624d1f66787fdd0ba41dd175d0a80399424ec9a3937b010000000730336ebeec1d049afe765b5092e44ed88687d13b0b2765a8aa09b6e456f130e7b69fefa933bc6a48338f2d37d5f64bd08c03000040403d6d9944c388446774d5abc4bc883a03c2b9debc8278a876c6a2c77e54bca153ef993227d0a784bd34b42c33476551fb81bdfe21a20f6e71a7699d2f53b0f10d2003c897fca799bd13cb9e1f1e89a62bf8b3126822f2665353bc28aaf08e21688b", - "interim_transcript_hash_before": "537116472b63a959852e0db398eb0b1c54ad631606129cf572042a95214472d6", - "confirmed_transcript_hash_after": "994b450555c5f159eb66bcc9ba47d52237dbf5de42e34085b7ea6b99e21c3a35", - "interim_transcript_hash_after": "e92384dfb0b8847fd2ee8c1b6dc833070a5cbf82f4a3d058d515a5f4369b457c" - }, - { - "cipher_suite": 1, - "confirmation_key": "bc0a8222a50c736e009d6e9e3afe25dd4c16995d8fdead9466cef58a2ea630b5", - "authenticated_content": "0001107e84c100b585c01bbd14071c51839fe4c1451942af5895f5010000000730b84e5df485ef03eb47f0c40f7de42d709e802ff2960a4b21843a7e0d03ceea2ce2b05a4e9c8e04199919fdad4f3b711203000040401d10820f64862fb4f02c8ea04b3bbfb95092141868d17de02bcc0a1e3d90fad0f5dd93fdc3d9d899545f08c219833c2e5f96bec1f66498db5f79970a7572780720b02c4d3888c8264b5efeaf553077455aec6d46f4555206477e3e797e4c45dcf3", - "interim_transcript_hash_before": "8781b20908ff4f1708fd9f9b763e1f83fb26ca9eb1199e4345d5b750b2e00e0a", - "confirmed_transcript_hash_after": "d78cfc601bf4f080acce9741f326b6908b0fd8f5b7455c81363254cc90ccf524", - "interim_transcript_hash_after": "2ec825f0db3646f43d60811c08501b5770f8253e9bc5b080445dd568b1e900f5" - }, - { - "cipher_suite": 1, - "confirmation_key": "5d4d03ad0f26797586635f24151e9396b5867f77f206aca5a03e8a793e6a8076", - "authenticated_content": "00011038d7c25e0619913dc5b2fc545ccbed5280d868380e79d6e0010000000730d9594836fd37e02c826336543e2ac0648e36ff275a26364c774e0c838c7d5e36f921ff0fd900df12e065f50572b74a3603000040409baae423e6b8d806fd623e14b7869293985f9a06dedb68e8602823a4e109ebac5e5564b6680b09cb5b0d251d1fce54f88803063bd03406e6879f5b3a24daa70720f40f86ba2ee3e9dbd109c7ece134ea392b63e3fdf930e1f9cb13e5919e315f6f", - "interim_transcript_hash_before": "39813b813f280bb49d83f13366156f7b0c56c482967390a863b82ff8ab4fe765", - "confirmed_transcript_hash_after": "7b6f6b8566d1ce45197d89e08deb0ef854f31d22ce68a915ad28367959c0874c", - "interim_transcript_hash_after": "2748813a58554eeb0c16fb3434d90251b22bdc818f0606c845f7a8da17f5875c" - }, - { - "cipher_suite": 1, - "confirmation_key": "43e5cb082c5715ef657af7ff4f97d109de325e7883ca959fcd131e3b3fad3e46", - "authenticated_content": "0001100923fb7ad145f0ee8a36c461ef2340d2be901428f40fd91a010000000730d7b7fbca20fa8704238e7e59d6ca2e2898bde4a68f159cde9a325549601b61f16f096eaeb19d45feb957be41ab6b233103000040402e8624433e0b2854da8864c752f7cfdb91a2f5778d8d1b45a60039df5768611f37c2f73383bc4432c8c8139406b3ea0d96eb5d671c1b913e198682a01e86ab0220fe7296cec569ece8ace6700544f5597396a2dd225b1125bd1890b6d409bf0a76", - "interim_transcript_hash_before": "d47b30b2f68fb186f9dcd83f6378ee20566b44ecce59b9ddb635a3a77d5c9a93", - "confirmed_transcript_hash_after": "b86c9b74c717fb1602287863ad57bb9a2b666638cb54a4f570c55c5a15f88d64", - "interim_transcript_hash_after": "9ca81439a09d05e0b3690f2e8679ca2892fa666ac41c00ccc83814f45023a0f7" - }, - { - "cipher_suite": 1, - "confirmation_key": "9de0a37869ccbe50d237e925539fb9c2178b0f23d882844379a5fda6130ae1d8", - "authenticated_content": "0001103286defdf728eef02040c2f57482ea256dc6213bd6c72fba010000000730a4bd871e9744d2bac07cdc573b635bf34e845cbaddc07e255c49760bad6e6a637d3701b1dfd4565831cc217aefca9f290300004040d69e8c193f2686469154a1f1f9c37ff7328495cedbaa3ca53aa9c3b07d283010b93fe9586fb2624df8d256a21aa1be1b7691fdd3a536ef5412041c42724f590d203a4fd9ddc4d9bf9bd55e9c148fa23c9ca6eab514ac6152a80601954413b1f39b", - "interim_transcript_hash_before": "c583883f67b16acb1d4523ca7543e1bb1aeb9fdb3fe0d1917bd81b608d47fae3", - "confirmed_transcript_hash_after": "737b3f516ca008e88a51f39b8b8f0808f71e0c74df1a3fea53e222479fbd8118", - "interim_transcript_hash_after": "1460ebbff34f0321f24b9d6429039e3d16220eb334684b2668a520a1e86dda9f" - }, - { - "cipher_suite": 1, - "confirmation_key": "f4063a3b4ed074ea65335eae1a202dc71a065e7d777a5a291e202d38aefb6146", - "authenticated_content": "000110e4a2d1f293e29851f5a9e8742a925fd22531c35a4fbe2afa0100000007309797f2041a584007535ebd74c74d2f79a5bb5d3cc2c21317c48d5bdd339bc0ae162adf3321dec27a092b7bd415904f36030000404011ab4c95d4016489c617a0bdc33b06d7932e775dc997ce84253a610e281eb13ff6e41f90401a9ba6cc7bbca944f94533e0b7ac3a8cd8428109b9b9288fd6390d2017db047a0fbe0131c5cac721d68998251019570fa19768dbdfcc5562802ec32a", - "interim_transcript_hash_before": "39cf823ba9e2c19ca376c64761641cadbfca52ad6bb97865ff3d632ffb6699bc", - "confirmed_transcript_hash_after": "1219db9207de59aa7b67674c4d871433282c857abd638c84885237a2413d82e7", - "interim_transcript_hash_after": "9b1a95a54c6477dbe2e095d78384e4843fe62595fd9104f08fc05cd29ba2e868" - }, - { - "cipher_suite": 1, - "confirmation_key": "d79ca2d699847b30aaed8c46977f9614b103af23c089771645893db2b23ff0a7", - "authenticated_content": "000110b8e54f8955193df85da9d61ff13aeade05cb5fb749d637b9010000000730153976a8887ef3c9ba238870b760293dd8cd58ac578cbfcf77e04c1d0033b8a42e54a03195134f6407e0c898f11bcebd0300004040fa916203deea914d29e5fc310807b361fdeecdf1ea0d1305525fa67872d06e4cee3b56ca0b545a7af5dd4baa0ae420f8161d2aade4952634c26c26fc1c89010820340f11349bbdcda87cab18177bd2c4eaa2438f04f904acc5bdf535eafbf0f000", - "interim_transcript_hash_before": "c677890badfbf79d7dcc42d17c5b3eb4cd3bd3cae8021228b91891a68caa5072", - "confirmed_transcript_hash_after": "02cb9624d5613fc32fcf98b345d7a3e0bbc26b2410fcaf6ae404adca6b3e9adf", - "interim_transcript_hash_after": "a83c85cefd5ff3182b49a2353113527241518a792335ad0f3a5eba9c8a838a4c" - }, - { - "cipher_suite": 1, - "confirmation_key": "1517be4a68d0764a840d6f40f52d624972de59c44e05fd062d8f45ee8bebb5e3", - "authenticated_content": "000110a5352bc16fe46c170732f33b71d0cc8e05d8b499e889b4020100000007300b71de1c0785f1935f6a2ca76c267ccbf9ca6295f39def64b75a882c3e23936c855fd3d0ac48c115dcf219a3747a84df030000404080beaf2471ff76827288175a2d33183d4a90636278613cfb98ecb2165a5a7945d7175ff867cb5265f3697b1388e037ae7eb05b454e8fe36b6e7c6477f272210a204f1d24550e0eaa4b3b1382a8327c481f1288ce3498a96771ddaca6b007d6c4cb", - "interim_transcript_hash_before": "a4a9fc63d30ddc33cf26f5059b6ef8785738019d2258c8e43d9984f2da65b4e1", - "confirmed_transcript_hash_after": "4993190fefca920356fc5d81943e273a7a2799f993815a2dd6471e0a25435c16", - "interim_transcript_hash_after": "a9c85f50b9974f4eaaac9d2d2c0140fb80e72a48d9a6f7356fb7615538c51701" - }, - { - "cipher_suite": 1, - "confirmation_key": "e3b636681b1c76669dd16577c23a3d4572384ccdeb5a2cce4e5b09d63f8eecfe", - "authenticated_content": "0001102d5f3d3fa3fd1d6dea6ef6dfa36f267ee43953ec640629d80100000007302892de23fd9e43e7b59ce8d83af0662fb7f93a22d61473a9ce297f6aa4cc01087df4dac88c67be1e949b35bbbbf877520300004040f11d7e18acd2ec80d205723cfcf66720799c9122973c994aef115d87e15bace8951ec4ff1d25a2abdde2b0e0bf32f203218dcbabd52848396d513e06836a940c209e7a5e019e1b42dfc63d8e91b16bb85b733c9e3748cbbd2eb6dca5da63ddc04e", - "interim_transcript_hash_before": "0900d4ff4a7d8ee3534a341b614a1010187b8a9681305fa198b29afbcb72eb2b", - "confirmed_transcript_hash_after": "cc0a196b6d8038e66f28c887515752f9e4320f7977436ab2489c09dcc28ebbf9", - "interim_transcript_hash_after": "7e4d5b42d712a642fd280121bf592fc414a3f96f9badede3e12632c315d44df7" - }, - { - "cipher_suite": 1, - "confirmation_key": "a1bb2601a7ea670798e286cf99e0c67193a8137c979e2af8ae1c9a1d48d899da", - "authenticated_content": "00011021741ed80e20fad5781708cdc8fc6b4e5126007489b0320c01000000073081f0ae24fd4cf700eb9b0dbaca73ca0b0e27c9637e534c97acb3f9c0de892970ef2f3f61be22de18e475e356b6bda8c20300004040c071dcd505a2f5e88c48427c1aff770199d22fe91be5e467020689fd39edea3d09add0badaebc4a51f48bccc7332c6e5b2df0ee1b8e54a1366e6b05902cf55032020ebb850bbf11f201390195b236bd7aa7850584e146e8345b4e85d866d2cdb18", - "interim_transcript_hash_before": "800633aca02f309a1855d8fe03fb6a6678268273b03ea7823ff53227641537ce", - "confirmed_transcript_hash_after": "69a6f02a1e441db3bd8fbd82b3bb6bac289db1c4a09abf99e7e0f4d3fe7cb759", - "interim_transcript_hash_after": "654230c3ad68c371d3338a5f3a84b39dead09de535b38db43cac59104def2d9c" - }, - { - "cipher_suite": 1, - "confirmation_key": "0c812685b8187c706b393fdbf370ddc85d13093dcc63b54bda3ca0e632069086", - "authenticated_content": "000110c27cb1e36ee5ddcdf3bdbfe4914ace8d7f794de46ca00c350100000007305688800a873f58184044f655cea4ad462bb06d604eec9c603a3db2dd906967c7cf1c5b1ba929699b7e39c96cdc5520ce03000040407930665f681a638376d50f97ad06bca4335a351e39deed2d596f6dd0f64c46baff2ea629a2b90daca1e2da196cfbbc957d779c4c2c59ce9ac2fe4a87cafb560820029bbd520abd711af11b3e99d89b4a815e0f7d8afb0bdb85424e469c0b23cda3", - "interim_transcript_hash_before": "e3cbcd31d6591981e984972883a6bceed50321bc157a6cdfb731d42cdd4234d6", - "confirmed_transcript_hash_after": "938b7167a9a35950195a2a7dd2e2fa03326f20ebbe94b4d90d3989724df16ff2", - "interim_transcript_hash_after": "b32e2f89bf49b64c404ce4dd0d65a708c590c9f0bb804bc97c7bc8f566006b15" - }, - { - "cipher_suite": 1, - "confirmation_key": "5e6ee9a0f701b1c424bc59593dfe3bdd92567028fda5a7019642f41870d7d348", - "authenticated_content": "000110add02064a04c3b0f1c1aab58dd55a15a7379a3adf50b677a0100000007308cf3a3e62b229ca35b58d20af734e76a41f3a5fc724dd0b976660b3a451d6f025ed975b3ec5d04cbfc70228edf01fdaa0300004040372d9fa34303b7fd5030c1574c238337b5430dfa2c305c89496e61b8485f6ac20c489781afd9adc4819d38b8cc07bb2ecd5d4ff5e4b9b756e8b08f7530f6dd0d20c670a4dcb0d8d34db3edf793ff497222875d98a55bd60f4740c48884628c501f", - "interim_transcript_hash_before": "7d63e7d83cfa8240aa2d61cc4e214d56893e01be4cf064a8c07a9ad3c5830a87", - "confirmed_transcript_hash_after": "86f290422f1e7e5f87efa15c00b1e0a74e9595aaf62f48aabbe7519e6928a299", - "interim_transcript_hash_after": "d4e88ea7e77334747bfd72c9e0bef7b14d09e68f2240b8286a4e4793b5363989" - }, - { - "cipher_suite": 1, - "confirmation_key": "607acffc4d10b469a5ff82b9f45017fa3a8b7e2b33aac366e88f4378469c67d1", - "authenticated_content": "000110df7b7a693b718771044f71f1f7b1f42240ce9b8a8c68166b0100000007302defcb8164d13ceccb804935ddc5c463544381402372db8e39c826a0763dbd4d035be617f246275f266974a781e8ac90030000404035c1e143ddc9a5dcea90f4ee74ac19bf62572d0261c65f42672613ec412d94880228c0c5119137e61dc2f0d4154f9589284c8b690c742fdb16a1dd15e53a730c20816bc7b3ad1570f25f7bef7d12919db06d9aba3080e37a8cca7742c9a65eb8fe", - "interim_transcript_hash_before": "694150e087d308eb9d9fa40a219b08332e34e618088662c053c4d5c9c89aa13d", - "confirmed_transcript_hash_after": "40850979115eced71506c5e24fa0ba44af581aa0cbeeb44f197d2184c7624267", - "interim_transcript_hash_after": "6bbf4483dee76fe4bd918f63b00d4f0b07410f884988bee048b5ee3e6201ce3c" - }, - { - "cipher_suite": 1, - "confirmation_key": "0eafb2f86bb2353b1de84d9ae40881ae664936f972740c1ce78dfe987c8c8647", - "authenticated_content": "000110f1d01e473914ec171152deb13ea9cabca1920d3fd186c26c010000000730a1e928f80d6c1595fb774bb5784af2b0930d8d1396d2b80b7c2486480842f2b0cf86bebce2ddbc02abe8516ca91e5b520300004040d23398bafcf80a9c44ea6b2090233c901c307f25e58335e1d814356b21c8f56643db518d480af470af001a96643cdea955b1f7070019516ec68162cea1a6420e204c40d5b3131f00f29c51363b69a0bb56e2372be19bac65f229c0db88ce4d6e81", - "interim_transcript_hash_before": "fb89931d16ecbff8f28192a2fb5f5fe91dbd5fd5cd8b8e7516b01ceb665c8893", - "confirmed_transcript_hash_after": "dd855cff2bb8b97a8b8f119ef969c9d3c8e550e7bc3f08aa057383df1fb5715c", - "interim_transcript_hash_after": "7f457bb7b7c0319ee6b7d38935053b87a305febb7bd874679f353c5434de4a9b" - }, - { - "cipher_suite": 1, - "confirmation_key": "20a2e1c8371b90b75c8fe8643abf9efb12ad5336081d52ee14c5aa555d9be31b", - "authenticated_content": "000110643282355f0ab4dcd2e494a2b9b2f96207ee82d317e2e31e0100000007308643da21d8c258e7005a412d8f3ae7548eaa153d023ecdc01103b6c326c1bbbf0b7ac9a70ae9b909d5f16c3d56351e0103000040402a94184768fa987e99f5091e1980b7968fe9ffd0d875d062a3413342d1dac5be24d31cbefbfd28754c2b3ae2bb4227f975294b139a50a47c8d908bb16d38500220d5bd3fe6f3487f38100148bcb8d8882433aeebb406eebb06503ad7df20f1658a", - "interim_transcript_hash_before": "c0c93f8091450ded61af66b344c3232c79b210251dce3477cc50b093ab1b3316", - "confirmed_transcript_hash_after": "e0d229bdb767f09e9305709cb1fe1c04cb4597b3ed87a44896dadaa9c15d1ebf", - "interim_transcript_hash_after": "3e9ba1df990745368b21e7c1ca4638ba44c6853f28afc2313fddacc3f8eeffcc" - }, - { - "cipher_suite": 1, - "confirmation_key": "50045f1ddb75b305aed9feb50a3f603555af69222cd8d5053dcc08973c12e729", - "authenticated_content": "00011073eb791468e459287685324cb9e67f5806e71711abcbf75e01000000073093481d0a1618eba9619cf23b8b8ab804148d4bf60f8aafc48748eaa2090da04ee2746a466aea50e853d4211c6d9938760300004040a6bebaf00f70397a8b9d529e8c6c1a3af287e13f7b95897a29621f672ebd712473eea6978f2cbeeec69ef9fe139d5d22f7695ba53f62d64a82bf712c1ae7120220666e00d85c71e58718961111819cf7eb0441d184e0d329d947a746e847543eff", - "interim_transcript_hash_before": "a97280a8776e73ecc3bba684fdc22f37b510759684972ee33c8414913082a51e", - "confirmed_transcript_hash_after": "586ad5afd8a82f6af3a6b5e7adcdd41cbd2444d47edb6736568cf7916b42a265", - "interim_transcript_hash_after": "701729916aeff2b4b3ba291a1e26d27f8f1bcfc31cd13e7ce89462e2e7e03f0a" - }, - { - "cipher_suite": 1, - "confirmation_key": "dd639f4ee83221723a1b9503ea8913a5a41ed1aa787f17b6e8d98c8a9a21059b", - "authenticated_content": "000110f1a2df8b9017763bcb1a9a29ce6a2dc813e994b05c3f9874010000000730009723798a0fa5594a3639adfd10dc9f384f29de83220d7287cc11cb1f255ddab23fc6a8c1feb1ca1db955fc6f43e2670300004040d0c9d0665aae9bf4183b3706b275bffe5ac950224d70a59fcfa479b35103d43ae6bc425557d8d435033f655d96dc6943243f1c104f11ce57a379ed873d03ab0120509bd97245445d1a03f461f329d29e79e8a26176a758def8e4b681af0b47bd90", - "interim_transcript_hash_before": "67bfcce08226dbe950f084e2b56489973a57a0fdddbdc00032bb9a7cc235a31e", - "confirmed_transcript_hash_after": "6748a79bcbc2c89f9e873f4828ada152d6d245ba0820dc8bb8807ab703511749", - "interim_transcript_hash_after": "2f27c86173e3b3a04f9ae1c909ce92c6f9870fba9ca92aa8133f2fcb307566bc" - }, - { - "cipher_suite": 1, - "confirmation_key": "646460e65842d0771933c2abe0fc2479ebd5003eed6ed9797c7fc60331b1942e", - "authenticated_content": "0001102fe7286f0cf4851e462c688aaedd177173367b7cc65f84d30100000007306724f6150980b3065543fef8b44b1f3b97460cf2eafcf4f1e47c2c18b707bb3b6f9662ed313176e93e10e57d3de38339030000404053f1c32562c757ae8874fb1c965718c32853cdfedabce49fb3b79a4eea6fcdcd283b37ff3714218f1ec59cecf25f72b3177cddb76e8978716cc0afde9af23b09205be0b5c2cb3b1dc9bbd947787348c173afd22aa59c85dd50d7eeae188422a4b9", - "interim_transcript_hash_before": "4a59233d909f345f8801f57ad11f2a582fddb8cd7cdc4ae007a5c1df1f657a0e", - "confirmed_transcript_hash_after": "8dad0f46625aa08b6703efe5c23c7e9dcdbd782b36106f8cd7a6176596003710", - "interim_transcript_hash_after": "58070270babc7c400afd6528530f19ce09581895617d89824799bda1d2b4a7f4" - }, - { - "cipher_suite": 1, - "confirmation_key": "db0471968ebcc26f71093fd13141b1b877e72e025cc255f9360f5b7f15a8f2ca", - "authenticated_content": "0001101d3540959980532843044524741466121dff6e6de3738e82010000000730c519ebdcb5ea40504c5655fda5d7c016da989ac8fba0b49c69142a0d430702bbb626822d67e18d18f389170b87a7ddc30300004040a622bb47f53c3441d319377bfd037fe94f12a1611bbadae02ab663990d5cc72a2b8f88fa7500d5b7cb3fe9cc1e3ad08ed4f0543b1830c6b6b6ec44f171ae670c2018c35b3231e3b1bbc9f16aa815fdcf1e038d43f0c33017f4fe8f207dcaadf561", - "interim_transcript_hash_before": "696d29256cd1f3ffbdceb8f87ea2d83e36199bdae0a19ca47db4953dcafad116", - "confirmed_transcript_hash_after": "faae510624710a19d94dff008e1b9498804a7bca23aabd39fc08be3646845433", - "interim_transcript_hash_after": "5e58292009a74cd7a8ee52dd830ed86385d725dacdef4a4f9c1c46e9ba709c94" - }, - { - "cipher_suite": 1, - "confirmation_key": "1af239bf5e62009a5b1a284e19436ee022af6f44ace683bed19f8e3fd2851893", - "authenticated_content": "000110f54678df96aa108f67d59031151aa2d64bd294645f55a796010000000730b80571df2be90e17ecaf087ec2d7893ce354ff1cb22ac94f176cad7aafcb5b6f9ebedef093eb1114512e2dca0305a60c03000040402a25f4d5597de1305f3b4a475d5566f161d145ca64e0b32265554198f1f7958a2424e8ff6e1fc5bc48c9196d6c4f44cf1432d809950d9caf9ca289574098340420c3c743f8f7ec78c2cb808867bc02aea3f61cd3097875383508287ff3ce9ce2f9", - "interim_transcript_hash_before": "dbf923110fccd0b99d35c6ed9f148de14e0813024582d69d01282fbfd5566f6f", - "confirmed_transcript_hash_after": "b2a5e25bebaba66518ecbe806eeebb9921dbbffb2a7e6dfc3e1b28fb857596dc", - "interim_transcript_hash_after": "2e73bc5b68a8e7c4b52f3d4bbafb2b14cde8fb9ccd593e765a79c481fea2da08" - }, - { - "cipher_suite": 1, - "confirmation_key": "f55c8f18bae071781f4433e1fe7706c470ad2e59ba1ed9d84b49cc19e49b65c7", - "authenticated_content": "00011027aa128e2ac3cc4d7497a69bcd3e2bfa3fcc038c1dfccc01010000000730c314a59b66fecd0a0812b584b08d59b1a1f60fdaf9d078e08de023e7dcd6bfe7169383fbd6e940e1292bf33ed03f6a7e0300004040a503716c74d757f7d013ccae77c6fdf58315b0c7744b84b26bbf0eae1260b46334f524c91900f0d7ea8cd6e32ad19b6d3e07b03241904431676edaf5f139900b20f05a3a4ce3bdf5579d224af1a931e4e4ab0911377b4c3e4311ebff78879c0809", - "interim_transcript_hash_before": "20d273b3e2eea0f4d1451751586186434ac26a5af0feff1ed29aaadc195b3655", - "confirmed_transcript_hash_after": "6fb710b7fd13ca4323f3703470519715253071649dd1fae0ef0d4c62fb585a07", - "interim_transcript_hash_after": "119e2fe060155cae7fb5754fa3c50e6d883efb6bac45f482ded1d58a793a1cc3" - }, - { - "cipher_suite": 1, - "confirmation_key": "092154713c49ab7e967258313f961478e2ae9ea68924b2d35a16a80c55bc8d70", - "authenticated_content": "000110d2236cf714055379cbbb9379a35746ff16b85bf486cc0c0601000000073076f060fa7d113320712bcdcf6ed0b5e4622dae393633cfb23e7de701b8d81cb632bbd0111ad6fe9ce2aeddea6cad690c030000404065e90bdc0eae23fce653558c47783cd698d413699ff5f7fe7e5c3e6a190c2d9c3c4d7c08182b1232472f29ddff76f5d8f4ef1f9896a730551b3ceab723584d0120a7b40eb2dd07352adbe7ffef9c934ae4a871443d288111d8899382ad62091aef", - "interim_transcript_hash_before": "2e8f1b25d7a2ce05f00ee1ac2a378a67c2a0b3dd04d79271926fbcff01bca260", - "confirmed_transcript_hash_after": "9ce21b926613ca6ebf2abd8f3e44b77fb416f0e3adfe7c3604ba8d51b3126295", - "interim_transcript_hash_after": "4deb17a2db9fec84d9ac2528260aa5f5c7aea7f16097fc3d7c8ddaeb65abaea5" - }, - { - "cipher_suite": 1, - "confirmation_key": "bab05cf18d325f9821655763248111b01241ee7f85d5c6ea22176998793b5418", - "authenticated_content": "00011072e127557a1005a6b6c05abbba7b4cd16daf6a7216d88d630100000007306fd5efcb0bd117bc1e205ed4b8adfce9a2cd8048fe7bc0fd3e421ed39bd67a1647dd27ec0a694d0cf7b1ce34670d247a030000404069193474ac4e037beada5fdb2f6351a839d8a3f8941608e7aa553bb57260d15e069e725daf87b5f15e5ba9658c4ebc7eed58d89ccd01766cf24ccf4562cd2501200589f1a03e90de22be09a23fb50653cb9b1f1691ad57329a6db00fd0c664f3e7", - "interim_transcript_hash_before": "ebe3bda2ddcf762e549bcd66be03b5f5f5c8e785b63bdcae1406b77fd67bc1f5", - "confirmed_transcript_hash_after": "bee38dcedb95f0c4ec0a32d92465717d9604a41d38f6fd8b864dbdea9737cf97", - "interim_transcript_hash_after": "aa0ec9b62c75188ae1cb6feee731aa0209b450e5b833a103539e7f715d840955" - }, - { - "cipher_suite": 1, - "confirmation_key": "ef591cd1a5535d5107ae9444761231ac235dae1e1459cbd3b3bd78d713288683", - "authenticated_content": "00011052fbd31ef4c204cf2083bbb47af30d60b26355c9650fdca401000000073005152546e370cd4b8bed643ac2665af58baf8a2f1b49f08557cb3bf411d0106c777d29e1cba3306c6bbd6bf9e2db1ada030000404006c614d6f1fbd80a855a0776fa9cdec1bea46fb79e2e49edbeec95c102129fbc8da231a152b95a28d679ea3abd6914db87a44f669fe8f3d374039545ded01d0620cfadc1208f4a944a04365469f9632272e8c096ee79d7c1a8c15e50ca72ecf629", - "interim_transcript_hash_before": "c9c0554e5b2e8de1c3b0c7014644aa4155938c0e68724a7b9609f4a7d136eda8", - "confirmed_transcript_hash_after": "b323ad7b4e94dfd15390969c63731651af4d2ebaa1648d732f9f9903c8f964af", - "interim_transcript_hash_after": "b462e4a2f93cfc5d0b49e9abfcba7110781e0416b75d86faf07d960275fad6d6" - }, - { - "cipher_suite": 1, - "confirmation_key": "3176d480cd9f375e28e6c527ecbd5d1791601210e68391d83266d745da4488e0", - "authenticated_content": "00011076f362b6f589773ef3303049a18675b4b7c6af1cb3c25ce90100000007300b14c58050ed43d6761ebcb613f9327576fb0e6cd4af88a722ee11c74878630f49f97554884387be71cb79ad3633a2220300004040e6f5101d25d7d8341efd20dfa4ec41607b9808b25c42d76f5d4af76dce37f3861726e57f54cc2bfef172b268a2f6c301ea38dfc53ce1a5b3daefcbf64f000c0520e5f634cf76e1dc16edeb2a08c7299993fefbccf1cd1d880eadf3f1cbbb94ac3e", - "interim_transcript_hash_before": "92686478d7b5562f8f3a1ffe8571a18a81fbad43e173167200366b610880af0b", - "confirmed_transcript_hash_after": "daa204f0e19142c750800c349abe544b2accacc3d5ff77cb22d88e7ae504b814", - "interim_transcript_hash_after": "e45cc95d45cbbea2031c31b457a87036e8e57c04d451ecb55ee96c545a719180" - }, - { - "cipher_suite": 1, - "confirmation_key": "600aabb93d2ec1e87081dde32b1ca6f31f6caf0e85be0ce88055a6f3bc3cf8cc", - "authenticated_content": "0001101e20f5a799a5f1bb4a9fedcdb25d54d4ae595fc891dbe1f2010000000730e15095e4a1484417cc7c542e5d8b01812412a1a0b7120d886dc5e3ebcd4d6851f0056079d7e36bfcce3e400ef5758dd30300004040f722995ab25e004657699604b1987d3ab19651a4c315770cc82f2ef39600920a2face1365a59c8460a4999a0ad2c285ed6db97f0eccd2432eed472162981c703200865dd64b7301bad275c7a7efd928498daa06a002e136cb5baa9594380c9dff1", - "interim_transcript_hash_before": "b946457afc9de252ca37a266f59cb8336a2c503ad512398b885cfd1d39b1342c", - "confirmed_transcript_hash_after": "895f5f93d0ce2b84e09614e79f3258d38f9bc458d515bf380f8a7740075f67da", - "interim_transcript_hash_after": "aa18759d3222db85ce858fa912499a7fc348474da3794a9a2ee2756bcfe1a630" - }, - { - "cipher_suite": 1, - "confirmation_key": "721fcffb6cb4ea3d69bdd4b451d5f7155d6728160df5213b5feb9a70e8dbddff", - "authenticated_content": "000110721f1efc8fba8ce9a23f15d8e982370cb8d0d86525fa3b3c010000000730a538739eaee7255c942a0022fe5570e4581a83906558bc2fce44fae360181ccea5463977cab78b95fcc47f2002e401040300004040c3062855a21ce372389d1863aefe7671f5450a8120c6558e9d829f8c79ee818c3121115944137f2f93f1bda52781db66901b21268bf246b1a2f1625784301a0020622e97be907ef6ceb7088474cc4fbdf2b1079e9e51b75d452a6d9fc3dd8c3c15", - "interim_transcript_hash_before": "eeb3639e9f04461a7f9933824b2c75c71a79504757b3f3c7b09e54fa61d8f32c", - "confirmed_transcript_hash_after": "200759a013ae31d33616c4276d002156405121eaf3dbb7ce131d1f911f06f5af", - "interim_transcript_hash_after": "a53d08ec5e4c3e57133202f36069589275a8aacc0f8875e8034a28efb62a8ade" - }, - { - "cipher_suite": 1, - "confirmation_key": "d0b9d78fa3c237b96245e937aabd081280283d4cee2a8958b052c355c2c2d2cf", - "authenticated_content": "00011077db584f2591b5400a37817bb817552191edcaa7fcf40e9f0100000007301372771b4cbbc795a7503448c3c9f08f8a03aed9441511455680ba965a54440f917238ec3f9c8ab9eb1cdd42fef23da70300004040025b922bfe89ebd5a1ebc17cc094bb0d9b29ac2734c65d9d8488d9c54ffdc1d80b04f1ab270712df8ecf80960bd3cb79e660eff7cd3b70a4543d836a54c5d70320fbd95c3572fd363d8373b13a2a91f16a944a9419aeb2d6fd7607a0fc669d09ad", - "interim_transcript_hash_before": "e06d5c8bed3fe7fa972cd715b69c30ce280bd30632c38a2bb56314562efc2934", - "confirmed_transcript_hash_after": "044659d4b90af86fcd717dd760099a242bdd8f522dabed9a817586ebbca5d869", - "interim_transcript_hash_after": "635990294169638fcf251de96f1afa224504a0ef179412897aa718f0275ac478" - }, - { - "cipher_suite": 1, - "confirmation_key": "3ba39e099628b924b413b1073e5c1c74d89c8d8ee42813c105cfb0fed6bff981", - "authenticated_content": "000110eec32c4a9d4a6adc9c30b7e8f63e02e93e9ae68d3170f686010000000730c0773fde1dbd7f537ff0df8f01da9b928438007c50a31ad6e04201d5fa7f4af67a5d782c8205535dfa7b3774027a4e9f0300004040f5cc79c38cb4e858a636b9b28d8ed78343dc2f2346748b926f0bc42adc933f73e58ef85df6e4d94e4e2091ac8d688b6f2c641e56649edcc93e3cbfb712e9e7022006609302b4a8cf8434d0209c22c3e2bf17d450cdd073888b5967e533366d1f8b", - "interim_transcript_hash_before": "dbe395d012ce54d0a7b35781b3cefc949211d751605b8d7b7bfabd9defa5a711", - "confirmed_transcript_hash_after": "ecde0c4f3e18b2dd776171bd1221443e29e029e3d01807d793f5f12147bfe082", - "interim_transcript_hash_after": "a32596b5e62b61297227e2472b7d6378a97e07c031b3dd9d282136fcb958500b" - }, - { - "cipher_suite": 1, - "confirmation_key": "4e0b7048e2f3756a7de4b3e06322f6c471c591989a35b81deaa12dc992c077e3", - "authenticated_content": "00011082b8334039540a44ed169974d0dde8f0362c3b6314fbd054010000000730e28e5d2280e87fc4922c6cf810ad6593d564f2b320f88c1122a4a0d6209b68c62e6efe37fd136bc992221691e18127ae0300004040bdf7c44866ecbd723038818dafae00534c9002ec43b40c7d3e86ca3d6dde2ee1a02cabae06995259bee0d10720c859b4ed0c841584aef0befbcf4f117ab71d06206690ed5506f1e09abf5dd767401814fec9cea34c7d97e2f37016ceca4091d804", - "interim_transcript_hash_before": "3055aea4d4e463897bd8cabac1ce5411c0e22395f7f1b0c05b990267141c6ff7", - "confirmed_transcript_hash_after": "b787bd5bf5a1d1ae48c2c22f2402ad1826084102ada07c8bd73a2bdeee4fa9ee", - "interim_transcript_hash_after": "f2a6e78bdb84001e8bdca4fad0c21bbcb2cf2db7039c8e8029042ef544e930b2" - }, - { - "cipher_suite": 1, - "confirmation_key": "0f024e54d439a1b653a010fff5ada9bfbaddb6332c6b62fb1b2500b4d5c1ce2b", - "authenticated_content": "000110d03905724afabbfe4822443746ef65d946e6f59bca398ad4010000000730209c0c4a8c93c1bc2ecd4c630989ad4619f5c3d1ff24d80f248dcdb4029b7742a8fff39ee16957a919216d04a121ec9c0300004040024f91ee25b60852f11a145e941982123a6b1613fe25c808af8a24f434c41a17fef6e9406d8b262fd949661028dd44657ce8f377f5cc81a47df4143597b63c09204720f9621784d84ac508f2be0244646a0285a224b29d83543e1101d7815b6eda", - "interim_transcript_hash_before": "6bf9a4e00a6a4c693cd2a327ca9b5bb25daa88a14fc030ffca66beb9caf410dc", - "confirmed_transcript_hash_after": "68cdfa8da64da89fcc0967e91411fcff6ac22092c836f66d3106024f96447181", - "interim_transcript_hash_after": "8312daec999167a4f8eefc1082fa3bf4914be5aaf48ce620772f632652666794" - }, - { - "cipher_suite": 1, - "confirmation_key": "875fb5c0b1f8f0d62488f1f00b6f9ee185fd97f0e1f02b5fb4e0f167068144a2", - "authenticated_content": "0001102cdcfd58fb3e37bb50c2214e55b21e823fc44322af42abd90100000007308f1043eaf70c0740ff558368fe72f38a36229777a0bd0aa80ba07c367e7bd42332f33764db21e62109dd5af0765b3eeb0300004040b93aaffbfc4ee0bef9a779454767e69c9263f94b0399e09d85299bd7e950a7402ba87f721b2391fdb4f380385a46a4e104e1ece7c9c5dec9b3269e049828070b2040951a10612407af66a56d34549b58203c0b0e46959882dca3e32f36f35f3687", - "interim_transcript_hash_before": "139c72f7c550f922de38bbf6dc9e8ec5745b30944fa22353bc28f581ced3b5ca", - "confirmed_transcript_hash_after": "56e70bd278daaaac197d17ff2f94d24e03835748fb8835f230e3c7ed94d4cbfe", - "interim_transcript_hash_after": "fa43e3e8361c3c514caf3d389372c1bd0f0bb3b591f04118d6ff1f9c5bc86c38" - }, - { - "cipher_suite": 1, - "confirmation_key": "a636c47e3d37a7bea1ee4374ae26d5e71a6630b54ee45e025ee93d483aecd89d", - "authenticated_content": "0001108df212b0c86587e04e7a1508b2b8c0d29de2206c3dcafc7f0100000007304ffa61dc0f23c599259d1973e571a03e2803aacf8e86e3ece8a29363ac644e30aa0a9aa561d5429f5c969f9637be2c220300004040bcb41027fd6e8dc16bc990b98c65d56d20fc64fcf96cbf970c97d730d9401bce85f31d3c69955e8d4007899510f7fb8d5caed323a29860b8f10ae42ae5b273082072d4677706434929063567b8253bb45e1e9aa62098da441f514d7cc7eefa2979", - "interim_transcript_hash_before": "2ff578f057d117765e1399b34f877bc69a27536d8ae24b3f7db069499fd8971b", - "confirmed_transcript_hash_after": "0fe57bcdd100a08a309cecc73d588a26f92b8a17ecdea6adb8d2fa58430d9b40", - "interim_transcript_hash_after": "8e5627ad4729c1cf982a9cb37361ef35cb323142fabdc195ce593e4a2037fdcd" - }, - { - "cipher_suite": 1, - "confirmation_key": "29d63e210a81026b2592133c0e38807b9a3db86adbfe3bc3d9cedd49cfa4c710", - "authenticated_content": "00011054596e9c4ac712bb924f10644870502e4b224fa5680815a9010000000730cce6b4e0be7be0c898978c44aa8b576f4f3728c2c67a306af25a7277c16e8e63cb702be794183df2e9ea2477ccd7ac090300004040e12373ea16649feb554fe4af41d2ba6db14a2879e5bb2c6caea1900610970d6dccbececbeeea03cf6488188c140039a2ae64ad0bbc691feb128a874178d4070b202d636a1aa63d1ba9d6f560af484df21774ecd30793ceada03e70fa3e9e048297", - "interim_transcript_hash_before": "0e3da00c6cc49d790f67d33116505ea9ced5417ab61f624ce1c480ae5f6cb316", - "confirmed_transcript_hash_after": "e007d8d9319fe5d0903fefdca1f5209a1068defa9cc48e409de2cf46c63e2398", - "interim_transcript_hash_after": "3fe38ecc19fb07ae3e2b40c52eb3951cc9516281827de299c5b71f22dab371d2" - }, - { - "cipher_suite": 1, - "confirmation_key": "c32c9cd76ccc04e7c72d32e5426f2e9257b36c445a5e753e110f4b640da06d54", - "authenticated_content": "000110aedf1d68267ae1f29cafe3d75abc9414eb4036974ee92ca5010000000730cdf7070879e87a99f4a33bd29f97323abb096c05b1432e4a1d0f91c9ff330c4b2bb4ad9e1e46197ba009a02f9ee1f61a03000040404caa80c443f85f712bfff2c51135d1dcd3f74fada54157bda393db4b17e054c2991a98f7d32f682fb7f24bda23a1dc7197013cef27e5d0706fc6db6059433908208c323f624a0f4ffc9a69be76bbc70edb7a7e5cbaa70813f5565156077313beb9", - "interim_transcript_hash_before": "0aebf9120cce03b3cd926a8b8a1ba285f4b12b488fec9734cc458932fbd5cff3", - "confirmed_transcript_hash_after": "4eec3017003a625efbcf7dd34f6c0377413b4838ac5ad6730c715aa6e5bccbfb", - "interim_transcript_hash_after": "e80be783e47d3d7506dae6a8f1ab53419e3024957b0ae22d0954b1a98534361c" - }, - { - "cipher_suite": 1, - "confirmation_key": "8836023390ce1497fcf4fe583c2a9a4a3345feb786c0bdc0183866617b28fdd7", - "authenticated_content": "0001103229189b407ebe07c35de5fda4ab043116ae190fa941dc06010000000730d8d0b9da6e5abf5fa665fc2984095f3d26c3e4000f3745d243d33b831391de69257b9d655074831807ec30e6b95bd3bb0300004040953a44247ee54e1a860aed0c6c2bfd20ef1261521ff37bc1b51b5941b6af02f4fba5b978708469a710221627668345e94c286e57308ca23c7ba32e542420cf0e20ef9cb62c6dfe9749a7b06f56c9cfaf33148c6393b32373f552b158b896598150", - "interim_transcript_hash_before": "5a6fc8b15988bedf9692892508a836c0742305bc3c67d2d62191db3882346116", - "confirmed_transcript_hash_after": "3540650addba5e5f17ef6f78a5575e497f591b5c74a244d348e195a5cf035aa8", - "interim_transcript_hash_after": "50d64e67fb6cc8edfef1eec160188371453a94461c6d450d1c637effb39e36fe" - }, - { - "cipher_suite": 1, - "confirmation_key": "6acba72ac00caff9fcf8a1190f6f3d9ec756b7bee69a13b6150cc90c8af70b62", - "authenticated_content": "00011047b53b0969940d8fc419bca6189983ea7d257247d5db7e18010000000730903ba12ae7657385f1fbcd5a0e703527bcfa0f835f7ed260bdbdef29ff61c9f33b7a7ca9db1ac35a0397def7f3ee9f420300004040c1da227f27e72af05a8fc27d3f31015ed9afb66d00f179fe092e3edfa211d9725f0e431f1c521dc9b95976991f546e423b090b467b1d606292959c84ff62fd0820cc3f4176337b3626f30ba800fbc4eca1ac6c3d81acc19ff8c1155c30de50e6a3", - "interim_transcript_hash_before": "d0abf08ca2b4e5c48b80011ad392dd67a8bc63221371ae61ca30cc339f0b2c3c", - "confirmed_transcript_hash_after": "8bcea07530b696ffbbcd9dac99b26330565c9305158e719cc00e790be94aa26c", - "interim_transcript_hash_after": "c7c70564bc01ec537aaf5d707d9984b63b27b8bd4ddc96bc9eb1c83817f40217" - }, - { - "cipher_suite": 1, - "confirmation_key": "5f7a7960f33ac414719aa1b0c42ba895dcfde35039979d928dcb08b28937433e", - "authenticated_content": "0001108c668f1ee5457092cc2f125bba32243fdc49a3c9023a2818010000000730eaededc0b4ecf99f2d0e2051470b7687857d6a52cafefc13890ee69fe8725681318974e2ce88747902a26b79e70d0cb503000040409e932c7729b72082d2469501f3f05bd3dcb067b82522e11d1a4c519403786d849a4b3ef06d5bf57d9a7348c8b0545101d814165ddc708a1dbcc209650a90b80a2058eb87330c1df80a237696733de0f15e429aadc012e56408015a97345d27aebe", - "interim_transcript_hash_before": "70f6eac0e36b32e4995d261ee474812b202f5c13dea8a50641dd17b1a30d2c28", - "confirmed_transcript_hash_after": "55b53f8250d7288a98b5cf5bbf733e31932370237903de79868dc310892ff40e", - "interim_transcript_hash_after": "a3932aa9b415894e820a697e21c53a6ffcbdc32c03492c4f6e57166e01719160" - }, - { - "cipher_suite": 1, - "confirmation_key": "50b19ac8ec12274f0613248e9b4e9c9fd633cbdb55f9f2f65a8d93f367cf521a", - "authenticated_content": "0001103e7571dde8fb45e4eae03638888fd084747764a9e3d816e80100000007303ecc528e2bcdef236e4fd882bc75a8a3c5a7f62253a245fd79c599f6cf64244ff237b424b5c80fee682d39eba879996103000040406da75d2f8ad950ec08f4feedc60100de2104f2c83d3ebc8d4ab10caf6d187361fcacafac0509966f636f7e8c5dbceff426f4547bc5f9ad57a89d01b3329414062013609107be5d60e689ce17cab8d46964177206762272ec721601c12ac1c91327", - "interim_transcript_hash_before": "31c2d3cf8299f321b5077a9a67a78a26c0dada97d3dfc67e9635796e82aa645e", - "confirmed_transcript_hash_after": "3e0fc7596ee24a1938db6fa98eabc892b37aa28a6d47b64203069521a41fe9e0", - "interim_transcript_hash_after": "8509baa9b2de2f640b0efc9a828e07bdd0ce9e7fcdec4221b76bea4fb7f16a64" - }, - { - "cipher_suite": 1, - "confirmation_key": "60d721af54de89ec0756e9f463ad9bcf98d20881a759d351db7738dbab1d5dea", - "authenticated_content": "000110be8d68511910dae0f7a71686e9b0030bbda3b2c87be3cc07010000000730d14dc3c1e99985c5467acaba02e78def7d1b19eb19c6b4d5e04d2741e883d78656fc8b2f6783297f3808e752ac8bb3900300004040340d9d1b3deba2e596c8be3a17b6be7f0ebf19fea5ad594c5a98ce5f8e20890b61948350a779611d2bc71b6847d9d595d777814734fd8e423588a76c0b56fb0720a72bb577b6e26d0a4d2123ed9fa55ebac7e7ba440cb81a7f404308e225d4cc12", - "interim_transcript_hash_before": "22241d769143c8f878a60fd01dcf34dc06625f2290108fb2cd08ff9b6f0bddc7", - "confirmed_transcript_hash_after": "5a76ab9eebd4c72e1d830a7a4924298ca7d642965eac88e8564903ecff095096", - "interim_transcript_hash_after": "e424ea2ef8d7e75ba00542f9a77380d9ce994df5cb42ec89d143f452918581e3" - }, - { - "cipher_suite": 1, - "confirmation_key": "43470a79b80696280454a75c0a43a5b969c847b1db4d4d461b8662e38ed8925d", - "authenticated_content": "000110a9ab30dc0323c3901839c08749465b2325b334c252c6ac980100000007301ed2d19b5858fb01e3c3c859093b941ac2f94943cea98b94dacf42d78c3f94450ffa521d435b8423f07c0fa4c90ea5980300004040f85cf57ca7ce1aaced932ef087443044f74ab6648de4fa82363b179957536ee1a489810074ec40f1033b5e9d73ab55c3426853d0ce0113a449b23f6a9c2d8201204fef01b666f05920fd0037d43964c3d436fcb61e87c2941450f07ca4ca2f4f32", - "interim_transcript_hash_before": "5c1d85e28df07aba865294d83f3ffd0ee264c41e7561ddf473a66330a330a335", - "confirmed_transcript_hash_after": "fff3a7121725fda1b59a946ae6187f2da9f0e141bdf6ed18feed318b5399af11", - "interim_transcript_hash_after": "1b3a8be06745f8e92ae0ad86385cd31509d4740b05cecd8ced43f829dd0c5aba" - }, - { - "cipher_suite": 1, - "confirmation_key": "309755a344a61466b7808baf6f01cfaca3a9ae837d206db5041760c4a391fa3f", - "authenticated_content": "0001109552f5f9008080f2dd47461059f9c5d5fcce6a8a68fae4bd01000000073010b647a732726954dab10b55cf5f00611697b7f432f6a5dadb0267219df68fe1b04a4630f88bf3ffb1edbb12c9979f2f0300004040388b0c7c2f9d9db3cffc6ba60cbe0231883860c5fde5bc4235066de8ae0f84f157925f99a15dd0d2fa71eef3f81b954b0158d3217b59522a8b77cb99711f1d0e205e36d4e7d6778f6201cc06832517fd9d317109d1059150ca82619e9cdfa02b58", - "interim_transcript_hash_before": "0e1e7122e2504517d5846228b936300c20fa83f207cb60a29f648ce8d8cbb2a8", - "confirmed_transcript_hash_after": "07c677f803a5ca92b578b20fb037360d8dc60ebd09c6356d98b3faeeebfbd9f2", - "interim_transcript_hash_after": "67ed3269459c6a0bb721894ae08fcd8a9ee2713d9acd474401a34df3a24710eb" - }, - { - "cipher_suite": 1, - "confirmation_key": "112c57a836a0de9c237806583d8ba5951b021c4b26703e2f72f92ee5b0cb70bb", - "authenticated_content": "0001109e50ef71ddf1f03d9a745262146dc4d672dded4d22dbc2970100000007308e795491f4b199379c19b3a51877d784f683e9654caae8df45a3e978343f8c75dcf4c1b560ac2d99c8ba02dc9db557a10300004040d96ecea956ea93080488bd556b890438467d5b7bd75902a24c23ac31ca0101e9fd47d6afc807d5aba96a39211b97b10632da5ac22dab277663e2d58fc697060920c90c552fb84f6c791f720aa463949d84878b25b8fc268ddd8460564e13dc348d", - "interim_transcript_hash_before": "11b11b9127c94066dec33c0b1b0a5f1c432f768968394fa9ddc73bfcfb291e96", - "confirmed_transcript_hash_after": "b62b96d12386a71746dfd41e14d6bc4a7fe526778e94ca1fe20febe5a8f7e57a", - "interim_transcript_hash_after": "351f326fff1f8ec06eb1173ffd2a48bab22b3d08c8911cd556214c1ed8e2d2cc" - }, - { - "cipher_suite": 1, - "confirmation_key": "e24b727ed8023ef284b2aa7da964ef8f48be54db87311953d28e09744b7258aa", - "authenticated_content": "0001103a888a1159022d61bd4d1e3790a4d590239492a08dffd091010000000730db8d9ae912009bdfc8736ee649980c073f13fc604fdc7c907d835acc92eddabe9f8d20480d97f2af397a5393a86dd746030000404072ac3ea630aafda5f6cb0434b62f3dec90b0d0b107627812b3d51bf5b446f53982eb9dc8e8b1f992a8096a2d789a4de432fcbffd8a655e7fe454da26c7b6c40f20b484f1ae8e74d605196553751f7b9904ea10d4fd29e17b2d2b4ade2f9f952c69", - "interim_transcript_hash_before": "a3215b445326b870d2f23331ae9fed514ff890e894f6f972af6a9918716efa5a", - "confirmed_transcript_hash_after": "2c176577402c0eee8b97e115108e1447751a01cb8c6f4fcae8b4374af91355c6", - "interim_transcript_hash_after": "ab63a6b7ddc1178a421b1c03b970c33e66bd9640bf903b0c7bb254c45f797d2f" - }, - { - "cipher_suite": 1, - "confirmation_key": "38348fee2a77cf72fb1839dbb2c33a50946462e489d837fd58f6c6fd3abe7926", - "authenticated_content": "000110d260f060db3f980b9d01842d808813b6ccf6875325c84172010000000730c6b1b480ff38ace425f20b9c20ba471f9e72edae4354fbc24457809d9bcbdcf935aedda34d82d7b7e6465d6d03d9f5640300004040736333d91aa37301e1a5e06eec825ca77f699b9f8786a13c7e787affe8cc09d916ef3eafd4f0259922515b5343476174a4d51a859fcdb65e6412b445fa669807203e59f362c9621030eb0819ec34a4688174b735127659abab8e2c551a01786e11", - "interim_transcript_hash_before": "ee5b550d0115f19ba9cab7b3f28529b555cd3e886d43ea2c610063b7d2082f11", - "confirmed_transcript_hash_after": "fd115f08687ca179f145e4856b2a1afcf068bade077ad9d0ee346659fe2c0ba6", - "interim_transcript_hash_after": "29623e7950169547a905ee0756723415e24be363ff0ae84b167e522536f483a8" - }, - { - "cipher_suite": 1, - "confirmation_key": "79e0dc1b3987f343dd2babc9707af9596eaa9aaebf3425ace42eb3d177e047ff", - "authenticated_content": "000110d6feb9fdc9950ce1f44a97e918ad562b4b4552e5356d6333010000000730275142a2921acae7e3eb3e844f1f82321efd25a8d90bb8c23fc67aef6dad4d0e43e0b998b77d2bd0a51955980b91fd510300004040b13e86b52548d7012ac7a68c86a3937063f85a457e6f58c9c60e785848594fce460b795ae426dc425148a56d777e344538b5c02c8fb35ab199787465196e70022033e2049a7fbcf6f70bd4ecd83c77f7404c5f4823b17524a01d54cf61c273ebbd", - "interim_transcript_hash_before": "aca848238f5ad42f28dd6f85df3a2c98e61a53398402a364caa0d1d756ed0adf", - "confirmed_transcript_hash_after": "3bbe0806e889918af07930c7b3b098b31e31eac719a38dd4e493e87f26fd8d6c", - "interim_transcript_hash_after": "3dd76df72cbc4fc20072ee0f34b8a526e8e1dbe24198d64c0656c78ed2741c46" - }, - { - "cipher_suite": 1, - "confirmation_key": "cccc1a0324d72d164d2153fa53de226b7d7db0c51dc4163c2350e3da88659dd7", - "authenticated_content": "00011075e6ca53de4037766cacc6b763de787f9e755205bfb1b921010000000730c773f395593bffd1ded8189f0bca9dc10164af2e0951818ae89705fde54b449acae848b015466cdec035d1d61a5ef73c0300004040d5c4b79fe984df421276b0847624da525b8dfe5de6daac79442dc52df4225d83c5b0faf46135b9ae442a692de096a04b1e1d29dda2c8dc75741ad1c64d9d1d0d20285083374fe098c9700e13e64548bab49eccf316c5e570cfb4fdc38106ff799c", - "interim_transcript_hash_before": "35a4aa8eea7c8f6c5dc8b13fc4782205090e9f428b4c1958368761e95a3200bd", - "confirmed_transcript_hash_after": "e5bc2dd4fb4552eddc4dc225b1b80512a19019364b24724a89ded647f5d50e62", - "interim_transcript_hash_after": "e064b753a7878cdb90b496e2d5dd91b4cd183d53194edb21d4e9c1dc624645d8" - }, - { - "cipher_suite": 1, - "confirmation_key": "a631d0c5e456535820317601b744ff3830c12a3922a6bee6eadccaabbd7a0cb4", - "authenticated_content": "000110e4c2a089d10c706a325ae76982085823ce8088cdf1c7eb250100000007304ea073d37f58c5dc32cacba7b294a729587f80487f03d5c917136f98f0e87a780835f432605141c4c1a3208324cb9b6f03000040404ccfd3bfbe7fc0c2c0079b487dd76bb2e65ac7a5b3f1ed2b962e964ceb0acd5588053f272d7c8e8514b3ad3cce200c4fab82a472c16b2061f0bbbd064bb7530a20c5deffa8070608ce4670b177523265be0983b2c83b9f5a95a020e36072409afe", - "interim_transcript_hash_before": "ce3229aa3d1d4f2afc11ef2763024d5d64ee22e0e0daa1d6fbe573747e93a47c", - "confirmed_transcript_hash_after": "16eac21ab9595d944a785a5af2f524ef56b7f174c6b77081afa8bf91c6d60cc0", - "interim_transcript_hash_after": "5cc1b7d3e43666fe6e5e444cc45576cc5b6a39d78732198ad505d3c71505c926" - }, - { - "cipher_suite": 1, - "confirmation_key": "3a52fd23d47b68efbdfc02ca4318ba55e74fa87a19a53cd9138a5cddba77d8d7", - "authenticated_content": "000110e55c74a43be8b27c46adcf98b6247e894daf394125789fac01000000073072efd2053eaeb00ebe1ffa1e51b377e19e7ae2f1150fec24b25cddefeba48edbd08c7c70cbadc7eb22db8596806fbeee030000404085564f5150c98412d8f4fda6a5c43eebb0e4196cd41b971a1089d054511f03d7bd9b5c662360570e2982b038b84167dc1e7bd423d4838d8e86f5a82f3b84090520eb70323ecef56d938d1dc5c4473425bdbd346c758c48644292c0f305313156c4", - "interim_transcript_hash_before": "542925178e68d24022fc19f2cbbed76481f4e9a378588f747401bc1a842d9b68", - "confirmed_transcript_hash_after": "31fceadba8951b59ec45d61e4ac279a6e9e1d9d52af310377f959e50c5de14b1", - "interim_transcript_hash_after": "e4cc7b049091155245a241fbfbe7472d525f9eef3f9767558d0347695b092634" - }, - { - "cipher_suite": 1, - "confirmation_key": "4991e1590cfcd33bf85601251604409f0678b89a54b6544c2a615ebc749c8cf6", - "authenticated_content": "000110a0c682b246081042b24464453d3b36dcb538d25a0590d33d0100000007301916b1a9ac8d3eb6236da66489fe3658e578854c9746f03118762f06d76b3bc4b26e34b8cbe47ab8e90ae846423fd5530300004040cbf674e3f417605db9a594bf84ad34c6a7dbe3fe11ba01b1b4384ba7d09b4550739f659c53b25d97790a1421a5b24b4f91f4c02fbd1104e7406e567643b59a002002343d6ffb2fc1f943d257bea8bde7cedac5dd6ef91af80f688d523a23d304e7", - "interim_transcript_hash_before": "d331aae5401f8825c8b968f66943fba7672fd6f0d959321d445442f1fe24888d", - "confirmed_transcript_hash_after": "ce82ed41d1d00bbee4b3ebecf0210f1bfe2f713edf7de94e3da6a189033e2117", - "interim_transcript_hash_after": "fcf8618c8800f67c6446a1d6a82955ccd201d6b603059f404fdbb3ec447f14f7" - }, - { - "cipher_suite": 1, - "confirmation_key": "382d8ac95998b1315c7b2c8079f005701338b95469cc6d04b92faf58b0961a2d", - "authenticated_content": "00011022ea307a8081306394988fd9f74b67daa8c1d83c8c00d391010000000730db40c4cd7d7943a56e3b41f3d135001b74a785fc2e15ba52c97ce19f013973da9d76b2d5489d1b424327d63795dfae370300004040f3508e0a22a34fdb19985dad397bf6b6ae4f133b1f15aa8056761d6c7c0947fc90e0a5d4f9df7c26ce86b31097ce5947efa9f3521c5dfd8e4d3269c8e220f20b2087445021315b27c6fa546150460ea838642cb89c43cb24efb0449eb3498ed626", - "interim_transcript_hash_before": "9ca5117417d54948f125f0e5ea844de10e222e5ce0851b28f6474a6653126fae", - "confirmed_transcript_hash_after": "7cb0bd6580d9bd692e0ea30f77db58bc19f131b0e4cfbba9e775b13084d43850", - "interim_transcript_hash_after": "9c2a81c9f970ed82c39782a3fedab46a48a6dd72ec017d4ec14b5fb960654569" - }, - { - "cipher_suite": 1, - "confirmation_key": "78787737cf09dce1f1d1b6d96112f3ac676ba1549d5d0d9b1935e64f49de42ea", - "authenticated_content": "00011099a3a7f2cae757a15d3697b1a3e1d0e7c1bac5506da3577c010000000730fda7c651f33b60235ade93a81ff3cc3758c7aa9fc8f450b26ea733398fcb48c903a994f8a48f77a406c937d5cacebf4d03000040405db2564cc81b8418c4520c9c0471b428a132d87def2f54caa497d256ade6ce9a9f5b505bad3df502d6f7299d8f20cd8e2f6a006f23502c38be7a00c5e9ba180220d330fcead77ff905ccfcf5ad92b38d589eaab1cb73f5ce7308c5d6eb8c8558b7", - "interim_transcript_hash_before": "8706d19d86d61721ab68e2ce60fc4834bf132db3520dd40d45e656795ec87af3", - "confirmed_transcript_hash_after": "4de185bdf5d3ceab019f88bd2ab236cd017fe6a4b3265decbea277f15a7e5248", - "interim_transcript_hash_after": "5fdc76ac66e9e9ffd7232d0849947a4fc28ee7211a20c5e2048cb352ba9277af" - }, - { - "cipher_suite": 1, - "confirmation_key": "6e4f716547e98533c95f605496bdb9cb98674ba6dd3a8e2486cfac45e9a452c3", - "authenticated_content": "0001104418260ce686eba34427bef5cd313e1897f9a650e49bc23401000000073013925bf6e192d692ea8881ed7a51c75c9b1b65bc0b1c93d240895679e180897de944ef76ab16d8586a911bbfd6a059f003000040400bc3f4a72f8029da5fd6722b5d2f99f419d6b235fb80c1b0d44b1b5be513d6193c869641160f41403b944c2e2bb194f8ae091c26956cdc60e53f08fbc0714705209336e75c0daf28cea4fbfbe0a1b409742b47f3f214eebe0cdb1b83019c226e45", - "interim_transcript_hash_before": "75a72e2648056c7d74fe10c15958c9ff0d03f3fad6d9956929b38b17308316a2", - "confirmed_transcript_hash_after": "f47b24e9c61d575ee4804ab5d3a04d74ba57725d5ad648b45dd80486aea77da6", - "interim_transcript_hash_after": "4770a48334ae65bbd1492c5202a1e72ca78ba57c883e7b25d287f4e34ae79b56" - }, - { - "cipher_suite": 1, - "confirmation_key": "7d6a558b3991012bf3d0797c70fd2ff96b5da80025734e06f37dfc91b2985cc7", - "authenticated_content": "000110289ac873f3e8c2098c17e095aa35f3d827308bae47c36b1a0100000007307702dbf3637a0f565468048a78a02cd1e325459fe024bf26682a13183c5c5c06fefd1f595792f523602501489333d73903000040404a1d6093ad94bdc35af1116e219713196e40088ac751ee40e953d6b48db43a822b71976858fb7a383e6b123674d00f32cb39c4b356bad4f3573ece1e7da797092068b33abff56ddaf3b80288b0a667c26c34a0958294c521ec47edf687660a59c2", - "interim_transcript_hash_before": "837a30f54e2ff221bd699047d736b33aff9b9e6d23ff3e546ceefe5270815765", - "confirmed_transcript_hash_after": "b2b1c4e7d46a0012d752ed50be922473cf27ce5c636bce24ae4217f5b9845398", - "interim_transcript_hash_after": "6955c4e8d94a2f9677258558604bfaf04d16c8b2519584753c129b0eb08c0892" - }, - { - "cipher_suite": 1, - "confirmation_key": "0dcfb727aae723cefca93d1725359b14a35fcddd677189b88b46ca2845668bfe", - "authenticated_content": "0001101431d329d6a3d28be951130c90deb0366903c5f9b1aa6ff101000000073039f3818515ee777eca7b514ae8b244ab0ed0dce7c2ca61e4f4b8602bc4d517bb8e2c300d1f5e6d99d3abc7dfe6cdd44d03000040401faa72b70fb722ae2441765bec1464e6977f59d5dcb6c929a62233040661b66681946db69bdea2a2b40e4dca07f5907f2bad005687faf832cb54d8810a39f30b20806f6c9b4be5e22b31797521fbc0e7075c70f9869a7341a8f828d61a73964fb6", - "interim_transcript_hash_before": "e6c9386f493e8e7201392137a9bc3f9064275c8da1e512732842619a6a4d29ff", - "confirmed_transcript_hash_after": "0f1047b43f1313160f9843434e69d5e2ca680ac772f5e6799fbeb3b36db29957", - "interim_transcript_hash_after": "bf5a49cc753ade562f97ce409689400a2830d4635e08948327b9261fa81c041b" - }, - { - "cipher_suite": 1, - "confirmation_key": "d356d8853a1b92e33ca94faafcae2c8f9e75b44c46052f0ac8703eb97d238d4d", - "authenticated_content": "000110a13e9946a2856fa51edb0160560eb075d9ea16cbe6b4cae3010000000730c58d77682468a12f8bc2ee113f11174993f6edaba4763dba9642c5958b6a18607c044b1c9addd0181628a313f14bdaf603000040401e3d4590ed7767f07314d85d46da1dde34e53835fa7c3bcffa55de5e19ff738f05349b50a8e9e8319175d4fe03e633af4dcc96d1386d1e429d28927d672ce704209f079f071505a1043378f245a412fcc7f188b7ef9947ff7b04a5175597a84ae0", - "interim_transcript_hash_before": "ffb8c81b0182425d74cf9f2ae708b4fdc4f4450b28fe09a195a0e84093ea32bf", - "confirmed_transcript_hash_after": "fbfc65b573f6cc475bf9eacae08f3845e3b5b08294ee7576da0bdb306bf7aef2", - "interim_transcript_hash_after": "10bd1310e6b43a48d2248bf82f98467a86f1d7f4e3e6ad27a3c61e2d1a540711" - }, - { - "cipher_suite": 1, - "confirmation_key": "006fec0953384835a89c1718ad579a524e42b31bd6434d48e93adac8e69d2b04", - "authenticated_content": "000110324666a107d199ee8ebbb46c2e6c9e920afc0d6ddf267d33010000000730df445d01fe773919f5aaeb335e64b98e331351a31550a7ba9825e9714b31712bd8fb25250156f4aedcb87206e8f5571e030000404018268292ce541200d7484a73a46f2c817b8d12f1ffb1936276758fe7ca67d210004bd5129f9b8de4376cab6c7c2d1f2160bd5822b1d07b1616f9ccb1b3870f0f203e59343c5f833bc3e74807b84d6eae87f28b3265973e0486840253e425cb2fc3", - "interim_transcript_hash_before": "d7fb1d456de881d5ba5b4fe64ae0c9b664736019d1d6cd987e9bc8634764038a", - "confirmed_transcript_hash_after": "5e06445afa0e20899f50af10840e6cee1b390e24ba9d49ce650b4c633a705f5c", - "interim_transcript_hash_after": "9767c144bbb9e8351dd055b02aa17c996f223ed8d83dacd88173f37ece86817d" - }, - { - "cipher_suite": 1, - "confirmation_key": "83be024e32509ac0018d4b2bc47d4666eabca212731e109796a072af890828c0", - "authenticated_content": "0001101aa33c1e7af2d7d6e0d52c4b136157e1c9d4cda02cef6b6c010000000730bd4a68fcb66c6b84c090fb5a3f1cffe5f7b408d799bc14507055f41acbc6bed7f77e1c19671030f1aa9c55d54dea3dd0030000404093d6ba3f6d1a3efb08f3aff75c5b9e086311c0bd5626adf72e0a6ca24a000cd5d96520b127e55bcc3088cbb326b335be00a5584a98138e668a64b5a08c5a810f20f9301a141531d4edd238819c4e69a37068180094d597fc7c1fa91c5fefbacf1f", - "interim_transcript_hash_before": "95929013da158edcc7e962637415175e1e28cccb3bffd33d24428da49bf15b54", - "confirmed_transcript_hash_after": "1eb4d5c295088d1ff36ccf8b3c69d9a7bf816b32ec18bbd1273cc9abcaccf674", - "interim_transcript_hash_after": "bab847e29c2afab990acccab6abfddb94e46c286c9e3f2f9d060e5a107b7eb31" - }, - { - "cipher_suite": 1, - "confirmation_key": "3496df1242c185bc7246be4762fcca624c07096cfd7bd6fdc205d989205c387c", - "authenticated_content": "000110a0d55943b038f5deb906741ee4276397690d3569283d8e1c010000000730618a5a4fc1138e5daf094b5da47399dfbf5baa9b0d4ade3b2135f0cc9c910d582970bcaa5bd88989539b3f87e8b787a50300004040adcfe570f6c2fecbf0957c9913090947dd597d0c4ba2c973d3a9ad8557ac3ac31f320df346a48e8b35ccd25c1447f4cfe455729380aa5b1dea64470eb817cb0e20ebea3e6824a96b78313aedf20f9d60765d475efbcec86a7af70c830870033089", - "interim_transcript_hash_before": "0482e052abfa5ec158cebd6af4dc13047d844068b26938bc27ef836ce2ceb888", - "confirmed_transcript_hash_after": "086098bb77c0f5d8c50c8e9192e6384f51b02b2152108a356af6dd738684e194", - "interim_transcript_hash_after": "d1eac7fe5566c563b40d7c6efed016ca8285898ec1cc2dc0a653534005a7ea86" - }, - { - "cipher_suite": 1, - "confirmation_key": "c153311f5b1672158acc781fd8934b7cc6254cf83e3ce4d484a2fc48ce5d1221", - "authenticated_content": "000110caea00b446b5c9a948e8c2944b5b89972bcba6990b7edfda010000000730c683150f9ca7ef8b631e5bbbe77becbb8d54601e6196d3eded7b474398b47321a9708798ce47d51c103263b737f255b503000040405ff226a21d15c2e032740c38e91618c3524a372d5d83319fd03447fa8bc22d7088e6f9e9f6b470d96b15a72d3799b6d0fc110cbd0c69e5f7fd3e6accdec0d006209e3085b47d3b70feb4765d1da529d4b9c7b67494c59efeaebff715dc1d12664f", - "interim_transcript_hash_before": "281f61e2558da5976f3e50d91b86fb8e93c2c9e39f587b1a9505ae33f6c7b7e2", - "confirmed_transcript_hash_after": "6c55ceba9cddfcb13f2caefa1b9bb1442ac2cfdb02002c8da07db1a315ddcd98", - "interim_transcript_hash_after": "12cb755e0c981132f9ef4cd479ec4d449542e168140d2fe9933e3ca1cb41113a" - }, - { - "cipher_suite": 1, - "confirmation_key": "9a5f61f6e15a22ee075dd87e22d859a0b159876aa3e7b95c07ad157eb468b77d", - "authenticated_content": "0001109039fa4943f68cba0836479dbdb5471544ceebf6f3f69feb01000000073095ad31ef1f4efd5ce37298da6ee129908d2fd87120076400fa6f0951de6ebbccf754ca5b5f51e662a249bda4edbf60320300004040eb569312e3e9a4d8f798d7b8577c8408ae805a0e18cfbf888e1fdaa6e646228f48604ea7b61978178c19981e64ee0914f359ab40f50900e3eb0a55080af41c0c20066f9b0e1ddca1686fc8af2611fe2914b36e76a63c1dc90802c91b88fca18035", - "interim_transcript_hash_before": "620eddcc93fa4b5ea0f68e95284dc6bd4bb2d97b2d8fdd5b46d4c05ac4cac23a", - "confirmed_transcript_hash_after": "d7d4967cd713ede0ee29797f708a0eb9a00b562654b5ecd6d12fb8f322419cf4", - "interim_transcript_hash_after": "2f3f37e0353cca879bbaf13604b3e9f5a5904c62fff725479aaf37b777feaf7d" - }, - { - "cipher_suite": 1, - "confirmation_key": "f17740b70b1f6abf49c46fd2222e149d7404ca8ff1bfb849b9cb4e9d5f87160a", - "authenticated_content": "00011081cfffce2c73c03ad1beed09a8a76fa369405ab5c52cc12a010000000730a1e3041783fafb38aa1a2139d71273d8aa9909cb36563560b54fe46c30c3c0ecb2f37cb36c34d15b520360e1f72be90d030000404054d5f1c21a30940ed49aea3361bcbc12b8d1f04f5247d2aded79dc08ed4c0f0204abe900dcc543501468e1e3d857a9d90cd1e810f4755f92f527b6f95d5f840f20c1656fa0bfe150d76f59885c6770f1615220c7bbd65bb31e5901d8b022108fc8", - "interim_transcript_hash_before": "019b719d1483e448df2ecee6d12382ab496b071b8d846981eff2ab1d63394793", - "confirmed_transcript_hash_after": "b8c912408c1080a33c2b44ef268946c2be0b3a2d9c1fc236a31ad13788d45b15", - "interim_transcript_hash_after": "397cc13b8c405ad8be4ef3eef20f45e5c9a1ea3006f4f81b7312ad61ab15d8e0" - }, - { - "cipher_suite": 1, - "confirmation_key": "79772ff2d2c80442a43e2ad6c2ba8d23f7f6f08b8ffe9d6cf9b28077e9691249", - "authenticated_content": "0001103ae01dbe325e95b9a5a44e9a099f13ee1fdb5f0eb9ced166010000000730cfb38d470c57e088919cde9e1c92dabe6b6205a1aa729a010e9237343f03477cf26123f151b0efa112d596f78355d6b1030000404083ae5461dfaf2ad9077423c227119df9485136a90514d4defd78102aa6f6b814142abf593cbac9b230f6e730c14dd106f6d70aa0bc414bfe26e6358d4a9dd90c206c4b99e5f1e66b38a8a06f8a6b1422f588d1f62b3934fedf39d1aba3648acbf3", - "interim_transcript_hash_before": "4c3b151441b01529b2cde9499670ca134e777647e01780f6895ba5edd21aee9c", - "confirmed_transcript_hash_after": "68fe614b49d9b7eebda4ee1119afbd968bdf8e95c16590a546b58d7079b2f667", - "interim_transcript_hash_after": "57b670fd2b0f8f32598630759b564144bec29e6a14028a2fddc4742ec0bdacf5" - }, - { - "cipher_suite": 1, - "confirmation_key": "9efc9ff1a7b754f52169d7fe2d345caeb18036c9ceaa1069292308e71bb58552", - "authenticated_content": "000110090743155eb109642c5fb17d0ec6c31a64031eb4388847ce010000000730ff4cd155b69b6073ae404c2519bb2928e255a4e4709fbbf0ff51306be2226eb8541cd86613ede4c97b375c8da2ae92e303000040403621465b175912f8db76ab7e6aaadaae440a1b184f5ae6df4bca672024da15872f9b4c1079c5fc8ac2a891ac231cab7fa7fbbdaa2096df300dbbf38db3e38505200185a0e0d63ce50a38182e6604b731b5ae9f85fcaf0bc3b4affc2c0a29d54528", - "interim_transcript_hash_before": "2fc088f1d6f8ae88f74866a5b61dc0a3340721e1cb380fa90998fa7896f77f38", - "confirmed_transcript_hash_after": "e9f07c1eafb6fae3057df5c705bd67b5b8f993aa1bdc1fbe18fbc457facc67a8", - "interim_transcript_hash_after": "d7c476616cb4a52439db25d945f42c053bb1ab5e4de5b50eb0de9101c6ccc1ab" - }, - { - "cipher_suite": 1, - "confirmation_key": "eaabe07963528424dab4b7c82de978632cd687d6ac929bb869e3054e9ce80309", - "authenticated_content": "00011026eb96430fe4f32f3f66c40efd975c5129292815db8ae7d901000000073088583aee6e5cf391945b39035fd51447d7928f724e80eb021bf3f65c8ea9247682d2ae2c9888a5ae0031565eb77d89f003000040407a158fa03b5cb8bb0d9bcab25530c440bf97e08957697044eb3099c667850d3e1f72d89a0cf86484a3e887d5e46922cbbc30a0054d29070a1b9c5ba1ce81690d2016ad89733733278c31c1f7bcfe6ff2a140f51038c860fca47763accbb9aa2eeb", - "interim_transcript_hash_before": "587380558f0670d38ab66df364255c45fbf7917199e0582f0e8ba13361b3a395", - "confirmed_transcript_hash_after": "542ba684f80d03c100bc1469ec7a704a2a00cd0d077e52f366248f3fdef452f2", - "interim_transcript_hash_after": "2703c97cf05e0056c178fa9262f9ef568c916190120db2a868cd7df5d7e19bbe" - }, - { - "cipher_suite": 1, - "confirmation_key": "7ce273d1b7ff72d460947f41a668d6a582258338e4fac2aad374ee0a2efe0efe", - "authenticated_content": "00011097134d7993eaae1e98772dafd936e5e412dc18ac4ed8968e01000000073049ba6c70a3cefe33001fd6feeda8871a10d3e133a425feb80ba70788c502a11a27b1eaccf537fd50cefba96b4bf3fa1e0300004040e8a61529074083593591edc90ea20b911bfb2cd4245e2acc32f08664a6703b52897d93fa9a372ba0e3e16247371ae49a617b15c141fffcd7e50aae15888df50620a896412907a1de2314c0695e7e2e0642c40d7b05e7282f8abd28c33377ec6cb2", - "interim_transcript_hash_before": "a12bc421d6c476a2e7c194d534adee12c9f67d115356a2fbbddebb6bddcc2d56", - "confirmed_transcript_hash_after": "3169efa49a8131062854dc7d483b27f5f27ca550865bf4f48b14c9d37116ca40", - "interim_transcript_hash_after": "a97e23344a76b64739fcc4fc614ac29de77ef0c807cea2d6dcf0d5d892c82ff1" - }, - { - "cipher_suite": 1, - "confirmation_key": "2a8d367164ebaa368627a5d48894d47f93c634899f4def68872e71b16a68572a", - "authenticated_content": "000110715257ec05b131b60bd1e711e8c8715341afd91c7c863888010000000730cbf02fc6fe9aeb124d4281309e6947cc56ce38c55be0e1b307739b25ed777da57fbf2adc5c1b78f8e000374ce437b7cc0300004040f5d7666ae675d3a91d0d953b128d9434ec4d7962afe0ecf53dbbbe7628a213c05af4e6ff0c44124aa14360225a5bf42ce5108dd06cbb05cb66e608eae461400c2064e3fd64de883487237455b61d0e54e00cb4bfb5d4f23eb23a3742dd9898e142", - "interim_transcript_hash_before": "5574e0715e1aa95ab52e0c310e41e9e60dc5ec5aa86c34fb8e3c73033469747d", - "confirmed_transcript_hash_after": "3ad42d127f454a565da8be1c3b6f1433c27a2a59fa3f14232fa6c36b5f29aa31", - "interim_transcript_hash_after": "dc970deeaafba23f4e1a15ffeb3713c99ce1c48faecf338fee69d5c1698468ed" - }, - { - "cipher_suite": 1, - "confirmation_key": "fe2cd4625c4ee967c496e68298d6b4fe882349176a7dcfe642216b34d61d4360", - "authenticated_content": "000110a79c084689ac55d17853b91722cf2270fdb8e7e7c33e5c25010000000730215ae9d1d7cdc3087ecf070624e2292449bc301f52f6730ceafde3af793a013e0beeaff77a6050a8d91265abcf5df07a03000040409c16ee92e231381869cc663cd864738ba3dc464c5b46216286979148c3ecca5838fefae7f3fcafa047aa302de5e5e3d527429ffbc0e5ef834560309adce81f0c200b5699b1a862c810648f82e6dc18a6264a027365f977bb63eb88bddf6ed005f8", - "interim_transcript_hash_before": "2560d18df3e5eecb701ce724c08b1b5f878387e2e9616efd787b287c31152f64", - "confirmed_transcript_hash_after": "c31fae153d8d0d571ff5bd7f93d41b3472c94b2c8b0ea417b6ad8e14993b7d8c", - "interim_transcript_hash_after": "8cda5d8e4accf88c382ddbe8690527c689aadb69ded38e96cb2a3f2afab1e101" - }, - { - "cipher_suite": 1, - "confirmation_key": "1e1457bab37fc02f0fb84fb947ecd48fbd8f7bb9a929c86d1287d9e73180c5da", - "authenticated_content": "000110d8de623f27ca8807523ca64b841c12ae2f240adc494ebac4010000000730d3f33dba788b87f387749c0c7d29fd690793ffbff8020cd9a7bacc66f2504846b453ddf842e7c6e9e88eb0ccc979f5c00300004040c63722bd19db5f835372b7ddf12753c7c4366a86d33d21f279964421e0e8200c84b594dc85fd78315ec80d4b0f332799d2101ac42921fbf0263a7c80b3fff50d20506a70b9cc8a2bed771d06ed81c03eef5888523f0e29a6a451f9a28a7e133ab7", - "interim_transcript_hash_before": "a7db864205b123b500be78bb85ed4526997034446b72ddff266e0dfb1d054a98", - "confirmed_transcript_hash_after": "56b89816145b194ae000b914b38ecbc595c43d6abe35fa31109209f219f97138", - "interim_transcript_hash_after": "cb87d6860594c9403d41ed4f52b26d58aaec23d779d5882511a3a1e96c14ce80" - }, - { - "cipher_suite": 1, - "confirmation_key": "ab04aecb97a33998e2687735006ab39a5ce06517cabc408a596137b39a452c4a", - "authenticated_content": "00011038230342728b5aa9b45c5bc3ec41a6537d59a47b2c98364e010000000730f797186acbc97ebba6194a83363a7b0473181cb1004326b0f2ee469ade47c5ee52682e3ee3f3622e0a574a161240721703000040409c1d5e26c71cbdf08838e1fb4857a320764fe344239f0c5ff6885665b9dfe071c3a87f133512fda083d93679cad104782299d333fce7cc7f605f0f5db86fee0520faed45519ff242f4a897c588abd49a429ea673c3967bd98f0c499fce39c14e4a", - "interim_transcript_hash_before": "7827c518ab619bfdf6d2712ec9cd4724f71908b62453d86be253331f9f731320", - "confirmed_transcript_hash_after": "6d5d73c5e63ed019c80f2ce3f383ab9028bcd94b5e2e682fceda91cee3306d08", - "interim_transcript_hash_after": "380c91e1b10994b2bb708b1d028b4d4cf1da1cfeb5c20061c2036ad47508c427" - }, - { - "cipher_suite": 3, - "confirmation_key": "a8b9b8d128b65a628c8d5954d670c982974ebcf3ef3724edbf7bb3a597a1a27a", - "authenticated_content": "000110d035c774f72b952229bb7f9e4374f432606d7606c915046f010000000730d8ee286de615e9b1a53055a367c56f9029be6aacb2cd7af301b215e77b6f7abba89104150438f8d75b2e5b2d01963b7503000040409535b9fef6d70e9dcb332468a6f4b2450122e44768db1f6d1d57b2d39c809497033231cf1c64fcde457a372aa7a4b0b1da1ae4b1d8ac6ff1d5c67eb9d40f7b0e203a82ac2ebe11346cb1351519e0108510dd879f18873001c1ab911a8690c01b5d", - "interim_transcript_hash_before": "2bb80731ef44c9477160ccc925cbb3d1e1caaf98428b6988ebbcada026e9007c", - "confirmed_transcript_hash_after": "bc97ba25d0a2ca39380118c8fcc97cf8812d4b211d821709e69e853bfc4b3ed4", - "interim_transcript_hash_after": "b9742efe9dc5a293d965a5c2bbebb33852ca981a152268a369d40730ccaa39bd" - }, - { - "cipher_suite": 3, - "confirmation_key": "c35dfb824e9a8f6ce5205821adca561c09d987876858eca82150b34b1733dc4d", - "authenticated_content": "000110cb4ccb444644ca61d7eff8c18834c6590c8662f081875c4c0100000007301ffb58db50e2cef3f14b61f46c4d0965d2598edbbb8bece507874da2382cf6ef9af6dc6c695f9aefd6e0b8e37f39fef903000040404eb58790ef7f1beb8ce172e27148b7efb9840440dad2ebb167afbb9e637a9a50096dbf2a498a079f9906382e914295f6d30db7ec3cffec6c4f30257bc4845006200ae29334aa19ab49db60ae2e1df764d9906500426fa9bd92f2f1d7f67baff148", - "interim_transcript_hash_before": "9409274fedc6173a678cac2605d0f51f1a7182b29d7911c1047cbb7e682fdc40", - "confirmed_transcript_hash_after": "42e8c4e67fec2c6ccd19feffed56f147cd65b8920282cbd511dba5580e1c7df2", - "interim_transcript_hash_after": "ab008b54a7d847ee17756085db16086522fe7794494a0100a730146099fed581" - }, - { - "cipher_suite": 3, - "confirmation_key": "91614c2deda7962fabc8231960839bc431c85f23a77752b62ed838425a577b3a", - "authenticated_content": "0001109193be453369c7adaf5c804a1b83d1a9705e5b83c991ecc0010000000730e4d5f52acf95bca9cf0d1c0e99b1e8e06a1fc6b8a5a0570695439736668a1a9b1459a22d138677bb59abfc61f70e4f2d0300004040d1a7881d7d9a1d27b95841d7b673a9ea9d920808be00e1d4771609d2dde11b6040c8bfed33ad285cd54d3805df330389557070bf3bcf80a2e5535730d51a780e20a3aa63c1033e7af7b0f782236f36768b0cea28ce6656122d3c1d4be0a3517042", - "interim_transcript_hash_before": "8e4324ee9faeef6dac00fa594b2e552d09e74d7b2ee499fd1345c164a5a08479", - "confirmed_transcript_hash_after": "f4adaab12193586676189b5c9f00254c1dfe19e23adfd90ab1b9ff25eb1b6bfb", - "interim_transcript_hash_after": "d2278a39cb8e31334e144b766f2fca8fa21c28ac49134b29922716ef9328050c" - }, - { - "cipher_suite": 3, - "confirmation_key": "c0003a070fcca0f32c93d55088e9bee57639399e920f8126381415d1b38d16bf", - "authenticated_content": "0001105e1e81e4b84759186a9d236710a00a4f576694f969ba2ba90100000007304750ff99b2f1eb61630dc2046e65f6d68f5c27b9b52aa531b4ed9f152c783d533bbbf15295a579c3513e32e8a50fb15203000040409645a5c61ccd92f1f5e1f6401a42cae6c2f7cde292309b2fe48a059182d47ffeb108449da31457198a5294f1901d47b979ea7138bbda57bf9da1b0f4ea71590420551ce4588e73950258792d1a3d97195bc9d6967b2625e71a6dc9bafd9d79852b", - "interim_transcript_hash_before": "24fc1f6b8d0376c7aeb58fa6aa6f32bebdc222573e8bff6ed0bb2440ba2c4061", - "confirmed_transcript_hash_after": "11d6ec31e1e38ef81267b74718179fdedb320e1bdaa62739653a3003181ffbe1", - "interim_transcript_hash_after": "37de53890122b2f716eb3a50a1d54ce294ecd64fbda4dfb34df3ea4708c35552" - }, - { - "cipher_suite": 3, - "confirmation_key": "418b61640ec641e422a2a3d3c6d8d9ddbac8f4685430a0d241c4cddd1a2e999f", - "authenticated_content": "00011019a6839777d68db9459987d01809f98be03661431716ec7f01000000073004dcc5f6d05bd88816995710e49e6cf8c17363f8b73438bfcdc61064a74f3f458da522c2b4c7b1d5fbe0eeb6751599aa0300004040e4d808c76acbf420659d8fb028950e4cf667c7b7a1aeccae02aa1fdf79228a12994e6f94002e00d4bd8ca8ccf383cf917e8b235f04a225fab9a73b315503220520d605479e99b96b2dbfc86d1ec5f5a45d32b9abb1f3c20149039a326e69d3bfad", - "interim_transcript_hash_before": "5794af3ba6427b8654df75effc3761adaea5488934e282b7033cfb073ad7298e", - "confirmed_transcript_hash_after": "b472380bc41a9412f238c3cb2d2a7ee99e0eb127d981ce20ff66288e895ac705", - "interim_transcript_hash_after": "78ced47ed2cbaa7c5c082939272d5c7656732f3148b3b13568de668ef33ec6ae" - }, - { - "cipher_suite": 3, - "confirmation_key": "50f97afee0c1dfb91f5934fd830e055d5c04dda79bc67d3d39b06f4cd1fcd3e3", - "authenticated_content": "0001106350587c10dc83b3c0d35ed8eb2d85a755dcddbf4ffdb9cb01000000073021a222777c1e7c6f93ed4ac78e46e7e2d2dec9778b8432737968b390c404b9a7710ace950c8bfc3b73157b5566a8101f03000040408d4a980ec6793af72bcfcd944dd76cccffcaa1c2647b7b49d93b2a4779e8933aea254120d45791d1956fa734f150ae4fa4ffd382b4f3078fbf14b8201f862e0720373c04aa19fdb02e2702585cf9732416863f1b79f503f01b5e3fe7bf20720ab4", - "interim_transcript_hash_before": "5a0f70dab8198198734bb0726e9b324f4e4a739071fc19bb13b8ecdb3a17433b", - "confirmed_transcript_hash_after": "899646fb7648bc897cce03a3ccab079e0f4aa4241214bbb4cd7cc9940d9db532", - "interim_transcript_hash_after": "ef03245e6b8ebd70660a253c59b4e4bd3b25e17ddaf51b26cb7a1728484e36c6" - }, - { - "cipher_suite": 3, - "confirmation_key": "fd984f8cae00884944ffd3190e3156c3f040f2f6ef5ae77813ce4237568a8dec", - "authenticated_content": "0001108fce82ca39122bae121ea15523bed879eb1cf62f2587d044010000000730fb08cd51078d67167c71aa277c8e76f088c8ca93bc38bfb5fb8ffcdd26eb5a809f54a73c751bc8a69333c00e57d14190030000404016e7b5ee51d5e182f2e9bf24ab16fcd07c4feb7df1b560b7fc15a37acbf7112124e344b8b07fa7ce1ff51769cb712cc51cb3ae93ef580210c173f88374ceec0120537fd894d5b0385c93c57574222b8d1c3ce0c16c9255d5a8e95da167f13d90a3", - "interim_transcript_hash_before": "add445329b100ecc5ec65b4b286930fc32649ee8d3f81e6e277542e2b3614013", - "confirmed_transcript_hash_after": "afc082314159e7c21d96d46d63b5dc89efe8f282d3c11b6f4d2f39045f53adae", - "interim_transcript_hash_after": "c62121538cdc6e812e50c277f4d55dbf2642bacc00e918158ee457a936b20374" - }, - { - "cipher_suite": 3, - "confirmation_key": "6a76a86d05eb540a6c904a308fd73aae520ec6d2dc26ef9f949a5eb5b91853f5", - "authenticated_content": "000110b2bffda0ab1cb1c0c0cdb1cec7ee04582caf7cf3cf91178701000000073036b076ddc7dc5e0315e08fa8d6ac53f12f69a4850f8a09d95d6f68fc8ed1e292ad43bffb80df482ff26af23f19dc1b850300004040f9289ce722b09f1a9b1c2d2ad452e57fa69eb15a34c704c485dabb6c8c93fd1af15f049d4b7928993b8bcbfb44d7ec41baeef2718f87680ffe81c8a688ff1c06203373be6d96fd6b6133b47f22bf65a3a56ece44d44ffec2b8e86e223bba7995e6", - "interim_transcript_hash_before": "747343589af16a27f47509116be68ec9b911ff310964714af53f4b256582d8ef", - "confirmed_transcript_hash_after": "0ed0deeff282f75893a4b6c1cb9d531c438a5ce3a7bb49a518959858a507e911", - "interim_transcript_hash_after": "1744e25617c2948a901064cee1a73e693b82f9c67ff343fa0b189ff1f2447527" - }, - { - "cipher_suite": 3, - "confirmation_key": "882df5b112535adc12ed8c3f7d05df039fb08dc484588cc0e4421a450b3421e8", - "authenticated_content": "0001108a504dc9be1c482ca08e5f6455b3fcc3cac65e10d91b8717010000000730f66ce1a42a5fb227116949462c0432ddfb91b12a0f9f479080b33c6e60f63140fb7e9df6016905f7d75de4eaf4db1544030000404073f30276a35374f93cbdea859457358393d911a8a6d5410d6898073b99bc9c0b987b3a5e814dcaa4dc56e0f4396abbbb15170808588020f4053478271f24d60420daab8986a4e7b6157302da9070edb8b9d334f9684fec46a0cdcd26038af79789", - "interim_transcript_hash_before": "f03407ebf6f7dd92dbc3965964e7ca8176fdcc456520420191f8aec685a0089d", - "confirmed_transcript_hash_after": "546061c7d52d69d035f5890593d06ba292e2f4b7b4cc422898bb4ec601008ccb", - "interim_transcript_hash_after": "742a484d27d13743fd9f621a93341ab7f61a53e45625e3516c727bcee56b212b" - }, - { - "cipher_suite": 3, - "confirmation_key": "173c76308046720db8ca9667df1f354d43c25debbf5d4df9326fbaf87ac67f3d", - "authenticated_content": "0001104b08eab62f064da09e22638bb78d5c482c238d60e25f255201000000073096de32d8356d53e4acd227e43eb47f67e4bf5bb9d9cf03a2ea677c82e8c0b143499c8c0c05b0c4f1dafd2922c7a85954030000404086cde45bb0780fa32876acc88753c6911bd7b09b64af9fd15cd56004a5e27b3fe7ddcc001aec06f0a3d479a305960d74b123958a0ba81ad8d317b4927ede9b01204aa109603d367644a054bca1ea4d71fdd98d05fdbed7f374770c4fdfdc5d8baa", - "interim_transcript_hash_before": "beb3ab0c1d1e56e469fef02c280244230135d9dc3fe367b26e7ab97c231ff75f", - "confirmed_transcript_hash_after": "238cadd5dbaa0f97d199037e42e7c5cac39bf9816a2d90c918d82792641095b0", - "interim_transcript_hash_after": "ac665f4dc073eefdfca05be719740768fdf83b4e3a874a176f064a977196a80d" - }, - { - "cipher_suite": 3, - "confirmation_key": "a9cf6f868923f9d8cbafefe1e3e1bf0db403e8b815a4a95aaf922f9a8e190067", - "authenticated_content": "00011044760b73d06e432a2306c065034e051430577b999c321ba901000000073015efc5576d0ba5df667896e4b870824c80c07a131e230829ccc68cfd9203a9d69d3bb286d0a7ead2ebe5d39c622fd622030000404021c9e4ca75f4558dada40769913fc0802b6c9475867919c265e0686fd832d1ee0ad87da47851d13336b7715cc99f2f96f569fe2a3c2d19ab1cdd1d6593cb310320a4b3beaca68d1a0521dfec30c892ff7d6d7f0dd3a80f2c3bf9a62f0bb5e115b6", - "interim_transcript_hash_before": "80a34de3f9eb04d7f177b9032cc2678765a900256a720256caddd10bb86392a1", - "confirmed_transcript_hash_after": "72e752f0817583b59a40ad8dbd6fb11b8d4ee48b14a053e5c9f5fa45016194e2", - "interim_transcript_hash_after": "6842ad190022a3c0a3f5cffcd591e5942bb825251d97d0014086928e01ceaeb6" - }, - { - "cipher_suite": 3, - "confirmation_key": "e02fb5c0c63e8902fac2f300c2b2c617fe15b7f0f017e360a3a948cc4def88b2", - "authenticated_content": "0001106650315e210533b1bc42dca4ef7b132d610ded2998130353010000000730a8379939d043bdfd1bccb15c8d60518b7d8365b396ac8482c2753a2ab10dda47bb123780d533bb5be540dfc8922cc4b2030000404097d676d553a3b5f3b718de1f6c140d4710c9265b8e2285ff95f83b35a1a7c627bd2093e0ea3163d3d8d809683b16531702cddf5e1bb45100ac0296043e7afc04205a9902ac72208a354b7120802b1859509dc8a679e53917e0a89729cf86570d6d", - "interim_transcript_hash_before": "25ed1f0047ec338727946756c84d2e09870a6795568fdd96b725043122a04cc0", - "confirmed_transcript_hash_after": "9e85088388fe65fb387c330af1595159fba37e8db2c99eb1ae41c1c2089b10e7", - "interim_transcript_hash_after": "956003ca980b02d66b6ed068079f29436e199c6003965fc39229fc6f778c6b34" - }, - { - "cipher_suite": 3, - "confirmation_key": "68414603528c8242154519bebaea3f57eec49afbfd9a159fd825988f2b19d1fd", - "authenticated_content": "0001104fc6c443d711efe12dffcaf81ce4d3acc03268a5d42ccb33010000000730e61221b0eedc8d0d7a6415b34e5f3be00fa4bf04a5b2da97359e7b5133a833efc07013df756826ffa963ad9919f91d6b0300004040a1aba871e4cd84748f5bb907a8d17bd8fdc2fd4751b4857b009394dd75646a1b5fa34130cd1b7e41f01edb2510b5039e46baf58fb98039617db9f79340962b04207576ffe370c19e7042442691a4bd89218077946e58df5e2ce47b8cb61f7b012f", - "interim_transcript_hash_before": "554caf7985c1edcdfb57623707dcfc10c5f6928a8819651bdbae20d642292a45", - "confirmed_transcript_hash_after": "41d68b10eaf2c1873e6c78d3725f5bd513e393fed6077f459ecd1c27159ced67", - "interim_transcript_hash_after": "e14ce661e86d2255c234a8e504c1feff70dbf6085468e11b9a78ce25f1db2c81" - }, - { - "cipher_suite": 3, - "confirmation_key": "e14f4318354e113a849aafdf07ec4d8d194c6403956f98db30bc0436bd34504d", - "authenticated_content": "000110376989c7faf5771db033c0c479d16c39a7dae3ef6752e3400100000007301a175872e5d15b53c88b7c1586d73670ff3f5046a2a70c363b83c71a38d4f74887c2e11b7b251ab1621e44c74e003d9603000040407ef0be531a8bc4d48559676eba1ae5615485f705bf8ea8dd5ede8d462e6c6ee94223d3d1bcc1d00501f80fb736d197f51ad564abf6b2adc21c9e389b62b78a0820672b5a375cc7a76128ae0381e9b0f75d99e77801926e1716204c381fa483aee1", - "interim_transcript_hash_before": "11c312ca03040518c641f96beaa0265a3b1b8fda1f21eba51385212f80e23c5c", - "confirmed_transcript_hash_after": "386232b2e540172de1807fa75656d96c909fac5551c6d3aee60843c90b392eb8", - "interim_transcript_hash_after": "5a060a2728c453a1256e3c55203d957674d0418e66a2d7ae692211b66d6bcabe" - }, - { - "cipher_suite": 3, - "confirmation_key": "7e73c1512ef0acf22d6be2c15c095231054270aff0979f64b0af085140f37ada", - "authenticated_content": "00011023e387aa1798664a909f4c6999a56d963338bd305bd71fa7010000000730da7dd7ceba2bbd35b9975215f6d0c1fbc9c6b771c87fa530976b690c8bf6ab19f7f7390c53fb7ed8672088f9165c085b03000040401437c5bbc5f6c7746ba62fc03e37b0120e1f3efcd04f02091e6909ef0931fd8bf2085d2ddda40f58f4aa02c6ab79859c60359f28eeaf67ff1133fed2edc3b10b20a38d42dd01e7087b9f9594213f6ebc3f7b7ff2af197a9e5973abdfdf1a40a2aa", - "interim_transcript_hash_before": "0323dc334050978c1852329806a6f77f03334b88fe3156b5de1a9704cfa3c9f4", - "confirmed_transcript_hash_after": "665a3bd887456eaab995ee29bdbd80de178a1ca0b2c0fc8f434dc8ec06f1ad64", - "interim_transcript_hash_after": "101d731fcd1aea8f8a7264689def66347b9dd0ed66ade18c26f9328578f15c0e" - }, - { - "cipher_suite": 3, - "confirmation_key": "90a6e952b0c78979dd5d52768e76fd5525af4e67c1afde89d22bb65fa6bc99f0", - "authenticated_content": "000110478cc9d0468125e71f1277e5a460d4be472a8e4517a50990010000000730b192ac817e9f1494eeb253199627843c207dbd58a1bc99b440ede199a751b766b6e9f250283854d659e6109c1fb5cecc03000040407424bd0a59ddf6a14c8f887c8c383f1e1399ba79c899dcaa922d0b5b12a15e129ec7635704f1b3f71abf136cff3aa9a7f819ebef0b8cab5f65a52c754865b20920166099513ba0cd3aa618eef3e2fca3e756c99009f1f947e08b1086684165d229", - "interim_transcript_hash_before": "b599c70b15f8318b849de080a2dd1d937c85e09997d6bae68bda4c316cc6c98c", - "confirmed_transcript_hash_after": "ff5aba6a8839ea05ddd98654aed633119a3dcf4819d6911aefbbdb40a0cb8049", - "interim_transcript_hash_after": "ccd7e3d8c028757ea8fec5d41861cd1189caaa652f22d870d161a07b61d51247" - }, - { - "cipher_suite": 3, - "confirmation_key": "32a20b95ad5f21435a3ff5655079b949116972623a36bbec12b621503df96e4a", - "authenticated_content": "0001102077c0b879da4d0cbb41b91f9f9cd952cbe4ba186e7e5a2a010000000730c944d40754827dbe7f829cc470f13c5c741fe86e37a4e8d7f963680cb97fed1a2f5fdf8574c1de04f6518ca30602858d03000040405b8873e7abc3f8371244359990b5903dcb2040df15bfb5bc0a592396de19cec651fbb06e6715cff7dcbf49b86635fb67bb2c5a92a39002676e81eec414a92e0c200df5ce763305c65b61f271f394913ec037e133901f76882f0d289877cf305ded", - "interim_transcript_hash_before": "afd469c783ed54b369fc8bb3b9900d2fa10fe2587b7b0b83c96a599971b0e232", - "confirmed_transcript_hash_after": "d4280b350e8f2c3d6f715965ff96e587bd0f3573b658aa2c13edfb361214fa82", - "interim_transcript_hash_after": "a66ca4fd0cc9750d6d383a25ce84bb61a764396e1bb84527643282742cd52edb" - }, - { - "cipher_suite": 3, - "confirmation_key": "b13749970805a8ad76f880a9dd976f15b182aae5bc97c253fedbfd11f51afb90", - "authenticated_content": "00011051aae94c6677eb02381f99e5cb6710550291931b6c1a4e07010000000730be7fa7019148b9afeccc355d9b51e94a03819955a46b4e6e2895b0d42b930eab4cb4a914d127bc24d5d85c40dfd6695a0300004040697e5f9b61138ade61f7fb94cb84137fd670a2720b5958c2d716785fc79e04206834cfec5fbe265e259eca72c4112cc2129b908485fbe72be9ace203320bc100206f32307d5468896f595a15ce7bd199dc40d84d6afc13f675201e3e89eb7463e0", - "interim_transcript_hash_before": "4fbc42b3965e5a47d0a48b38dd1bdc68cf8eae45eabcaea5222439a92b7175e7", - "confirmed_transcript_hash_after": "0a22b58410b17637cf06b28255271924aebbf2d1261ec0c6392fc4344133cc80", - "interim_transcript_hash_after": "ce19be3e52e35c7961a0515aaae8dc7d5e4eae147e3f4a76b03abf8592b5a3c0" - }, - { - "cipher_suite": 3, - "confirmation_key": "abab5387b3bc945f0736c04ff172ba1e6c3c27251b782325c92d575a90013c23", - "authenticated_content": "00011007a9cd1ad7402feeda585305cd91e7d92d040aeb1c64b2db01000000073003c37fd542a0e4419439be7ea2949dcb876b468904948765e4da5cb0a01921642cb2122ec652ab336b1085b34f88218803000040400113b20d9d64d0d4f351b817cd080938acd6df3ea23fd1493447f9e658c9c588c0b555555504b10b8f0f8323cadabf1db0b5baeaf9190274bc917f77404e200220d2edf5720b3a1db0a0d51ece4bc85c6a8aa40e76b46b4631201a81a129466ce4", - "interim_transcript_hash_before": "32f4b1b48cb6133da8cd1d330642721c89c242a858fc6a4ba0034ac9d46fcfff", - "confirmed_transcript_hash_after": "e58876e66996d60c0e38f5012cb1c5666d4399a43f13ebc28cd0a1b1f6441de1", - "interim_transcript_hash_after": "6e493b4eae59cb3c08a46605ee38685fa00f14d51a1309c594a5f2eacdd6de3b" - }, - { - "cipher_suite": 3, - "confirmation_key": "922d8351a27455f208cc794636099ba7eee3bdc55df14b9e1c17a1f61addc48f", - "authenticated_content": "0001106ed70bc2539b9ff626531617c3ec1096b2f264c3e8f69e49010000000730693d237a488361e386f5fe254e89b6f7efa5dc0f869dfe468dc7c65316f142a6bdc06e16c494f9de4b5772fb5b1a6d3003000040401736df24d0ab554c32eeef779be55c5d42cffa8699a407575a840e88a46ed3ee539d15f92bf75f4078d5b12a3b89caa28c5d9df59201830a9bcef3fad6d3820e20df446202237180ad51acfb2d65e870daa182c4e1f812d6a134cb548a8c279ee8", - "interim_transcript_hash_before": "638360d8952fb1ff71c9d78f2bfc577c33cc4e10a81c86b641dc697dd678dd1b", - "confirmed_transcript_hash_after": "2b87698e02022554a21f47ccc2cd5ece5f5f00b7822ce512fd9cd1d4de266447", - "interim_transcript_hash_after": "885fc78b484c33c18a3ff9111f2a40798d1f4452a70c16b1ac35750925ad2d29" - }, - { - "cipher_suite": 3, - "confirmation_key": "718b57a1c88f6022d0f77ed1c5389ef561da02d9224bed5400bc53ad5f9b0045", - "authenticated_content": "000110762e258ef376d841068b58e268ec3d7f0c707e54ff043bd2010000000730a8884c8d115c7bfcc04a11796655097b5e9102fc0dddbd57d3a46281fb5a6c2a99e86787800d0a7e074d470303e0cd9a0300004040455563a19084fd439924a3a95cf1b5c28691b05e3653c642a0e5f73afcc7410f42adf2e8223b455297b00d7c8de9698661ee7adde4131b43173778277c79ac0f207af94d579ed3f090dcdd55e5b992355e4d820cf2ac10896e8c1edcfe4aa38919", - "interim_transcript_hash_before": "f81f5c60cdeb32d70da563916e3c13f9510b1c76ae8c8e9dd55e41a9845f4ef7", - "confirmed_transcript_hash_after": "ea361d7dedc86bc4bdf9c387f5b8f90c136a8e8bedcdcecb740e2b0e36bdef64", - "interim_transcript_hash_after": "62ea867d4665d0e332ed9a24e1cbeeee6ace32eb56057463495d31f82c63fff8" - }, - { - "cipher_suite": 3, - "confirmation_key": "8a3d06020ea3bbe2eb2fb03d2c5c36225f0118956ca6ded61ef0cfb35c1d9763", - "authenticated_content": "000110dcc06843773a8bd5aac73d6f1234559e2b5fb2c617c3aa9101000000073091b47d2ebf1d1fa6affb4dd3cc764c4b4dd3ddb1b87e42ad22cb94a235eaa0478a45dc5d84469932c202860890a2109b03000040402d1fd75b281559a72da7ae04542ff09f41eeb49f1f68e295ae7528b36fe0109e2d68ade64d256d5f12e206ba9d26fb1edbbcf25ef811c9d76145d12f0dfadc0f2040d968b80a58a9f468b5224c322584272b355f4c3f00f332d920d49b3c4ac137", - "interim_transcript_hash_before": "6f42f3c938c57a1f3560a22e66d740e0ad9f49b09910cc9d3e2cf9fc77e3c3d7", - "confirmed_transcript_hash_after": "62b21fba318a5f69a8b4ee0d369665982829f1cc42621c5ab0cc86e0289b5dba", - "interim_transcript_hash_after": "ad395a1245abe43bd1eb5aae4da352e1a7c3f2bdaa75e5053820c0d4e5ac9d78" - }, - { - "cipher_suite": 3, - "confirmation_key": "8580c279fb168a2c3456e969b1684225969be24d0b6dfb6b139ca1cfae0155af", - "authenticated_content": "000110c5d05449b26fa610ac5772badac85bcd6e7cad79441d2e6f0100000007303e96053626186267558304a145adaa829a3fe3dc17a86dab828610eb977f83fd47501579a30117b0a30f86dffff213fc0300004040d80411085a2bcbb2c3cdfcd873280613f699380fe1a1f93902ad78aea1571013324ce8aa8b3711cfaabe6b71c05fc774c8171afc78e560b19d23d7468d4d1d00209502ec83190f6899849547e3218d7bdb3e3d8f0bf73faedd48dde52ef0faa650", - "interim_transcript_hash_before": "5cde9eaa16246340f7e5c67e40937c05a4f705b88fcf46f1dc38bc158899166b", - "confirmed_transcript_hash_after": "28f4f309db22e230ec272fabeb409f8e1295e0e9998013705b847a41f8e31906", - "interim_transcript_hash_after": "5877fbb2afbffe36b9d25c557880a1a4e5da33bdc891f21b7f834a0ef17a3509" - }, - { - "cipher_suite": 3, - "confirmation_key": "0020bb097327287762c8f721178901a4b2305f99eb10522fd075ba24c4fc1568", - "authenticated_content": "0001100f385bdc2c0c429c6ffea1006e28f9a579537869da0e2efc010000000730ceca109c14465141ae0de4855108c068a84b26feabb1b77f122f955bb49ecf197301003cef7cf3a63ea302d33b89d5c903000040404a1102816e4eb5a4e079deb5d6ca90dc451fe3909a98f86429c2f1e95e1a3313df5ac7437472eee3bb1492d4b440f8ee0e71507be837a059ea8e8f359a002a09201f97f9608011ce726190fbff52992edfd272422a2bdb089bc1f577efa2187c75", - "interim_transcript_hash_before": "a200c228ebdcc7cd85328a21fa90b723a36521b8f9ac6a448dbaaf94414e4838", - "confirmed_transcript_hash_after": "e007e713344ff5edb5193c2aff37f24ece08903f887b09672406f6db84f1fb8c", - "interim_transcript_hash_after": "a36863d7e5dc48920d34ba6f3546057450a641ae2cc217e96bd9b7ac5dabc6e9" - }, - { - "cipher_suite": 3, - "confirmation_key": "c59a5e066203da8d078d906514f896f5f833e5dcf4248b0dea8a5714166262a6", - "authenticated_content": "0001109c07753e0db1816dec56cbfa67e6a682a1b45b80967a68a8010000000730925a6abb79407eff3edc263a7e8c7efe4fbb74b0efcfa0d3f36f8641dcaa12a2daf2ff6ef3800afc4ee286a716d7f6710300004040a001eafb038632ce9041008d5ab48815bb734154fc1fc3b05bec8b9ea93502cee7d59e2d0298b4a2b704da2356d98f0fec85dfddb222d5adc7aeed1770bcf30c20930a25073843ee26cb36ecf8dda8ca6a8a6cd5f29932ce07bc14789b7678addd", - "interim_transcript_hash_before": "3025d3b5450dd347ca90da66b7ec08fdb6a78859f6fca8a68dedf3ad94195912", - "confirmed_transcript_hash_after": "b3363aaafc4f3b55c2618c9419c8569edbaa401cd50ca5e4622d0fffe6e14e05", - "interim_transcript_hash_after": "8f10a3e69a443b8ef73fd4a0e1da00552132017216d18d2e15164fc5f5b34ca1" - }, - { - "cipher_suite": 3, - "confirmation_key": "858968f6bd28d7cbfa388e48ad5ab39775f06def3acc9acf389eb6bc0c6e8057", - "authenticated_content": "0001103e5109d016aafcd8a4f35ac6c75865e361b6017296a2210c010000000730dfad253c2ef78d0c1c42fa12faa3f23dc2f8c880c577d375cfad2d4aa6113afbc300259e58aba36c8b633d2081daad7e03000040408b80913bbfcb7af4e90fc30515e679b6975d92bd435877b31e9efb5d4bb933b69bc27b4e3955d1c731afc4d47c848ecac4140f1e998738e8f27201a0af40cf0b209062d244b5f79f75bde127e75abf427a45ea1d432321b88c753133f090a9d7d4", - "interim_transcript_hash_before": "dbb31816bbf87b12445321944ca9376eb885673099ebf23f3b4d4f53b87ea7d8", - "confirmed_transcript_hash_after": "3400892ed4435795d6c452a071818bf9b3bd7f11d3630b1b388011c376348e8d", - "interim_transcript_hash_after": "b64842bfbe1a845d1e0b18fd5277a940124bf813adb569e18540841187b3e681" - }, - { - "cipher_suite": 3, - "confirmation_key": "c4204010798c3a5247ff5c438259d66c710170e3659d3139241079112c79fd0d", - "authenticated_content": "0001106c52f3af28822cba2dcaf95a026dda609cfeca45ffdae65b010000000730b32dec50f50d50379cdde16ba8c3c849fd00079757a4d8c8b99064b88ab1582645be0b4cfaddb81e0c7dc6f2f14e17d60300004040fb32c5a96dfc271ba125af1c5201b6e8ce5771a99b52e509ba2e6463bcf5c93c1855693c5f93dc2be08c66bb5f764d19b5efb56e67142558050e4af92ec0de0120257753fdfa28cf3e7d98e0c6873cd7e956730ab3c294ad22783a4100bec7bfdb", - "interim_transcript_hash_before": "aa9afebdc1ad5921fe91c147b3116f4267a9400b244c38766e41a5683769935d", - "confirmed_transcript_hash_after": "ef46935103c94248b7750ee19f5a88265b896701f188e45dcc169dfc04221dbb", - "interim_transcript_hash_after": "d9209ae375175fb6657cfe128bfc243390707b3cb4760ea3f7d373c8eaa57341" - }, - { - "cipher_suite": 3, - "confirmation_key": "58c26cdf6969ac82c02b48a0f38e3e0856f9e38fa1795ffa177f0a9b9637cc85", - "authenticated_content": "00011033b157f18f16754cf5e01bb3d1fc734f4c0881080370c62b010000000730d03557c30ffd887bbd5dcbc9eca05ca9479fd2bc5b987c5c348bf92c312041bfb35c2242077db20663413f083030030a0300004040d161b98285813f10405892bb7a7e8fc97420876a97242ec829763a6fe3b9a92b5bf1d745368e2b8cee60c8a8a4e6b7d57da562f2736285d02e2abc9cbefe010b209c99372cb5d88fa34a1d02d33890905fe636abc0e6502edc2473ff652dd117f4", - "interim_transcript_hash_before": "d182b5dc49d10d91139f7f4f46637266cdd04249adc48056a9b0608f70f3c0ce", - "confirmed_transcript_hash_after": "d157734397ef809396673d66a0f27cc738d30f12e7095bb95cf34df65e40c37f", - "interim_transcript_hash_after": "2027b2fb087149fed6e07f7a03dcae397519b92d818e7697292c8a14e4c16c5b" - }, - { - "cipher_suite": 3, - "confirmation_key": "636c3561ceb6917d07b00a64e7b65615b90bd6b6d8c74f12f46dfece2f66d4e6", - "authenticated_content": "000110a6b83b23ee8e100d3ca91283c489bdc8be6a42340c9c1b1c0100000007300e70e521978927b89c7d9d967081b95deb6b6e1fff51c469988bdc840db8cf53b625e483dcc6a44894fbbe9f284e4f6603000040407169dbc07f34b6223e1a6c82df2cb3ec82e08019442fad4e315c065dfa881e7ccf957e576f8ed262012f42fb6fcb0cb571aee608980a52482c545099c1af87032026c719a200c71adccb9300d36b78d922c011437f3c9ca98acb4fc8ad63714f34", - "interim_transcript_hash_before": "2f3a2fed89c132882d95b9a771bcb55115eb00593159b33704f2c820699a00a8", - "confirmed_transcript_hash_after": "220ecf42121396d174581617df5a58ac51639924429e2c2befbc6b3d4ab306b4", - "interim_transcript_hash_after": "0d194ab1fef223a01062c301466c8197736c975e19551e8b2a92ede6aff5018f" - }, - { - "cipher_suite": 3, - "confirmation_key": "d850bd3772d8dc388651fefa3cd8492e0fc16c53931c458f2df98269da2fb75f", - "authenticated_content": "0001109874ad63b6da82c572f0c0fbeea294043380970c59b0bda801000000073007b1859b9b727e098160828ac0a6a7e2613c17da04492e6cd2ecba90b858f6940cccdfd7b545613ada17c21decc7f3d8030000404021bbfe6d11d54852660b89ee730d11c3613df9febdf27cfe81e82dd51dbb185defd225c773e8248bdbe06f7fc3b36a134913e6ad055662c628dae91e94455c0020c65a526f96b8e2deb71c738f5aec2e085ee76c859791956b22556f9b3145db58", - "interim_transcript_hash_before": "22f6b4e7920f0aec7451e71e95af7ef0da2cb1814d77f4048d383b27fcbcbfba", - "confirmed_transcript_hash_after": "5a3e3e43e1b12e6f760b8d03cffe6e0d9e7e0b923e2d60fcc610bceb9bc53f2f", - "interim_transcript_hash_after": "78fe1352ffcb546957c8e9295c98d44ac89a5433d5503cf2218193b332e0507d" - }, - { - "cipher_suite": 3, - "confirmation_key": "856991dfe8e0d7686eb4719917873bea0260452f0224c067ad3e28c6e16c93d9", - "authenticated_content": "0001101aad1fd148ba95d4be252889cb31c079206bc653a4a97e04010000000730cfc1ec4908c3e33c293af17e86f5cb14858a00621ff4bf3dfe5ae11c6f95270521dfb3389496ce5909e24de998a2394603000040409199321a033a49787937318b5104d59fc957134b6cf6d5c44f5ec4653e78768029ce5a20baa386241a4c04725d23bde147d7da10fec626011e5be27d0ab4190220fa86c71a8bc2122ccd57b24ea8d52af092628756b87d844cd66278f6c2ede473", - "interim_transcript_hash_before": "784a2c7324e3b1ead670b5e3a7d4d10fbb97316cf0c78b7389b778ccd005a9fe", - "confirmed_transcript_hash_after": "78fc4e7e83eb39dbc2d41b8bf04f8683d861a98e43a8ae0b5aafeeda2be4662d", - "interim_transcript_hash_after": "e0fc31d1f5fb06dd74603274c0419998a1371e199fd6ca29ccdc072636d426bb" - }, - { - "cipher_suite": 3, - "confirmation_key": "e2e0951c965833f972f4374f3a164b2d65af2e4ac0b9fc1351bf78d81fc90524", - "authenticated_content": "000110d6c6165a3538ade71500d875b8fb62835d2c907693237d070100000007303861247ed965fcd46110f07117abf7960add50d793824d9b89d795e474f793215aa189565a38ebf63351a59a3873ef6f03000040400d5b8f0214d5c2d7057d74fb5a342351ce2b503fbe3ff6276dd284dfb99417e27cbb656bc3852647d6308ec1077e6523da3c5da479d99c78d9dc6c7d36b6360b2008db00f2a20c4d78afe47df863adb8886752074b633db8868bf94bc32584f72e", - "interim_transcript_hash_before": "b18e1202d2d3616af53646d8891f2df114f8abd25964695c450d825fbd7fd7c8", - "confirmed_transcript_hash_after": "86c766d6653b63d09d69ea90f3c376b3de953e5701740eff1363b7387a8cef5e", - "interim_transcript_hash_after": "a40525b964c04ab7275de60f075886e47cab82772932c06960f23a2878c1312a" - }, - { - "cipher_suite": 3, - "confirmation_key": "5c9a17f70ce3b8de96158d176fb8207ad92c29a36171cd6e3377830bfb33e2ab", - "authenticated_content": "00011041462aca6b75ee173eb027047dbf58129825570d21a0a1df0100000007305de29528547179f610ef26d5b1c21cdb6e8ab1ccdcd4d190b828ceb82d3d247b9a2a08f251eea5bd3655b853fb998ba403000040404e4a3295a7e82ef0d7d4dcaad02556a29276fcee51d5c8baee5df6cdbaa23efbe64444427943f27001ffe92c4947220675a4a620ed2a715dcee9bd27e1f55b0a20640a6abe7fd79416d6ef659b5e1a5f8657fcf8e1a324e546a9656426e32df937", - "interim_transcript_hash_before": "67ad40dbc382c6670a8d5a1475c6f17c98119847fe11d0041190dcbdaffd609a", - "confirmed_transcript_hash_after": "0ceb03f67e8bbd232eb25b5d196b2cca23f25cb402dc666247aa16e10c321396", - "interim_transcript_hash_after": "206458c1d3db964c3f3e56b84b05378cbbce45e6791a535a3c267c35227ebc5e" - }, - { - "cipher_suite": 3, - "confirmation_key": "0048deee81abd671631c6824eadfdb375dee5a12fdd381cbe33acefc1ef46a2c", - "authenticated_content": "000110c18fe3e0dc81b82886c0cea11b9e5506b283e89858de993c01000000073033051479be44e789ceaa32055333b784ead14e3aaeec81c5df544bf0e4e886b83c2a2f8f04b1f7cc9b1782d423e7382e0300004040d24742df628f320a59e26e875654284e7ffbff00e64a1321d8a252ca93fb42b5bd81064653e4d7ffc16b8caf228011000e94e5b21b4a7ae9abf7dd7e14fe6c0b20bb00489337dd33c0e693fe2d909e3d9e38e94398fd59f844b2b0882d345599b7", - "interim_transcript_hash_before": "0908a976c267df8a5edc1899f071b8eb9517611324bbca9e8d517bac9fc86165", - "confirmed_transcript_hash_after": "b7f0aaa896e1f17727600c0d7770eb6485b6d4e1559870a351f3ea5233cd6e59", - "interim_transcript_hash_after": "3c3d354a7a67d99d792104d52417df0c2c306cf9e00adbaa223e1703a7ff4036" - }, - { - "cipher_suite": 3, - "confirmation_key": "118fb4d638ac42dcf74f1ba7b5f285a95a51d5bb184a5b12421cb89d0b3155d0", - "authenticated_content": "00011073afe0735b30fe37773b9fc243244a1b6ebbf2c797fd38830100000007307162e1a201ae3cf6d16ce9ffd45aa9fad1a4242faad578e2743bdd30ffde57d20b59ff7555400cd4cf2e86d7994f6bd00300004040d27308b524682b33fafac4b94aa96b451aad8b1848bd7ad0b7881bdab367d126d715647cd86ddaaeb0f7e11e882567a4674a807c5c90fb2dca10e63924079e0f2091e31c84b2a5990cb8411628092a2b40df19695a6f5dc6e1a02291d1c2820492", - "interim_transcript_hash_before": "b79afa6de50823a63c05df9f3fe8ce3ad67378b8bfdd212a65b55e581525c5ea", - "confirmed_transcript_hash_after": "7b8debfaf20882c5cf892e4f97d84890aff25dd1fb335a1d337a4db7c020c585", - "interim_transcript_hash_after": "87d37baded3f1b9b72fd16fe5583fcc6848b46381163b460f04364bb802b3ae4" - }, - { - "cipher_suite": 3, - "confirmation_key": "2eec554a64eb991fbc88d6376d32f13734814f4d89fae087883c83ceea816a97", - "authenticated_content": "00011025d02814e12cd4a2c7a9f0814af4e27a92fbfdb5f778dc93010000000730d04d483c1dfaff2c7d80cf9b2d16375d1f25372695f7dc18783237621441c68ca773e7bf3de8d40dffebd6d5e08ccf0b03000040402ca3371714cd5a973b6fa39eda67bee6cd4a53a5868295f9aa822ad63420c84a992246dd32cc0d0833d45ac9556b2e9d976f957711b93316a974a88dcf55970a2055c3601f3c602bbb2205dabb394532abadc5321cc8ecd8b56cf0f59f33a4c8ec", - "interim_transcript_hash_before": "7248476628a4a450f695a3cf0a1fb575440d4425854330f8493bf14e590a90b7", - "confirmed_transcript_hash_after": "414a7c3248dceec31733cff7c9e1b2373d99f5dca616de24081115d0d4939288", - "interim_transcript_hash_after": "a392d07506e27a3ab3b77af5bb777e938e899bb6acdc31f6b30177455cd07a0c" - }, - { - "cipher_suite": 3, - "confirmation_key": "80d98c64da20bf1308e31f3db9ac2a4662a2c154a7533127cf4d9c5e76769076", - "authenticated_content": "0001108c80bc426efffcbcc24a5e59f021f941ae4a00382136e0a001000000073055cdb50fc47ab936f14bc1b978a71bb0fdb56c1b3352f90183fcece43cf6f8f2153479a0d7604abe8cc201448129dbe20300004040f85c2b23cbfcde85ea331029b19f53583c01deb6c380b0bbd5a9fdd0ab7aeea48593eaf329952494422fa61faf8a77b3181d6db93865eb7a6fa55f566c60480e2046a4adbc63a55dfcbeaf99682d2b2387a4aa40248495ade7229348e4b4f734ef", - "interim_transcript_hash_before": "33ad6457f188cfc3a55d154f1b151e9ddc16f5ea418a8001a1ef67ced84059e6", - "confirmed_transcript_hash_after": "54dc7b6b2a303cc0bc49bdf9fdc3d6ed54eee0249b9a8b56c2e77b7ba92288e9", - "interim_transcript_hash_after": "84f0bcbe5f549351e6f8f490ac3e288921dee26eb980da9e6e5ce434e7b25d22" - }, - { - "cipher_suite": 3, - "confirmation_key": "967af277a655f17cdc9ff5244885db086ae96cff7b31bb787e88246e7332dcc6", - "authenticated_content": "00011017b41197d857a0573ec8b016126f2af44c94cb25286d1335010000000730fe8281b0a9db5c54377addc823ba56c6201c429bf7eaa07e1aa5a8f9ef3058a3d80744e0897f6e6f810caf5294ddc1e20300004040085c0e0a6478b704c9baee2528a924cd12c8274431b51c744cadd503798ee93fbf88134189d4f7fb9cb39a4725dba16ded9ad863839e79cc8ea58241ee4a930120aa613151ed7990dadd7035e4c1584b14f9897e6eb695a1a90f722170bf723e13", - "interim_transcript_hash_before": "2fee865d9b9f6f3ae422fdc2ee6f807d7265d78c67a5164b0e50fc5cc068d00c", - "confirmed_transcript_hash_after": "bd58513783c21ee0e26db44b226e9bde416e016d7f176a227fa2b55ca6d993db", - "interim_transcript_hash_after": "484f35df545e9aa4653d5a2c55924f70849a1fa5d468228c9a35e5d078e7ca3e" - }, - { - "cipher_suite": 3, - "confirmation_key": "3da63296ebc9881dc1eb3fe80e62d9d1e66f004a2a82c2e05c937f50274d815b", - "authenticated_content": "000110bb2f7c37483ebbe247216bedcf8243d1bc72db44d35351b90100000007306c3c0ba7c76d0710f9a5e7a3f2deb794c8c424aba175dfc2e4e4e4882035ab491c7c5f3bf1fdf8975121cb141466b0a2030000404099f2cee0069b13ad3e0ad91d1e6700c0a0be07da06d964c19c2bbea3d7a3f9125156366ae152d274b3a8b4fb66081cb81e3750478d9e74df510f624894ea510c20f3509b484e40d4414d9bedcd39fa66b3b4929ebb2d7e998c980b1acc3220d9c5", - "interim_transcript_hash_before": "09cb404eae78cc267b1c19dbbd9ba88e937bad5a0215dbd163ee6cd426e87225", - "confirmed_transcript_hash_after": "38a3e0e42793d73fdc9cc289836d1edce8f428c25a7fa996b3e5982957cd930f", - "interim_transcript_hash_after": "48a8a591e43dacca825d776312157166527fa71e5d676bf005ad9c64b4a2607a" - }, - { - "cipher_suite": 3, - "confirmation_key": "b3446b5028aef8712cb2a6862bd369f584a2646fe939d047a26406b929e2d198", - "authenticated_content": "000110ebeecc8f3200420b829d9fe0eb4644f88b6dc5c0fc79a7870100000007302cfb9c7e6032205f2581bd152505242474dba51e0ebaa84bd006076c6fbf99b78a153475b40c7afea8b33c5a96de6bb40300004040ba156376f2c6990c1d0f170984550f16f20d2597e7e473e68d369b7fd3a890c0c114a20b78f5265415b24db4450087731ca56f8fa8237881b937b86aacf76f0e20d6f8f5b5c02372eb258e06bb6a4fc242ece70b85646626807461a6637f917c9b", - "interim_transcript_hash_before": "0b7d5441287a5a31f2e600d103aec07675b847ac606dc1e8311fdd0fdf43ab16", - "confirmed_transcript_hash_after": "1320bfe2ca5c15925eef1d020bfa2ac1e2f2e6bad905807d347d934d2a0ef893", - "interim_transcript_hash_after": "4f9e15841067f44f6855b84d76799f29197f448cc392e44fee16a281dc830130" - }, - { - "cipher_suite": 3, - "confirmation_key": "e17f63b3e4a0d37a70e2ba30df238545c27d92c9dedae317703819cfb240ab97", - "authenticated_content": "000110a2338bb11fdb9ebc99c60a0f852ce67cbba7aa3395914578010000000730c5ae0e7a03a8548df24bd687bf009909fed8d5a24a97e42a2d3c7b2be7d103f2ee4e15dee0dcbc88a553d6f49abf147703000040404373f856193b464aabeabb94d766018fbe0cf98b2327543f6cbed638e2211a104c01fde10744ad42c8762801138bf07e70f787eeae0d79080ba9b731251a6c0e2067c7c9d59ed7982011bf8b97a94a755793513a3254046d6184dbb7c6c9cfd86b", - "interim_transcript_hash_before": "a2ececa8686969669bdacc1c9a27c88bf10ff1b3bff64496551cbaed1784b160", - "confirmed_transcript_hash_after": "35b7d2aaffbd7576fe85562f0ea9b9f4901948d531f7dd19432a857b7fb5b3ef", - "interim_transcript_hash_after": "22ef79a0d09d061fe4afa4d97aacbf7aad14b47ec64f07e18b756ab5510f3ac0" - }, - { - "cipher_suite": 3, - "confirmation_key": "ad5e927dc7fbb141b166bae4588deb9d8bfe0527d2f0f6308503e6cfe6defc06", - "authenticated_content": "000110993e9dcad1a4f18e0fcf00d982287c4a3f75b7b795e0ac1c0100000007308d576ce510601980e6be8b478911f37d8d6df94a3728fee94d03e70790ae09a5970373a3eef888f64a12c6983c48d18a0300004040084b071d3753459410fc1a9031b70c4c385fa73ad87608bd7f7814d5d22580d9ad04e0f4d64e545f574ab0c7829d11ae1ca8224768b161201d41a5c0035f63012093c9124461834be53ac98acc402948bb9702673a485a8aa57b8162f942bf60c4", - "interim_transcript_hash_before": "34240944a8e5b2247c5e3642c45def196306869b232ba8446d3300c93fcb59ea", - "confirmed_transcript_hash_after": "7eb521781b9f823ccced5a752b77b65c8e00b59ebc789f5eab7f7e879df75521", - "interim_transcript_hash_after": "ee7dc738dc482dff3a4c7848e63d8b536d5c86caf4a9633c90b9dc26970ac5b5" - }, - { - "cipher_suite": 3, - "confirmation_key": "3ae568f3bee63b38fa3b03a2f872f7858a6c9e3c1cb12ecc2d78bd34c70cbacd", - "authenticated_content": "000110532e294efd4a588218d379b386af727f0bb6b56763a2982f010000000730b2f4c67f438c9ef06b5bf78811590a63ee3d97349d8d7ce8a190a8b59042deb1cb397e4dc327d7b914fddfba82b98216030000404034de27087efb4b16e0079ff7eb1728d8d66c6cbbe44c80e4ea8311b8dd79131640202eb96f46691999a75cd6c0473e6f9b0918aff25108395a6372950d661d0f203652b653f39afc0d85285b8d0d56f3493441b053c15418264b59aff3e5422473", - "interim_transcript_hash_before": "4bb59fbe4baad5ef39357a0d0d846565309c85c349e5f2d665448a3b3dff3a8e", - "confirmed_transcript_hash_after": "1b76d276b9155af3adf2b96ecabd38947b26c7bde0e4d0eb86dee0c577f7da0d", - "interim_transcript_hash_after": "d7f3a19062e170f089b1d7614c140004dda2cc66a75513418426a08ec2727190" - }, - { - "cipher_suite": 3, - "confirmation_key": "8007919f426f1accddc30dbebfbd94764a7102c10b4ab8f54b3658c3778a0a31", - "authenticated_content": "00011006857cb044f6b53a70871462059f943e7a381c1bdd9477be010000000730565bc7bc591dda2ef5d6a4c05a6fff5ddc31e99a106ca48ee8cdb6a88ed6ea7297cebbaea18d8564114e51361cc307fe030000404064a435292619b9b4a80541b32a77a3b3d5f23ea66c16511ea0d5fc98decec4eecbf5250a2754bacfc81217b5051a4789c9d9c9ced6c950742a3d928edecb120120faf0c86186ddb68d7cd3255299e8303ed2391fef504e0f34595fea659aa3f9b4", - "interim_transcript_hash_before": "c4fa9dc17f92198d3bdf87049898e9160c1c6dd1b8cedebd65b95edf4dc20006", - "confirmed_transcript_hash_after": "12dc3fbf8b5500ec14c8c3f3ee856cf5db5a6111c31b3969ed5f0b7c2e64af94", - "interim_transcript_hash_after": "f94efdc3c05f6e072176c0bc358344e672971327fc7b78797c6e07668ac9cb87" - }, - { - "cipher_suite": 3, - "confirmation_key": "5792b3c6fb1f2f0f0ffa6b7e793bba11f3f6e683a52ecf74c7b1595ec20ee213", - "authenticated_content": "000110188dd4ebbab722bd4df044ed6e79da71b477a07926414f72010000000730e91f0108fb22e3500324a8bc8c5a0a69493f85ffba17e5400b297b97cf7d74cee2cbe33b69ecf4e37b66a406e5e838170300004040658086bdad4aac9b54b3fda124946a44e90988243d5e2cde9e707f818039f953f99ea6715f32f919bb9baadcd1ddd0ea5ca8c657e83182cc75a5c60c4c8c390520be5e73b5b7e6ebdc1232d6f27ee0273443d43a5fc7f82d638a28c370464f51f4", - "interim_transcript_hash_before": "8a613fedba6620ff7a63835f65fd210d73809a9ebd9e46e2e59d8da4343c4330", - "confirmed_transcript_hash_after": "7c81bf00bb915a2e8128460581a0260a86400166992ba8f4a191746b2b9fd9d3", - "interim_transcript_hash_after": "7db8aaddc80442fbf01767804eb647dc9f63b4d93c924c7eb4d81f4bda4c92aa" - }, - { - "cipher_suite": 3, - "confirmation_key": "6508fd018e968ba4cc3e0f53c6d562563e284985d5f276010b9889cd1c90243c", - "authenticated_content": "000110e8d172067f9b528e291270b689ef695ae96586683abdbd67010000000730b1863d30066c26e11c0c3acb2cce5771c8598749e5a0960f0330cbb03094a02b2a9a8b60a01d644896921f75bef3e67303000040402361ba7f1aaeb4967665bfd41ce90ab6ac164fd9dbf107414e3ad24f4879797d69774ff527ea0dbdb6ad50743923bc1940a85df2b9ea8db6adba2a55aab3fc012061cb9361a0885f7fb74395a4b9278e399a7dd694cbfd27613a5dd621948ffc4b", - "interim_transcript_hash_before": "75e4f30fe78e1ee9007bce918b270dd34c39c844b9f72cc320a4c688b9cc7b03", - "confirmed_transcript_hash_after": "1a0ccebd9c858f9a9388e603294d20918c8e76228132eec7fff5e30755d150df", - "interim_transcript_hash_after": "7d2586b355edb482189ab6bcd4c166012f9f8c70b942bbfbc8626804b0570298" - }, - { - "cipher_suite": 3, - "confirmation_key": "8f35e50bbeb3ba625eab413d746a8a2e7b53067d1c34920da44d869597fa1777", - "authenticated_content": "000110d3bf8d4ae8ad5d71ed2ab3313e3fd6e09daada4cd217fae00100000007309fc5ef01650cb8fb98641acdf2f84335a2644053747d92a811c99a7b811316ed2e7ec1a59944e958b87e7ffff230e8f903000040409d4498402bcd08587be015aeb6d779b973c984fb9eb97c920832b47620199298285b04ea398a95d6cee14862f6d10f3e0e8d4cf83faba737c1c6f02e81aec00620a8899f13e20b60006365f72f002ba05b9a14f76a88dd9c5bd9c669d9546be3ac", - "interim_transcript_hash_before": "72f96b800a3e5f396c0948f58821a98f1e17d9c0b1771477d654db7ac5fca53b", - "confirmed_transcript_hash_after": "c13361ae34dc091b1e16ed4a55e66cc532f575b48cd207dd664fdde267344d79", - "interim_transcript_hash_after": "cfce1f1d1b442e8b881db24967cac43a8da2cd63cf276ce22e15fbe19ffbb9f6" - }, - { - "cipher_suite": 3, - "confirmation_key": "802ffb27bd3d4044c30390d4bd48d895b9e166ea8d8eef6f486ee1fbfbdecc93", - "authenticated_content": "0001106d820e2de2a79e1c8f48f4a34e8a8b86867c5edaf554e038010000000730827d289f20cbd375ec8e9364e4241881dc06f7fb306cf2487ff5c72aea6066b2b4993891e3f6fa6c70ce0f1766fc09040300004040d0f88b69f4ee161f212ea8d3fdf1ed8d061d204b448b114fe253894de5a1f7ce59c5797e5345f7f20308f8de3559f7b9d63e241d11514bbe8c80ae49d8e71605206bdb8f973160092ead50ad78674eb44e1339e13123158c8cfab0c97857871c83", - "interim_transcript_hash_before": "2ea4a49b4ca781364e28ec6b49aa7b220e3c15f4e18f889c725604fe8da389bb", - "confirmed_transcript_hash_after": "7be6882e73dec751d7bfe38982290bc80b987fa76a38941b6a60e347a90a1028", - "interim_transcript_hash_after": "031e8975afed83b80574372facbca6d1760540e909915f87d48e327ad0e031c7" - }, - { - "cipher_suite": 3, - "confirmation_key": "7f1d15665593e9dfb52de7b142aa7fef914c7aefff4359a5d63e889e7afa59f1", - "authenticated_content": "0001106ac03d3bccfb1e319fa205b5f17387a08dbcf115d76c63fe01000000073029dbca702be8bb1b45cf13712662b45359ee3d959c6c80f48c5c5d0a02cae763cf7feaa1771b27e92a5dd7bf61a3eb730300004040c8755ec5d3060b149d15a8ce24bf1efd05097503a3debefb6f09c98562285b350d2dfa269c045e8f41aee09c2363275774b4db67abe4afa4b07cd9143159670c2009af9a716f28635973746f9510a0b551721a461608c4504d837428980bcc9c0b", - "interim_transcript_hash_before": "bae8885883e4b261ccb62818113fedff6390e4a3754e0527a18ef7b71dd30305", - "confirmed_transcript_hash_after": "06c0909e333f1e9a1e09c89d688ffd7764b304fe6c5375bf23a729080a13b84a", - "interim_transcript_hash_after": "b70b6c8589e1d46be0fc81869c3107fd703d4ce10152860e0ec468f784dd53f3" - }, - { - "cipher_suite": 3, - "confirmation_key": "1fbec36adc8c7afdc93e86482d7f5ed028326bb72d117cd8b116a52bcf3d7dba", - "authenticated_content": "000110cec480960d7b7642952c88d5e591f0aa81261d806c3c99d0010000000730eff4b9a00f2967b48fb27e1e31f718285622306059e6663dca78cae98b343b2bd7e5368eeefe286d5d99cb8987da803f0300004040784c53dacb134605dc07415edd63d340ec4eebdb9da475196b051a13bb06daf05168da72f2222a0a36a2557ab07a8401241ff5e6fd287ff95d34a20ecda5e4092034dec873feaec248545a1f3c144141c59d2a99e329b844d001a323322c35ca3d", - "interim_transcript_hash_before": "0520a8cf66a71b86b69a5bffd4160b24a6b158920952f90cc5b1a6d661f2e2c8", - "confirmed_transcript_hash_after": "e83813aae04230da5f5396e0b402860be211a6a377fbb476e87b98e28524e383", - "interim_transcript_hash_after": "950b5070106f202b556f5a5b75195c7a5febfa2bda6423348ccad04003171dce" - }, - { - "cipher_suite": 3, - "confirmation_key": "96ef2ec4679e40700445917b0019dfc1fb5720d610142657f020b62f565b21a3", - "authenticated_content": "000110a97d3fe812c49813e2dc355376e1e2829acef9816be12bf80100000007304f772e86e310ef3eeb7a61d3ae4ac940fd45c1eb9194ae8424cb958029b3a22fff6083e44a2a9eea991ec22cf267469e0300004040f2de01d075c890f447df1eb129c74041fbdbd7cb12834e6eff1e9de4f96523e6ae271bc46eea2e7de9a52fa291930486b8c5d45d706d25e2a9d65543d1c2730420113ba4d492fffb83e49162d6e424001fe7705979d98ba52f2931ca2856db8291", - "interim_transcript_hash_before": "8124cd81f9fe17d9b8dc6156e1d39a28b21652a460a8602a6e6e2ce065a3abfe", - "confirmed_transcript_hash_after": "0be4df6d3a5c23b266aae080d171c41e9138a249d24164312954e7ecc8299ccd", - "interim_transcript_hash_after": "631876dc94fe17ac092e27a90c07741d4242e7f3643c74650af0a29a470a0787" - }, - { - "cipher_suite": 3, - "confirmation_key": "4552fbf5d09192db3baed8bec30b12fdd2ddd3b761246f888d660636c3c42156", - "authenticated_content": "00011010e1bc4f5ad88b0f817b214321a9ef7ddc83b6583992b655010000000730479c4467dd5e27f3e7f9bdd13cf57e32bd1b8f7b63991edb7d89ff3e6f2cb26d702d6330e69b0218a27302679bb4827c03000040402d00e405c3f32858e2d950428e7af89a9fae98fb53b602823363b439340c7609ea5291ae9b5c03a032df533cba62f31cc2207ae61dc5216110f7559873c3f80220bdf616d3de8652a801d3e084656f2fa896cb5fa03d130c9a0e447c87a64057a7", - "interim_transcript_hash_before": "c7db4aee0eb4ddb5282ab98023aa97eb7120ffc4e8c4e59d2f3d046b4ebac980", - "confirmed_transcript_hash_after": "f537cb42f4d2f0cdbc6316b50d578f40fee50185b1fd253742f3d3ad7ed84a40", - "interim_transcript_hash_after": "52e7e53fd409c25c87cf363ae0befbb6bba50b4b7a978d6ec053aa800cc5fd5d" - }, - { - "cipher_suite": 3, - "confirmation_key": "104d075674a706035fd42a8c70cc2f64cbd61c712128fdfd929a84361aef36d2", - "authenticated_content": "000110e0e73582cd9648ba28605d7e7b7b11388edb7932cbace6a1010000000730a28a0fdd4a3917cf9c20c0ac429b9a05590c842de804afcfc5e994dc9a38bce419533b36537d69bb45f0efd2a2a95af103000040402c21ffc1113d10a25b6bccc9a1460b4ad409c101cff12d785c83c04b66e3a2608eeff21c0f882e1897617a52fc9ba16ce7afc5d44abd7047285d3d798467b20f204e3dfbd67da757759351a2256734c1ad89835738d5a65af7c9ec6fabf06beb77", - "interim_transcript_hash_before": "c4b3584dc14a36751af98da2f3fab3faf981d4ce1d5c6b925828d4a19060f2f2", - "confirmed_transcript_hash_after": "0848107e4057a9d18ec3328fa7f731f3168c63b19eba8dac5abaeefce4585244", - "interim_transcript_hash_after": "b71079db27e6853cb3256eb38fc227acb67f756a6ace6db19413bac42b38571c" - }, - { - "cipher_suite": 3, - "confirmation_key": "007ef9da18889457061adfb22aa4d7f8d24f3d16d42d1e22c45bf43184fc1199", - "authenticated_content": "000110b2a3e7045075e72e5791d5054d08ef5abdbebe4407d32e4301000000073078f91ab4bb16308e1613a7f5e8edac2ae0e5f5362487effda62134d7c291f3d01c6d119b47345891d78e81fb6f85dab303000040408ed7a6049abcd78c3b661503b57b0ebf80de940e53236657eecd9658368ef8952435b7f576c7411855b5e4f2094c9389180c011a0fcee81ed27e9c4677498106200f37cc95e2ef5bd0ba2fc620725dd643838526728cd76dd10f2a8beef39e4a3f", - "interim_transcript_hash_before": "3dcd17e586e847531ef0f6015e3c1e63218f841386d7cd72fd28e0378f1daf2d", - "confirmed_transcript_hash_after": "70696ba86bfe163dce1083ad834dde759b64f2f1353cddd58197f00b8e674214", - "interim_transcript_hash_after": "cc938566b512f75fc5ca4e6ab2a6ca404ece50f3e1d326e26b3803762825d51d" - }, - { - "cipher_suite": 3, - "confirmation_key": "e75dd43973299280535d1fec6423236590d17d8d61398d86ccf3f27cd840af49", - "authenticated_content": "000110ba3192b0bb7af2c4ea85ac824e14c4abbeb5e08260abf364010000000730b3f296a142fd4ff08f704fad2bf49e788ac9bc1cc364600693dbd2289f6e9ef264f75a02475c7038281589e8b932ac57030000404092933f1b941300fe243d0d6474858bc62a0465476df09e39836d6411fad5b7e0c83cce4a4966c4b79b1c6223e9957c6e420c952590364f6b7cf8709f7030fd0420c943b504742fa9e9a01f1ad95456d5313dff2d713f0a6da86c695ae3067b47a0", - "interim_transcript_hash_before": "6d2ff019f8624c543a0de7b1e2067d9f5ac6cf2b5faaed38114dfc0b8ef6255c", - "confirmed_transcript_hash_after": "c3b8e9c837a71166c7c4fc60cda56f135d85de8ed7a771a0af4713f444546e9e", - "interim_transcript_hash_after": "894384d12a954db815b4a81365112da7fc8c25f6b20642e2398d4741d8393b71" - }, - { - "cipher_suite": 3, - "confirmation_key": "ec3ea189866e1dfeb3777e9fead6f1cb9c43c81cf32315dfc91a0092865ccc49", - "authenticated_content": "0001107c6293bc263657717fcf3f03c5994efef339c740685e9016010000000730a329620e50eda5664c273377c9928f80f5d8d72c7faf9587c80e6d6450e97d6c7bebf631b25f7206ad36eb610cf5ee780300004040b5506e22213bea126c1ef2f2c7f163ab982cb51c68cff638ba28155311ea5ae703dd8b51fc2134e91083ae548132e29e237e367378706e54943819ddb61d8e002088d0ac820a62b804d92fd08209f4ec6d1f0f503639cb81ba61b8d72cb1515cbf", - "interim_transcript_hash_before": "f802631adb4e126fbe433b6c86bec605993bb81c8cfdca1880ab0c73c61c3292", - "confirmed_transcript_hash_after": "2305b18c3797f69598dcae3863ad221ed0235b2021121b22872719bf0b81cd0d", - "interim_transcript_hash_after": "52dad37155556d62e736b40f02004978369380d24238b8fe9b70a14e5d041288" - }, - { - "cipher_suite": 3, - "confirmation_key": "2fa19d882f999e9bbfb079804d8b825382742025cb9ebe34045326fe0b2fc951", - "authenticated_content": "000110fc7b23009b007d025463d168dfc3a9ed819fb078cb41cc86010000000730ca9ae3cb76f878eff3d95090b93d30b47dbdb44053722d218133379e949061b1c0c149e71cb52e14a28579a0be3c2fd20300004040529d7eee9dd4cef87a20bdf2c3055225891e873ed7f9a7ab1beb90d6e6da753e63938d85479827d6b82dcdc9a1c885b02db9b2a0b2840e7d32684458ac56b40020a36fc28e0537e3ab4f506a4f8310f4a774f24a42cbb0e94a2d66264da50ce094", - "interim_transcript_hash_before": "2bff072095b531cab9633a207ca7d4a7e721b232dabc4a3a0c7857eb69c69d30", - "confirmed_transcript_hash_after": "8eb70140dd81044d68f6392540b676bed9f6033c633a0784366d24dd845ef74c", - "interim_transcript_hash_after": "1cbf6a7186ee8c137912e3a168bd975d577e7fda20a46e33ce0592014cb6ca61" - }, - { - "cipher_suite": 3, - "confirmation_key": "81507e678d864e4d20803eaa34a7c470051012ad6ca9f242bd9ca423fd1de783", - "authenticated_content": "000110ca12d24b4f2722be63d2c9e298305779dec2d8a5511e8676010000000730acb626cde4232cd20e2d20377e0ed6fd46cfc1936f175b1c58a29c79f2290d2fbe5f2c967366c0100c7f9a0af62fe20d030000404096e5c2626cf1b7f578178cf5cd1439716df5c6a2a6e935c43f8fee330986538f03383349930c564b18d6d7490895fa3f0b44a315ec9506aa4e80e5e4cd20c40e2081f8769f0dc9a1d00632edb21e3f46371a6a3ffa0f3d474d3c4b73afc712d689", - "interim_transcript_hash_before": "18bf156b74cc41fe5abb04bab027f602a6e9ba4d3b4225d537a14267246641dc", - "confirmed_transcript_hash_after": "99c0c311bb5f876f78b9dee421f3c2748b45ca119b6f967cb40635fbd180159b", - "interim_transcript_hash_after": "9a24239755e1a9b6b28b421a80dc2e76300ba0ef686cc589d449031123a9875c" - }, - { - "cipher_suite": 3, - "confirmation_key": "b33d9d90efe8308c4058fa83715c0935b1e85bff07d9866ba934e529293382c8", - "authenticated_content": "000110a3b2b51fdd6b2832b2305c1e7b4aa294ef866c398bde243301000000073056beb96a30fc232f5657067bb1b43dad03acac8c36807f33a9581e1bc4d8fa438fced586b20e336ed4e3f0f0293a13c70300004040e50ebea7ffac26cfa6c6054d4d17746ef31a8025d93775310981c057b53d5072723f0f0527c445c23dfbe1199216db7401359afc55fc2bd782114725f4dbad0020381954b2f49813eafb55c10414c4241f809e984399d06136eb74d34aa1a3b15f", - "interim_transcript_hash_before": "e86414e8a253e8ff891f7188f4f0c2566cf38955db06f3a1049825f4bc4f43b3", - "confirmed_transcript_hash_after": "27c5c7139d4b9ec6aa559b09dd0e4e0fdc5396078d45a0e361aa2ecc5d1655ae", - "interim_transcript_hash_after": "984b5c741bf4cd43687a6b552aaefe751dadfc23464e5e413bf65470874e6918" - }, - { - "cipher_suite": 3, - "confirmation_key": "691dd84ecf64f392dd591f8f5a4e715640be2dbb2490d15780387c124a6c9b09", - "authenticated_content": "000110471a609f4a0f5a86eeaaa695e486d0e08fcc0709b62b5746010000000730abe5a39ec4c0c2f23c67144bd384f3b89954ba8418f1c14a0b252c537ef5cb19fbac9005a2be340be1074da690f917900300004040e41797bff5b74327fd662797cf9a84f571208d45705903ff06ca3156feecdb5d395931f46151df207bd9b0211dc7d558feb7afcdce7e933a12c6283bad6d460d20e52a5bc587136e0b42364581f1d0fef392bc4a3c836cad8224336f8c9b26fda7", - "interim_transcript_hash_before": "f538f6879d814fb3ee1110b0d91f660d2951592e249b812eec02e267210d3d2a", - "confirmed_transcript_hash_after": "5ba3b76192877b3c5013dbb860f7e347d2f14dab400200d9040c989590280ad1", - "interim_transcript_hash_after": "de05f60b93caa71886e69bb66188200705b730822059d0800d81dde01562ce77" - }, - { - "cipher_suite": 3, - "confirmation_key": "ba5f60bb8d26112be02d50c73bc48bc25e76a85447fa2c47779e53250a292eee", - "authenticated_content": "0001107130cc0b891885992ed95e4885c43b041b1156ec09b311aa010000000730318fb8d6c55fa8d7a5855073a40ba835e1ec4c32678f556b257ae0eb97e5984c0a3835b905650c6a6261703b239c30500300004040ed8f4ecb7ceb7da14f1d82934730c12f3ce789d9c95c53dbfcf002602289a389e17981edd15df72d9d04f64da1df61d48fe06bb34cb5bc04dba379998edf470020624368dbdde8c01c069e09144658a08baa5c13a52ca57ad0ca09050effb77495", - "interim_transcript_hash_before": "2a5ce3bc2f0e4623f4cfea4f89c4c9c054db693c9ac264e0389518c174f7ced3", - "confirmed_transcript_hash_after": "a7d6790930fe2ad4bd61794338df964fb71b81b979adc9ca7f114c691afe284f", - "interim_transcript_hash_after": "6bb1c7df6e59b1a2ced3942635206c08ff22c959a37dcf4f619978dbbac1e292" - }, - { - "cipher_suite": 3, - "confirmation_key": "f5b5b317b001eca3b04c853c6246026d3ad3b3b5cb62ebec9273d2cab2b1cdd8", - "authenticated_content": "0001102e113514b9f58e2c267ee1f8e97420455ce04d71d20b300f010000000730f5614ea42a1cca607bef8979aadbcc74b43b1f17b8ff098fcb67ce1899aa60e371f011e1f856fc9cf8ada4b81ef3f32c030000404064062ad4a85874a884abe241f07af4fb3a9e991c63cc7e4f8b0cb9b1d6fbc4606501a7e4c6b21a53e9e27d965402dd4bd2fc5fc3e4c57fe2db49cfa1470d1109203b6aa017e744c873b18961c8e626478e4574f26b18e228461ff8ef94c7892369", - "interim_transcript_hash_before": "5f14c6207f9e693758fd48bf26a92b4ccd6278a8c250ccc13a5113b7bbc59c06", - "confirmed_transcript_hash_after": "fe75a96b729bca54879b292d036094acf9e182162ee09868ad0fa683a6496675", - "interim_transcript_hash_after": "cb514b8c8ce8f3806c6dad194d38e5afc618e27763ff72b6d46be41fcd0d6882" - }, - { - "cipher_suite": 3, - "confirmation_key": "e117c6c51fd26e444438e2392eebcd26ceda907bc3169f39c54486c39fcce57c", - "authenticated_content": "00011016a454d74adbbbdfddb9d6fee2070972de3f8e129912b48e010000000730d48d17f89a946784cf2444606c6a7ddd977f60039e569edaacbdfa3c207ae0703daea5787405144f9b4e037d8c95f96b030000404064af808f4ccb527ae8ac878f8ebce402c426a41924ca29de6c06c87ee853207ec6a7dcfbb7ce4854fbf5909bd589ec85734f5b4ff6ae00855707bd9a492f100a20507b01c492927927c9cdecba21e5bc829c2856aa4885d4d3fae2fecb27d06722", - "interim_transcript_hash_before": "119d1d34e7f217ac8cf66317e7e764964de41980ffc5fde18f12febacd93c42b", - "confirmed_transcript_hash_after": "c54a5c727c116c8285efdb814b98496aae5b9eae51f5b6709e7221c3536c3272", - "interim_transcript_hash_after": "45723c7d55920c537bff70ff37b2942637b2acf0c29bf648faa169d5e17b2714" - }, - { - "cipher_suite": 3, - "confirmation_key": "aa88ca8946a1be1efd71e1265756f132ac10bc25c78c71a97576d8ca266649fb", - "authenticated_content": "00011064e38183da0693235df7197000a25bca999c36666e64f06e0100000007308f32b5272d870e6b027985267d2c9e545fbf7a3bf40cdbb61c7f31953f5e061c5a5f56966f2fe5345e12d3dde5a222e00300004040a59d72e8c4d73fa039ff5e743dac55c14222a161ff677e1fb3dbc46461f29f7a3bebfd14bcefbe21b3e028aafac0175cbf46fb1e003f77c455d365a00fd8fa022085e5d0cb1400c7bc82142721866687cb1b3e28ab819209a9a9e44989c0c3c366", - "interim_transcript_hash_before": "98f518a4bcbf342be94342e31f057aa5e468b2b4181f4c319317ae37407d3e2b", - "confirmed_transcript_hash_after": "e6ea7835695b07be290767696d9ca232ad7ec9be788f4b2749d48ad4f4660157", - "interim_transcript_hash_after": "481bd1054ce508f8d8f54d5c284a8760f13ab2c302c4fa92bde150f1f960f41a" - }, - { - "cipher_suite": 3, - "confirmation_key": "e70b55431e074f93fd45835a2d203acfc35332697e512d6e28b0884011bea447", - "authenticated_content": "000110fe3d8157121bf1701e20b1f876c0f0f1bf58e48c6b162e2d010000000730a2b780aa6c832eccbace8e9ef618767838fe57015cdb11dfd023de2a629377e270e391d4aebd8cf1d12b0700b1710bad0300004040ed84fc980a6f2cdb7af8c46dc6051d828eb8b89be1425b18e3c6a24fa831e5d2d86bcd070e6452b3eb35004baa15a30941c697ec01c0a707930451f3cf4c3c0620318647c335a26983ad121276e0cc3161e9094b0dfb16e792496ca7de0f4db053", - "interim_transcript_hash_before": "402b4dc4a4ca0ba05a9ad7ce04dfdfa45c594646ea5ddf581cbf47955ee4b7cf", - "confirmed_transcript_hash_after": "8b0cb6acca3abd1b18f5f50359de087c61039d8e350dcc63d6c260a3ac3ffe80", - "interim_transcript_hash_after": "a6fd57f0bd90692cabffe8fad496070ae247a830c74ae41b083eeb9788bf233f" - }, - { - "cipher_suite": 3, - "confirmation_key": "16afc0fabadeee09152480cb1af9c0ea2057433f7925e0f38d714c45774e6471", - "authenticated_content": "000110073f30c7a1a1ca3e215def483b7f3007460072fb6aec83fb010000000730534dcd39f7c8b55f8d2f47f71fb8ce0d92ef21f262c9b9971dc8af3d9b122247214a5791d041a051839ee2d04935e20d0300004040fcc93675f522dad124416d9d77a79e00fa9d41f87b64d6b0248443c47f01fb8aafa4911db854f10a5927fa6ca48538b8090a5ed8954fe95ba18b42c39c23c60e2074fc8e88f9d1b41aacdaf98362bda73820ae7dbf7976ac7d1f897e149caab979", - "interim_transcript_hash_before": "4b02ae6ee81b53108ebcba68ab10ae0887a92d4838ee73a948d27472ea7b71b9", - "confirmed_transcript_hash_after": "9657245d6825bff360078e4f7df7d082b40235e785938b05df4cde1fae2aaef3", - "interim_transcript_hash_after": "5e3dc56c8f21ba5ca73098518a772b04eff693b8968d9ba42ba0767f6510418f" - }, - { - "cipher_suite": 3, - "confirmation_key": "289a2a826d44e7e5cf51874d03ab6b86b9d27e4d680c9be966e81cb68ac5b1af", - "authenticated_content": "00011045fab63c6fb1b68264387e6443a38520851e3867aca93e4f010000000730c1e094ad01ae4f8906afb677f015ea9e1e0208ca30eb12604a848fd6bd8faed117a3cd52e14b29bb2f000ea66e6273130300004040c7d91b0d0b414225c044f149b610fffab1abcaa24f291e85d0bcaf24afe49e7e26ac449c34fde21ca55f2ccf41d0d7100e10c46951ceddd08f622ecc2a9fec012091d61f01b42261b4bb40cdce0150ec442d19c5c2f07761afb8802c22308d3764", - "interim_transcript_hash_before": "12d3bc700594a7f9dd3a45e00ef0b40e252faccfe43fd58b2c4bb3e12f56077c", - "confirmed_transcript_hash_after": "541c154762e42c96be3942cfdf9d86364b5b106ef15c9581e76fb7d2aa26dd97", - "interim_transcript_hash_after": "7ce5eb7840daece897d8f78b5192695e336cd7288e4b5fc9609b99a55d10500f" - }, - { - "cipher_suite": 3, - "confirmation_key": "3fc8431dfe5388d81a56fdf436b27d47f19c132aafd517ebae622937179c62c1", - "authenticated_content": "000110b3bc25e30675bce43a7f501da1c5eb601c18c3be3a9146bf01000000073069bc915bada9d2d30f4b5b0e88dd2f107e69ca4681644469cdc3ae8a76882763c575a5cc5e952281f89cc7d7e379d5f903000040407a5094966ce102e349f1721c6a51761ff8345d2253fd8123a5812f2c186e5d6c498708b62233a4db0699913ac929199fd5ce7ed15d539dc6de20094f339a7e0f2001b62f4cac08aaa58587d485658880430de28f9b87f946a257018cefdda1a48e", - "interim_transcript_hash_before": "1318d0abe413681bbb875e24e5a75a54c8f670ee0084210a98f682d5131c4706", - "confirmed_transcript_hash_after": "2bf4ca6266abc06ef3695f48f95975cbe57fa0773ed32aff4ccd085305d932b1", - "interim_transcript_hash_after": "b925bc2ef408be6b9571344dbd4833bd29028488038c7b296bf98f650bb0d6ff" - }, - { - "cipher_suite": 3, - "confirmation_key": "e449d1a7870e4544fbf9ae2005245592b0359954c7f05d04f254a15a5549dd5b", - "authenticated_content": "000110f7d403752a3ad9bce2363db6f8cc444617310a691c3a407801000000073080d93b75864fcf3f5a70bcfadda63f997821c4a7a45148aea2516a14a0e5984d8dcfb549b5ae577cdee6090856f847160300004040a1fc878e064a305b2fd905f35147765c22449787496c411481259070cbe3bcafc5c5fbc9982db6c567f95c2147b0c1d0ca30cabeaa9eb96ff2525752436f010420b9ba99ae6c28a2b9ac4837fbe16b5af06cccc6524dabea6ae535979688ef5d31", - "interim_transcript_hash_before": "16aa7f323c8004f9f9f8ce5e748ebdc31265e8ee74f1b57631d0cb67a9751f79", - "confirmed_transcript_hash_after": "db62fc785c3292a77d3c148ce3ecac10dd166b2bae9e82d1ff2bac7e93d4350f", - "interim_transcript_hash_after": "31ecdb63af4b05f5da3979d3b2b99f61755e719d85e71f002f734b731e5554e8" - }, - { - "cipher_suite": 3, - "confirmation_key": "4fb4667bd57a5a2746549e77e190e21a97695ccd2fe5d3cd5ea9719ac6c6ed9e", - "authenticated_content": "0001102be847573bf5edc6aff9776f3e1ccbc15c8bcf220d07a52a01000000073081e318360a61c6de16755455bc7a08a0a4a3baf98fc9f4c19bd3e0942a23a2e1513e2b5cd6c960c34373203d31edef9703000040404357fb51b1a47435af0b9d0c438a7302db3603de17d6e438ce0cf5e5d5352a868684cfa030b77f01c18e7350417110b63160d37756ec59735eb6ecbf51a9d10320db8ccd33ddec2d10b3b5603b567dbc7c15f2ac37b38ac971ed24bdd7acb23b72", - "interim_transcript_hash_before": "7e21b1af3739e312856978f68503871106375937b5e17ad17e48bd3c34915c7e", - "confirmed_transcript_hash_after": "97af3773b368bf041aed5ccfdf4cc4f26f6971e555372e5afb9b2b642ced250d", - "interim_transcript_hash_after": "ec18477519a1f70a013494be8dbba9cc8dea6d5f384ddcaeb72a73e3d10af929" - }, - { - "cipher_suite": 3, - "confirmation_key": "ca988641f5dc015a7a0ac9431fb4405798abd6eb1b24e0e6fbafa553628e3aff", - "authenticated_content": "00011084f2bff216f55b1d3635a1ed17a5c717785eb152fb0afa100100000007309885d3de5405922f9be56660527e30fadd0e177dec0baadbf072fdc84d6a9519b6d00be295fcb401261e423ab36e9da503000040403f8f12b25b71c0e94449f0ca8aa7a35632afa3def984c7d24be3df9b61b611833faad43569d64e60b75069f1d0283479e46971df6b2cf7a7d2b2500a1fd0a602207685e282f3f403771b88516f3c2c9ea2c3f71022ab370dc6824ae9a963df396a", - "interim_transcript_hash_before": "2ac504c8a362cc4e3f2d21ace71c7c314c5a8c265ae302dbe40746e643d54aff", - "confirmed_transcript_hash_after": "19846029dafd3177bcbc0ecc87591c8b218dd367c40eff1ee19d6e7c1ef462d0", - "interim_transcript_hash_after": "611042a7042252b7607b71fb639756f669e6cbe410dfa318af9e6e8313cf6bd3" - }, - { - "cipher_suite": 3, - "confirmation_key": "36e70d5d4f29ae5ed17c9edd720473dafe76703490c17a1681e8848c8948e8f3", - "authenticated_content": "0001106fea23cadf41cc541cd1336cdb30c82965f379b7b0f7113d010000000730244b77c8abd6273b73ec3dc1eaa42d62d18455d0dd07e5008414139d4da00c6694d9ff0cb2b584d822117fa2a011d78503000040402b63d9c5064903fa2bec71be971f238644eede23a6cf6f8b581c79a41c0164af3fc91c154d81cfe5e423ae38fa70687b7927427e15e7786c7ba14667448039052077664893a4ebb43c724c490cf683fcb1a9757d56ec1171e0e036bcbf39464b3d", - "interim_transcript_hash_before": "56e63db6029409cc24b70a3813e144d7b00059b11813673a30d3dad73117fa83", - "confirmed_transcript_hash_after": "0f1d52bcd34d166f9e0076376a71e4bfedb3b90429458b3bf2b815b61e05cc2c", - "interim_transcript_hash_after": "a274fb898e114e38c68e19468fcdc7579ce32bccd02ff59cdab51ed989caabdc" - }, - { - "cipher_suite": 3, - "confirmation_key": "f5e08cbc71a9dbcbd94fdde7059d7104c6c54cd66194fff06b79d8b435015847", - "authenticated_content": "000110704f7940e354a52894c6593458b96f888affcaa35d73e0f4010000000730e597c851f952a2989b19f3911edff3054403ec9ab95b5f09fdc935a4b1c5f1693bd9eb3cadfc62eeb0cfb50401ef56e10300004040033a686f05a2e1f39a9c2ecfe0741bac3b4f16c29a2d0c836e29353c24af7d66414ca8cee19e450854b092a31c2cfce259041d2bac515441f258f748022b5b0c20337a7af89ddf7463405c3df517dcfc74eba55453667e4221f06eb522ba544f9f", - "interim_transcript_hash_before": "9dbec51d8b38a365723285567c4cd6eec1e708d5d6008a544ae118cd065ffa71", - "confirmed_transcript_hash_after": "f6e1786d69332bbba39d73aaa218acb1fd50942f5ea339e943199d9f7f58082e", - "interim_transcript_hash_after": "8f6e5de0dc1410505ff74f4e0b041625bed7a7d5a5ce3754067aa647a7dae69d" - }, - { - "cipher_suite": 3, - "confirmation_key": "7e021a63d90cdbee0c8d60c3184c619e46d4d7756206a0537e3512fad39689d5", - "authenticated_content": "00011057f28b7c7c7b0d221a92500b05cd7ba04aa69deae5349012010000000730a856862d8bcbdbce57c30f41f48695f75fe3f05667d731e0e626ed9d6c7a281ea19c6ddf949c7bcb65352d9d9f1c779d03000040405d581bce22ef7484b83d0ee28c08c143ca48271e63855729107e36f9d6270d98d4e015be6151fec879ad82d6ec82ffc6de9bfe44bc4f4b8f4f39fcddbc5bc60d20c664a03c108d7ec822759954dbf6f78abc0ca594edbfc3a6958433ae33286ab1", - "interim_transcript_hash_before": "1909a0e51587bd4df22f35cd27737d72c483c36fe8cbfdd89f69a63f70e4fc44", - "confirmed_transcript_hash_after": "6bcfa8c0290f2eb6ed9c49d77827d36af9ea27de86802111f917cf5bd9090d3e", - "interim_transcript_hash_after": "95efae2733464f9fccb2f42be4b2c6b6cfccb6506fa310c0ce7b0971f34466b6" - }, - { - "cipher_suite": 3, - "confirmation_key": "a1292472d2eb79475a4dc6ac1d8375afe65030aaab252a5a24c8294db059aa27", - "authenticated_content": "000110eb047e29e37519d1b4f160772d2b374a1fb0417a39b7b5e9010000000730a825abf1de2b5481d689cbb23ec916f0673ea34b27cbb69c1bb344b65bd27ad0b0a647620881fa9b31b3bd13990c7ae40300004040e852964cd3d39fa20e4cf0c22ca59be1b3a146b4af46f60a6282a2a012a77b4abb3c776f6b1f3e8ea97a5df92e1f41dc09451b35203332975f3c3a58606ed00e20d56e3601413fc46bc9d7e196d20d0b3a223653476b0a2661c94b7de0b8bf1a60", - "interim_transcript_hash_before": "873fe76fba6408c1f13664ea26689cfd27ee4d25fbce26afee16bd164cf19c21", - "confirmed_transcript_hash_after": "d9ad68cc7010a2f9dfd7d8317b0c768cd88ac2e7fa98fe649a0c6602f7e56aba", - "interim_transcript_hash_after": "68d8ca80b8642899916e31e405feb7df8052978fcf7124eb7f72d3a5379e833c" - }, - { - "cipher_suite": 3, - "confirmation_key": "b51d7ced41ee7abab278ba87974c7814e8e97f8e283fa65234ec2d289af5a79f", - "authenticated_content": "000110339a7b4a8987aa486e35e7a742301cf719faca1f291837c8010000000730fee8c98d864a680c8d8e3e1244b26c7dc000ff2068b5d864309ec7c084ece2d0f17ed1637ef953b2a08417b5d5fd7d18030000404029d44ce3dc75c4c1571b9a089914e59a4c5d82398c12fb26e0ac8db2398055a3d8b9ff76479059031f8bade0c7c35e1808696d0daa11beacdce468e1c2fc850220da8d4cabe02dc7d20019d00cc2930b27c10af86606310e9a6a973b6dea1a7f29", - "interim_transcript_hash_before": "2e45f652a46dadc026a4cd3554b0660de55b3e1a6503a35dabec837040518ab7", - "confirmed_transcript_hash_after": "96b2a33627113bd9823170e3fa1290793ae1f9bea58c85ed3ff598b316cf7e29", - "interim_transcript_hash_after": "249a49854821767ba027ded67c8a3f07a68a96d16f654e6d49111c27642d8782" - }, - { - "cipher_suite": 3, - "confirmation_key": "435f7959fd2d2f25957dbaec604f80878eb01e76aab1f9f593c4fd70f24c5fb0", - "authenticated_content": "000110b673ef1aa07add570e1a6620b7c72e4a63898465f0bd40ed010000000730cf2e34024a2009befb7022afa9697ffa1380be1ca9e9d52d5427584fc80d09e1f0ef97fdd2bd4e73e7dbb702477808170300004040144d06f806f07234c4f5afe7d2c6109b52e91a29add91cb19597a6fcc04a361045c693b8f0eba2dc52ba5937e653c3866a96d3016c5d57f7e448a8c15a8a1e0e2075b264728ddeb7ade0879f9fbcd2f15c22f758e9e2ffc6bb20d99678ccac6fa4", - "interim_transcript_hash_before": "a9a4c6ae2e0811f5bfc7c176ed48ab8f34d54287b44f29d93f9e5f8bd3a57835", - "confirmed_transcript_hash_after": "486e7ee278e83aea9290aa32587f5565b702adb1cf317667e0f04bbe51fac32a", - "interim_transcript_hash_after": "5eec6c71865de261bb30ca12db6b57f5f7d0c322c6a076da4009b2e7aee46f03" - }, - { - "cipher_suite": 3, - "confirmation_key": "18da535ecbf9d612d4a36dd68f5459838be5237f332fceb5dfb28885a470b64e", - "authenticated_content": "000110b2a4fb7f25dcd5cf2516af5b5a9e0af72e8dd9603dab578d010000000730a08e43ea415a35c382ef502b5dc3ea0ac2bc9465da79ac438735527eaa4925d9c9ccea0e1052b1234cafdbbf30d5f2d203000040404892a61b4f08a527a4f2c4ec0b6ccdba034861d109e261730134a84e6169017d42cfc6e5afe7d6e9fad2a3e9aed088fbdaa9967db0a9d8cb0d3206e164c55c0020ebf746ef5f6e3ceb563ea630900bddfa104b13ecf2a9c5f98e8599c40883e87f", - "interim_transcript_hash_before": "39381d63d4a36e740b5ffa1cc5ab77d39fdbf6e9c809a26b9b58206b6adecf1d", - "confirmed_transcript_hash_after": "d79836cf93a80b9d29b97562185f0d8f45de9b2ac72787901d91a08ce8b42c52", - "interim_transcript_hash_after": "f0fbd894226b7a68f3cc1738951cf762fcdb9c3672139435e6c672a1f4d12edf" - }, - { - "cipher_suite": 3, - "confirmation_key": "8098b86bd0ab4820f8d290f14722376795abba7bbf5e6baafc23d82e3bcdfda7", - "authenticated_content": "000110aeb43aeb339125b5f3fa00339953ce5c4fcbf585e4c2ce950100000007309482d8f979fe9b13d0c48413bef71017cad0631a8b62f0abb1dd3468ed9fda8223a64e418efb85755bb0e35ccc8adaba03000040406f42a7f95ee0dc245574c999e6ec816e416247cf2da7d933f91314ffb6bd26ce1cc7dfe83495f10effca9f4e5da6d68669998d1f8d6b4b446cdddb1199c8ef0b20e7e47dc29a31ad06fa5610be82934998bb3b47010e720730a4fd5fbeeb11eb98", - "interim_transcript_hash_before": "faaceb4992df9d81c486b088e29890f4b03dbfe25d6e079857afbee4c8be470e", - "confirmed_transcript_hash_after": "39ee18dbc4df8cc6ec551b660fe039a909124ce484a56096f8f7e127ff39f375", - "interim_transcript_hash_after": "ff3fcddc99171da2433cc915e319d8c05548e0a097785f47a2e84444e2b9ba64" - }, - { - "cipher_suite": 3, - "confirmation_key": "44d4b321968e8a67797df075163240baded2f77489e929d3fd2643f3d2efa3df", - "authenticated_content": "00011028b21614ac91ba64602988e29a93b4d977d5cccbc7f1a6660100000007308d60387cbcc58a4f224df11c07868af1da71620e6b4b5f1af0674fa2293ea86020630d81890e5a29b08a327430044f7203000040403bf02e9fcaad6526c8e70b04b1bba4835975186280f297846dd250535e148f6a76c1e6c5bbd4d65e3fd6855851c7f0c422c1fba6e7b62209d548f0e867fa860520c5893be392ad18f43ab176fd2a7b69df1bac347ddc22d98b7b4aa59a28930548", - "interim_transcript_hash_before": "aafc911838625d1206ea05b6e691186fabc9bc017fa823266c9c48103e71c22e", - "confirmed_transcript_hash_after": "3bf74a207c97b03d81b6c99433d831ea9490cd5df1d8621b886081fe05e6465a", - "interim_transcript_hash_after": "a90fe27241db613eb63e9058aa2a78ae8db084803d9cfcc83eba92edf4cc4a1c" - }, - { - "cipher_suite": 3, - "confirmation_key": "172885a8ec80403aa901dcb7c15dea0aafd3b19ca7a5b472df3b498f6834f6fa", - "authenticated_content": "0001103ac5a1183cf94a62c0e19df9fd1f98da5e47a5b10dd6c6860100000007300d906bc7436d33a2f2880d16c5e035616898bc96787339a518b692d1a87776a12404521ff045e9374a6f1884a6da0ff60300004040f0e7d912a003197ade4407fe9dbfea0f4c9f9320240b6f6c37d8ff49a2a76d44ad0e77df28966b493974e64006d1060498eb99c574012d618727d88079c9ab0720483b2f8a9661f52e4f8333d2c9a67748c1dc705e6a67069b630db661e88e1cf9", - "interim_transcript_hash_before": "8931f85d529de679cad066dacd31023ef6fd05ddfd5eb09942be0f39453c0b63", - "confirmed_transcript_hash_after": "8a36d81064cfebe6cde4e88731c1a011d6d60d974eabe57efe6496741f5bbe4d", - "interim_transcript_hash_after": "6522a77481e811f028b213e3fb5aa4178704d07131c65751528542733b9348af" - }, - { - "cipher_suite": 3, - "confirmation_key": "175eeda24730600c01b3a6fe9aef6a2d18ce41d4937e3d45ea72efea94b53121", - "authenticated_content": "0001100794f3795d9fb707e12adc0c8cb24c832f74d012db5508a3010000000730b4e01e9ba12b8fa9ed341cdc82603a030f9d0dc107bc181d477f282533cc692e92c202fc4ed8f6cfc4c88d3d9f2daf6303000040403bc2d07180b84d536d86b99430700f6019a9deef5aa087fe0e89044b6189e4b6f98ea2a3dcc74a6585a97b3e800366d908f4603210aec9c41997a960cbce8c0c2079abd33f53e8d54bf0d59f0bcb69fc2bc305a7b336a456e0edb2be35693af873", - "interim_transcript_hash_before": "1b60735b5058b4007b998673973b8680b4af7123e6f49a7f1fa6b3c9a7bb77f4", - "confirmed_transcript_hash_after": "7b11761f3a9be2f8547909d1a3765b2aadad4d7a921d0bbaf79b693607df7fd4", - "interim_transcript_hash_after": "ffa1d085549e520cb4348b8a9edcccfce0c98d99645fc1899acfe1afafce7ac1" - }, - { - "cipher_suite": 3, - "confirmation_key": "58c7eec0ab230e298ce2979c25d60d5bbb2c40dde481d0fe9dbe9c0bd636c5f8", - "authenticated_content": "00011089938be5b1a0bcdec37a17f5df7557dbb65bd4eaf2016c300100000007304010589ffa4b0ede5bafeff594c618f5782e081e398d5af5bb7dc9dac3e6f65194198fbcec0220ce82ef096409179e7c03000040400a59d037b8b9eefb418011f27d53c88eb25d60fbf7a4b78dc62f806e5a17d7a8cccc91053f2e0804d39bb0fb7a7758970cdfdf435610c66f69a9bcd7adab9c0120e2513cedf485e57203c913299ca93189390c6e4ee4469aa1ccb2d3dfdddf6d7b", - "interim_transcript_hash_before": "1babc828c2ea4e3f03508e34e5279f796eae50433b10f9653cfe11fbea49bc9b", - "confirmed_transcript_hash_after": "820a39afb2e1b65390aa3e16ad6d35b46835f54e7f4407983d7ce721dd681039", - "interim_transcript_hash_after": "46f64f4696372cbb4fb6275590610927427eb9089035f6edb9b69d13c08a3cec" - }, - { - "cipher_suite": 3, - "confirmation_key": "799c7e6ef50e72c44c952593ab4bcee888533e9428650399e6f420121d42b296", - "authenticated_content": "0001107c3df1e3fea01e5b06a886cbbaa2bf8a5c3359e0f52b91f5010000000730b0ded6fa8bf1fd3b9e808c06ed645a9b9ce83b8e3d69242817be9bb4e989d30dd57b7e924280e2f769d772ce5112f0cf03000040403b2ec54146a2491a64fd8fc32b9a449ab13be32f872cb87918b39bff98e34be90329f58d0b0f175dbd72aa87c9b81af8e5e1b2c7ac2059442aee4dd419b9640f20c1fd6d6d08839535ae60a0c64b51a8696adb7b9895969b4e170a913c25a19772", - "interim_transcript_hash_before": "695902a9ee2163ee2ea1482e22387624dbe2b1e22da9fa30a2e8b656931fbfb7", - "confirmed_transcript_hash_after": "e631114d94740c5dffa81bc24e763b02fe79458974d7d09611dbabaf8d3022c5", - "interim_transcript_hash_after": "7ba3d5baedeecb6239b51ff04351d72ec9ab57daad3275a03b2096bd3a71ae83" - }, - { - "cipher_suite": 3, - "confirmation_key": "c77c82486823503ebfc1c8e137872d23a59b7819687abad87eb706100cd2aa88", - "authenticated_content": "0001104367eaabffeb7601a73d4a7324ae5a5bada92e1b6f87607a010000000730800b65e7c9794b0c1a6e9605a8f36516bf0a7899a50991f8a1d239056a2ae06c87d1aecf72efba5c8ba49d7049082c0e0300004040917cbbe5cd0711e05b49a4842b3f0805f7e2fad519e1429e74c50ba17314817b9c2b260ee915711b18db7444cbf1654ac2f7ce147dcf050d4afe20fef80e66002008cb71cc2eb16b56d8bb5a5f0cc169fc7e32b9793c239b98d943f12eae27104a", - "interim_transcript_hash_before": "28955f3ca33559e8fd9444100184d732bf862625576a9465c236f4ccc63a0071", - "confirmed_transcript_hash_after": "52512fead03b9e1b5cf68651f202d2284e00bd894b63d06ee477dd19d7cfb527", - "interim_transcript_hash_after": "0aaca3a227d6ef4209b875c7b75d35d6ce06e7ef3b46eee957c25cebb97f27aa" - }, - { - "cipher_suite": 3, - "confirmation_key": "5b9d8deddd9a01a51542ec0844d510866b027cc6ea9272467684fa9acea7d375", - "authenticated_content": "000110df70ff2e932ad3b9371f3cc25746f93489c7a9daff90a6710100000007306aee798ac116142b3e21694116a008469e22b43c36d198913fc2224ee1e1dc548d1e3ebf4a9d417cce66a5387605e3b00300004040cce9a7d8389a7978992825ee175e7b50230e663f57783fe74e7844e416e947971573079e2b96ad54908f4b31b5935fbdcccce09bdf1bfca061f43c1e90da730320390196c286b0db16b11735bc9e869ef74969773a0d2dbb27d0ef1f63c05661be", - "interim_transcript_hash_before": "a9d8263cf3afc3e85df8387b12d4aa976fb922c216c39b7f687bd6b5776fed17", - "confirmed_transcript_hash_after": "120070796d42e53f66fe5bdc2f9b3a7651ef970e0be1241df93440f2c8b18882", - "interim_transcript_hash_after": "8111b73ac7e3bc7735be205e97f96587eb5e3bfca7892360acf822dccb404d50" - }, - { - "cipher_suite": 3, - "confirmation_key": "20655cd9d1f653f330e01157572b364ae2c71022b2952884258e132440e2063c", - "authenticated_content": "0001105acca21ed42dcc240faab079d99a9e629194a57d64b7b01c0100000007309ae54f6c95b67c4cc84d046a5ddeb596aa77e41cfc8ceff0a838d6518ee93cc90ec43062133a40762767b176c1d0839c030000404035c4470b085d2eaa11ffdf9f97ca145e01e086bb6be6d3dd55c140251f8c3d9ff3de2db6b62248e98a81f8d4126bb548d5adb9c2140b7358493497dcc36f860f20e3826094bfce5d21500239c9e4d756c047ee2cc58119c49ca4a2651f1855671d", - "interim_transcript_hash_before": "67b219aaf664c84a8f93bb9a8c6c6b5d367cd57c87d8a90d10f339e9cfd032cf", - "confirmed_transcript_hash_after": "fca7404fe8befaeeeef45412e5844b4e0b9a181562960f983fb8ddb3bccdcf2b", - "interim_transcript_hash_after": "bb9744b28d993dbd97af4e73ce6c38f4fb1706b21627a3ee575ea42d3c0f8b97" - }, - { - "cipher_suite": 3, - "confirmation_key": "ddcfccdf7aa3ff4c435648460a54abfd6f149c7b8ae6325bf1cdb93b665b76a2", - "authenticated_content": "000110aa0f82deba641f95fae2a089c669b4d1ef13314df5599711010000000730cffe9b9e5d6b79e4fe4577cfaeb71c8ef0e45e0dea725f9cee0b593e9a64375d51ab6814497f95ffb9dd504aa5c540b80300004040ee52094411f61f6227f02fc059c087f130a8222bf09a652a98a89aa0125da1ca6f1cc18c8f3342ed32588aaf43518092a092aa92989dd60dfd6216d2fc100c0a20264f62bca03f12698fc447355fc37df4f532685f42a6e4be68aa14a8c55cac62", - "interim_transcript_hash_before": "26ed2fb5d67de59807c35082889e9d26b2c1e4b3abd951fc4b4964bfe9e1bd8f", - "confirmed_transcript_hash_after": "7bab62556e3a300e252043674f581b0ec06945b046aa21ed9aba0b2acc2b108f", - "interim_transcript_hash_after": "9949d3467de68747a188222612fabaa3074cdf324e1cd667f7602b2dad1e1db5" - }, - { - "cipher_suite": 3, - "confirmation_key": "865b228a78afef8161a9b59bc64330730ca9fd9aaa2eb6f5069b673bb6be495c", - "authenticated_content": "000110ad53a5714f94f60e4db568be11a04ab6ad83e896fbc413b7010000000730361dd539d863e169b3d0d723861f9f4a6494ea646686d8d2d73e9a632d3a4d2528a3591afc5467974f7532eaf5c4cd7b0300004040a1b8bc11cede2d964892b28322ace415bd5ec43e9f1cc5014f8f481c1384302a5a7b17caff8b2ca0af1eb818e62b971a783e55b1bbf24840b521a2c062c3ce032055aa0db05ec61b4d0ed93ba5f5846ae64e2bd6242cd892dde3a3c0a65dadaf0a", - "interim_transcript_hash_before": "3118881639983df0802099615ac7014b02d9872cbc16cecb6423011366388f0f", - "confirmed_transcript_hash_after": "7ebdb98a05a1aef1110ca7397b90638678b9271e983f6e5de202b306759c7e33", - "interim_transcript_hash_after": "57f138019feebbc0d3c01c87b441d64fcaa936498b2536994f570c280261a7e2" - }, - { - "cipher_suite": 3, - "confirmation_key": "1c4b41978f0036e6a050c5540b2b99ad82b6dfba3ec193ff91031821cb34f741", - "authenticated_content": "000110638a18ec975d4e96257fcacd6ecaf547ab78f43c195f81d1010000000730a5a27c6d183b35b3ca1b0c0210d8e35b5034c38bb5701a317dcc452b6fcf81339aea9d4af520bbb19782ba0f7747cdf90300004040cafc343661db455e44054eec01ec0177a5f8c8404fa3f54da12168b22dfeb079e9435111d346df74b2cc7720f7c4f56eb720b915594d6f82affccbde4e344600202c9f7e75cbe9d610c3f5c95daae04685f87281d2947c4ed22d5acac7399237d2", - "interim_transcript_hash_before": "97e0c82fc9ebbac85dcb0da6bb92d6e5e5946e9acc62e8d0e49c8d61f7cd0b11", - "confirmed_transcript_hash_after": "2a232ecd7f4f498ce19ee29a9901883f2c2eeeb205580abe42a033610d1ccb63", - "interim_transcript_hash_after": "6d519cee43599ba5380c329435b1e8d2b2b0e9aa0664f5b90ac30dc9dcabb19d" - }, - { - "cipher_suite": 3, - "confirmation_key": "d0a52696031839666ea6c534369e817f2430c157a347f5d0052e7fe03c852378", - "authenticated_content": "00011007b6a79437f9b15b85a9d62296974c53234879b0907507e4010000000730b63d4dacbf4019b530b2f9b304fa5b5155d28a39b784fa3da2689fdd124f6004b865c3afdaa4d912ff1664bf4e1015c1030000404041fba1e5a15c561b522cd15c3954e4cb15f69af6a871784f210141339f50c39977180c492ffbdf3b8c997ef13e51ea090ea2cd1c77e37fa2c01d72f1636f7f04203448e5bfbd947897ea4772bd576be5e333d5446652aa05dfe1c306e12b8328d5", - "interim_transcript_hash_before": "7737521b01b1b2e60be45b9acfde362718027ac7332632e1ec1375331a4c6415", - "confirmed_transcript_hash_after": "3a76ac852fb092585bd32ffd459ffc2cfa53e454b49d39da17a6e031e4a4d5a0", - "interim_transcript_hash_after": "636222e27d3b370dcd87c6a779487d37ce68278081342d91c254cc068fca7725" - }, - { - "cipher_suite": 3, - "confirmation_key": "eefa7dc91f4c8f64f9ac984ee159b637b6bbcb9ee0cb3ee216d0f1fc4c35d9fc", - "authenticated_content": "000110045e974d8f6ea5bddcc1ad67c891be7ee72ab167dc859bbd010000000730ac072faefbd52038f94c73442580f42981f1965fd012ac8c00e0bb1992d0d1c0fb09aed71975be098f3450e085c54ef7030000404043a6f25cd8eff45a8078f3d1ba68c9eea5b25ed67078fd29dae046fc95c9d982d3bf87273c19b8585860823f3c7cce4fff06d60b4126b692370aa1de8dea11032011583877dc7f6f600204ec90ebd2733e319628f37a5befcfd7363a5348302135", - "interim_transcript_hash_before": "09554cf50d9d952f499c2156d23823f24aee09a8ca6f2a9c1f16ed3b3f886594", - "confirmed_transcript_hash_after": "56e028a496f9925c36476dad07070849f629f9ed65d23ddcd553feb7e10cfff6", - "interim_transcript_hash_after": "24bb2e2e3d167541589081eef1546d4b23bd17da4e643cdb361c584a705c59d1" - }, - { - "cipher_suite": 3, - "confirmation_key": "d27b12b3fbc7ecc0bde8b9619b305a858e815dc2e0e8be439e599eae336d707a", - "authenticated_content": "000110e6efbc2dc26981c4dd81e1c63fc2b5c65047cdcee867ac88010000000730a094870424f11ad6e76f035b55004ddebda28ab3c804c8ec44b6732bcc047811e4366c24edb8bcb5e2ed15f6305a6680030000404092700ac1ca3bd3fb56ee8177bd58b719b68db4691dc39d1e0b742554cfe7aa12cbeacd7919f7156f0b2b703145de3212ad5e05ff273fe426a264bda63b2abe092045d7d8b720c5f116fa4889f537ea1ae77e3436d7001dd6928a469da4712bd5bf", - "interim_transcript_hash_before": "9b8ba58afb357f0c2d9292859e5cd0f6e2dc892c25b981ae205cccf462ba1dc6", - "confirmed_transcript_hash_after": "a2d80ed01bc8cd920d65ef555f78c446d20dee825ef5b9c717275f9e9ad00a16", - "interim_transcript_hash_after": "59ed1fe32aea04e1f98bdbe78af09a6697bf3695be4b6abd7702b6eac69be1b1" - }, - { - "cipher_suite": 3, - "confirmation_key": "f7df9aa502fca1895c7ee4c45170231a155cb13790552c4ec4e513b7726bb430", - "authenticated_content": "000110d807d265c4bb63a7126ef767c2371cf1d1e198da97eadefe010000000730f7680d41874fb48a64e093eccf73b967b4808e4082a51023f8292744e3947afefbf035fe4ca9cdc41c56c9ede6babc5b03000040407840267d8a160e68c6003acba174cd88d488ccc8cda6692ff9059f53366be65e6843506bafd3b12545a0cf1a176e67a1df5bbf64e14dbbd6d99090083ed9030720f566ee2e6f02908101ae6a1d3a6ba6b799a3648c900c4406b0d58e46942ec65b", - "interim_transcript_hash_before": "e7c1234cb58a10f2da07475f5033411705713902dc86b3fa64c3b26f6f01dc4d", - "confirmed_transcript_hash_after": "ba647cb7c2a89e865e41ea6f2dc23ed3bb7d7e4f0cde1fcffd23032bd0009ccd", - "interim_transcript_hash_after": "a1df04b391d2bb601dab7061c7270eff92fd7e695d3fcf3c615d753390ae60bc" - }, - { - "cipher_suite": 3, - "confirmation_key": "01c33401ff860fad9e8b9550d4c094505a58dd70207d14d9c38321e087e7a94d", - "authenticated_content": "000110fe2e1778fa6ab3c7ce30c66edf827489802528d09301b1cd01000000073003827db3c6ffde0895648375343f4b8eb699b332264f55b14c161873e27472a4c36b941272f0ed9d4b6f468b64bdff990300004040118a96e64fe144fce7e9113103bc243258bd5111d2edfe022fbfce0a8a1c2cc448329cd251e353277303b8e8f1b2b37845693fc7c95b237e43ba2fb7d131690720f8ff65010617adbbbd4795db57482eadfbab0cac1f7a75cc71d97e92c0de5536", - "interim_transcript_hash_before": "c22e8af216a4ddd5a30017388fe344780f32c1b17d3bbd3aa6be59c297d44776", - "confirmed_transcript_hash_after": "cfd979c8888232f6b57a4ef0969d7e0513bf5262380da088e30708357a0a797a", - "interim_transcript_hash_after": "a5ac6bd7e51f340de48994da1e5c02480865a9db07a0a35ce04ef3c1414a13c0" - }, - { - "cipher_suite": 3, - "confirmation_key": "2c78fae66b9a6e9dbbef861033b0e450ca5061ae1a6fb328d9b75a7bed06313a", - "authenticated_content": "00011093cb79385380f08a04ed1eea4047ba9fc72d109e89fb32bf010000000730d7b617034ec295dd3635f6dfee841561562ae2e8f64e37520c1c51edb7a6cf348f77462b87d4b928e1f774af4e172b88030000404085cf2820cb49cc5f788ab02dbf1b508c70d338e682c26a0be28a63641cfb9b8c84bff0605403a0e373dfb06b11fd3d480279e3c312d85e8045d6370c048ef30e2026a843e21f89350d0c6b2b5be40f347883ab02d13e25d53db69ceb61f6b062b8", - "interim_transcript_hash_before": "ce29d84824d227e3134e157938844bed7210235ff2c4396cf6e23fbc12c0fa56", - "confirmed_transcript_hash_after": "b32ca3abc5fc9ca2a6c0776eebfc9ccc4a4bbc4998eab338fa1eebd7603fd761", - "interim_transcript_hash_after": "ae928a5fcfe3fe73f6bcb272ebc7a8340e6d11b0890b32ec024873033d57c954" - }, - { - "cipher_suite": 3, - "confirmation_key": "3b496399765b6fca0d7cf584eb6c90c1c56ec557791f1b55e7f18d35a9e03395", - "authenticated_content": "000110cf473d67ecc2bb627931233b4eeb08ad99bd87f1e7594755010000000730ea4a63bab41a3ecf30e33a69a8626183023694c4fbb2dc41e32357311acf89b54123194da7410af11f1325b3a3e97d15030000404036abc45e753e357b55534b13c17144f56d1483fc411a1273c8f676ed4b2378a32aa5ef4878c3f630558503b4eb312fd5cccdecad47e4b2acc8993cb6d7f3c50620b79676525b036415edfac181448a5cce5e9cd727bb877e2a1c48e669b3d09b16", - "interim_transcript_hash_before": "8ea47042239c8dd017b69339e1732e72e94d3a34e298d5e25f09be121ec7b9d1", - "confirmed_transcript_hash_after": "b433c3f1a50eb9993d1e5b95f305cabbe9e31e9db2beec2961f5c8f2f9be8188", - "interim_transcript_hash_after": "1ccf265f82660884d956fe26c5a7f19d187207258459347c83496a5e3ded3560" - }, - { - "cipher_suite": 3, - "confirmation_key": "d16b90bbf751eeb88cdf782b05ad05a094a1a96bdcf566ab08939375cf2837e6", - "authenticated_content": "000110f1054a4c083a3736fe96b604d56204955c254c1dfc99bfb9010000000730778d7a14c2c77ae1c0644ef8200790f61bd312caa67c8bca373920e1e0985f0009fae59de917b7b32c86da06d0d2eb200300004040a56710d6f6ce657467d043e3e0a3b08bfa0e0437fcefbcec7505501d38efa27c88248ec381c71ebcf265745b0eb54f6460ed1f185eed148e9484ff046355ba0420756f4028dcba377f65f0878966d0b15a1ff7e342e036a0b99f21cf8dea15d6fb", - "interim_transcript_hash_before": "e2b3d61c83bd0151471bbe72ebe4b3241b30e753445cdbe678db4f1a36a339fc", - "confirmed_transcript_hash_after": "64d4c6b4d874cfe9a159b59be3bd314027e02e56b6ca151a935dbd0f9fd8f5bd", - "interim_transcript_hash_after": "9d637bdfead3dfc8095801ccaf4381b3bb5da95ad98db8f9583fb61cf2a68e6d" - }, - { - "cipher_suite": 3, - "confirmation_key": "5a4b94fa51bbca39edd679c79196984b05fb878635b4c83fb3a65a73188bcb8b", - "authenticated_content": "0001104a99f3d23660af17041fb26c6ddcbcb8fcd980bc527d57d5010000000730854f274f19d2cab8b2934edeea417098468f8d7e5cc80cf697f582d24a842c2d35650e7cbbb72a6f5d001b579d621552030000404047dd379634870ca612061aa1783e03e5df7b9b28d281dcb90e5d7be930d8aef658524d900499288d6b5a84da8fcfef9efbb13aab4e66ca5038242d2d9103e70b20bcc6eefcd575f9569888b9ae7ff0c9df54d74ab474a8a196dc8b043cc8a05ab7", - "interim_transcript_hash_before": "3fd2b9a88f8408628504405bc213d1400cf85d37253e9cda01721b214706bb63", - "confirmed_transcript_hash_after": "38b5cc041dba9a0406b83ad79b441d956fb0f76342675b7dae12a838cb53510b", - "interim_transcript_hash_after": "834b5fa66a7c2777d5d708ae97069b202d000d2e2cbd2811cbecd654680fd9c0" - }, - { - "cipher_suite": 3, - "confirmation_key": "40fac7786ebfd40c5ee7c29bd3aae407888d10c2036b9f88fd6eeaa358b7f8c8", - "authenticated_content": "00011082fd35f45609bcf9b90e91aae87420a15a2c32cb0a8c3077010000000730f08f95d9e0b7220623aa479c51bde720b197931e6ec67325f78cc51d60f829180678eb3406632bdeda7c41db18dc823e0300004040bc28350489834fbdc30285aefd5407a50f5716004fd4e2641e9bb86eafe34a5b46ce3755864909c4e30f50a12b912ce1ab8b31e2a02cac12aff5cdd080f926042066048fe4d17f5c6649cb1c676fa4f51925ed9812a935fab5f430473a1197bfd0", - "interim_transcript_hash_before": "398ea3ccfc675a1af05393533e643478c58ea695da504223044b450ba6d5aa37", - "confirmed_transcript_hash_after": "a605520126a43605e5555dc7a52bc784a1234c542dbd5675314aa4ddc2ac932e", - "interim_transcript_hash_after": "bec6989acdf93f51ae3b835c02b92b4158a12ff7b1a28d474af9ddbf59efd54d" - }, - { - "cipher_suite": 2, - "confirmation_key": "0df33596df23ff3685f0352ba2a73d86c3b5775f9cf12b86f48fa30966ffdd41", - "authenticated_content": "00011027f144ea6fd71caa69f83c1306e4ac076ed094973e080eaa0100000007302a3d6e2c0a808eaf26ba99b2e986e39c4a63699057e79d68f50a724496d674cb7e61223f6cd9aab2c1e78958ceac23ff0300004047304502205c09542c5a194c1d2582fda32c685374dc978541ee5e813a62880c9ec56081e1022100fa21d21b347a8491ccbb9b6263198ff867baaec2cef0bc021cf573849d41a76820661a7c5c08172862f0921cef948835150d2ad4a2bc65195bffa0c285efb6e46b", - "interim_transcript_hash_before": "05313e0ff03435ffd55137cd37468198a9b58bd74fc07ca6d58c99a1826e86fb", - "confirmed_transcript_hash_after": "faaf670a1fdf295e0e00919cba9d1e10d4cecd7a043ece3a7c45d82480e56ecd", - "interim_transcript_hash_after": "3524e9d0e12ca273f3fd472fbd1996c4f3367671ea1fb2aad0d4cf9390f76307" - }, - { - "cipher_suite": 2, - "confirmation_key": "3a6d52ac999095dbc9ee1554c2c95d54c8ebf08b285412b74a83a2b1030b406c", - "authenticated_content": "0001105d59bbd30c40e855404efb96eaac374f4b4f6c58754ee28f010000000730eaffaa2e4276c357075b0ed941533cdd81b9316be5e1028ed6ddf2671e71d4485a5c9b922619b33d7a9b506c32a9508c0300004046304402201b8a57b824d84749c99c94de2dbc897d642e3aa7bedd962ef9dd59c8fdc36d5e022035f54a736df096cd422926ec3dcf2cf0d2bafa53e3390bfac5b93be856ec65c0201d235e5dbc938b3907085e5c12676fed98e8724f6afbdf9244ada3ae0329170b", - "interim_transcript_hash_before": "70eeee14af6eb172b149bda17a950859d3073d3d4b3372145acf03e4d255f50c", - "confirmed_transcript_hash_after": "9f916a16dea6bcedb0ae4cd7aa9fc1ef011a2864bcc9d29c38e8aaf1aa28932e", - "interim_transcript_hash_after": "d62169c3ffef768dedb1ea5f7d668ebd7643a41353fb9052e43a7dbd4596905a" - }, - { - "cipher_suite": 2, - "confirmation_key": "3fc291d4026b034cdf6c7fa535e265f056585c669e0a01fe35b33a02283b680d", - "authenticated_content": "0001102bba1de3b2c8018f5d29a18e619e384bf433871430ee716d01000000073006869d7cb64bd447c66530560ca9b0af88a7b3b722903f7915574dd400189653adcf19df1f8de535d85ecad4180381c70300004048304602210097f47ec8d401093dd0f36d2a10efda6ac7f91ca155ad995c67f85534e51a6349022100e76ad21661aa3e217856f166ed00db3d6ac790fdfc4c4a4424875287caec41e920442d51984fcc803b6f9ed73ca16aadfa329d22ee1908aa91bc6cc49f255fee7d", - "interim_transcript_hash_before": "6d2c3b438df947ade12406507e1519a95395335a869eab5d52358d2af5c42c18", - "confirmed_transcript_hash_after": "5e9a5e9bf50d0541d4e61ad743b102ace191997deba2da1f4c1ad09735db4289", - "interim_transcript_hash_after": "7fa8937f2bac544f2745bc9874879038a414cd4123d47e5cf46c3f10881565ca" - }, - { - "cipher_suite": 2, - "confirmation_key": "a1b78d8c90aad7efd6bf2f11ad0bab7d8efc5a6cc1b82cebf548205abf282fe2", - "authenticated_content": "000110f853df8278297657aae7c2b8ea9533ac733dcb7cbb019e7c01000000073098a29db69dcd363d93e57083f94ad0ac26e1e753956192bb9c95102b9033e205b322bec6e255506a7bce1faec302a66f03000040483046022100af54f9c7ee17deff13bbdb13ce7e11fbda283050d4e10e7df589962e07ebea43022100e5bc0fd75a33b10f096ace7e2bf398c0d0375300eaa4c110e427fe675718f3cd2064efee564375c3e2e9d6a18c4eaf38d50d3c2907fe569f54eef2b569379d2e4a", - "interim_transcript_hash_before": "0bab8f2f7bfd8c4bbcb758689d5d65337e9fbb0011ce23f4b384c01768c73e8f", - "confirmed_transcript_hash_after": "53916bfca5f4d04a9c3e7b4907ded4301f0b42eb255ca4df764244f665dac7f5", - "interim_transcript_hash_after": "c87458d3570e27416ec7175f269f14815f1e081b07300fc57d899d71da4b28fa" - }, - { - "cipher_suite": 2, - "confirmation_key": "6037781d9aefe6b8115227af5a3025013bfc2947a8420af2d632839c0b95ff4b", - "authenticated_content": "0001105423e8fa3d59c4228b7b36f082b9fa993740a54eca54b5400100000007309002a8e13b4d3281031a58f2f991e54d608783e2ba630245a5d576c8d60ac03e85ebffd42d76ab835094b05a8ecdfdfa03000040483046022100ba8312b58cbfe2f89df93a9475f4c08456130c6e31098c0aa0990e09ad7e2451022100ed9a8a14aa226df8bd7f55520dea8614e8a92d377520b5fc0e5ff4b5639bb87f200787f753591a1ded1ebbec282cbe306e3ea7ab64f2eb287918e85928c2a530ca", - "interim_transcript_hash_before": "5c38d34a8bfe03688382354804607da5eea2b7e1f84408ca29eca0214ba8018f", - "confirmed_transcript_hash_after": "486a3ae647a442d271be5978855124c076f5cdf9bb03c362a1b183b440e2b030", - "interim_transcript_hash_after": "8916831af6ddaf8073f2c7f25ae907f87831bd420a12b060187cc8572588b8f8" - }, - { - "cipher_suite": 2, - "confirmation_key": "470bce770fd89752df64f5e45b089f062ac0d1e2318b773c6f32f7503e124943", - "authenticated_content": "000110bd4d7fa9e96d6e39a8d872c5896afd002e538304afcb3233010000000730d185a1c5746b50ff14ee2ab1572307c26e90896f1736aa5213bf3f813997453d5b6b16d9c98170a5de6acd966b56e3490300004047304502202f9e4363a2e6f8060ce29c29c0c12157b6b0ed72cee86ca79c51ae321c8f2ab9022100ad72b5276022902a4d8e0076995a04130055986ee383ec2405d18f132d4ab14e2076a17d9089befdcc69546e680cda721891fa8ed51d204abe8ac2853691b27bef", - "interim_transcript_hash_before": "caa7e409cee914c5db5dc3a684fddba196c4d060773475b68a7d77bcbd8f54e1", - "confirmed_transcript_hash_after": "798e3efa60a879fd526b17ee4ff59921e76d4f7c6971808282905b229e4c20f0", - "interim_transcript_hash_after": "42755b6c04ae85f629698f99eddbf83ad5c33af3a980c5f3a45d8e4ad42df67f" - }, - { - "cipher_suite": 2, - "confirmation_key": "8803c157c74b7a4409fb34e32ad4eec1d5107d4417dff757cbdc48cd8376564a", - "authenticated_content": "000110977ef8fce0c65e4efde1a12957df5c6643812cdec09a9ca901000000073025f8e954fa707fbba8054706fb01b9fde1a977bf1771d2f08d2b4c138d821df0482d41adb4ff9800b201bfbd2f790097030000404630440220171b4eef7eb3b5624e04ec3da4eab52ef8c8f89272a85f15d69f28817c3902c502200530e7700b3c4d771477e71a4214768e9797386b5e43143c9d80c18ae25d4aa620d943b9c6d27f95b9dc8a9b3e8411ae9348f5e035db8601a61136354759c753b8", - "interim_transcript_hash_before": "23938c1c76fa5431027aa8cd1ec5b11f0c448f7bb039a1a57099be61407d8d92", - "confirmed_transcript_hash_after": "811776c5c0eb4c044248d16eb8693cb014aae961798d1ce93256a972138dbce0", - "interim_transcript_hash_after": "adf248e2b19186d0f1eda9f5582a0cd43fe23ec9d7f1db996e2c4d7de1cb62ac" - }, - { - "cipher_suite": 2, - "confirmation_key": "7362d83aed3e517287bf3553d72c38ff5c3bfbf999386bec8879e954d1679441", - "authenticated_content": "000110ae7436a96c9cc734734ac2397a9734cba9f3eb94646d2004010000000730db78dcf10587e44a02a9bdeaf94a56624a62459b0ebda17919ed54c4062137a329e365544aa6a04441fc2a65765615b90300004047304502204c05ecb8125d796a5f9b2010ae3deeb5a24dc5cb252eabbcc6202ec5a9b4b14602210092b80407333002f34ae455c0df74947d04ec55195958afff3e15c0ff6e6941c3208e1375ed4cea2bedec60b9b08e913e30790abb6a663d0105cce8088353403deb", - "interim_transcript_hash_before": "7381b5f1a304990445b5608d54fe8d4ea82fc6fdd360ce39218afb1a8c685f2c", - "confirmed_transcript_hash_after": "94b6c4b72db415fc87d110cad3da6bc6e02a5f241732f07a828aaffaf0a6cf24", - "interim_transcript_hash_after": "ebcc7e92eb58a55c61424261e67cf821e09c4147f318740beb430ebccaebdf9e" - }, - { - "cipher_suite": 2, - "confirmation_key": "d69143c6af75758495316d258b89445cde8c461a1768ded7ed5c8ce80c6d7610", - "authenticated_content": "000110dfbf37bc9e50a131ea1099ffca8bf2834bc9d309cf0ed0ba01000000073017d72f8a659d31c9d1f36db1f688b2ed4a609ea74da705e882228394539fc424425850d4434c4ceac08523b07ccb840c03000040483046022100821d5936e9f0706e8616799c845a5feae21f2de1f15aecfdcd46c09500630280022100c8a18f24ebd550bea920d0157296aec7ed524327765711964f5a8e9ad62c89a72075da1d0adaaccf5815fe4a44aa28e5a39f7f0104682f492847f79dbf9a2ee9dc", - "interim_transcript_hash_before": "dfe07fe4d0403c706db59e66858f0583ff7e2b450abf6e3b088b163a78b39294", - "confirmed_transcript_hash_after": "8c25f71fc4bf33e13e402f97415a946639249ba1da6956ad71183a527e7a65e8", - "interim_transcript_hash_after": "ca9e37f91a2ad906fefd9e133d16b454c4d3e633a09ebc992c824a1ee6283f24" - }, - { - "cipher_suite": 2, - "confirmation_key": "89f33e61408e54f841e52f375dd63e5519f64cab72037d4a48f89e8484b3de2a", - "authenticated_content": "0001101ad86cb8bff0f102fa2cbf8cc5ec49efbfd0b2058e73e8f201000000073082f5df6690118d46ba6b051aca71da5c2862e812ab7a01b264ae2ad5c1a7a1c3f815f482eb9a8b0544ab6e01fc9597460300004047304502203358804faebd0fc5bcbbfe2139f300e20e9c585fdbb8898fe0b0c9189b75aba6022100d396d25c61b70178c8c992002539adc1c02210c295b9e6f4cba97fd15b45c23b20d992dccc83587755b0174bc78abeec1cbb2e6d19271b6cad34b5286f369d3c64", - "interim_transcript_hash_before": "2357b959e2b26fd983327dda51da3930c106a5fa4db7b01b14bf9ec525fa547c", - "confirmed_transcript_hash_after": "85e89a9834e24d77ffcc9cabca2791271090fd5d07625f8c8ea3055b5f3fc4b5", - "interim_transcript_hash_after": "6bbae27ddc26831532a694c35f3b212ca4e17e6615075edd47c9aa58e7b99336" - }, - { - "cipher_suite": 2, - "confirmation_key": "e4f958845257828296fe283d11b108388f3bc5fe7857679526903633fa618bed", - "authenticated_content": "000110bb693ae4ad4b7d9b9a0f5095b32f49ee750b85f57173186f0100000007305e10ea122d1b71169b0b5930edcd48d8a05754514de841961e6628a71fe77fe90246d1f0e3079d90bdb74a64ab5a497d03000040483046022100ba13afae45a0e81c1877d4ef93a09e306718ff85305f906c31b3426092fa8345022100f190260bcd6f2cf08614e935f9d9375df75831307455ce0c2772a814069ea8ea20b6bad50f6b891352009d5a4cc6d7e140c4c247ce1c97b10d5e0cbff44e75bf50", - "interim_transcript_hash_before": "b14d086e81d3e44e33015d9136180bbbe93f01b6fcdf64af6af35f7e0d7bca9e", - "confirmed_transcript_hash_after": "321cd7c33930520df18f8e013717242fa059fda7076cd8294e546a8ba52b871c", - "interim_transcript_hash_after": "5095bc3c748bbf663bbce2ae2094c84e5137d18f2565760bac9827381d396222" - }, - { - "cipher_suite": 2, - "confirmation_key": "a69bed8aaa29586be0c641f961eafdd6826e11066c65fabe60a4de8556a8a6fb", - "authenticated_content": "0001102a26938930f59c334154ed124f467f54cc833012022accf9010000000730046148ed15cd01fbc1bde92bddd64454fb8a02600846ab6e422e1ed645c82a7e5a1ced784aeec38ac67644d9b356da9503000040473045022100d30f28910952ade1ea7ee26f91547a176b1606b29ff267cf0fa24b7c2b873bbf02204dc3712c99e12f4f85a4a511d1f72b547ce6898465c602263053a5ab370eb77b205eda1b239321120a6d4e066ba0d1d3d7413f1b9bd3250f86d00fbb8704342cdb", - "interim_transcript_hash_before": "93689ea13ebb3ed18f778f5a4eb4643919f21ad1a6464209858e1820f2652ca1", - "confirmed_transcript_hash_after": "be7d0a76b2ba48ef5835d97613f68276bac398d5c25fdec96cdc319235fea109", - "interim_transcript_hash_after": "9ca80f25aff056d371da1465d0ccfd30815d1817556d1f6dd0e857676221efe1" - }, - { - "cipher_suite": 2, - "confirmation_key": "4a187b4d4b74f396752e7449d162a1b81b32d2a627f30a8a504fa293ec612579", - "authenticated_content": "0001106a8a643e22f462170834206606b94a5687303d7a44f31ea601000000073063b6256dd8ebf9d61bcdef30afb322fd8873ec4f43c2af88d6f657eb9f3a64f7641c6d1ca4da76adc3af09fa926476ca03000040483046022100cbddf942974d7382bafcb2bdab05556c4eb9f19070966592bbf61e1cd8b9cea60221009d6e62803a7b823bf795bd81884a66a95a0e603a50a215f04c6c6b441c46d316208ed8032c21dbda1941c13931404df5d2011397627f055acefe6a354d80562118", - "interim_transcript_hash_before": "2fd71f912fbdbb2ef5c6949f5399fa77d4234c1d301fe79ec4cbdc32f1d0330e", - "confirmed_transcript_hash_after": "0d44db8f1ccc5cbe200271fa95b281d16f54cc7dd80181c02f190b99c5245b6a", - "interim_transcript_hash_after": "6a92e76a838a7fb3f9d60d7fc039879df96259513afbf43db1d92250b3c0df89" - }, - { - "cipher_suite": 2, - "confirmation_key": "f24096ebf4b27c1c424114f69ea8a76553c620cc15b7e88d06bed035a19e02e1", - "authenticated_content": "000110290ccda9a429c4a60e4b916ebbfe5e07b69fc1252b1400b201000000073085f8fb9fb6b82e7cf7be01f75284e862f209db39d7289697c456136c31c563947c7098081a9ebc7b7c4b6e7d4c71507803000040473045022015e9e76980957533b7ad5e7dbd71315541236bbfa402fc5ee2bdfec32db3937f022100fffa58d51d99c66184e75d099fd028f87ac1b94f3aca08396808f2be63f5566720e587f2a266f36d98b8d12ffd6f87b09683fd42f417ba9cc21ceb7d82c5882f60", - "interim_transcript_hash_before": "1c8c94205a09bc509be91496dc4b475d4cca3e5aca4abe575d77ce0978849847", - "confirmed_transcript_hash_after": "bb8ea05f76b935ebdaaed06ad8f25d55cbf689cffbdf36b1e936e4aac0f2a6c9", - "interim_transcript_hash_after": "fa6de202c78d89093c06651f0f9a089c44c924239ee359f213dfd2855e61ec70" - }, - { - "cipher_suite": 2, - "confirmation_key": "b88d551c04ababf0cfc7012011710ad8206c00bccf8c18bebfea9926a9402005", - "authenticated_content": "00011027573232faaa934b3eecd8cc0926efb22aeef479926fde5e010000000730a33cbf0dc316d348e32cb6eb754a4967d6ec4754fa23a830fb1cc1003f56931b485adcd18de78bcbedc05a50cff3f1b6030000404630440220030340176ee0544802695eb9434cb40bbb73380a10037ce9f4d1d73ce3c9e3a1022050e55713caee3aca8adfcd3c4beb8f22fa1aac9b9f1a8eee48e688677252347320dfa357d0cb3e81b7b049edf4daa342ee88a34d9199c99cd1a687cc45c4a352f1", - "interim_transcript_hash_before": "85084550da8b8f7593f1549942504d0766b9526eccde17e93bbdf4a61ab2ac4c", - "confirmed_transcript_hash_after": "417ceaa449102e699156f0bc9581de30687afe575b4f2a67c3dbc0dc360a1bd8", - "interim_transcript_hash_after": "499759508b600f2f55e758097d1c4b417703627e45ac38b889aaba43be90070b" - }, - { - "cipher_suite": 2, - "confirmation_key": "050a6c3a0282d4b6d40b4c1f978286dd99658e49cdce69e213e412e4a45f129c", - "authenticated_content": "0001104392e772a52bf153b850e949899dc1b942e1504c600c2b7e0100000007309aab64f882622ab9d47700fdc73e0c9a648d9ed079c466eed4dc54a5e81578a566b86b201c25bb7e13568a7bd9549b7303000040483046022100fc208602297832b6dcd893703725f3669257cabe1a86128b0fc0d2bb3d902438022100f7e3db149f1cf10a2cb093a51db5176ed45364940b9f32d67f19ac2ae38b375c20dc38126d94dbbd54fb585da4897b5f7fe23e910321ce83c9e619521bf02c86ac", - "interim_transcript_hash_before": "a19629af5b7fa0daad1625ad8a6797712a14037c12a565bfd5ac64c36cf92945", - "confirmed_transcript_hash_after": "d073e5c678d0f933b77bf5c5a119518dfb7bf283e0e1bae7a1ba6c462c48cd57", - "interim_transcript_hash_after": "520bffa8b5d2092d8e93a51d6066ab97cc07f8c6824b25e6388f9f311a2c71e8" - }, - { - "cipher_suite": 2, - "confirmation_key": "9eddc22621e59e025cf9f75c46fb2eb3f374bce160c44de19998c42d594b5a11", - "authenticated_content": "000110966627e9199c93761dd1e93e65f47674f3824e57f02f0e52010000000730a81fe8db7de2060387c224f4d758b6ae50d836eeb3ca1fad355391009a11cc03010e552d99e9f6bc29b71ae1923ecb3b03000040473045022023b62c1c5a21ef92e07cd8af9412341a62c970d1b9f594261c698415332fe400022100ae4df3a43b2a3b724faf4372689c01326f09b81431184df349d60fa8824dbffa20824e71ee0f513525d83da184b3434ab3c4692766e7f0fe5011afd5604895b26c", - "interim_transcript_hash_before": "2c1e890cc1d410f1c18c56b6c93f37aece28b180f63afb2c3432a4e85c12258c", - "confirmed_transcript_hash_after": "ffb4bd56f467aaba9f35890056aa53e945b8dc14cd2ed4a85f5779629115d8c8", - "interim_transcript_hash_after": "2e85846606925d00689cd17e4e82aa02938c95aadca410af150284b53c1ef2f9" - }, - { - "cipher_suite": 2, - "confirmation_key": "630cf0f8feaaecd63cf7f465bcc5089ae7bfd147061da133a0adbf35a0d0d755", - "authenticated_content": "0001107b348f2714744c995423138d8978366a498e6a35a127cb25010000000730c3a3a3cfe52a4a4f3b6d5c68d92a525a601e5feb59098ff9d240436bcde8459b0ad7ed1f1b41ae535019c159ed8ef1e00300004047304502202e78767e3507317cb1c930490281e4408d07db5b44a23f55f668312b24f46407022100b5eef377cc075d2e72ba680017fa30c7a096043ca76273bcfaa865c0fb71dfac20e5f0fa055a5a0979617a8516ca8afe8c460e4acbdace1a257185f370bae96984", - "interim_transcript_hash_before": "f20c8b901bb78480563594f0f83a446c86e79e535b722e46e09363930c8026ba", - "confirmed_transcript_hash_after": "6909ccc47677e8b6978bf0748197516e29272e4e1677a6a82adad324d58819a5", - "interim_transcript_hash_after": "2b3fae9afc108fdfaee3d7d3fa8c04b4b6e58a1e35c47540df01fb55591c66af" - }, - { - "cipher_suite": 2, - "confirmation_key": "6a1bbbe83c2f7815f1d8eee19a6714fa3293a66e1df5ce5fe3d7d39d7a8907d7", - "authenticated_content": "000110825a036104e8dbd1f5da22dc5ea26e19bf53202a39186281010000000730c06b7c839fc47d03470fd4c828040846e0d6780bc8fcbe13b37fe3fa9a4496c832bdfa9c32473df4d8d2fe4214892aa6030000404630440220462c5e860832f271fbe4689572ab7bf452b155725f7b3cfe7602627a3420f59d022061bac4b82badb3f3ffef32ab5fd677acb774c94d59dbc1f20b85f32df27a8ec820b356f21800c4f372b85f24c515cb58a7f2a63546ae3316bb4435ea49aeb7a21d", - "interim_transcript_hash_before": "a26dad3abb0e4f61b04658da01e9d490475188f1271468033a6d0a81a69a6ac7", - "confirmed_transcript_hash_after": "42ba45a81d92c29b2298b05dd811e9b6910fa423fce285e67563dc6bc4cfd7b4", - "interim_transcript_hash_after": "405f2a4588fdfac6343a1ad943436abd87a3afdc83501d47588ef876ad38d7fd" - }, - { - "cipher_suite": 2, - "confirmation_key": "1fe763846d6a49e3ef15e095671978de75f369fdb58b203cf3b8fad5ab0e46ee", - "authenticated_content": "00011031588dcc3a68d39498ef4d7734c7149352ab46fb33aec3ed0100000007302bb9063073e92c3639b1f4bbba4054ff0e93cd50c268fb2ac6cea77d4a3d2604fd79d581c10eea9fba08b437dd850d9c0300004047304502203d0d671a247eb9f7865087b261f9b453cb5735eb977e2e2f73e72ac981c3a639022100cb425e4241eafa4220f1f64f49fa911f94d22ef2c5799c893b70afc4f7088d8b203f60573354f50d0e6f2199d3584f5fb444e339c17d5515499c39103be4a6cf6b", - "interim_transcript_hash_before": "d914dd3579ceb8998c1045cf142635b793d206fdd96b3be37949f3b56a1c81f6", - "confirmed_transcript_hash_after": "f6d5f880b9cd0501ead5f92edac7aacf8e7a6bb89619588a51a805b926b9cf75", - "interim_transcript_hash_after": "33c70030389c80aeee9505446facb2b3eee16c894ad3632cd4a4405909ccabaa" - }, - { - "cipher_suite": 2, - "confirmation_key": "c42caf81b34172e8221df19375c70868bb952b1e87ef89fdeb99fdf791e85bed", - "authenticated_content": "0001103f95bbc26b7295d79f8531fa49035d5e9b3efd4310b8cc170100000007304a1c805eaa1d8a1e737aa409c706f368773694fd9b61cee2d2c902397088c1f08eec7095b6f1bad5b2e82bf1d8b33f3b0300004047304502210096a9d6d6163b609656a30b688423532ebd1bbbbe818960fdc5454b569346357a022050286429aa0f7c1621cf68204c80904173781ab9a5b812445e61bb1d3e4d6a67207d498252eebdbccb748ef39ae8844599868effd3825603da649c00618c2f7862", - "interim_transcript_hash_before": "4c461de322f0c56ee9e011ed087b1993765e11c254632d834de634f0956c3bb8", - "confirmed_transcript_hash_after": "73d8f96a91e2e9446ed703fdfe862b8b9d56699a9a04bdcfd946a8198688f397", - "interim_transcript_hash_after": "f4b907ab5fc6de8db0b750088c98380862e64836c0268bf14596b3590d66d713" - }, - { - "cipher_suite": 2, - "confirmation_key": "324ec1e7db3427ef571f68e039fa8db9c26019de92ad6b34bf1c3231ee889cf9", - "authenticated_content": "000110e31506063c88b746e20ead2d750be1c00b9f130ce33a628b01000000073084c45d09d7fdd1f5a4a30eb29de84a9e9763338a048b6eaba378da50f03e434f5f2e76d62d3481e68f51e8e69a07ee190300004047304502205739bdba12ed7a48511efcd5f82a7c5c23130fbdae58efa8000ef5fa5f268b0702210094b60ef6773b130de06009c1fa86260626fb9e5be30bb47a6e5a42a97434896620061be3c222eb9cac79e14194042651f72731aa1ad2c2c3aee877c1ec367f5dcb", - "interim_transcript_hash_before": "ebf4adc0b46cb28a9763773b981181b0f0bfc3f4bc0e70ca7f0c8e08d078ba63", - "confirmed_transcript_hash_after": "e78a361392435269b4123a609b9a7a61645d926c9d6f054f6fc859481b9276c1", - "interim_transcript_hash_after": "c2a3b3b1aebfb978b66e25959f464de771a4ff39f1377f7e9cb406421b4e2410" - }, - { - "cipher_suite": 2, - "confirmation_key": "e2f99012775ac1b360225833ce0ab2aee91c54dcd6ec665cd7a3fcf4a82fe4b0", - "authenticated_content": "000110aa8776b27e7cc4a272cc96df5cdf4eae21757ca714d707e2010000000730631193d4559649b7bd3721f773a8e703a60a6e6c54ec357836782c1ff5fa847d7f1afee33f3abfb311ed0b2e46eed0980300004046304402202620cc825b1f9ce4c681ba101019d30416aa9ff1c86107624a5c29830654f17302201f37ae6cdd4c79a190bad0a43d85356f5a9940988baec8bbf5bdb8862881339d20ae7c0c1807a8eb691db20afa4d85456f8e1abd61c3f74a48ccc4f076f6302055", - "interim_transcript_hash_before": "5d1d6c3e055392bfe6c07abfda310e098426594efbfb79a81c21733f711e3bc3", - "confirmed_transcript_hash_after": "f436f28671a0e8ef25dc7f62c9059728fd8b751bda50a58978be911595ead1b2", - "interim_transcript_hash_after": "a4a7d9ff73b1691362c336245f55d1bf7fbd4ff5eb71e1a8b8ca8557edea28db" - }, - { - "cipher_suite": 2, - "confirmation_key": "e542d1b6db77ab18b2685c576b659b274a4d9aea5e00bc70f6f8fc5d8b462106", - "authenticated_content": "00011070f052a76016b5cf6a430232a883a9ab8f288dac349928e30100000007307a135604b71967fee9f176d8eb79545a89b665dd80f998d7f8a65ed080e438d20544b8e1b837164faf78116e249f49fa0300004047304502207007f57209f3bd07f5c27a9e0875ca9ff9fd2853a83768571c4ff87d4bdd793e022100f26e1caa9e68592dae3a9742f6f60395f8f533f12be37cdb38dc8a1fd64f4fec20b4e395fc3d02dcd2bb6783e86df40ce0ae11db8c44b6b172b0bc54d8b89b246d", - "interim_transcript_hash_before": "38b1c8ee2add355174c56deee379df92cd96ebe9686943986ac8b6e2d6874d53", - "confirmed_transcript_hash_after": "a3a44f18164abb046aef3187cb20b853d1e74dac9b9d564d877fbf8a3c9c41ea", - "interim_transcript_hash_after": "7f0e6baf14cd49d768e075b47216452998804f2b49e904fc1b2fd648b26343cd" - }, - { - "cipher_suite": 2, - "confirmation_key": "cc02d8ecd71368b7d943286a50afb1e0a7eab487bedc1160fb2b4ac5b58471e2", - "authenticated_content": "0001104430b27998f0648e9fceb371e31802200b614d681a8d24a101000000073070bc237ecf893c78079bac43506a1fb6d364d1c9fa4578bb1ffb453f4c668851d0c79fd8ec343227c4650b88f0cf4d3f0300004047304502210086327d769758d2ebe2fbd41fe86e55e3ebbe73750412dd26eff7312c2b8772d602201a0c168005e30356a0f0d5175c4ac8a0daa2a0a0ab56fa7e1a9e3d231f407a0720a69e0a34c8cf487a612c363d6357ad30d4b4febcf86e4c75032103906381c6f6", - "interim_transcript_hash_before": "12ad91719a3ae98068b24288bd724f0c7be881ec020798b1a71daab7a070fb67", - "confirmed_transcript_hash_after": "228ce963e85ed3d31122b9f1b71382dbc11b21ebc910b0785fd41c688bad4c98", - "interim_transcript_hash_after": "1e7c3429a8e0937e0261f035e4f2ead19b72b39530351a496767932b088bf59c" - }, - { - "cipher_suite": 2, - "confirmation_key": "464cfcd62c03de0c27d353084b68fa43d18a562339dd07d439a5e83c9548c97d", - "authenticated_content": "000110ff1b9f2e124d104ad2e7277f86347eff94501e84e0dce79e01000000073085086dd02af67f63a0be8258223a7b984df713a2837b3e6499bdef7221ae6bae6a416aa297dee7f7063954345b2ab9f403000040463044022014302a5f99c9872ecb1f52e9279447275785d568b8d551a6dd068b2bc598151202201688a6d18abac106691b01a50a485cdb173bc2f0ffb14ac5fd8d8b0dbe261c6e20c6b28fe201220f9ba02176c0d40ddefa3c8b201576aec1db80298cedfdd1de59", - "interim_transcript_hash_before": "1707818f8ca02caf07134b4a05056ce991fea8e6216ec5b8298f0c454563d475", - "confirmed_transcript_hash_after": "fb7822025b2407c37c14f328132839298ee1c0142c9c2388053763522b446252", - "interim_transcript_hash_after": "5d6464dab15ccd7f653b85e73c59dcd55b2b01ea215006af82b7dd3cdc37ad51" - }, - { - "cipher_suite": 2, - "confirmation_key": "6817ca4c8c3479bd3a229f0ae72840ee65e39d0ef300ff82b6033d2952814657", - "authenticated_content": "000110aaaaf87b2e203311dd7efd3566d7c901408f3c5492a861b4010000000730319bff4399d50e656362119ade870e1c885095f9d243a2e6c02449add5dab9456351168dcf525dc6132d96cf012c57bf03000040483046022100970ed36aad8a33912205215b9211caa2e6fd2df08f2730655ab8fa6ee5ac6fae022100a9d5b9bc916cb71aacedfe5f0c3d807bbca4c9e791f3f829229bbdbdaf9fbe49207badc6550db6d71aff379922cc7ddd90fbaa31a0d2e5c6370ccf0f18567d093a", - "interim_transcript_hash_before": "c55832f433b7d32c19c41407fa0671b34400188d5d20488ce3e7e9a111cc2ac2", - "confirmed_transcript_hash_after": "b6234a4934b88f8a47b99f8bcc2b6509ddf026ce928c6ee9957581a2ae596816", - "interim_transcript_hash_after": "5cdfd87d0b74d47849978288216934b32fea65bb84b1891e702797676be3ae8f" - }, - { - "cipher_suite": 2, - "confirmation_key": "1ff2f55e08d4498b599c5114e45dc35e3ebb627ee122194e80502f0ac1fddc33", - "authenticated_content": "000110880089a3a65c3eaefc1295dee1d5a184d00b3f5ec99fa6f3010000000730a980ea70fc5e9b4f4103ee176f64ff22650d0c6b13daeb049297e0a52ffa86a8f1ecb4163da7e22b3f75592ab5821db20300004047304502205c16030522e4e1fd3794dae2ed9f6ac6079944dfc2fefc856f1f2188235e6e3202210082e720401396ec864900abf37212e47828c422dc44d69331a70e0e1b6324093a20bb230f7f1473b8b9633c75948ffe2216ff390d6b1b67fe8e63a7fa014b2d41a3", - "interim_transcript_hash_before": "d18298a4dc399231849283b032e4d92f3b6b3f1ee9fa12e870d4bc72b8210edd", - "confirmed_transcript_hash_after": "c25299d8189dc62c74ff2fc0933c09af02c174703aec4bd4eb16a27f93d4fd4d", - "interim_transcript_hash_after": "1079761e5e678164f5f7e09dcfa27cc39235e723a645b0bc99bca7a9384a225b" - }, - { - "cipher_suite": 2, - "confirmation_key": "15a477d349a27559d646b5f04e92042149eecd47620c3c3e21803d37820c9005", - "authenticated_content": "00011053ac31037eb7d56ef94d686c446068600c44899faa950865010000000730f1467aebc3bf83dacd70ea0ec7e5bf2541dfd4efdf4248f8db574b31d739052922851ea86f50bb4930b6500f66a2d28503000040473045022100b2fa7631b0305a2cd7f49254b8b9602e1054f7e22af61ba188003a3eb3199ece022030c02d622dcf0b33f0d6c99081c1fc05ede7cee69b8cab9e13b6f7968c2b17622060252c1d01d2bdc3d439591ff868b94e5b4f3b972bf71f7a05040e35df341b30", - "interim_transcript_hash_before": "71791628f9b0d3084871b48ed609548e1fbb64800b867f423afffdeb3877f42e", - "confirmed_transcript_hash_after": "5cfe30d9833659a9f74c02102f46cf32d90e8718f930ea87c8ca1072b1ec2a9a", - "interim_transcript_hash_after": "4bdf7d127e83c87ba90d3e2999c3fcff6381ad087759962255b8a073f433f64c" - }, - { - "cipher_suite": 2, - "confirmation_key": "91d6ffef301e1da3df28de30deb8d0d0df896e81696169fcdcd43feecbeebd7d", - "authenticated_content": "00011080cf732293282664692dbae27e800339cb8b875696e4fde4010000000730b0f828ffa8dd39e7320250d28f074903eb42b08eb80477d1eeb74ad9493383302fd8ac9083e17f36a55f983730a48edd0300004047304502203e188868b5be1bea284a07b050015a97caa9a3ca2684515bbaeaa6a75ac52efe022100ecdbd8da079be67a9ec043d3566137ccd3500e49c52dcfbe7be2d18ba93b0b4120027038b82a5bc62145ac900c0193a9f5fc7130da0200dec6f2f5c2f3def58c43", - "interim_transcript_hash_before": "8b2f1bdbcdf2cb3005886cfe0e4fdb4335c1a6021bd261cc69d2ce7f8e0fccc2", - "confirmed_transcript_hash_after": "af486576b7f27135c696472f7a0f750ef0e71f33660380c599fbeb9b14ad5870", - "interim_transcript_hash_after": "c8d9260e14f087ec95838301835ade65568088f69cccf749ca877ff90cd31666" - }, - { - "cipher_suite": 2, - "confirmation_key": "686e1e61fbef4bf882fa17d47f49020f4661d5da6e35f58089a7c5519ec718cd", - "authenticated_content": "000110a3c4304d3867a9db3e25149d45a385b5bf8bd81c8d022657010000000730597cd89197d7e6d3519925149376fc1493ccad42663f89dd55f62ca979aa8c0ad9a6f66e07be48699aa3bca07dd0b3450300004046304402200f4e3561830b69d96c92a3da20de53a5eb874e658621d9546277dfbea1a973620220503056a4a6d98dec5113669166496743a6be7716139cd5a571a77f9ff1643f84203ed7651cd81a5acddfe4f96254f851bbf43befee2b440804638be689c727d917", - "interim_transcript_hash_before": "a96be922b16022249b5c83fa14097aee2c647aa4283da556f0ea1b9ac171e186", - "confirmed_transcript_hash_after": "e2d5ace37b68d3d25595185f65063c41b00d4fc5d4113493470983f7a3b15e47", - "interim_transcript_hash_after": "80dee3ff05bb60f1e0b51afa347cfece8bb36ec8b4d3b3948731cf7d5a434896" - }, - { - "cipher_suite": 2, - "confirmation_key": "cd9e87704de1be707ae56e2f7ef7f3613586f91c6bb0d33797bbccf9f7b2d6e4", - "authenticated_content": "00011033d4a9cc24de5f327ed16257ef52ba53d55c4187dcfe27b20100000007309a73f11e2597072370864e0985fb0edc074f86b02aa8d6e04ce92c8bfd0f12111545ec2f23ec52d792086511be6ad281030000404630440220380d21d0962a8550372e1b49aaa91a8614f9c4f4a73db17a49b194896055509902206ac5d7c4255c1382509a4bf6a448195403edd26fba3fa7f1f8b04844ca69faf1200b36d8e3b1fa16f65397141dbe06b312603f3b0fe8dc0ef7ca8a068d8f37e036", - "interim_transcript_hash_before": "a168269cb2b50c7fea2ebea0c459f3d40156e3bffb80af819179f91852376d67", - "confirmed_transcript_hash_after": "ed7ce1d4208d79c6d80fa55092613d94f1daa020252514b2e0273eabc3bbd884", - "interim_transcript_hash_after": "13749dcabb09e4f72169c9197c21e53ccd0c6e557937ee757a1ce92f105c7f53" - }, - { - "cipher_suite": 2, - "confirmation_key": "f7b9a163c7d75d71daf895f46df15128639e8746f62ef3dd53cb59a26b00c8a8", - "authenticated_content": "0001108bb68a5ff60c051e23be26f7cea406e68bdfb461f53f7ae10100000007302da93d44c40426471597c7683939ad3ad655511b44a51eaea77c4023fc63f4766d0986c3740d73c9f4634c2ef03fa92103000040483046022100cf4c6b610f53440fc95dbc706abb78d76ec03252650b77dded30aea21bff7c5c022100a4d7fad388e999917e203cea13c543648889947e29820a10110ee7fbef50cefb2019a274894e9443f947508c3f8711b5ee7157b588ce371d0666ee91006022bbdb", - "interim_transcript_hash_before": "597095a7cac11c140d9b7c6e97e3fde27ad059accbcc0873addd927ba5eb5b3f", - "confirmed_transcript_hash_after": "a07138a801541cc950fc09db21169d505f98f3d9d4efae34fc070a41b301735f", - "interim_transcript_hash_after": "85169f48649bcc721c66d71a610e4a91652c1b1492471c4f78d794897d1a9474" - }, - { - "cipher_suite": 2, - "confirmation_key": "e0991a8113f3164251e7f703fab33f539461f0610ceda0d458a1826013ef3628", - "authenticated_content": "000110c229c263b4874df06ac01fc3b5af0bb5fe55af00546716fc0100000007308374a556c9887637db682067318a6cf29134396a8621a8d8b647ee9dec920b4c22f81bbdce8064d56b8770f058253f2f0300004047304502200fa9e803273fb2e27bc986ec31297412a5bd3d1092747f4e4bbfe721965a24d1022100a3050d13cbd00a62e96ab0b0e7d9d28df7e3edca560eca54f8bd30af970954c920dbc7ee303586ffb5e672e0776365569d7cb149c11eef2ed68b43a0d9a088447b", - "interim_transcript_hash_before": "c5c0180006a2f95ad2e12bcdc44897a4d7908b273996575748614830f0485de8", - "confirmed_transcript_hash_after": "f9743940b2c9892de957b7621163ac52794992af3d95cc1fc4f9e0cf1b78689a", - "interim_transcript_hash_after": "484fcc071f02c9ddd7aee5f81668a3c90404477f104a429ba0faaf1f1c0133a7" - }, - { - "cipher_suite": 2, - "confirmation_key": "c6787e3cb5fc06eacd3e3164e354a53638cf18fa387e007275a4672ec2194733", - "authenticated_content": "0001106c26f5648d83f168208559e4716dd43da15cb7235ed306f801000000073010d4166aeb8f8c0b2942c9e54c8eb9c8cf98c91e157b237e81b0a226f426c91550e6d2220ed4fc587478b6ad3b0e9e07030000404730450220365b214ed37b3f3b1db437c2b6d257983a5c52153aee0af9ad531ac1163b01ea022100f60c7db3ea428bf0461fb5098aa8413f7ceb543e9f6131841a2709e27b78a9fc20a60d1ef98329c5742654c9194a0eff4be164472f2ccf3ee2a098bcc1c8543f82", - "interim_transcript_hash_before": "cda74cf7fc170549ce176583cb5e82bc8c5c5376b494ef91fa253c4351feddb2", - "confirmed_transcript_hash_after": "6bcc2d4ecc44342d2e0b83d6555d418220987ddb0bfcfca7d58f10f34ce69546", - "interim_transcript_hash_after": "da672ab955d75cbd879cf01acbf6584367b7ea26cef34494fe3828f4569c1d1a" - }, - { - "cipher_suite": 2, - "confirmation_key": "788271f14c3b2dd0a4d4b8dca7e6b0e88d3220e193bf7c2f8d554f0819b04781", - "authenticated_content": "000110bddd67eff0da139009bc2f2af22215c71fdc36750730f07d0100000007306ef87ff956c2eb9685b26890acd0e57f5afad0db5bb7a03f2e622ed0a68cffd8168053a0ef68277f88da164d92627a770300004046304402201f43c359675639525b36c28b4ae2cbab438402a024291529f9dfa9b9ed9998c702206f6cf2175ddfa77fe453078bbbcb6432a4e9ec6c09e7c31d34ae76de9dc119c220b2f7bf523bdbbab9e532402cb4eacd4670b530fbb18c5bf81e5294f86ae1b4ad", - "interim_transcript_hash_before": "41c38f6d94b1b901dd534529ef6a4bcc816d3e177a8ca4f8ec36117afafd8bf8", - "confirmed_transcript_hash_after": "4c14f2b7de0305ea37a627741465d829470440b45efe710da589720e0be515f6", - "interim_transcript_hash_after": "f06e6423206579ef7f5b55e890f3bda2d6b98decd55a2b292602f42c1166e9e6" - }, - { - "cipher_suite": 2, - "confirmation_key": "6160bb79ffeaadcf326632f43408f9832613491cba61b010e80478f0ba03342a", - "authenticated_content": "000110a12ea01ed15c820b8b0114a1df27f559d01b9eac534040a5010000000730360876a927d08503619448355b5243e5c90d63668e22f49ee64a41c2d14c6196cd682280be829ba3f8a731c282153e0d03000040473045022100d7a4ac16d8176cdb4636c9039f7164193dfd9a90c32c24b9c856b9a0e9fb53b3022001d57f3da1c89e970fdd293a3dd40243d1888692c806b9fb85d05df02649e537200e3c0937a7bd82c424321e60e8d46ddbe24bef9b7d83cd093c2f5c82222c9456", - "interim_transcript_hash_before": "f65720e29542c49e147027e7e7835284d189b2b94af06b19d5044c8dfb996fef", - "confirmed_transcript_hash_after": "d7f97ae83e20f930b7c6c385eec09321cc3650f455757ddc93a1a24091657c08", - "interim_transcript_hash_after": "cce9019a32a833ca98ab815b0a734c4260d2de6f69636f751aefe5bb109386f5" - }, - { - "cipher_suite": 2, - "confirmation_key": "28761f87c63dff0108ad39da26318428ff4679f52cae1314998d2bc95f2f32f3", - "authenticated_content": "000110784e693d8a441f04913369c06e088e532d299672851cd915010000000730bd545339f2f17e6aa58ca76f1e3dcffcea599cf2b04e0ff17be0d20fb38ea1a09e7274cb53cefdaa9e68b1a892c46bf10300004046304402202983c8c39ff798452b2f77886dd2a4fba32a4ec664fdbf2b75e524ab6efdce41022004f485e9a29aaf38956c0f6631b315f8770f568b1024716394ded40f1a248f5320d78616535d6850624a52dc32713aa6cf5e982d62f3b13755dcfef43b391d8fab", - "interim_transcript_hash_before": "24b9003fe9d765ac7fa5eca3a52c4bba7162200198f4bc98ac41b820203c210b", - "confirmed_transcript_hash_after": "9f49d6a60055b721203150d95ebfe8af87bccacc193f0d7a4c658289236e1d1d", - "interim_transcript_hash_after": "7392416f98686cad2efafabd0f813288e190ea61deeaf77895e143abadfb286c" - }, - { - "cipher_suite": 2, - "confirmation_key": "b2f73b9382226e5e6cbeb6efd98b8f42c927c038fc3fea92552beeb6348b1fdc", - "authenticated_content": "0001106beabb52e0f8faf6f5793322ba809827b1dd990643d5159c01000000073054a3ef5a9fb225106c00403891315bd34994dcfc1772eeb23194c075774f9f73b2b3fffbba48c8cbac2033def08a5d0603000040483046022100ae1fecec0a135b11830d4b8de4d7cb3061f3f3f57c7c1aa95decb0a3ff1a38560221008d71eb513ea3e81b26fbb3ca2fa002d07ce5a8a1cc3b2d4e0dec5df39db6d29220f768df80d08000666e6bbf83477f544b921cbc8ac9a71d9a91194c3e4e396bf9", - "interim_transcript_hash_before": "a081f81d5ed4d02c5f41f045e3a035e67a1caa54079ed9664f83fe1b111df8c8", - "confirmed_transcript_hash_after": "c40759faadbf0c3631039090bfa7ab9e057633e80b3204681e8a3eb7d4a99100", - "interim_transcript_hash_after": "9e5923b9da263038cf4c407e9a5740ddedea2ee126afce2d574d78e1f141d38f" - }, - { - "cipher_suite": 2, - "confirmation_key": "48253f92f6ba18deba1c954b1c64ed3c17736513b397f3772717416d503cd927", - "authenticated_content": "0001101241891fd425244b7737406afbbeafcfa75b59d314ab405f010000000730a4d78e3df6b2e3e828f162436024eebbef031d3eaa2400e1e34ee55fe5a2937db3f1bbf41195bda1894f9199772413fe03000040463044022029698ffec968312aadb3aeb3f772fb77d8f901c666a523b09e351092a76d086f0220378f0381deedda8c59489b04dbdbd96541837e0fcfbb9b7316d56d45b03e1bd9200214de27a96f8c9315255617c066f71b5d963d7750d0d7f6ad1e39eeb03c49c3", - "interim_transcript_hash_before": "53ded5540661f0dec71f5d741d4b80e723602b64607a045fc84aee13c9b62c1a", - "confirmed_transcript_hash_after": "c25f31e27d0af6a9dc1eb4d6a8002a445132cd5a0d55a13957cfec9518445b07", - "interim_transcript_hash_after": "6db00d794fa98d3662813722e819aec3b104bfe11c0fab64fde1c041ca3b96e2" - }, - { - "cipher_suite": 2, - "confirmation_key": "60bb7e62b63e54b13b076c55c0128beacad9b8ea8bd2bc8719eb7f59115aef52", - "authenticated_content": "0001109996fabc10b636da41cf27c409327af2e78c4a45de7cb87a010000000730fab10a4085da0f60fee600ed25fcf8f034b15881c9eb3c4dc5dfafa89de034ac4413c55171d6063ba1409afe66425cf803000040473045022003469ce94de08169fe1ba19e1a4f5a4cd366a72453dcc4615b604d0a532a6254022100ca52e41c84e226bda687ee713a6c24c72cd3782004ef50c1d24ccf3e50bc0b63204c07f3e1e4b54ed10a97157b0253331ce81933c6f7511fc26ea9278361efd5d3", - "interim_transcript_hash_before": "70c94cd067243c294ec0251a2a62640b08daf62722a7bad715b930823c49834d", - "confirmed_transcript_hash_after": "3b12cdc02be201d814491bb85356a47990caba92499ddc05e175b8c2cbe7e8df", - "interim_transcript_hash_after": "2db46b61fde449fc63ef6b280a5048478f97aa3067212b704c2e310a42ac25c5" - }, - { - "cipher_suite": 2, - "confirmation_key": "d2c08bd8c92fad25a7f722cf499246f25aa99413c4ef2b80c6995b26c22f69b2", - "authenticated_content": "000110e83a382c83f45c2e5660e5cafebb8410d2f90cf29a3bb9760100000007309265c41b286eba2d9fd86a9d3c7dd71be64704fa7e51aeaa4082190f6a03e7d408d8a99e21f60ad40f5420916bc50bb10300004046304402201a93c99f6a525538eba846819b864e02454355a0579df8529ec9719094c3668602203694ace24f1b4ffc2251d89d0023700d91d1446c6902d0afcc94a8e7919ff1302014dd3607a2ddc5384aa0ac9d2a143ab7730f493d31d0968c3b4daf945d1ccb6a", - "interim_transcript_hash_before": "e12b7bed20cfbd1bd9d660bf991e0f1e60884bdac2940e744e17a95037c7bf07", - "confirmed_transcript_hash_after": "fd084ed859876cf818800be827686a1f9d8280b30ed0dfa22b8e06e4f40e50a4", - "interim_transcript_hash_after": "f8980b93a02435e660a10c3460829de04128e9c7e23c2fd557de8a20ca4fa4c9" - }, - { - "cipher_suite": 2, - "confirmation_key": "13858de8cca5f0b5ba09e56676f3b8244b3ed70faa81553b9077ead4c0e81aec", - "authenticated_content": "0001105827000b30cb92a202180e3c90a1e6fd83f54ddfa1b1d5b5010000000730758c9c77b58543210ee54c574e23fad0628b85d79fe552e72348fb53fa91f89dfd38be4438948fa83e96c03eb354e7f703000040463044022068b84953aeb754ed6bc1dc39a490d6632d5c9e0fa72228e8a8e90c25b9fb00f7022019c9ad4d9967564f18a13465a4b814b907fc9ad8cabcd4fc4bb8f1dfb87312c120246bc202e8bae76eda411f2af2dde715122e1e7c72b1fcc12615d1c573891d8f", - "interim_transcript_hash_before": "6648a75cfc9d52cde82126d38cb053a4517dfec64e40849e6bab8f7b3a6f255d", - "confirmed_transcript_hash_after": "084eded07f5b67f6437af0118f56d88248930349e1799234459d8d5b037c3ee3", - "interim_transcript_hash_after": "fbc03a133071b57d18e5a6f8ed6b3adfcc60cb0a71131d56f98fe39d38afee6f" - }, - { - "cipher_suite": 2, - "confirmation_key": "0ce5826057521fbe0426b4215c549d02c0f18ce3b0bd26100b879ed227166b16", - "authenticated_content": "000110afaadfd8d85a95a1e09ac988eefe85af6591707d15b60c36010000000730a85f306e53608b7141628278168d19debc1a961941ced9f938b302820a639bf973ffdc57be761abeeb22a37e3f2f2eea0300004047304502205ad6f427434405889c9472c348480d150e636c04df34bba9a8446d92c42f6bc8022100babee4f85dec75da309d8e2e524fc38dffa832d399b07b27b115a85e4f32988b2070f0df66b0573c100be40b2592668340e4095d40fca625b703ab19a7d05d2683", - "interim_transcript_hash_before": "7feb00a8a6dc78ddaa26fc9c73bac82e62716cc13f2db13cdf7daedf7d20b86e", - "confirmed_transcript_hash_after": "8ff169c8b579b24d8b452831e1700c6b09720332c73073e364e7452e03c1bc05", - "interim_transcript_hash_after": "a42568ef0059dc06342c5ea06d7b892381ccb339f40b14e585e146f2a788b62e" - }, - { - "cipher_suite": 2, - "confirmation_key": "c67f0081dfbedca3b6dd1817e09395b983ffa5f8b526e840126a230041a1ea7d", - "authenticated_content": "0001109057754e3617c127113b42b869ab57dd37980a7a28575eb8010000000730bc95322221e8027c4a513001dce2fad5157ba1eec784630908fb6ddf572e30847d78765be47742ac1dc7f8026db3899403000040473045022100867049a54be593648faeea5a08883699aa1ee129f31fa939d884e19a9a80bed4022042f554d7ddbb8f19eeeaacc3472dede61c97555c67de77ce005c888fec27e41220671c42dc63fa18c187c88abf97003276512798203973a5dcb0e5c7a035399172", - "interim_transcript_hash_before": "74be6a34330ece24edc864e0a17ef0508d8229fd4f512d75194d93d20b8748c8", - "confirmed_transcript_hash_after": "b0fe45ff129c42e6751ad7a926cf29e92bd46f4c7925d421c4a90a4ff6b8232b", - "interim_transcript_hash_after": "f8e0d3bc99c52394c51327e6e75c1dfc7d2558430ddb5a425d5da8a211f2bfab" - }, - { - "cipher_suite": 2, - "confirmation_key": "c6f8a38dea0127787606e37e133f7022fc1cba158ed6da6a932f296e0e6964a6", - "authenticated_content": "000110275542899da1407b6be19bfeed65a82f5123c51dfd579d7d01000000073012f106bc175c1d7c9bfddedc59e8276214a3ec2a37ef3be174b6e22e2baaccb0597d073bd55617a58fca36ef7d51dc460300004048304602210087bb1aefc2dbaa811aae54e20c6d6c4ae56e1e63905aabb3514088b77d1adbf6022100e26a3f5044ebdd80143770d9aa30d0410ebf51af8dfa51727f51fc98d48dace02086d7a473d6fc7d0b4c234898d7edaedea154e339f0a8d9a6dd0e69c84533ef2e", - "interim_transcript_hash_before": "be7e3fa0928d9aadacfa024f6695a45f587d898cba74c68ba746d3cf2d697f39", - "confirmed_transcript_hash_after": "9b7d7446ee06738b26a3c7ad3b90e91edd207c4ee8e81c2a8e21dd3c8e39cf9d", - "interim_transcript_hash_after": "5e8e133de2a035ce4dcdf72731c0058b18309bbb83e60480d81bae9e3baf365a" - }, - { - "cipher_suite": 2, - "confirmation_key": "b35b5c89bb73cce9d3f57ff771c2df95981bc8a424f9712d7b8a1089000ac7ba", - "authenticated_content": "0001108c4c50b89c0151f2738eec5a0be45edbae76149d04f52186010000000730cbf6d0d7d7d22a53572c43e031e119755a343e5b5210396293c4c73363c67301b355ea3648c946896cbaf002f4cb3a8f0300004046304402204a064aaff2cf6d1fa3dd5b071dfecd37f8fd03f94baa068d5f9e0ab0cf02004f022050d138216aaa5706787d07b58d3625da7cd8a8c9a68bcc7cca3c3e8eac0e35c22029f9909d4f35e74e43d0269481cedb38d32d095aeb448e3e28175f82dbbfb466", - "interim_transcript_hash_before": "79addc06006fd7059f0d43e9388c5c50b5d007a03cd7f6496da0479807d0197a", - "confirmed_transcript_hash_after": "91c206bba07efecb9667a969b3ffc0948b2a630ea0dcef7223416995b494d14c", - "interim_transcript_hash_after": "7625c6444d67763d57ea99fb4f1c4fc95728969c90a389f0664040ee3fb94c3c" - }, - { - "cipher_suite": 2, - "confirmation_key": "b04ee744d6aba325d662e247edd5097a42b0f69358a51c652e35ba61195d4af6", - "authenticated_content": "000110ad622b892b72fc06535539fa5b20686d49862afbced27403010000000730d636e975421e849a393c60bf01da08d158906f688fa9d8dbb464686107001f37546cde0af79f44f928945c3554c82dd3030000404630440220504384e551994e7460a8ca56d36dc0f8455e2107f78e123aabf5f71e4a2cf80602202aaf4b4e6e652caaf03aa0b388ec14d9bd5a2e3d0633b3ce68320a80909b140920cec0c6d0761b8b92253995581282fa131239ec9537e4f612ef82be868672688f", - "interim_transcript_hash_before": "41460512c1e3cab95f858229ef7015d558856c97d3852efe22cfd4fe5fb95f3b", - "confirmed_transcript_hash_after": "f9d3130fccc46b1b9d172a37cc532c38aefc80fbd58fa26d51291e654189e4fe", - "interim_transcript_hash_after": "2f02356c9d8cc8de911432ecae493e1f2cd492f273ea35caf97236c8aa668e7c" - }, - { - "cipher_suite": 2, - "confirmation_key": "84bf1f5fd29d6f765fd9b80c206255d14ab888554a64d04c5d11a346794163c4", - "authenticated_content": "000110257e9d144a0d4381b4c60c0b98ca7cb105b475ffc311a4d801000000073097ab7ad648a3ebff259e6e9e75ac9f7ad728c1298e3d662846fd47fe89a86aeaca45865fbe3fb53d55c301ad85521bb003000040463044022025b0fbc2079c94b60ebc8b5478f91b219e2fe08c89966b3ee971b01af93ae656022056cbb07f257fc4b70f8fbe4d70e40fbe6635d22aa847e7935f215d45deb022ee20a598282649e77c894f9918d0209e4ecdd0baf8c0b2344d6bbd08fab3d62e4def", - "interim_transcript_hash_before": "564991080e51761138d1c47e69e68eabaf816bcc6d15717a9d77e393dff26dbe", - "confirmed_transcript_hash_after": "52a8685aca22928f839917cd11a94d9814d56647e364e5dde63ae00c3d65405d", - "interim_transcript_hash_after": "3d1d5b3cb2cefecc80e50ddef22e3726e0b0231d298486a866f192ed2d6c5ab0" - }, - { - "cipher_suite": 2, - "confirmation_key": "f1f9eef9ca5855e7580bf789e8f04bbcdc57a238493216a6a88f39ccc144f259", - "authenticated_content": "000110105d7eb4c75c12d0b2d8c318b3f8513a4c78a6416d41558a010000000730701e8f7b4e3608b6afac2c1b799c434466c46e23bd83ba8ac9fa7da6e2a6862bda53b392ae35f9e91a435e695d992ec003000040473045022100daf8d0fa30f2753ab51a9321bb814fe7af8cf1315e6c5fa24374a4f65f0b78cf02200ead1926eb4b55a62c87874895931c6944cebc6e25b921e0181bd18ee1f22f912035c706f76ccac7a7a69647c08f1425ca02423888c892cb8ad44136d81f2e5865", - "interim_transcript_hash_before": "1cecb690049765e96f6ee91f2992fb65d48d9f7f2fef10835cd15d63355e4761", - "confirmed_transcript_hash_after": "acf6425ebbb2a8164ff14543f730fe2c00229a2b0df61b3afde0830b907063fd", - "interim_transcript_hash_after": "5912d2aa3639f0148358b7a6893e645c578eaab07faf2aaddb36fffef0f22642" - }, - { - "cipher_suite": 2, - "confirmation_key": "8990720cd6d5dcb8a855068724ad5d76dbe3b5d8a01d3a5b8704c06f430934e7", - "authenticated_content": "000110693e92c9e2c80059a7f89ab5b2675e033c6bf4e383ed620601000000073095bf5347a56c06716afd399cfc73fe56f9552a7d52854651a769cd6187587fcca6663bb0c1b34055b9f8976a52ab3bfb03000040473045022074dbf873ed6b3ae76f80778f9e9ba5d8360988b5fb8ac82bff9e2d2b868bb22b022100e53ff318bda07ab62887a2bd561ceaf1e1716ca032f9f9cde78426bf230b4a8f20351bc6bfb3a53993878595c545e8a3e02677c03d03409b0ba27f065f6e3b246c", - "interim_transcript_hash_before": "ff1d8d09fd28a58a2766f278f41cf09a114ad2d2896edb5fa3c4a913bd481ac9", - "confirmed_transcript_hash_after": "fb0c87a5135dc6545916f1a129bbb3ad81c9649651236050c027a5148cad14b5", - "interim_transcript_hash_after": "4d192fc3ab4e6d9247afadeb0df26b22f1324518c434e608c4670a7a6761fd25" - }, - { - "cipher_suite": 2, - "confirmation_key": "da53da211de33a08a7a942ca997eaf425b174afc0750bce7db1fa7d53d8f265d", - "authenticated_content": "00011090ed6bc0aef02b73c1b075fecaa65cc8588f7b1eb2eaa17a0100000007307fb280cf79ae272c2a4b55cec3f6a8e4fd1d7b822bf63cc94ff9bf4b1e842a288f0c298ccbf5bbd73f509f7340c7b9730300004047304502207ac8a944ce0126aa1206073636ed4a42a8348266baa1841c753f45cf9c177234022100e2ee7d7dc8a7e652c0ffb133288acc2f60cf73cf9fe93c197a4da250613e2203208338ee0702a93cf2d245f45c532602b03d7969edaccbc3b1d4fb0b5e9a188745", - "interim_transcript_hash_before": "a3230d2062bf549cbf5000a564650e4e0cf18baf92790a55f2463347e696245c", - "confirmed_transcript_hash_after": "0678114fb5d92025dcf71d5e7645daa6d2c99a7dc6be6683019c3ab091e3b2f5", - "interim_transcript_hash_after": "8685e214831f67c2ca7a4ba803021cbd6fd92bf48c0b9831ea36f5e7d13dee55" - }, - { - "cipher_suite": 2, - "confirmation_key": "0f386a47f1c49e4736515efd1f24c9fa45ee30d213b606f6c73f7deb9b21e918", - "authenticated_content": "00011011ba027982cf79238dac4a53c68dc452941865ed81b33f1c010000000730f5edae5ec152e880d204693f58b406fdae88c01f8b3b22fe9424235049689a289bf0f7be045d5371001403ed8488a83f03000040473045022100ef8c51a2459aebab7bba59e158c1dd4e7b02b09f019f11d0075bc1d66cbc1ca10220532c2de9323cb22c4c0546d64890c285c7b77b612fb8a082cf7b6b44272cf41420ae3315c955bf5e76e3a546ec42297b8b9443b8bdc1f28123d1faf3ed99cc132f", - "interim_transcript_hash_before": "9e23954aac78b0750f5868b5c2d968f0f6299c9eca471f0a7cbb57829adb809b", - "confirmed_transcript_hash_after": "1389ce524def883a250001fd80fef64a84d7efb22f5f998acb395f978c3af5d4", - "interim_transcript_hash_after": "ee85c26a748d840e630d41fa34d647b1de0a91335b2685163019dd7e69ed54a0" - }, - { - "cipher_suite": 2, - "confirmation_key": "010c946dd0203e1661fb1d24a2cdf4ce940a9d6df5baf70bfc29212fb84951a0", - "authenticated_content": "0001107623370982daae4be7f56fabd8e6d7433fb39cdcb5f7af5601000000073097066d61a16469c975fa0e81b84d6507d0767ca23612cbcb6b55e5690a5030f11955af3682bf23c9c8d6c28f52996dbf0300004046304402205d14465ebca925f8af84eb0bf6ebd095ba97459a1df3a75785904bc945d344510220399594466999df00841c2ad13cf0b3ccaed68cc0d6334e61603b9c7c085997b220de766fe5e321d8f8fec6d5de1cafffb40604e3f8aa82c0040c8060f5ef418335", - "interim_transcript_hash_before": "ddf581c4e4be3575692a4ea52dc8cf5d1a2378ceaf2d6796e634fd8dffae166e", - "confirmed_transcript_hash_after": "6f504222009ea43910bc0347adb04fcb117f661e27c9e5cd8c349f1ee9605bbd", - "interim_transcript_hash_after": "bfddbb5a8cfd147fa5aba3342255c04a8653b420628b9cba90f2ba490cd63864" - }, - { - "cipher_suite": 2, - "confirmation_key": "c08f90505a826d622fd491a508556853e0afb252511dd4a5c6a6a8daf66818cf", - "authenticated_content": "0001106c42402343bde156fabc376110eec21e43728d403d063dd80100000007302d68d3b9e6304aa1bf7a5fd5e036ad08086d11170d16592738a2f3d3306f07123d147037caed4e68917455e8d55acad803000040483046022100ce782b734108588aa08fe939ace1ef989903e1cc066a45e0bbfa431cb11b119602210080c882e4f7bc83d16367456a2b986c23ff2dba87d3e89c565bfae50a89b793c1204e90c96da89862ff9380d0482f3abf73a3f798e5de0cf7c6860f6884d2bb66a5", - "interim_transcript_hash_before": "e3fc740cc809117313e7ecb602c9a449e7f32bf4a5870c58bacd679ed3fe59a3", - "confirmed_transcript_hash_after": "f070242d3d4e7153013b30f62fc246aed2276720fd01a1477279f472d3e7ee80", - "interim_transcript_hash_after": "1da3be96d3471c08ecf0cabcec511052c1de7932b822282942ce4aec28f02ae4" - }, - { - "cipher_suite": 2, - "confirmation_key": "d70b625d8bd0073d3cb655bd8b62d9f21325f8ede74f6266d292ecf4f1cd9414", - "authenticated_content": "000110f86b2ef3e3cef2959d17cbfc98c811b9916e3544bb649789010000000730db26178f178184a11cc080d16324ed1ff52704d6395f7d59e94be7f3e6dc70492151f4668c68735064d22f222a9e389603000040473045022100cb3c16e7eb3e9252ec5b21c0bf136e4f663f405bcc0137772f33b26a6a4e1ae602202ec5be78c2038d84c5db911a373a672d1b6e8fc5daedd4ff12465c0cf95edaeb2036ff41a9742c4f231216c53f1946d13d83e501d8bb3eba3154c3188dab293969", - "interim_transcript_hash_before": "c721ab341a9c4ab7a2384d649eee04f536aa43e8ba86180374e33fc690791476", - "confirmed_transcript_hash_after": "abefb62247fa81586450725e7e05b1939866f191087256bd1175972b10b518a1", - "interim_transcript_hash_after": "63d1c17cdcd043634070d60daac3e8dddfc19e5819bbf30bdb1786e764cb957b" - }, - { - "cipher_suite": 2, - "confirmation_key": "3e2c6aae965381b29148bdeb9bf93afe9bd656fae6bc1b343980062ff4306bb7", - "authenticated_content": "0001100b8e6e6c333ef2fd1c8b5b47504c4b7c0ea01a9fe364d23a01000000073072a591db12d6a9c854f0a775b851d0c9599ff51e2694cbded28e92234a2105997280f03b15a519c0685c40e5d5955f2403000040473045022100b640f4636947a7bbe5527042263362775470982f4db780238493f6407027001302200cad14cee898cd02ef95b2ed1b0cd369f53605d414e4a1a5b92745827978a29820b248cd87964032d7564fd6d9955b28ee7e786960eb1f6faa154b98309ea980d5", - "interim_transcript_hash_before": "fa7c986a80f83ec2a3640c63d32dad6e834a653ba329b102abae3f815be4a674", - "confirmed_transcript_hash_after": "2ba792dbd1d583f869d8dff6c2738caf7c3577c129ded7de59ee0fa0abac8bbe", - "interim_transcript_hash_after": "3add041fb2f3450ff2ffc2e440d09485b7d0f9ddc95ebf49e57616f7c199d928" - }, - { - "cipher_suite": 2, - "confirmation_key": "eec52cdb81fa9d7bb28590e4e7659e108f398985cdfbce41f85544075f836f31", - "authenticated_content": "000110a1917a2ca5263168fdcf420eef2aa8063cfd7d963afd49220100000007304ab90482fe51d8153dfa8053e9f6d707ffb6f7c1d923cc552c6a1af47f155b09dc64c33ff281e242dacbb4631b1ca6d00300004046304402201088a7d6a6b462f8773dea493d137bcfd1e0061c10ee7d5fca00d317f508c337022021d25c1a17877735c7eb9c680869ebbd294be5f4311318380a880f4a6a67a54d20b1c315f7999733e1183504c4638a0cac2fe56d0d8f7555e62846b95c0e65868c", - "interim_transcript_hash_before": "62cfb2dd55917035913a3ee4c739e05c4de1515e827b97dda8cb92fbd214c29b", - "confirmed_transcript_hash_after": "aea6c518bdead06ffd1b82dc4957f005ac6a70fb2db3ba1efdc8ad84e41c730c", - "interim_transcript_hash_after": "9f5158804c4a80577c81601c88d7163a742cfd632c0956a8f1b07462fa9d0924" - }, - { - "cipher_suite": 2, - "confirmation_key": "1ff777c79046fd19769ae5421b386b8e620a25e0348b82c435a4fff07ce8d41d", - "authenticated_content": "000110ac7f33889a363ad4040b0ea2e834d0501b2c75939677172b010000000730b9a30ebbee8067808b8c25ff7770ff8e699dc41990ff3dda3fbf527fd66462330e45872bdcb53b7277887fc14c7482fc030000404830460221008c5f241618fbb4a3fbc03cde6a95802642a1568d120bd31cd18432c2162a090e022100cc760b0dc0f844fd2c4c9e7ba15eb0e546c23584439f7efbcfa3efb554548a5e204f71c8290f68c49d5145d926c018047c3ff81e04f91d221fb8f951940f10c457", - "interim_transcript_hash_before": "87048aaa0cacf31566726918af1e660e2acc923079c59674fe278b3bfedbfb9a", - "confirmed_transcript_hash_after": "f2cdfa9407ac09e323eb6a8e9446003ba09764b9bd7854f16d308be7493c3156", - "interim_transcript_hash_after": "2a39bcfa2d490537ababa21c6b0e4faaf5a76734a34e9231b5bf69c19ca977b3" - }, - { - "cipher_suite": 2, - "confirmation_key": "ade2f876e876bc18727004406f8d01c5e4871bd91666c6957ac63560e161b6ce", - "authenticated_content": "000110a9fce66147688d9950bc9dd1e23480fa2f2f8bacdcd5c4e2010000000730e27ff5dd76dd8a7248f914b3216ecdd3ed2201bfc0c83ee99e578521faa77087710c5c25f014d09083af854f88bf6c6b0300004047304502201ba5b3edc5e5be823af9fabf1c831001b8d30cd19f1d29aa2493bc5840d382a8022100c312e27f08d7113776676c4203194cc9e1c7d2275865d537e15addab80460190202930ac54f4ceb9fa07729e540abadf5951bf00e44bb12b77dc2cd3874a86a33e", - "interim_transcript_hash_before": "bf997f46af5029e81e4ee11d4ece0b0b5a49bb32f5c86854b28376eb9e106b02", - "confirmed_transcript_hash_after": "76b8dac9c4227fd7543fa7cabc549549629bded7f8d06cef7e97b63cadc5894e", - "interim_transcript_hash_after": "1c51ce6b1ee824324fb759fd7f0488b83fbaedd4d422f2d1d33663409f2ff005" - }, - { - "cipher_suite": 2, - "confirmation_key": "3597fdf6be7006edc695dee380b576bd0df3d407d245b027bac20d857ef51966", - "authenticated_content": "000110a593250628e8cf857b4c87435982bc484c3054cf43cca35c010000000730e33077dc78ee41ece6a9e280321460041cfe79d373a1994a0ba7f386f660438b040b97af60c4708e4f09bd2e452d1b6403000040473045022100d97e30c8c88e59a42acfbd93c4040109b37e49ea52d5fee4de664a99f8c5ac18022005bd78bbbd08117def915225b3d85d945ba664474362480a2b7874999097f88e20b4aafd5ea348ca63450264828e4de30b7037e6010b7499db35338dd38b850e70", - "interim_transcript_hash_before": "ac6256074faa0f62127e9d912b6802bd661877ae1cd8eca2cb99cca87419fa1b", - "confirmed_transcript_hash_after": "60e425f7756c8249019fcaed63b89e115570f458ed48525b9b42d37af8c3f122", - "interim_transcript_hash_after": "dd582cca7be0fa3c994b86a31b568417c71d2be399518ae3900907cb48d7e55f" - }, - { - "cipher_suite": 2, - "confirmation_key": "8d4d3a5f7cb41f1fbe9707d53aeb20fa222e553b39ce33be01f241ed8b672c42", - "authenticated_content": "000110d730d7050c60bc35e2449c9adfa4e60b65aaeaa7a9ce5c1001000000073068b034b1ff439245ce0a1cfb630977a6a60d87cfa966f79399afde1ba0b4f2863fb6a79b13a10b015efa5ca03ee6e4490300004048304602210098865de475181892efba1511738623fcb3b1fc06d82603ff044673c88508c00e022100f8be07d5b252d68efb5b0bfd782a252b1cb6a1b0f8f29dac1baadc31c1cdc9482090340f98c85000b55862ae80c557c2e4f670aabb60be287de3df1179e482bf0c", - "interim_transcript_hash_before": "e925857cbb1e9c1f5aeefcf826ec2ff6430e027d99e71d9450e2ec94cb269e8d", - "confirmed_transcript_hash_after": "d6e3dbe79976e07dfd8bd95594800a145aa89537b45b3d13d1bd28487497eeab", - "interim_transcript_hash_after": "2e651b02e3b8e00bb8b69766016fa151651b41fbf76bf389df465de30cc1b401" - }, - { - "cipher_suite": 2, - "confirmation_key": "ef55e3f3c622c8191a489d3698ee10439ad43e8fffd1cfacf90a07a265f4e908", - "authenticated_content": "00011000020452f5a765d2d784b936863171ae9bdcf96106f818780100000007307bf87e6f24997c3ec9673a39a01e71d94009a5d32408d3f984152b10de482504664a493c0b65a168df697dee0adde7ac030000404830460221008826e9c8c59942f0a0639527ea744f8de10c6022997d2feca7a0ba06a326d4fe022100a171077ea8d4aa616df7194a30d652095d9f04b9be00cfe54e8f2173cb54c1aa20d08095adae8e66df2c63b915ee41b46e34edc0dc6135bd4cdcc39f2cdb9c999d", - "interim_transcript_hash_before": "2f6f1f46496641a042fdf2ba10971045e23e3dbd8272918af9583289d833ff3a", - "confirmed_transcript_hash_after": "eff3fef063f3422580ecea4b1f061e6b0cf514158b2e451fc87f22fcc1cbc6f9", - "interim_transcript_hash_after": "2ed3534e3d1949803b3c9a414d2b46a8088026262115f21a461d20dfcc995e0d" - }, - { - "cipher_suite": 2, - "confirmation_key": "c607d3040b18fe746ed8e76ef958920df43f00c9c2a3e1a9382a2a7195cd68eb", - "authenticated_content": "000110339c15eeb7a1e5909640d0878c45c4d9da6cda57e96405ec010000000730cec7e3d10ff08709a2cf47b2eb50215caae8ee971f5d7a41233d5a022c74faf50c843101206f7036d127dd4cd98b39f60300004046304402205835a3e279bb099c2cb16400d736357cc2f0844892f84b50fe6df4ac2ed69c9a0220035d72396b34e9f5066b60bcc6f4653a546e93b259e6544160ac3398407b021820e1822521eaae175f9459233412bc241b5829663cb2b148942ca33cea30112416", - "interim_transcript_hash_before": "53e49cef33adfe2d358a5363b4fb6d55f68e7bef6aaac8db8b8ab8580d3a749e", - "confirmed_transcript_hash_after": "2b2a36c037092573dc9cb432f05cddb1d2441130f57477b637123e52ea9ddacb", - "interim_transcript_hash_after": "22af8d6f3263ace381e766892bf9d90e6807ea13df8eef74ca7430ee8626320b" - }, - { - "cipher_suite": 2, - "confirmation_key": "8431941e5e819493d2e4a0aeae92f7557c5ff1e55f3b702742b7870a5b52b06e", - "authenticated_content": "000110577b2b4a34899ffe488c089773b78e6ce027d14cec24ef90010000000730f38db87fc3dfda2403d8cc1b34f219fd5bcfab004e8ed8673afbd99a25ddb0a56dbc41e80d42062b87320c282ac8dd2d03000040483046022100c226d1cd509ee558fe70b84f0c6ec38a2536eb39f87c98799a5b0df78be64b7e022100ba189c00e28b195e0f63f5400921d8c4fa25c91843c30c4ef915c9edbd7dceb020d10c8d7a5c7fdd1b027b5c3984f6b0b84f406ede17c9895781da6afa83d96c6f", - "interim_transcript_hash_before": "7af2816f9788358cc6f3eb40cd018dcff12cd7a52c2efac0327f15d21f5a2bdb", - "confirmed_transcript_hash_after": "c126ec8bbd678c47dfa136bd3069158061dc5c679ddfe092e6b68d3e96a5aace", - "interim_transcript_hash_after": "3f6480dadd8aa40b7ab061f29d4987110e705893606d25dbe8bd4182346b6124" - }, - { - "cipher_suite": 2, - "confirmation_key": "377ce1a649a9a60c05fc2df3b89a3a97630c79e43bb043600e174e337d08e639", - "authenticated_content": "00011079c89135a1496c2fcf838c33f22970a9e6fada51421666f601000000073057700cbdeaf5f20830e3f697cbfacaac7d320469545a2c6b2924d29a9eafc22b79c44f7dbc28cf152a3d9a5264c78055030000404630440220223079ec8ba8dea2fa16baaf4cb71c37cd33413512ed558e2aa7bb43f0269d0b02206b0bbb6972ef652b6c2480dc4fd930820becb70d1662f64a2002712ff40844ea20d740e9adc358f866385f00134649b92468bcb5007197886ead4eaf901547f5ca", - "interim_transcript_hash_before": "4de1be7de2e3ff70f234253f6d632dd37233fa5eb3a386b5b3095036d92e5b62", - "confirmed_transcript_hash_after": "48f25ac5411b1a0bcadf5134051dc331faf9241ec1044320c0d9c25af651bb06", - "interim_transcript_hash_after": "fecac4743066fe757cff520f7a9852c6c889332e343e4f500d3fe59d267968c3" - }, - { - "cipher_suite": 2, - "confirmation_key": "ce11aa1ee038c5c383644a96173cb9b840fe68a91a7373fa804e7fce4e3840d3", - "authenticated_content": "0001102c3660f0e78c5b0f919a31229b409125f1f0223e45d72cb701000000073007d86b0fbfd04d37cc9eb31bbf9ef543c2de649bc18d6254788bf081876f7b270ba892a32ecd6252df6d43d321ecc5fc0300004047304502205b368dbe66c86ba98f9253a925ab1ed1c2ead99598b007c53387b49dd2df6116022100e50ca37fc91af8722f9c6a9b5f3cad817b534aaf48473f965af55fe34c7bd91720470256efd38dbcde5c7e30294d87aa70df4cef39c2439e2e518d74e5d1f7f05a", - "interim_transcript_hash_before": "8ab3fda27fc5ed950a254cd4a60cec450dd0284326cf2b0b3406c24772a045e3", - "confirmed_transcript_hash_after": "9536914343b9cbd32a362d3c9032a822bf19258b43b90db4df76e6edfdc54323", - "interim_transcript_hash_after": "4d920c615b03375ae940acc17f5c5f4c60eb152ec8e66466f6a477c631051110" - }, - { - "cipher_suite": 2, - "confirmation_key": "2aa3db36440f4ffc0390978e630bb42b405260262b286a0d84d6d7f48a5087fd", - "authenticated_content": "00011059b54709943b24ba10a2aa64ef82e91f1e5af76d60cd26b8010000000730454559e35f908b689d877a0d1751fb61c1b40271881cb6e8f1af5067427a4f451ff0cee03d854264852a787db909697b03000040463044022071c50b86bdb403e6e574a7d43c1ef68d0bd1ddeb9d130d8cd6b8265e7817b682022078796952f2844c04a21698f991dcaf2d871da846b8b4a308085870f2142e822a202a40eb06a9de7e39c620b2d859bdef6618362f968cc98a8bb23951ee6c774351", - "interim_transcript_hash_before": "dc32e4807d559d56447fede9a4253099109ca6629273ad63de40862b65b02a36", - "confirmed_transcript_hash_after": "0b4313d33829a95475b40b4d0ad08d1608d8a180743dcd2094ac3cfab166c56a", - "interim_transcript_hash_after": "587eb4bddad36b416423d60e830d6013bdda62ef4b0825ea45102a121b4e028e" - }, - { - "cipher_suite": 2, - "confirmation_key": "6f63e2992b68d45bde3c7944aa6961060658824e2e32dd1610703adfd5339db8", - "authenticated_content": "0001101bbb718a1baac3de0413d0f0fc1b2ef77cc1d8d7a9df0aff010000000730198a2f1c975b1fb67659c604eb3e1f797b5530326c8b9ccf7583252f4cff7537567d134c78d15d77ba7a67655605bc3b030000404830460221008d1e5d12bb1a3add60e18b89d5452a16a1d384bc6066ef5587d6208cd4ff1927022100c0ff55b4ddf2a496e3717f2ac0038bceb3fd921c9f461c670d2166d346ccbf452022e26cc28e908ec29c8dd89c7a2554637988bb4db9c93145a4309bd19517f3e1", - "interim_transcript_hash_before": "08293a319e89aedc9de8b24c403eb2ec2a33eefb29b1cfe11cc0adcf71639d1f", - "confirmed_transcript_hash_after": "b811a5896a9c857dbf6a0a7c54599acd07713e008b0de929a98f393fe272866d", - "interim_transcript_hash_after": "149d003010508ecbaa386289518a9b86219e8068644c0fee2172ffc8dcbd7ca2" - }, - { - "cipher_suite": 2, - "confirmation_key": "430bd2215fb369aea047bd02abec66b1d552770eae552ca2da8dc3422d06b585", - "authenticated_content": "000110e39be3eddbc9871d4e11a46199a5a66555ecddcec480949b010000000730d4222c45e47a9acc83ff898f8896a98bdc99ff4c2c7f8c5e2fc73993d002b21380579ef39f22f4086c1fca99f35f1c5d03000040463044022078c9b3c10c1375492ca9aa718ff85ffed1fb1d60c67759719dda914f2e480c3a02206505bdd675a0d4345700ab2156951ba791c5ce650624e867c662330e8a076fb92086a60ccf0b89f9f700379364f02fa2a5725059a3cb1581879d50a65121f51397", - "interim_transcript_hash_before": "e6545c17f2b3776867f09e6467e86e66d790a82c351f3ba12b5ee9c7cf0d1c28", - "confirmed_transcript_hash_after": "1c719388983ca0740e598412bc098ae86a2deedba92e69ef1efeaf20c9509e94", - "interim_transcript_hash_after": "4cf8901382450ad9d2331da4c47af14d0a928e695a1a3b42ddc7f9396c83837b" - }, - { - "cipher_suite": 2, - "confirmation_key": "082d1895e8982d576f4f308d7a2f5c813893f6d439163b089e9268df2b94f95c", - "authenticated_content": "000110ff316e0ef6c2e7bd5df5186b75bbf83c996d088eeddca4d6010000000730b1b73d52f1cd810f0f065ad7f751aac96481611d64889544943625c173fc92b6b1947039e2e0a94cc44310a03f87bb800300004046304402203885992bd7d5c97427697c2eac1475f8bd9ee121da8fae68cb44336133deb93c022079faed15e7f63882aeccd28312493368172f62d9d8eb63816ed68442a71b5332201d325501adbbfd546480121672dedeeccef2c6ff3b65f91cfc1f3c45af310ac6", - "interim_transcript_hash_before": "ecee44a29f3420628756e2628139cc9c386b9f40fe091d1141e428df1c6ca169", - "confirmed_transcript_hash_after": "3ab3d13e6244da16a6ae06a43e0a8c3fc3c6509c282b9834d28784327ff9f77a", - "interim_transcript_hash_after": "9b023195f9fde6d08eb95785d7db563bdba6e5efa021eaa55adb3f8c4c136ff1" - }, - { - "cipher_suite": 2, - "confirmation_key": "a41bf5765e94e33554df7f26a2b9c103787453758d81a9dded6648effa5df88d", - "authenticated_content": "000110d47b68dcca575dc5bff462da2ebeeb4387147f5a6a4f6c24010000000730ad701b892726da31b53c47edffa2e5a6d6053f4eecf8b2502010f72da03811de64d8d79c0c00f9a4d2bcc3222ada600f03000040473045022100fc7ca6d40b3e950e1938c2bb1e20d99672533e75419a2374832bbcb8a6acf139022010b1123178ba2e45166db5c710918a55f4d66744444b93b276bb372c03924a6720ad743d139558ad6ae67b00de373c49bfc5d63820f7d281f7f347c761d4431b3c", - "interim_transcript_hash_before": "aa3ee619ebbc912684323e52eb34aa220b8ff8d2814c45822ca1038b43d3c379", - "confirmed_transcript_hash_after": "948b2b19ea769889e869219c83839b5ab786dee651978a7771d83a4594507ca0", - "interim_transcript_hash_after": "a7be4bfc1456d9f6160fdc2ffdc6f064944691f5bfbe2cde1c5e9a871a33b6b6" - }, - { - "cipher_suite": 2, - "confirmation_key": "dd2c1ce295686a5120366ca66011c816f2a132820a911044ece6316175d8cf0b", - "authenticated_content": "000110699046bda671b763b096ed1b8c9013ff2a5ea81d60f1b62d01000000073038bb883ce769b5e029e36e0a45c13062e6b0f4d21b9940487167b667e5306cd1a7681bb94f5e82f3be9c02adc33668f40300004046304402204e4ab345878d618afee58ef972d7cd0171229c48f76fe8889ca016a2dc18d286022058909e3e1a39529bf5b8363940dbaaca909ed54d8c9391a2508fe8256b6733f420031d90b85b57ed29e54e1c456106c107e55fca5201160c42ec5681a325a7d1cc", - "interim_transcript_hash_before": "42504bea9c06bb45e947638f6f68f3bb42cd70db8bd0b348a9f1d279b6def2fe", - "confirmed_transcript_hash_after": "0c0e9ef1e4abca1b54d03e91e4edb68df11af934843efc0d45402acacf0e4126", - "interim_transcript_hash_after": "47ca5c72f3e820592f909a61165c10285d219d64a43b57d3a831e9685bb7894e" - }, - { - "cipher_suite": 2, - "confirmation_key": "086fab7ea38b7c6b6b454fb1e5c94218e9c46c5804e9ffefa36cab1981a76c39", - "authenticated_content": "000110ccc5dc05264c89fc95bcdcbb93e27736ef44d9deb971ce53010000000730a4d1c4b6ea2bf28bd6fc934d1735843ef8ef5b2a2c2a2e2c54773cf3fa0456bb009f8fdbda6272f6fb3c4dc929c1f8da03000040473045022100f4bd247ed3f712880d10e1fa5c816900dda79609f582dc085482ca5dcb4b43f102203a246c1ba184f92ec1edf798b233f1993c55750ece7e8e370f1754647032261a20fdcf3f5d32f1149dd4226825efc70f483e36a3529d870306d8402b24021f7d5a", - "interim_transcript_hash_before": "f759f5031ebaa61893995c19a2124f32ca83f59dff229a9195c68910f3c03980", - "confirmed_transcript_hash_after": "6fa23bea11d7bb4174e1ec1c4af9fa1b6b4cd9ff6052c4af0c6bfda02401a934", - "interim_transcript_hash_after": "063da793da875da01a2219dec86db57b6df80de7d6e085f5aff7cf74d54bc266" - }, - { - "cipher_suite": 2, - "confirmation_key": "2bcf21b9133f95f33c14d23ab7a3a9f9ff0c235b944625fd153443e1e406e625", - "authenticated_content": "0001103c22ede5668abbbbcc528f8b07296e45003d2da5fa76e8ca010000000730a3ca964f94c23efa7a55fe006f8d8099b25391c3adcbaf083bde1b1050b9e6e950d00e62799edac78a8e2ca64b44a98403000040473045022100b2ae41072ead5e64ff8658f1e0a95c9ba39434a7bf29b261cbf2610018353d57022061842359d4606b8c375b2957d1b776be8f674ef9e93c2af1306175fc61d13c1c205ad38dfec48a0d7cb722a9db27f367af459033fd9b5ca1a6ad43306d31d7ab68", - "interim_transcript_hash_before": "2ffb026027a90d8af46c83f41beabd0374834efbe68c5ac87f31babf534211b8", - "confirmed_transcript_hash_after": "8c586d159ac22e0aab0d57bb0121e5b2c471a52b99e6c63fd07c09e4a3247dc6", - "interim_transcript_hash_after": "d66bd18e98cb617a80313ab71058df36166e755f33231c99ad608a37f4d1a163" - }, - { - "cipher_suite": 2, - "confirmation_key": "2cbde1c29bf258221fd5e14baf60146106ee3899a4e8ca373a723e7117da2ad6", - "authenticated_content": "000110f5bfb70f518095f47f08815d7c4e3c3b200512c7782ab0060100000007304f6bb2ddbac3c65026f48d037285e56fef2f2907abc6e0395e7b76d29919a29bac562997c7c202b18585e3524b937b81030000404630440220499c54d6bb1b7cbda9744e0920d00738a26ffc33ad07e46667e3776401d472660220659fe4242e4c7eaa232d78de7280a08825419bf0b65991f321614cd5eb05c3de207ebb428013b38827671fffd5a360e40f171e67a9d4374325ef70e4d3dbc178d2", - "interim_transcript_hash_before": "212fca7b3cfb1a9d1f553d9794626f5aeda67ca2837275cc331f48ca615ac292", - "confirmed_transcript_hash_after": "635898715fe6c1c27d527fe3bf0436c406e28065039fb49414ad2c5982172088", - "interim_transcript_hash_after": "f2a0df54bf64955fdc3a67fc34054ff45a9db217b4e34609bf6376a524070177" - }, - { - "cipher_suite": 2, - "confirmation_key": "cb12ff962226aa2ef9868b619810cbff08450336c3ab677acd03ecf48d557d46", - "authenticated_content": "00011047d90962de770723ef306f2677844bd9af1c75f172fe8d69010000000730dcd5ced4c6d2292943d8bf4b013381bdf10c137bb4d117f4bd1b73f203fd5013a6d40936d8a126b8bf44543941fa7b8103000040473045022100c363b7e5ed972c34b9aa54a8cae4fb0ae0597cc66959318d1e0c1c91ad8a14cd02205ce14597327318b1961c804a1c69be16a196a5a8527dc5e03df2eaa3ee1af6d020f0255353c4ea9b2bf717cdc227569b01e641cab01279e6e4a66a81d4d3a72fd6", - "interim_transcript_hash_before": "7c3a739a03db04499ac1a90009ee127bc789ed97966e6e35dc97f9cc62ba6e4b", - "confirmed_transcript_hash_after": "6831f5773a4c324ea301e3e934c5784bd4be15ffb867964353df4817c204d51d", - "interim_transcript_hash_after": "3e8b28b816f49915fa16fba80eca6bf785f6ea94c1038821fa1fa18a6658280f" - }, - { - "cipher_suite": 2, - "confirmation_key": "d8f8c2c6ae7c49dfa296bff17fd12b6c9a084ffa69e045c32c3c6a66d4e1795c", - "authenticated_content": "0001107cfdb0b8fec5c20ecec8643f8dc9f1fa68d70f33f95bc4f30100000007307416b36cadc29f8dda0e94b229671b49147bf8b96ab0d2101569059cf88feb78f947550da0100c115674172d297fa5f903000040483046022100a23a49628b09ab6783e90e2bfb591b5dc38d89cd42ddf794c766cb90a973673d022100ea05b590c8acd69367a60ddc5232e112d0587f8a5ab2b1e9e967298aca497b56201eacde130a3bbb96da68ee35529ba7d1c790fc16aa2e5ff81a1c364a716c1091", - "interim_transcript_hash_before": "cb2d4f0f5d9650f7923d0e5bfbb05085c9145abd92c08711f97543ff53fcd976", - "confirmed_transcript_hash_after": "d7fedb5b866fad85d63140aba727235cc5ccc9217cdd3eb6df02f19b181b0862", - "interim_transcript_hash_after": "32e28b1e6d9b8f3f802080901b8588b7c85a67fce3cfbb76969fecd652cf1cac" - }, - { - "cipher_suite": 2, - "confirmation_key": "78e809001155858dc5d8705d9fc56feef48c64860ce63c0a5d9baee3de37c23f", - "authenticated_content": "0001108d877867cd7ba56826dbf7851c7ce8695d446971901972470100000007301e55b0d1c1cc41c27bea9ac9266cb23b08be12048aaefa6197688eb9eb7f5cdc3312c3e9731dccfd2bca66b750a210360300004046304402200bcd51db884b0dfcb1c36c2d143f39e2c609ebde0b6857d59ee51bda976de16d022063086c50743a4dcd0fe691d99377e136210558ae341d23b79e5b43da2b37053a20813d06d94c5c6a13074e2df329e83b5271d9bc5dfdf4e83f150ab3e163c5c9bc", - "interim_transcript_hash_before": "2526c5d835dbdcf9f5a7969ac276c74443108582ee8d8ed9b313a3c2fdbee772", - "confirmed_transcript_hash_after": "eeda7d5c6337b0d9aefb6c49d91608b765e6549b2845b2c75edef2f51f1fbd7c", - "interim_transcript_hash_after": "26bcb6c55f51b18425a3eef36951dfe670b85139e273e03069647394c58851a7" - }, - { - "cipher_suite": 2, - "confirmation_key": "1ceb63e1d3cff44afdc8a4a544be5d31059de81df889f625231a49fc62491da3", - "authenticated_content": "0001107b996ea3083d619ca69c3113152e058167d8439255be725f0100000007307c9df216432ba380b40e906862c855b84ad8d4f56ad0d2f6ea4ad060793ee064f8bfa8f6295ad8694076c31b0f072aec03000040453043021f45ff892e8c82e6e2849c117fe54d5896c6b5193569144a8d8500a7b1cf498802204b8e7dece2549fa44d1f30945bf54809598e27f41d25ca53f93b661591e2586920e73e1bac531bc3691eb776529c8699199e144700d66ae2bd27e7280dcd06f9f2", - "interim_transcript_hash_before": "da881052342405a672d4fb358dc247344592cf5e48a1b7ee6deabf9a47c6f75b", - "confirmed_transcript_hash_after": "500ac9e0491bb690b9ac4246fe4465aaa7f525f0ccfa5a4c21a28f6ba744cf9e", - "interim_transcript_hash_after": "032d52da1088ec9922cd660cb1c10e5c6f9557c202b834be3eca06c1e24347b5" - }, - { - "cipher_suite": 2, - "confirmation_key": "12d9d40762191eb1de9bb2c9f6b19692a8bcd3ebfc359e74c1b043c27daa67a7", - "authenticated_content": "000110941835fa059c464e73ac4901893f8379cc2ddd88552b1a2d010000000730b2b147d47a98c413c91be8d895653201d0213d187e43c1678830bc4ceb1258560735cf1523854b0b2044539fb4efe3860300004047304502204366ad63485cb406f08ecb287379bb0755fc39b210009ec4c963c8810431b409022100a1ed1706ae508041b7377064f480f5cd6966bca3cf3a3e5412e499d92daea2af20222d6aea8d787f4cd16012d703a02e151cb3a12b5f903332fa82b9ac8d1cdd21", - "interim_transcript_hash_before": "e80c404e4f5008cf6881ba11d12f79d92b416120094f4b734981aed67811dee4", - "confirmed_transcript_hash_after": "b994c3f695bd98ea6e0b7237907a068767ea6801c5659567a808f6356eecba04", - "interim_transcript_hash_after": "220eaae0cdfebad31275d0443586ae4698c96b3cbdd1fc38855ffa37bf2900d8" - }, - { - "cipher_suite": 2, - "confirmation_key": "c502044181d7eed7f84e007b14ad96e4609100bb118f923fb553e9dac970b5fa", - "authenticated_content": "000110bd08d0c2d7d1c11a44c5bcd9a1f729b520170a003ba9f5f50100000007308982a1ad3e83a9b3f2c087ae4ba5ec7883d7a0027ade2a4d264ff684dff94bebff40e1b8604bcd163d56819e749eeea703000040483046022100d7b64621955fc4c217c18b8166ec1698b9cffb7ac219efa972425d594bc782f3022100edcf53a77888f25cfcab3f4830973c5ae0e46be97ceb12c41030fbb479d1f8ad208bb7ef07e76c3caf519568a582bedebbd1dfeb987fd8e707e9a31cee049ccad7", - "interim_transcript_hash_before": "df3e55d65e205626010dd34e110f8aba1ef7dede8fa16542e43c7ed234290234", - "confirmed_transcript_hash_after": "21bb6179ab2f213fd82e4ad8b5106d81adf32f9262705e3405e14ae9a1d33d44", - "interim_transcript_hash_after": "b1e58a1f2df2e5f14d48c514ce5acd670ac3a77ca24882a5e420a2c7871eab18" - }, - { - "cipher_suite": 2, - "confirmation_key": "9f5b3250e6df1771d9608081fc3dc54d8e9484a95adbe7241c69612d89453fbf", - "authenticated_content": "000110f20c14059879fd6731ce761560f58716b28792991bd5f34c010000000730af99ffa91d0409812f661410c4bd2e96048b5a419404afe21344486bc9f2c64f412b53216c194da692b5cca7f2c0d90f03000040473045022100d0588fe1e90a5953dc391e5d42468676e789529e116ca0db1126488aa076614d022017a77c43221ca744a04184e551e9e08782dea950c8047bb3aa63222c6efdf06020a07b67a2c6f5ee2c7cbad351bef481b64559c68b513985bcb71fcab51a2713d4", - "interim_transcript_hash_before": "f29bdbf0c0ab69810a7470f0ff3d01ed33dd952c27fd789f285a3e3e81bfaad0", - "confirmed_transcript_hash_after": "84223e55225d1209401f1ba2b9fc088eb515d9a1b056a2639a781ddfb36050d6", - "interim_transcript_hash_after": "10b5aa0c02f2c4e37d616112fd65edbb9527ab26e5f77a22f067a795a3c1da71" - }, - { - "cipher_suite": 2, - "confirmation_key": "d85c1f1b613454a9e8f96d3c61f25ac98988bd175c2d03fdb70a9b419935018e", - "authenticated_content": "0001109960ea3236b8bacf312e9d5a633f13a4022836893b498d5b0100000007307acfd984fda6999f589df7cf747082c38a2af4e116c159a47ad33dee138b0d3aa59f2ed4a261953d3c34c1ad79f25a4c03000040473045022100c08eee33055e329f0a1607cc8ad54d062877cc382330e5a3a5192b6fbfa00f41022014cb1f94cd64079701380bd389d8e854df4e1f4ee0bb487183c869dd961b589d201127732ba89ce1d04d4089f4dd8f900420c15485b3ced33c8645482a7d26d4f9", - "interim_transcript_hash_before": "206e45be8a191068466eec005886966063d270eb31d6e12eb5659450687ebad4", - "confirmed_transcript_hash_after": "721b5b228c33d459c119856604d522a2ae2f06828aca62cacfd4829d6b2c3bfe", - "interim_transcript_hash_after": "a8fe8661d2fb55be71cc9b8edd63e8879259cfd17cb8e476e5b6f0c31fa22538" - }, - { - "cipher_suite": 2, - "confirmation_key": "446007306f051074543f4ceaedb57d72e8fcb231cb56b7f9bedac2219e73eee7", - "authenticated_content": "000110e51c51196872d29ddde9e1f9d867f01de9ce61dde15e855801000000073008aadcb07e2b059fad8c6013139a2047ae76a465b74a5f40274248fa157a4958ce065eb6d15fc8379211f96f72c64c5303000040473045022048799ad5d39ca3aeaed0f569597a00fdce7ffb3951862bd6d610c78fc1b77a2e022100e118aba10b9c6d150da57ba44163365fc61b84843d1b3f5dd14864e29776d8e3201b83f9b0381d92cea510c4f7031e4969119fb5914188c1d5ecc0481cf961e524", - "interim_transcript_hash_before": "97054a18a2978822ed541d0ac8a513bcf61527ae3967ae331dc640b2f13be9a9", - "confirmed_transcript_hash_after": "bbeb99539cd604f08c4a33b4883847ddab5b6725fe63c47548c6c6b95d57178a", - "interim_transcript_hash_after": "d27dd9c050c8f1fbeea9f286caa1af1599a007bc78c036d92a9e7a249155e206" - }, - { - "cipher_suite": 2, - "confirmation_key": "17f7d8afa863f8e955cfc4e71ccea2a8106cc25dd877a2a15909f8a55aaac453", - "authenticated_content": "000110c0f6583fa271c80e7a83a5823fd88f9266ef9641e6f590ea01000000073049e2dcbb68135ce6c43c6b3b6fb57b294fafef3f6db2f8fbc04e1cd06f206846ba5bf69f8e59219506f66159b88de69c03000040463044022066a65236016a756fbfe57e47ea60c4151fa913ce9e85cd6568e20d195c0350090220354b7e54fe40928dded8141db8e365a382149590edea8803a39f57459dc090db20a10bedf01d2cffef37ea263345360676962eff89fa9ed09c4343a0857910fff5", - "interim_transcript_hash_before": "b175d58c6b2f51a65c718b0dd90e7f112df589cbe163134b32270f56751f6891", - "confirmed_transcript_hash_after": "109a3e0a8c257a00aa84d712408d2227cbd7cc930f5d21d4e337b90972c3447b", - "interim_transcript_hash_after": "0bc3643cce5f9c37065dcb748a2b68df767719c0fb21eca7d4a16a5a834363de" - }, - { - "cipher_suite": 2, - "confirmation_key": "43d109416469adeed20bcadf748397753a920cfa8c0f401a2e2bca825734e999", - "authenticated_content": "000110e8a6811777b94feda46111a5dfd13986d4a031ae6051051f0100000007307280009f30a45954ab9cb8d31d9c425dd54e8acb8ab1930827e73ef36043eb3e103b2597562c6a7ea41aaaae74ec848b0300004048304602210099d17df7ffe3d192208b4050df8ece641697ed8d5a8943a550915f86ce8980980221009da42a6c668feee600a33df131ddc0ea7551d4645917361977ddcb11f15b70ab20ff916a19980291f27f3ee34398e1a3269c6eac603f17ed561012cde74fd38ee9", - "interim_transcript_hash_before": "be2afe0a47b0bfdc0287d521e3f7fb56d23317dfa08eb1013cef058af9109184", - "confirmed_transcript_hash_after": "8dfcc82c7df4b0eb040ef0f02573e381b6e51dd85c9dd07ba02a79750fb70003", - "interim_transcript_hash_after": "990acffe41e398b48c693b6a24824dbbd65b94a024f2d652fb797da2686a1444" - }, - { - "cipher_suite": 2, - "confirmation_key": "25915485a599dbc11b6d5f51471d4c1e2ce691436d6438faa529770a831e3cd4", - "authenticated_content": "0001109669da4ffdde16e6af9e4d3d6e81e51a9ab370973a1914a80100000007309829a2058ef0c072f61465926385db89f0045a95b031c1e74a4b7e0e00df4eca3c9e57714c071b8a0b65883bcadc380003000040473045022050e0ed74fb3b460a80cb3c2eac727b97d456da538cced14dea16937bdb6c25fb022100b633a08616de5597c79f437401c8e810f3821145ae7d96c23639af571ec433c320b152c7138d60d1f519fbc73519cf504e6de6e0dc162d070deef0a32d16a38cac", - "interim_transcript_hash_before": "5492822253b5b2226ea3d6f2d9ed829fdabe340ac9d138624a5eb33451563583", - "confirmed_transcript_hash_after": "c616b5bd57882b9e0cc047f30d7b6ceca6996eba61b3dd4d733ebb34efef8003", - "interim_transcript_hash_after": "8447ee33fb597017ce67f797ef2340538d32f914b62ee6a239298cc42775d3a8" - }, - { - "cipher_suite": 2, - "confirmation_key": "9f37edba342b6edb37736a84d24f41235aab631a5cb09b172479e8c395cee0a0", - "authenticated_content": "0001106ca9b16a530b30b55f9a1f856b32bd6cb147186b06771dda010000000730b6b6663ad29dec39a58168f0a63a5ae9614a70722964760f48ecc162e7c4445c647a51343bcc91daf2e25de8edff92b40300004048304602210084135b8c4443b83091f3bde147c8e7cadf81a673c0eb5c1c2a022c2ede46760d022100984d97a1f7ca29d4bc411221e58e3f2ade33b1565ade5acfb83d0544a4a9031c206aff7e02b66670a1e9c9b8640f961c0055ceb40ee6254fede9f5385d620e8a57", - "interim_transcript_hash_before": "46aaf3141011db89ab33af4cd81a8c1b744bbe9ce73b78e3cf5e0bb14e894aa6", - "confirmed_transcript_hash_after": "aa214a2deeb91bb4930518aacfed6503e6c50553e6b45b8b7dcebbd69fa8a64f", - "interim_transcript_hash_after": "6eb240450bdb2396c4a87d1f16cd509859d3425496c0cc2e45d8f182f93c87a4" - }, - { - "cipher_suite": 2, - "confirmation_key": "b676dc4de57bc9b5f5010b265ffe2217027e68d185bbb9b239a8dec2f3b80477", - "authenticated_content": "000110578d0f2c7f5ed2bec0d62867dbcec5aff1625837c015e8e401000000073048b6843a19b87f3b490678be239947a5ff57459d817d3b881d06704a92b4a9ae016a121e4a17414e265e40ceb0f8b7f203000040483046022100f8cb4f5ca2040ae535c9844ce94933978dd50cd69226cb4ab71727dc3168e150022100e3a86088b05306befe65958c281dc2709e039fd4e796b4e534f740e23a73bd712018d20d9a6a342e75d10ad26b6db1fb7448b094b1f69215b3570f6901a10fe710", - "interim_transcript_hash_before": "0f7968d1595acaa1af577cc261a51db95436d383774d2143be329b9e5c0d88d1", - "confirmed_transcript_hash_after": "8f4e66860453080f8ac60eadccdaa2940578992a1d426965f98db2f8eca0da90", - "interim_transcript_hash_after": "fa22f32e6679ad19dcea921624837c7baa2d6768b532b944e6607bb8dc103812" - }, - { - "cipher_suite": 2, - "confirmation_key": "9cc298f640914a2f2322616d328dc17db55a25e0b01ca2032cef9be1b7483c24", - "authenticated_content": "000110cfdadcafc832e4dd0c28007f9467b670865902efe2477baa010000000730a839050dbafef10998404b0a35d327a7a2933c04a8f1aea54e2c5e548951726e817e870456c440fb3567d19c0fd604e803000040483046022100c86aaf2d039edd23070123698541fcc6a8e2f1cdfd9caadf6d936fbbad1ab50f022100bb5cffe347dbdbaea74ee40ae97582790f7f221b5866cf265f4e4a6c94df7dd02000a7bbb2cc3b315b43cee5c52887ef1dcabfbc1383411dda347a62acca4b19a3", - "interim_transcript_hash_before": "6d53de30d75efd808952ba40e96872b47d6c13b91455d3103120065e29eb62d8", - "confirmed_transcript_hash_after": "dc64faf5386f8cac6f77db45428625749e892310c8659d89e119146e03b5d66c", - "interim_transcript_hash_after": "a0405d0ee7bde5ec5d1b1ca5df77f23eab7781785166f5e604a9bca08d72a4c2" - }, - { - "cipher_suite": 2, - "confirmation_key": "5cd505cf274b13b0dc0209fa6dac151c41e38559f52b9bca05dfddfa02c46bba", - "authenticated_content": "00011016db27c8f0b74420835363cd38332c4e1addf6a246fd0c4d01000000073009250153349084ea51ff8fac61383ca7933437055bce426105e96f56b13eac4db3078d1f5dd4c310b96fd407be0141e903000040473045022100f2ed7f5eba4118b8b257d532ac6c7e83cf4b437a88ffab3eda8b7e6a24602929022016ae2488aa752ab2e19d362595f58f8d19e5b61b57b8b025cc214b1032bcc42a203974cd82035a41217802ae2148d9f3db401d17fda85c1f0306961e0264bdf040", - "interim_transcript_hash_before": "063f050976848f2a9b56d1be71dd9dc4ff375ca9e561d8a4965876a03d82032b", - "confirmed_transcript_hash_after": "a189f6d5a577770c0b213f00e5b4efa1e6e58c5702b4b21e20e4bf0f9b73e7f5", - "interim_transcript_hash_after": "981128d70389c2c1525d21afd5f1d4b0ea19871b7ce2aa559de317947e88976f" - }, - { - "cipher_suite": 2, - "confirmation_key": "acc1a90297e22ba4944bf45de2fb1443e7e17d5824093ebfb940ff39d6cecb93", - "authenticated_content": "00011063f57957cb1eb258cc8349399b0d6ad0b31f82aef2e2dd690100000007300b8fae69fb12de9227c544e3966fdb3066233320278743d09a1986a7b63714f7d6cb468a5f15ffe5c67bbb33bf959e1c0300004046304402203bfcc395675d5d7297d071ef9f3f3b9420240bd4bbba55b04baf7b5aa15e3f720220082c88246c95a858a9c96f93a9ccfce65e591c26cf9e9725dd8085550df09a7a20fe88c3c3e8ed8a3e030a18411b1d3d42368a57f59fb4aa103f9ac860d1e1a0ac", - "interim_transcript_hash_before": "2cd1c3b8b51541866d52d88098d24e3bb3677d9052b601ae8ceae204ed7dc336", - "confirmed_transcript_hash_after": "9d614c6e0fa64d369ac2b19dc013dc5107dbac2f4b35a92df2546573789c7a76", - "interim_transcript_hash_after": "7694118bbe806b9b00da1c7f741bfb091e9777e54ab753030fb5a209bc5d24b7" - }, - { - "cipher_suite": 2, - "confirmation_key": "08dcbf21198cba0eb3d34dc3b8b2f6a19e91086e97ae8ecec6b69f7f5ba8ed68", - "authenticated_content": "0001100c2ff49eef9378887695fe58707a8edab1e1907bf8e4bb27010000000730dfee9872a6c81ad6118f0afe5b09c46b96e4b0386d1b11f1e970bcf910f8eec8c17b0af75fb7f17968f7e838e1b6541603000040463044022062fb138cbb620af9db9bd7da6b2b90dc5aab37ff78d50b658d85165a84db1ebb0220100e285a87672e4ab4579a5deaae3ad4c3676af2789c8b6789b741efbee8a054208f4b92e82743110953116d0b1cde8e8be53687e22fc0d73cc419207050c9f095", - "interim_transcript_hash_before": "51a119185fda7f800636f3b208cf03a4a9abe49a4840885ad7ef35e23109efd1", - "confirmed_transcript_hash_after": "2c0643b4ca1d64c24ef95ddec6a367d97562414ce7e153e4c1e401a398f68f69", - "interim_transcript_hash_after": "98b288f83186fc5341ce7232219e69f00912fa7dd7ca44c166376c1424ddfba6" + "confirmation_key": "26ae359a9a9760cd1066cae16f1456ea7eaf52bd85d5dba5bb15cca04a90c83f", + "authenticated_content": "00010567726f7570000000000000345601000000000003220220e740a6faf2db65f5853148d75d9a335d7c4b94ab106fe5f237bc34fdcfc745840040402280c921b6b2836ed4eca496dfa74a13c171f78b80822894032c4c03cc4fb9c24e505c0d0ba4e8edb63a3327442baa12555a57f28e4cfebb4f0c42997030e207203a1d8805d8390f441a622f81bcb7347be86db7cc491d0313163fa63c2f66aa92", + "interim_transcript_hash_before": "2597552c42df799cd70e99c77c4f80e834a9418096a0b2c7bc66afbc48eebaa6", + "confirmed_transcript_hash_after": "51a85b21149c86f3f8c2907017c449e96987242b7ba2be9db1ddd53fb2db0d1d", + "interim_transcript_hash_after": "193f9e11118fd08ff626069543b481ec5f04145680b612bb84d8962a2e609211" }, { "cipher_suite": 2, - "confirmation_key": "da1aea3cd230c718b9e3f9e2946a82c55705178b8dfa315a21ea4c61b11a4d54", - "authenticated_content": "000110b67f7037b10e8d35f0b084871947444c4a0c8392989345cb010000000730463791f9b68ea43467393fa2db51ab636483a812ac31794bc673749455b04d344543bd75a34adad2adb75d9ad6f6f5240300004047304502210082de42e0103451e4f599d90896605613098af672819d1a36956cf4db9d167a8902206ff53542748dc77a5d0663150691fac29b618a101916e50ca3407b27476d12a92004a2b31de7d59d7beff2c5a1325fe87c511937d33f5a9b276ac438e982be1cdb", - "interim_transcript_hash_before": "f18244535bf172744519ebfab2188a59fe2c21af2dd8d9c97c37e1c24568580f", - "confirmed_transcript_hash_after": "3c24a5278acb34f615987c22e849b17ec89b82849c8ec59e24b769e2f92cb839", - "interim_transcript_hash_after": "5f03dcdb32ca7cdbcd61d514fb16551928582bedf66fd3032e17cc0de6b7e657" + "confirmation_key": "6999e1655b7f4bdda3cf2991965d889a331b487526a9c99c19d1060e4d677996", + "authenticated_content": "00010567726f7570000000000000345601000000000003220220e740a6faf2db65f5853148d75d9a335d7c4b94ab106fe5f237bc34fdcfc74584004046304402206f5ac008efb1d7edc106a27b4f3b71aa34821ca679543fd8bde8d728517b53bc0220223fb7226cc477e31ea25910d712fc915ce3df9f0399e0e7615babd593e2cdbb20fc804973ae28d04b9f3b71930414e29aa508f0711df720519f230e43a534b714", + "interim_transcript_hash_before": "de0a78a0008b6c5c921c910d68da44abe0e692e1eea7e9f8226219ca34560f0d", + "confirmed_transcript_hash_after": "e50ae43acf8ba84f712d8f48fa6ccd4768e48fad9c95feaf3061c54fe87a2779", + "interim_transcript_hash_after": "87829eecb1aadfa10cbe2630fcc7ae9769d7fb2520f27b44ef76341c43ad834b" }, { - "cipher_suite": 2, - "confirmation_key": "eb97c3af617ce34aeffa901aeda0fa99d8b930b60a9e1d083a023ba1bdd1848b", - "authenticated_content": "000110642fba9ae705b59a910ed3faf16ef418a10eb6636c3835bf010000000730479078b8eeaba9e99b7aed290926269aef19487b14ca046f0d735a4a0ca7942cfa103b254c83b6dff1bd8278b426017b03000040483046022100a822007b656966ab2831549ae568bc5792cf9bcc717810fbcdc6cb57fce90fae022100eec486bbe8792b0ab4c6489f15e52ce90ac070d3375cba708c9d45b2312d179f208c7d1b6b24f606b7ebbfc95308f33b3b288a9902ff095be0813586a185783354", - "interim_transcript_hash_before": "e118066997badc1f2ae0d80277f6cb22b3087a0dd0be2a46fff9d0e4c1d51831", - "confirmed_transcript_hash_after": "d804aca1c92a809e6a47b9107ad1cbd484918fd4f80fad55161393c06f2ff507", - "interim_transcript_hash_after": "c416231f254052a29760748d8b34db5c4e0a1c95d624c3fc79bb8ccb48ad77dc" + "cipher_suite": 3, + "confirmation_key": "45eb25e0f6024d7dca9a319bbb5d86d22d156613e6319507b54f4844f71bc858", + "authenticated_content": "00010567726f7570000000000000345601000000000003220220e740a6faf2db65f5853148d75d9a335d7c4b94ab106fe5f237bc34fdcfc745840040406d0a302c5106ea9d31f3aec1d43df3fff47c1c0b059f4a0b1884798c5f6c973b4e3933b927b7e5f841813e1c14d8513c579b3662a3ce5e4c50adf53c4d60df022043646bfab3e0513b627b89997a2c3192afec8c9b6e9ef839dbb5fc7c0b90c9cd", + "interim_transcript_hash_before": "0a56a7ac12b2fdab74b9356d100f5458dbce550c1fe69d43283001496fd08901", + "confirmed_transcript_hash_after": "93e90c899f8a485dad5521123de058cefde35d5659521efd9b19c16fbdd8b42c", + "interim_transcript_hash_after": "4184f0b59dc75aa1771efb5601124785ed6d590fe8cd58c398f4ccdbef46f3f0" }, { - "cipher_suite": 2, - "confirmation_key": "b45c61267984ae3436fad0080ed127a27dc908e44e7d184e5a1b4bef4fefb302", - "authenticated_content": "000110e0700a58744fb656667c72a09803073626addd27dbdf7f38010000000730169eda27e04c8d5580d42e62509d75e580a92a8a3db2ef11e46b4fc1b6b0c5ae0fc5c8f1e8102b7c58ebc6b11f83653803000040483046022100a12c25a36f3380274591e19ff9acbe44b0d0a6c74bfeca161549093528273de50221008606081601df5d9b29eceb3a4d01c4ae453b99dde18f95469a878a7e60079e2b205437c08c4008232dc4af78d4ee1313ca4f4e9062bf117e4cc42b01fbb6b90a5d", - "interim_transcript_hash_before": "fe72034740c5b1446e6cdcd828af7d842fbc342ec7164d6476502969923d7e9d", - "confirmed_transcript_hash_after": "a561fe5ff6ca3631a5affdb02fbe350689616cf75b1a764ce542ea8d1e80c2e3", - "interim_transcript_hash_after": "15d74582ce58d01a9712eed0194fc7c4e8796d3308cd23429a0963a7e2ecba0b" + "cipher_suite": 4, + "confirmation_key": "dba36f71716934f071b72fbe8d7a2cc226e3fc81490d88a9a0c91cffcc18667b4b97ecad4b2a0b3d85de08af0e1c1b152e9a60f45f6d3b8512b35949dc0cf9fe", + "authenticated_content": "00010567726f75700000000000003456010000000000034043024040f2efb619b16c8a50925b397b427283ce18e4bd3f252eed2ebb1ed50f388be97000b9f502d2333ab89c69d1223a43fc519216e4df888d4a5060287d917ec92409004072b8b32fd646350886db3bc7fda8d1c3f2c1405eb836d2115a89d350e4dce9e10841051e8c3a530325e72db0a38a1154b95991d7833b3094ed80dd28d546412fa406dd133f281aafa0a40c9cae575d48ab1d568db6d10008704500ce934ec6b0be2c08e2fb49b8f96c692981644cf9ec4d3d004040b2ce522a8d03bf5a3f3a0e6b3ec0a10f6d31fab3868e5aabff0f0fc75110d5fc8e5e89175a7443c9f4d0329c25147793a1aa857c055f137e1f79c75c9a6d04a2", + "interim_transcript_hash_before": "817d930807489c5338c69087bc43506f6a26b86ddb53635bb32b45a49a98e88959a35938262689f33ba673f902eeb7c59443565dac2cc9378bdb6e6e96432fda", + "confirmed_transcript_hash_after": "970789a954c5c4780f48bba738f60222dc8378c916fc24bb6b76b40724aa7fb5d366ea15e2337173c307f657095e417f7dc3d9200ad1ebcd7cf13e5bb64d107b", + "interim_transcript_hash_after": "29c66c251bfab15b12542cff0de7a43f319b3a6cd1a1392444bd9cfd3de3ff4b5c7b82c466bd98a548b6f7989ee010af0ef8dcca427dd088d6b575075b24af83" }, { - "cipher_suite": 2, - "confirmation_key": "f732d54284cf569bf9d6f9a2cefd1a0044fba474f5239b959bed7231fda0f574", - "authenticated_content": "00011004b36290e04e8adeee1412a40ae251454815c31f58574c64010000000730041033afa7b9f715b4f20db1a874a1220bd74502104d59a0bbff8be1cb36193318dea8614925760d0e14f82405cb59ec03000040483046022100d040706160481cdb6a6ad108ffe8612fa09c900c50c6d1fa08c64d7b3d530c65022100eb9577e366f7343e456062a3a1f5ab09b5447ca902560214969ac1dacccf84d12011e8f1096e7dfe6f50f84ea97fba4911387ff5233e8890922ef093163b16469c", - "interim_transcript_hash_before": "42729d2455e8f5c59a5d4849b6592f68f81bcf50ea3d1b53f502c79b3afcda3e", - "confirmed_transcript_hash_after": "840e30745e4e5de20ceec5509ee628efc7446fe3bf93a4c1087a5137b6db8843", - "interim_transcript_hash_after": "46b323815880b572d0e4140d1b633c8cd9e648488fe1aa529e93b27cb4287890" + "cipher_suite": 5, + "confirmation_key": "5517432725b5ced63b9426a6a8bf8d2fa418687900ca43d76a7703427d480af24e5ead9a0f1c1fdbd3771fdf4e1ed66ada5dab6ae243ea01472b6a8e83aaf733", + "authenticated_content": "00010567726f75700000000000003456010000000000034043024040f2efb619b16c8a50925b397b427283ce18e4bd3f252eed2ebb1ed50f388be97000b9f502d2333ab89c69d1223a43fc519216e4df888d4a5060287d917ec92409004089308186024170074d08546241ca8d2645057d9f2d37d33e558d10de8912e9162d5415f41049e63f55d97f752a5b7a36019e942d27fa2c198b4978b538167518af6544df653f8d02411e109db56bade664c91ba482d513d5deae4c98b012fd05b66071b4a2bebbc15af11f2ef14c2dcaa774ce2a6c707a139498774dbdf28c154631a5abfd84feb0c9c24040e4a8d5aa4d9587d97b26065a434c13e6aa270c414d255c8729965f051bbb56d0e6da7541e90bafef69414a5f1ba58d065e1e6ac71a2d00ae0ad7fd7306946dbc", + "interim_transcript_hash_before": "4367aba662819b19bbfe567492f5ff12dc5f06f773e06f66f08f031c3ca4198813ba7a72d4ce3163cf4cba131812ba0ea086be5ed52ac8c8d36e9d0e1a5bacc7", + "confirmed_transcript_hash_after": "5fb99f1a775dc2702a71203928794d1ae8c3afd493a682d9aaa247b5d35b4f38658654de80df6dcf6b5d853e413b4f37c4277b5e0fa812115b278c0b7013c5e1", + "interim_transcript_hash_after": "20e58f40e6c5040ee89669ed8b61cb20fe614c81cbe3a955ae2324229cdb0e6457fc9e724dd831970f3b9d59cc46ad1630e2f17f49d746a0581ed3b067edc639" }, { - "cipher_suite": 2, - "confirmation_key": "ede4e0c40ad80376583770323ed7ccdeb473204ea8b0fc478313377f80e9686e", - "authenticated_content": "0001105a73a2f4b520e40d0e881214a17228be07baae8f725e75b401000000073042bc7f1777ed3fd29e1671052aaf525a806e614cfe65ddb06b9718a3aeb5f3f999bc47f2a56b8879994eca4e7bb3aad10300004047304502203e9831b5f9c842bcfd68c986ccac230ecb81b5b2578175a228c5e1bc6ee06341022100a9e78b6cf2ab640ed3d1bd1470bb2313940f904fb1131852cfde65a1511ee44520c8508b32800c7720058fce268cd19eea515e7590142d5ad26eb08a8b91a2de90", - "interim_transcript_hash_before": "15edfbe0793f13f1015f36fab2543e8e0bd70618213f4ec49f4f841903ef9fbb", - "confirmed_transcript_hash_after": "04d0fb25885b15b4c7db14b31e5581c6476d673be822dedccdef655caeeab304", - "interim_transcript_hash_after": "76074c2cb4b525013929de85c9a4a93ea3295975ef21a8768d6051c9f335e87a" + "cipher_suite": 6, + "confirmation_key": "b3fbb1585bf63169bf62b481074e64e825443856e7d2fa9423e4904c3614320a311ba3f88324fd0dace96ab59ffca820787c74a800bcb4cc4ca414549ccd68c7", + "authenticated_content": "00010567726f75700000000000003456010000000000034043024040f2efb619b16c8a50925b397b427283ce18e4bd3f252eed2ebb1ed50f388be97000b9f502d2333ab89c69d1223a43fc519216e4df888d4a5060287d917ec924090040728d711701d873ed19f7758f59ee0055fb31e5ffa5a31f32e050d5f14f128e88f66f52bb367d78e9330ae9810e2e34124ef4604d0dfb393c8a006dfba06e2288e4c952f16cca1db029788b290d3a733c028a0bbe85d662bd6aed37c4223948a7ce483e4a922001c8e92ebd95c1cf3fe60601004040da60819cea3b7bf683767c2e9592ec3d6398c37d861728587ca6870c8e094b9aca370f87533e8ceee6d00929f507e4e5bcfd10bf7c60867be87c1055e5677ed2", + "interim_transcript_hash_before": "33b97e89e3a6af01e6363be3ee5b65ed54ac6bf0e819575ce39d39dc8e84e4aefde0a43ae6317a56bf84d05a1ec87b775e91b68d2dcb5e6282b2bb03ff1b0554", + "confirmed_transcript_hash_after": "2a4da3d1df04477e33689e7d393e8034963de31e28d3c42c842a9f6033738eee75a3aae311895d80da99a3fd09701327afa4edaf0e1247f92b1335767b9a401e", + "interim_transcript_hash_after": "402f1c404fdbbe4a5aec512c8ad2a9cdfd200e592737e2eb223ef2fa6f824edcce9ebf0a6348610753263875e024db56d8efb6efd0de6d36ca3809b205a35b48" }, { - "cipher_suite": 2, - "confirmation_key": "30f56f5e170474c31fe75eaee114198aa00ff574a94eb0ab296baae5f9001ac1", - "authenticated_content": "00011030d3c740a9f82e647acd786422fd9fad765cf172b732a00a0100000007300f6b31474c871f15c779d57742b0efaa33fe522dc1932a3ebc4420dcfcaacdc2caef629cf9a1ae32429f73d6d5136de603000040463044022005fb527546b15b6c1ad862c7ef8b1955edbce91168ae24ef007d3d7abb38957d022036924c840c8a96017993767ad65cf1d313a050dc281e6081539b2e73e4cf3dda209a11c418ce7019da09d4a38542fb2a7f959c2765915484d8980e8a7f8f7b893d", - "interim_transcript_hash_before": "8c742a3317db3d5dfa3cbb242aa497157dacdcc7cecb18228ffb2785270dc48c", - "confirmed_transcript_hash_after": "6d6aad75699ea6e36bf5f0696920d8deac62187bed7a3655064cb36849f490f8", - "interim_transcript_hash_after": "0e88941e05873a5d0b19099afbbf8a1828b58681c2c94392410b6a41fb04d61f" + "cipher_suite": 7, + "confirmation_key": "92c24eeb327550191152ee1a87fa7824e1bb293b57406c8b9b1897fb4a8dd00c23c03e8b37155a02bbf326161a374983", + "authenticated_content": "00010567726f75700000000000003456010000000000033202301422bfcd03851deea9586fd262d0ed10e7b755690244559ffc453f2feab08d5449c3e73416d08c374ae3b3774b8240eb004066306402302cc549d5eb36696c8c6b1ed4f6cc26d0107b211bd2033457e68ef98dd34f36bef6ea298e7161223558167ca1c2f2895402306dac15ca4e480ec158afda65097cf1069c41b73f513624ce2858791c725c1fd6241e945e56ee876f3d28cb0ac55e89a5300ec2d58ef75efda6c2c7edd5f2e18d47da5d0a7e1d8afd4853664efdff16f1463a5dd4d4d3ae764bf6b908b488908883", + "interim_transcript_hash_before": "7fdf11509f74014de36a3ba22db62e33845459da72c3e44f59674df36456e36a3d96473dd63318ca772a8eaada76808d", + "confirmed_transcript_hash_after": "ff5984f95a10075bd5fbb2e06620502c499ae65a2a07d53af940dbcac491c6dbdf7223f25a111b1a3dbfde12086277c9", + "interim_transcript_hash_after": "da6d7bcc59becfb58511d14b511819d282ae1fe5d89a955216ad7ce367bd60ab0285e56005e9ab76f018ff381e97c880" } ] \ No newline at end of file diff --git a/openmls/tests/book_code.rs b/openmls/tests/book_code.rs index 91b457785a..edaec7335c 100644 --- a/openmls/tests/book_code.rs +++ b/openmls/tests/book_code.rs @@ -19,20 +19,7 @@ fn create_backend_rust_crypto() { let _backend = backend; } -#[cfg(feature = "evercrypt")] -#[test] -fn create_backend_evercrypt() { - // ANCHOR: create_backend_evercrypt - use openmls_evercrypt::OpenMlsEvercrypt; - - let backend = OpenMlsEvercrypt::default(); - // ANCHOR_END: create_backend_evercrypt - - // Suppress warning. - let _backend = backend; -} - -fn generate_credential( +async fn generate_credential( identity: Vec, credential_type: CredentialType, signature_algorithm: SignatureScheme, @@ -42,8 +29,12 @@ fn generate_credential( let credential = Credential::new(identity, credential_type).unwrap(); // ANCHOR_END: create_basic_credential // ANCHOR: create_credential_keys - let signature_keys = SignatureKeyPair::new(signature_algorithm).unwrap(); - signature_keys.store(backend.key_store()).unwrap(); + let signature_keys = SignatureKeyPair::new( + signature_algorithm, + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); + signature_keys.store(backend.key_store()).await.unwrap(); // ANCHOR_END: create_credential_keys ( @@ -55,7 +46,7 @@ fn generate_credential( ) } -fn generate_key_package( +async fn generate_key_package( ciphersuite: Ciphersuite, credential_with_key: CredentialWithKey, extensions: Extensions, @@ -72,6 +63,7 @@ fn generate_key_package( signer, credential_with_key, ) + .await .unwrap() // ANCHOR_END: create_key_package } @@ -91,35 +83,39 @@ fn generate_key_package( /// - Bob leaves /// - Test saving the group state #[apply(ciphersuites_and_backends)] -fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Generate credentials with keys let (alice_credential, alice_signature_keys) = generate_credential( "Alice".into(), CredentialType::Basic, ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let (bob_credential, bob_signature_keys) = generate_credential( "Bob".into(), CredentialType::Basic, ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let (charlie_credential, charlie_signature_keys) = generate_credential( "Charlie".into(), CredentialType::Basic, ciphersuite.signature_algorithm(), backend, - ); + ) + .await; let (dave_credential, dave_signature_keys) = generate_credential( "Dave".into(), CredentialType::Basic, ciphersuite.signature_algorithm(), backend, - ); + ) + .await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -128,7 +124,8 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide Extensions::default(), backend, &bob_signature_keys, - ); + ) + .await; // Define the MlsGroup configuration // delivery service credentials @@ -137,7 +134,8 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide CredentialType::Basic, ciphersuite.signature_algorithm(), backend, - ); + ) + .await; // ANCHOR: mls_group_config_example let mls_group_config = MlsGroupConfig::builder() @@ -162,6 +160,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide &mls_group_config, alice_credential.clone(), ) + .await .expect("An unexpected error occurred."); // ANCHOR_END: alice_create_group @@ -177,6 +176,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide group_id, alice_credential.clone(), ) + .await .expect("An unexpected error occurred."); // ANCHOR_END: alice_create_group_with_group_id @@ -190,6 +190,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // ANCHOR: alice_adds_bob let (mls_message_out, welcome, group_info) = alice_group .add_members(backend, &alice_signature_keys, &[bob_key_package]) + .await .expect("Could not add members."); // ANCHOR_END: alice_adds_bob @@ -219,6 +220,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Check that the group now has two members @@ -236,6 +238,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide welcome.into_welcome().expect("Unexpected message type."), None, // We use the ratchet tree extension, so we don't provide a ratchet tree here ) + .await .expect("Error joining group from Welcome"); // ANCHOR_END: bob_joins_with_welcome @@ -257,9 +260,11 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide &[], dave_credential, ) + .await .expect("Error joining from external commit"); dave_group .merge_pending_commit(backend) + .await .expect("Cannot merge commit"); // ANCHOR_END: charlie_joins_external_commit @@ -295,6 +300,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide let protocol_message: ProtocolMessage = mls_message.into(); let processed_message = bob_group .process_message(backend, protocol_message) + .await .expect("Could not process message."); // ANCHOR_END: process_message @@ -315,6 +321,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // ANCHOR: self_update let (mls_message_out, welcome_option, _group_info) = bob_group .self_update(backend, &bob_signature_keys) + .await .expect("Could not update own key package."); // ANCHOR_END: self_update @@ -325,6 +332,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct message @@ -334,6 +342,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // Merge staged Commit alice_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging staged commit."); } else { unreachable!("Expected a StagedCommit."); @@ -341,6 +350,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide bob_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Check we didn't receive a Welcome message @@ -366,6 +376,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide &alice_signature_keys, None, // We don't provide a leaf node, it will be created on the fly instead ) + .await .expect("Could not create update proposal."); // ANCHOR_END: propose_self_update @@ -376,6 +387,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct proposals @@ -407,6 +419,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // ANCHOR: commit_to_proposals let (mls_message_out, welcome_option, _group_info) = alice_group .commit_to_pending_proposals(backend, &alice_signature_keys) + .await .expect("Could not commit to pending proposals."); // ANCHOR_END: commit_to_proposals @@ -420,6 +433,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct message @@ -428,6 +442,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide { bob_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging staged commit."); } else { unreachable!("Expected a StagedCommit."); @@ -435,6 +450,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Check that both groups have the same state @@ -456,10 +472,12 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide Extensions::default(), backend, &charlie_signature_keys, - ); + ) + .await; let (queued_message, welcome, _group_info) = bob_group .add_members(backend, &bob_signature_keys, &[charlie_key_package]) + .await .unwrap(); let alice_processed_message = alice_group @@ -469,9 +487,11 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); bob_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Merge Commit @@ -480,6 +500,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide { alice_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging staged commit."); } else { unreachable!("Expected a StagedCommit."); @@ -491,6 +512,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide welcome.into_welcome().expect("Unexpected message type."), Some(bob_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // Make sure that all groups have the same public tree @@ -524,6 +546,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); let _bob_processed_message = bob_group .process_message( @@ -532,11 +555,13 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // === Charlie updates and commits === let (queued_message, welcome_option, _group_info) = charlie_group .self_update(backend, &charlie_signature_keys) + .await .unwrap(); let alice_processed_message = alice_group @@ -547,6 +572,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); let bob_processed_message = bob_group .process_message( @@ -555,9 +581,11 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); charlie_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Merge Commit @@ -566,6 +594,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide { alice_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging staged commit."); } else { unreachable!("Expected a StagedCommit."); @@ -577,6 +606,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide { bob_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging staged commit."); } else { unreachable!("Expected a StagedCommit."); @@ -632,6 +662,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // ANCHOR: charlie_removes_bob let (mls_message_out, welcome_option, _group_info) = charlie_group .remove_members(backend, &charlie_signature_keys, &[bob_member.index]) + .await .expect("Could not remove Bob from group."); // ANCHOR_END: charlie_removes_bob @@ -646,6 +677,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that alice can use the member list to check if the message is @@ -657,7 +689,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide }; let sender_credential = alice_processed_message.credential(); - assert!(alice_members.any(|Member { index, .. }| &index == sender_leaf_index)); + assert!(alice_members.any(|Member { index, .. }| index == *sender_leaf_index)); drop(alice_members); assert_eq!(sender_credential, &charlie_credential.credential); @@ -669,10 +701,12 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); let charlies_leaf_index = charlie_group.own_leaf_index(); charlie_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Check that we receive the correct proposal for Alice @@ -698,6 +732,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // Merge staged commit alice_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging staged commit."); } // ANCHOR_END: inspect_staged_commit @@ -734,6 +769,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // Merge staged Commit bob_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging staged commit."); } else { unreachable!("Expected a StagedCommit."); @@ -779,7 +815,8 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide Extensions::default(), backend, &bob_signature_keys, - ); + ) + .await; // Create RemoveProposal and process it // ANCHOR: propose_remove @@ -799,6 +836,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct proposals @@ -847,6 +885,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct proposals @@ -881,6 +920,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // Commit to the proposals and process it let (queued_message, welcome_option, _group_info) = alice_group .commit_to_pending_proposals(backend, &alice_signature_keys) + .await .expect("Could not flush proposals"); let charlie_processed_message = charlie_group @@ -890,11 +930,13 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Merge Commit alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Merge Commit @@ -903,6 +945,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide { charlie_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging staged commit."); } else { unreachable!("Expected a StagedCommit."); @@ -926,6 +969,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // Make sure the group contains two members @@ -957,6 +1001,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Get sender information @@ -1007,6 +1052,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Store proposal @@ -1020,15 +1066,18 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide } // Should fail because you cannot remove yourself from a group - assert_eq!( - bob_group.commit_to_pending_proposals(backend, &bob_signature_keys), + assert!(matches!( + bob_group + .commit_to_pending_proposals(backend, &bob_signature_keys) + .await, Err(CommitToPendingProposalsError::CreateCommitError( CreateCommitError::CannotRemoveSelf )) - ); + )); let (queued_message, _welcome_option, _group_info) = alice_group .commit_to_pending_proposals(backend, &alice_signature_keys) + .await .expect("Could not commit to proposals."); // Check that Bob's group is still active @@ -1057,6 +1106,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide alice_group .merge_pending_commit(backend) + .await .expect("Could not merge Commit."); let bob_processed_message = bob_group @@ -1066,6 +1116,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct proposals @@ -1090,6 +1141,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // Merge staged Commit bob_group .merge_staged_commit(backend, *staged_commit) + .await .expect("Error merging staged commit."); } else { unreachable!("Expected a StagedCommit."); @@ -1114,7 +1166,8 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide Extensions::default(), backend, &bob_signature_keys, - ); + ) + .await; // ANCHOR: external_join_proposal let proposal = JoinProposal::new( @@ -1134,16 +1187,19 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type."), ) + .await .expect("Could not process message."); match alice_processed_message.into_content() { ProcessedMessageContent::ExternalJoinProposalMessage(proposal) => { alice_group.store_pending_proposal(*proposal); let (_commit, welcome, _group_info) = alice_group .commit_to_pending_proposals(backend, &alice_signature_keys) + .await .expect("Could not commit"); assert_eq!(alice_group.members().count(), 1); alice_group .merge_pending_commit(backend) + .await .expect("Could not merge commit"); assert_eq!(alice_group.members().count(), 2); @@ -1156,6 +1212,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .expect("Unexpected message type."), None, ) + .await .expect("Bob could not join the group"); assert_eq!(bob_group.members().count(), 2); } @@ -1194,6 +1251,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .into_protocol_message() .expect("Unexpected message type."), ) + .await .expect("Could not process message."); match alice_processed_message.into_content() { ProcessedMessageContent::ProposalMessage(proposal) => { @@ -1201,9 +1259,11 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide assert_eq!(alice_group.members().count(), 2); alice_group .commit_to_pending_proposals(backend, &alice_signature_keys) + .await .expect("Could not commit"); alice_group .merge_pending_commit(backend) + .await .expect("Could not merge commit"); assert_eq!(alice_group.members().count(), 1); } @@ -1220,16 +1280,19 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide Extensions::default(), backend, &bob_signature_keys, - ); + ) + .await; // Add Bob to the group let (_queued_message, welcome, _group_info) = alice_group .add_members(backend, &alice_signature_keys, &[bob_key_package]) + .await .expect("Could not add Bob"); // Merge Commit alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); let mut bob_group = MlsGroup::new_from_welcome( @@ -1238,6 +1301,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide welcome.into_welcome().expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Could not create group from Welcome"); assert_eq!( @@ -1251,12 +1315,15 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide bob_group .save(backend) + .await .expect("Could not write group state to file"); // Check that the state flag gets reset when saving assert_eq!(bob_group.state_changed(), InnerState::Persisted); - let bob_group = MlsGroup::load(&group_id, backend).expect("Could not load group from file"); + let bob_group = MlsGroup::load(&group_id, backend) + .await + .expect("Could not load group from file"); // Make sure the state is still the same assert_eq!( @@ -1266,7 +1333,7 @@ fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide } #[apply(ciphersuites_and_backends)] -fn test_empty_input_errors(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_empty_input_errors(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); // Generate credentials with keys @@ -1275,7 +1342,8 @@ fn test_empty_input_errors(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypt CredentialType::Basic, ciphersuite.signature_algorithm(), backend, - ); + ) + .await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::test_default(ciphersuite); @@ -1288,20 +1356,23 @@ fn test_empty_input_errors(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypt group_id, alice_credential, ) + .await .expect("An unexpected error occurred."); - assert_eq!( + assert!(matches!( alice_group .add_members(backend, &alice_signature_keys, &[]) + .await .expect_err("No EmptyInputError when trying to pass an empty slice to `add_members`."), AddMembersError::EmptyInput(EmptyInputError::AddMembers) - ); - assert_eq!( + )); + assert!(matches!( alice_group .remove_members(backend, &alice_signature_keys, &[]) + .await .expect_err( "No EmptyInputError when trying to pass an empty slice to `remove_members`." ), RemoveMembersError::EmptyInput(EmptyInputError::RemoveMembers) - ); + )); } diff --git a/openmls/tests/key_store.rs b/openmls/tests/key_store.rs index 7efc5ccdfa..ffb9817d98 100644 --- a/openmls/tests/key_store.rs +++ b/openmls/tests/key_store.rs @@ -3,11 +3,15 @@ use openmls::{prelude::*, test_utils::*, *}; use openmls_basic_credential::SignatureKeyPair; #[apply(ciphersuites_and_backends)] -fn test_store_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_store_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // ANCHOR: key_store_store // First we generate a credential and key package for our user. let credential = Credential::new(b"User ID".to_vec(), CredentialType::Basic).unwrap(); - let signature_keys = SignatureKeyPair::new(ciphersuite.into()).unwrap(); + let signature_keys = SignatureKeyPair::new( + ciphersuite.into(), + &mut *backend.rand().borrow_rand().unwrap(), + ) + .unwrap(); let key_package = KeyPackage::builder() .build( @@ -19,6 +23,7 @@ fn test_store_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto signature_key: signature_keys.to_public_vec().into(), }, ) + .await .unwrap(); // ANCHOR_END: key_store_store @@ -26,6 +31,7 @@ fn test_store_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto // Delete the key package key_package .delete(backend) + .await .expect("Error deleting key package"); // ANCHOR_END: key_store_delete } diff --git a/openmls/tests/test_decryption_key_index.rs b/openmls/tests/test_decryption_key_index.rs index 9d6de463ae..66bb084d12 100644 --- a/openmls/tests/test_decryption_key_index.rs +++ b/openmls/tests/test_decryption_key_index.rs @@ -9,7 +9,7 @@ use openmls::{ }; #[apply(ciphersuites)] -fn decryption_key_index_computation(ciphersuite: Ciphersuite) { +async fn decryption_key_index_computation(ciphersuite: Ciphersuite) { println!("Testing ciphersuite {ciphersuite:?}"); // Some basic setup functions for the MlsGroup. @@ -19,10 +19,12 @@ fn decryption_key_index_computation(ciphersuite: Ciphersuite) { mls_group_config, number_of_clients, CodecUse::StructMessages, - ); + ) + .await; // Create a basic group with more than 4 members to create a tree with intermediate nodes. let group_id = setup .create_random_group(10, ciphersuite) + .await .expect("An unexpected error occurred."); let mut groups = setup.groups.write().expect("An unexpected error occurred."); let group = groups @@ -46,6 +48,7 @@ fn decryption_key_index_computation(ciphersuite: Ciphersuite) { remover_id, &[LeafNodeIndex::new(2)], ) + .await .expect("An unexpected error occurred."); // Then we have the member at index 7 remove the one at index 3. This @@ -65,10 +68,11 @@ fn decryption_key_index_computation(ciphersuite: Ciphersuite) { remover_id, &[LeafNodeIndex::new(3)], ) + .await .expect("An unexpected error occurred."); // Since the decryption failure doesn't cause a panic, but only an error // message in the callback, we also have to check that the group states // match for all group members. - setup.check_group_states(group); + setup.check_group_states(group).await; } diff --git a/openmls/tests/test_external_commit.rs b/openmls/tests/test_external_commit.rs index e357406046..d3edacf221 100644 --- a/openmls/tests/test_external_commit.rs +++ b/openmls/tests/test_external_commit.rs @@ -4,7 +4,7 @@ use openmls::{ }; use openmls_basic_credential::SignatureKeyPair; -fn create_alice_group( +async fn create_alice_group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, use_ratchet_tree_extension: bool, @@ -19,7 +19,8 @@ fn create_alice_group( b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let group = MlsGroup::new( backend, @@ -27,15 +28,16 @@ fn create_alice_group( &group_config, credential_with_key.clone(), ) + .await .expect("An unexpected error occurred."); (group, credential_with_key, signature_keys) } #[apply(ciphersuites_and_backends)] -fn test_external_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_external_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Alice creates a new group ... - let (alice_group, _, alice_signer) = create_alice_group(ciphersuite, backend, false); + let (alice_group, _, alice_signer) = create_alice_group(ciphersuite, backend, false).await; // ... and exports a group info (with ratchet_tree). let verifiable_group_info = { @@ -81,7 +83,8 @@ fn test_external_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr b"Bob", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let (_bob_group, _, _) = MlsGroup::join_by_external_commit( backend, @@ -94,6 +97,7 @@ fn test_external_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr b"", bob_credential, ) + .await .unwrap(); } @@ -104,7 +108,8 @@ fn test_external_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr b"Bob", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let got_error = MlsGroup::join_by_external_commit( backend, @@ -117,6 +122,7 @@ fn test_external_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr b"", bob_credential, ) + .await .unwrap_err(); assert_eq!( @@ -129,13 +135,16 @@ fn test_external_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr } #[apply(ciphersuites_and_backends)] -fn test_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Alice creates a new group ... - let (mut alice_group, _, alice_signer) = create_alice_group(ciphersuite, backend, true); + let (mut alice_group, _, alice_signer) = create_alice_group(ciphersuite, backend, true).await; // Self update Alice's to get a group info from a commit - let (.., group_info) = alice_group.self_update(backend, &alice_signer).unwrap(); - alice_group.merge_pending_commit(backend).unwrap(); + let (.., group_info) = alice_group + .self_update(backend, &alice_signer) + .await + .unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); // Bob wants to join let (bob_credential, bob_signature_keys) = new_credential( @@ -143,7 +152,8 @@ fn test_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide b"Bob", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let verifiable_group_info = { let serialized_group_info = group_info.unwrap().tls_serialize_detached().unwrap(); @@ -161,18 +171,23 @@ fn test_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide b"", bob_credential, ) + .await .map(|(group, msg, group_info)| (group, MlsMessageIn::from(msg), group_info)) .unwrap(); - bob_group.merge_pending_commit(backend).unwrap(); + bob_group.merge_pending_commit(backend).await.unwrap(); // let alice process bob's new client let msg = alice_group .process_message(backend, msg) + .await .unwrap() .into_content(); match msg { ProcessedMessageContent::StagedCommitMessage(commit) => { - alice_group.merge_staged_commit(backend, *commit).unwrap(); + alice_group + .merge_staged_commit(backend, *commit) + .await + .unwrap(); } _ => panic!("Unexpected message type"), } @@ -183,7 +198,7 @@ fn test_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide .unwrap() .into(); - let msg = alice_group.process_message(backend, message).unwrap(); + let msg = alice_group.process_message(backend, message).await.unwrap(); let decrypted = match msg.into_content() { ProcessedMessageContent::ApplicationMessage(msg) => msg.into_bytes(), _ => panic!("Not an ApplicationMessage"), @@ -197,7 +212,8 @@ fn test_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide b"Bob 2", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let verifiable_group_info = { let serialized_group_info = group_info.unwrap().tls_serialize_detached().unwrap(); @@ -214,18 +230,25 @@ fn test_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide b"", bob_credential, ) + .await .unwrap(); - bob_group.merge_pending_commit(backend).unwrap(); + bob_group.merge_pending_commit(backend).await.unwrap(); } #[apply(ciphersuites_and_backends)] -fn test_not_present_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_not_present_group_info( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { // Alice creates a new group ... - let (mut alice_group, _, alice_signer) = create_alice_group(ciphersuite, backend, false); + let (mut alice_group, _, alice_signer) = create_alice_group(ciphersuite, backend, false).await; // Self update Alice's to get a group info from a commit - let (.., group_info) = alice_group.self_update(backend, &alice_signer).unwrap(); - alice_group.merge_pending_commit(backend).unwrap(); + let (.., group_info) = alice_group + .self_update(backend, &alice_signer) + .await + .unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); assert!(group_info.is_none()); } diff --git a/openmls/tests/test_interop_scenarios.rs b/openmls/tests/test_interop_scenarios.rs index d5cef989a5..1084929eff 100644 --- a/openmls/tests/test_interop_scenarios.rs +++ b/openmls/tests/test_interop_scenarios.rs @@ -16,18 +16,20 @@ use openmls::{ // A->B: Welcome // ***: Verify group state #[apply(ciphersuites)] -fn one_to_one_join(ciphersuite: Ciphersuite) { +async fn one_to_one_join(ciphersuite: Ciphersuite) { println!("Testing ciphersuite {ciphersuite:?}"); let number_of_clients = 2; let setup = MlsGroupTestSetup::new( MlsGroupConfig::test_default(ciphersuite), number_of_clients, CodecUse::StructMessages, - ); + ) + .await; // Create a group with a random creator. let group_id = setup .create_group(ciphersuite) + .await .expect("Error while trying to create group."); let mut groups = setup.groups.write().expect("An unexpected error occurred."); let group = groups @@ -46,10 +48,11 @@ fn one_to_one_join(ciphersuite: Ciphersuite) { setup .add_clients(ActionType::Commit, group, &alice_id, bob_id) + .await .expect("Error adding Bob"); // Check that group members agree on a group state. - setup.check_group_states(group); + setup.check_group_states(group).await; } // # 3-party join @@ -61,7 +64,7 @@ fn one_to_one_join(ciphersuite: Ciphersuite) { // A->C: Welcome // ***: Verify group state #[apply(ciphersuites)] -fn three_party_join(ciphersuite: Ciphersuite) { +async fn three_party_join(ciphersuite: Ciphersuite) { println!("Testing ciphersuite {ciphersuite:?}"); let number_of_clients = 3; @@ -69,11 +72,13 @@ fn three_party_join(ciphersuite: Ciphersuite) { MlsGroupConfig::test_default(ciphersuite), number_of_clients, CodecUse::StructMessages, - ); + ) + .await; // Create a group with a random creator. let group_id = setup .create_group(ciphersuite) + .await .expect("Error while trying to create group."); let mut groups = setup.groups.write().expect("An unexpected error occurred."); let group = groups @@ -93,6 +98,7 @@ fn three_party_join(ciphersuite: Ciphersuite) { // Create the add commit and deliver the welcome. setup .add_clients(ActionType::Commit, group, &alice_id, bob_id) + .await .expect("Error adding Bob"); // A vector including Charly's id. @@ -102,10 +108,11 @@ fn three_party_join(ciphersuite: Ciphersuite) { setup .add_clients(ActionType::Commit, group, &alice_id, charly_id) + .await .expect("Error adding Charly"); // Check that group members agree on a group state. - setup.check_group_states(group); + setup.check_group_states(group).await; } // # Multiple joins at once @@ -116,7 +123,7 @@ fn three_party_join(ciphersuite: Ciphersuite) { // A->C: Welcome // ***: Verify group state #[apply(ciphersuites)] -fn multiple_joins(ciphersuite: Ciphersuite) { +async fn multiple_joins(ciphersuite: Ciphersuite) { println!("Testing ciphersuite {ciphersuite:?}"); let number_of_clients = 3; @@ -124,11 +131,13 @@ fn multiple_joins(ciphersuite: Ciphersuite) { MlsGroupConfig::test_default(ciphersuite), number_of_clients, CodecUse::StructMessages, - ); + ) + .await; // Create a group with a random creator. let group_id = setup .create_group(ciphersuite) + .await .expect("Error while trying to create group."); let mut groups = setup.groups.write().expect("An unexpected error occurred."); let group = groups @@ -148,10 +157,11 @@ fn multiple_joins(ciphersuite: Ciphersuite) { // Create the add commit and deliver the welcome. setup .add_clients(ActionType::Commit, group, &alice_id, bob_charly_id) + .await .expect("Error adding Bob and Charly"); // Check that group members agree on a group state. - setup.check_group_states(group); + setup.check_group_states(group).await; } // TODO #192, #286, #289: The external join test should go here. @@ -163,7 +173,7 @@ fn multiple_joins(ciphersuite: Ciphersuite) { // A->B: Update, Commit // ***: Verify group state #[apply(ciphersuites)] -fn update(ciphersuite: Ciphersuite) { +async fn update(ciphersuite: Ciphersuite) { println!("Testing ciphersuite {ciphersuite:?}"); let number_of_clients = 2; @@ -171,11 +181,13 @@ fn update(ciphersuite: Ciphersuite) { MlsGroupConfig::test_default(ciphersuite), number_of_clients, CodecUse::StructMessages, - ); + ) + .await; // Create a group with two members. Includes the process of adding Bob. let group_id = setup .create_random_group(2, ciphersuite) + .await .expect("Error while trying to create group."); let mut groups = setup.groups.write().expect("An unexpected error occurred."); let group = groups @@ -190,10 +202,11 @@ fn update(ciphersuite: Ciphersuite) { // Let Alice create an update with a self-generated KeyPackageBundle. setup .self_update(ActionType::Commit, group, &alice_id, None) + .await .expect("Error self-updating."); // Check that group members agree on a group state. - setup.check_group_states(group); + setup.check_group_states(group).await; } // # Remove @@ -205,7 +218,7 @@ fn update(ciphersuite: Ciphersuite) { // A->B: Remove(B), Commit // ***: Verify group state #[apply(ciphersuites)] -fn remove(ciphersuite: Ciphersuite) { +async fn remove(ciphersuite: Ciphersuite) { println!("Testing ciphersuite {ciphersuite:?}"); let number_of_clients = 2; @@ -213,11 +226,13 @@ fn remove(ciphersuite: Ciphersuite) { MlsGroupConfig::test_default(ciphersuite), number_of_clients, CodecUse::StructMessages, - ); + ) + .await; // Create a group with two members. Includes the process of adding Bob. let group_id = setup .create_random_group(2, ciphersuite) + .await .expect("Error while trying to create group."); let mut groups = setup.groups.write().expect("An unexpected error occurred."); let group = groups @@ -241,10 +256,11 @@ fn remove(ciphersuite: Ciphersuite) { &alice_id, &[LeafNodeIndex::new(bob_index)], ) + .await .expect("Error removing Bob from the group."); // Check that group members agree on a group state. - setup.check_group_states(group); + setup.check_group_states(group).await; } // TODO #141, #284: The external PSK, resumption and re-init tests should go @@ -259,7 +275,7 @@ fn remove(ciphersuite: Ciphersuite) { // * While the group size is >1, a randomly-chosen group member removes a // randomly-chosen other group member #[apply(ciphersuites)] -fn large_group_lifecycle(ciphersuite: Ciphersuite) { +async fn large_group_lifecycle(ciphersuite: Ciphersuite) { println!("Testing ciphersuite {ciphersuite:?}"); // "Large" is 20 for now. @@ -268,13 +284,15 @@ fn large_group_lifecycle(ciphersuite: Ciphersuite) { MlsGroupConfig::test_default(ciphersuite), number_of_clients, CodecUse::StructMessages, - ); + ) + .await; // Create a group with all available clients. The process includes creating // a one-person group and then adding new members in bunches of up to 5, // each bunch by a random group member. let group_id = setup .create_random_group(number_of_clients, ciphersuite) + .await .expect("Error while trying to create group."); let mut groups = setup.groups.write().expect("An unexpected error occurred."); let group = groups @@ -288,6 +306,7 @@ fn large_group_lifecycle(ciphersuite: Ciphersuite) { for (_, member_id) in &group_members { setup .self_update(ActionType::Commit, group, member_id, None) + .await .expect("Error while updating group.") } @@ -305,11 +324,12 @@ fn large_group_lifecycle(ciphersuite: Ciphersuite) { &remover_id.1, &[LeafNodeIndex::new(target_id.0)], ) + .await .expect("Error while removing group member."); group_members = group.members().collect::)>>(); - setup.check_group_states(group); + setup.check_group_states(group).await; } // Check that group members agree on a group state. - setup.check_group_states(group); + setup.check_group_states(group).await; } diff --git a/openmls/tests/test_managed_api.rs b/openmls/tests/test_managed_api.rs index c95b520fda..17635a687d 100644 --- a/openmls/tests/test_managed_api.rs +++ b/openmls/tests/test_managed_api.rs @@ -6,7 +6,7 @@ use openmls::{ }; #[apply(ciphersuites)] -fn test_mls_group_api(ciphersuite: Ciphersuite) { +async fn test_mls_group_api(ciphersuite: Ciphersuite) { // Some basic setup functions for the MlsGroup. let mls_group_config = MlsGroupConfig::test_default(ciphersuite); let number_of_clients = 20; @@ -14,10 +14,12 @@ fn test_mls_group_api(ciphersuite: Ciphersuite) { mls_group_config, number_of_clients, CodecUse::SerializedMessages, - ); + ) + .await; let group_id = setup .create_random_group(3, ciphersuite) + .await .expect("An unexpected error occurred."); let mut groups = setup.groups.write().expect("An unexpected error occurred."); let group = groups @@ -31,6 +33,7 @@ fn test_mls_group_api(ciphersuite: Ciphersuite) { .expect("An unexpected error occurred."); setup .add_clients(ActionType::Commit, group, &adder_id, new_members) + .await .expect("An unexpected error occurred."); // Remove a member @@ -43,8 +46,9 @@ fn test_mls_group_api(ciphersuite: Ciphersuite) { &remover_id, &[LeafNodeIndex::new(target_index)], ) + .await .expect("An unexpected error occurred."); // Check that all group members agree on the same group state. - setup.check_group_states(group); + setup.check_group_states(group).await; } diff --git a/openmls/tests/test_mls_group.rs b/openmls/tests/test_mls_group.rs index bd6df1e1f2..8d8ce03223 100644 --- a/openmls/tests/test_mls_group.rs +++ b/openmls/tests/test_mls_group.rs @@ -6,7 +6,7 @@ use openmls::{ use openmls_traits::{key_store::OpenMlsKeyStore, signatures::Signer, OpenMlsCryptoProvider}; -fn generate_key_package( +async fn generate_key_package( ciphersuite: Ciphersuite, extensions: Extensions, backend: &impl OpenMlsCryptoProvider, @@ -24,6 +24,7 @@ fn generate_key_package( signer, credential_with_key, ) + .await .unwrap() } @@ -42,7 +43,7 @@ fn generate_key_package( /// - Bob leaves /// - Test saving the group state #[apply(ciphersuites_and_backends)] -fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { for wire_format_policy in WIRE_FORMAT_POLICIES.iter() { let group_id = GroupId::from_slice(b"Test Group"); @@ -52,21 +53,24 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let (bob_credential, bob_signer) = new_credential( backend, b"Bob", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let (charlie_credential, charlie_signer) = new_credential( backend, b"Charlie", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -75,7 +79,8 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr backend, bob_credential.clone(), &bob_signer, - ); + ) + .await; // Define the MlsGroup configuration @@ -92,10 +97,14 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr group_id.clone(), alice_credential.clone(), ) + .await .expect("An unexpected error occurred."); // === Alice adds Bob === - let welcome = match alice_group.add_members(backend, &alice_signer, &[bob_key_package]) { + let welcome = match alice_group + .add_members(backend, &alice_signer, &[bob_key_package]) + .await + { Ok((_, welcome, _)) => welcome, Err(e) => panic!("Could not add member to group: {e:?}"), }; @@ -121,6 +130,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Check that the group now has two members @@ -137,6 +147,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr welcome.into_welcome().expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // Make sure that both groups have the same members @@ -162,6 +173,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); let sender = processed_message.credential().clone(); @@ -184,7 +196,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // === Bob updates and commits === let (queued_message, welcome_option, _group_info) = - bob_group.self_update(backend, &bob_signer).unwrap(); + bob_group.self_update(backend, &bob_signer).await.unwrap(); let alice_processed_message = alice_group .process_message( @@ -194,6 +206,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct message @@ -203,6 +216,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // Merge staged Commit alice_group .merge_staged_commit(backend, *staged_commit) + .await .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -210,6 +224,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr bob_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Check we didn't receive a Welcome message @@ -230,6 +245,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // === Alice updates and commits === let (queued_message, _) = alice_group .propose_self_update(backend, &alice_signer, None) + .await .unwrap(); let bob_processed_message = bob_group @@ -240,6 +256,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct proposals @@ -271,6 +288,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr let (queued_message, _welcome_option, _group_info) = alice_group .commit_to_pending_proposals(backend, &alice_signer) + .await .unwrap(); let bob_processed_message = bob_group @@ -281,6 +299,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct message @@ -289,6 +308,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr { bob_group .merge_staged_commit(backend, *staged_commit) + .await .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -296,6 +316,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Check that both groups have the same state @@ -317,10 +338,12 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr backend, charlie_credential, &charlie_signer, - ); + ) + .await; let (queued_message, welcome, _group_info) = bob_group .add_members(backend, &bob_signer, &[charlie_key_package]) + .await .unwrap(); let alice_processed_message = alice_group @@ -331,9 +354,11 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); bob_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Merge Commit @@ -342,6 +367,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr { alice_group .merge_staged_commit(backend, *staged_commit) + .await .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -353,6 +379,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr welcome.into_welcome().expect("Unexpected message type."), Some(bob_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // Make sure that all groups have the same public tree @@ -385,6 +412,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); let _bob_processed_message = bob_group .process_message( @@ -394,11 +422,14 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // === Charlie updates and commits === - let (queued_message, welcome_option, _group_info) = - charlie_group.self_update(backend, &charlie_signer).unwrap(); + let (queued_message, welcome_option, _group_info) = charlie_group + .self_update(backend, &charlie_signer) + .await + .unwrap(); let alice_processed_message = alice_group .process_message( @@ -408,6 +439,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); let bob_processed_message = bob_group .process_message( @@ -417,9 +449,11 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); charlie_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Merge Commit @@ -428,6 +462,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr { alice_group .merge_staged_commit(backend, *staged_commit) + .await .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -439,6 +474,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr { bob_group .merge_staged_commit(backend, *staged_commit) + .await .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -471,6 +507,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr println!(" >>> Charlie is removing bob"); let (queued_message, welcome_option, _group_info) = charlie_group .remove_members(backend, &charlie_signer, &[bob_group.own_leaf_index()]) + .await .expect("Could not remove member from group."); // Check that Bob's group is still active @@ -484,6 +521,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); let bob_processed_message = bob_group .process_message( @@ -493,9 +531,11 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); charlie_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Check that we receive the correct proposal for Alice @@ -516,6 +556,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // Merge staged Commit alice_group .merge_staged_commit(backend, *staged_commit) + .await .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -539,6 +580,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // Merge staged Commit bob_group .merge_staged_commit(backend, *staged_commit) + .await .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -578,7 +620,8 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr backend, bob_credential.clone(), &bob_signer, - ); + ) + .await; // Create RemoveProposal and process it let (queued_message, _) = alice_group @@ -593,6 +636,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct proposals @@ -630,6 +674,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct proposals @@ -660,6 +705,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // Commit to the proposals and process it let (queued_message, welcome_option, _group_info) = alice_group .commit_to_pending_proposals(backend, &alice_signer) + .await .expect("Could not flush proposals"); let charlie_processed_message = charlie_group @@ -670,11 +716,13 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Merge Commit alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); // Merge Commit @@ -683,6 +731,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr { charlie_group .merge_staged_commit(backend, *staged_commit) + .await .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -706,6 +755,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Error creating group from Welcome"); // Make sure the group contains two members @@ -738,6 +788,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); let sender = bob_processed_message.credential().clone(); @@ -770,6 +821,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Store proposal @@ -783,15 +835,18 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr } // Should fail because you cannot remove yourself from a group - assert_eq!( - bob_group.commit_to_pending_proposals(backend, &bob_signer), + assert!(matches!( + bob_group + .commit_to_pending_proposals(backend, &bob_signer) + .await, Err(CommitToPendingProposalsError::CreateCommitError( CreateCommitError::CannotRemoveSelf )) - ); + )); let (queued_message, _welcome_option, _group_info) = alice_group .commit_to_pending_proposals(backend, &alice_signer) + .await .expect("Could not commit to proposals."); // Check that Bob's group is still active @@ -816,6 +871,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr alice_group .merge_pending_commit(backend) + .await .expect("Could not merge Commit."); let bob_processed_message = bob_group @@ -826,6 +882,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .into_protocol_message() .expect("Unexpected message type"), ) + .await .expect("Could not process message."); // Check that we received the correct proposals @@ -845,6 +902,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // Merge staged Commit bob_group .merge_staged_commit(backend, *staged_commit) + .await .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -869,24 +927,29 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr backend, bob_credential, &bob_signer, - ); + ) + .await; // Add Bob to the group let (_queued_message, welcome, _group_info) = alice_group .add_members(backend, &alice_signer, &[bob_key_package]) + .await .expect("Could not add Bob"); // Test saving & loading the group state when there is a pending commit alice_group .save(backend) + .await .expect("Could not save group state."); - let _test_group = - MlsGroup::load(&group_id, backend).expect("Could not load the group state."); + let _test_group = MlsGroup::load(&group_id, backend) + .await + .expect("Could not load the group state."); // Merge Commit alice_group .merge_pending_commit(backend) + .await .expect("error merging pending commit"); let mut bob_group = MlsGroup::new_from_welcome( @@ -895,6 +958,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr welcome.into_welcome().expect("Unexpected message type."), Some(alice_group.export_ratchet_tree().into()), ) + .await .expect("Could not create group from Welcome"); assert_eq!( @@ -907,12 +971,15 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr bob_group .save(backend) + .await .expect("Could not write group state to file"); // Check that the state flag gets reset when saving assert_eq!(bob_group.state_changed(), InnerState::Persisted); - let bob_group = MlsGroup::load(&group_id, backend).expect("Could not load group from file"); + let bob_group = MlsGroup::load(&group_id, backend) + .await + .expect("Could not load group from file"); // Make sure the state is still the same assert_eq!( @@ -923,7 +990,7 @@ fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr } #[apply(ciphersuites_and_backends)] -fn test_empty_input_errors(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_empty_input_errors(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); // Generate credentials with keys @@ -932,7 +999,8 @@ fn test_empty_input_errors(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypt b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::test_default(ciphersuite); @@ -945,27 +1013,30 @@ fn test_empty_input_errors(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypt group_id, alice_credential, ) + .await .expect("An unexpected error occurred."); - assert_eq!( + assert!(matches!( alice_group .add_members(backend, &alice_signer, &[]) + .await .expect_err("No EmptyInputError when trying to pass an empty slice to `add_members`."), AddMembersError::EmptyInput(EmptyInputError::AddMembers) - ); - assert_eq!( + )); + assert!(matches!( alice_group .remove_members(backend, &alice_signer, &[]) + .await .expect_err( "No EmptyInputError when trying to pass an empty slice to `remove_members`." ), RemoveMembersError::EmptyInput(EmptyInputError::RemoveMembers) - ); + )); } // This tests the ratchet tree extension usage flag in the configuration #[apply(ciphersuites_and_backends)] -fn mls_group_ratchet_tree_extension( +async fn mls_group_ratchet_tree_extension( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { @@ -980,14 +1051,16 @@ fn mls_group_ratchet_tree_extension( b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let (bob_credential, bob_signer) = new_credential( backend, b"Bob", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -996,7 +1069,8 @@ fn mls_group_ratchet_tree_extension( backend, bob_credential, &bob_signer, - ); + ) + .await; let mls_group_config = MlsGroupConfig::builder() .wire_format_policy(*wire_format_policy) @@ -1012,11 +1086,13 @@ fn mls_group_ratchet_tree_extension( group_id.clone(), alice_credential.clone(), ) + .await .expect("An unexpected error occurred."); // === Alice adds Bob === let (_queued_message, welcome, _group_info) = alice_group .add_members(backend, &alice_signer, &[bob_key_package.clone()]) + .await .unwrap(); // === Bob joins using the ratchet tree extension === @@ -1026,6 +1102,7 @@ fn mls_group_ratchet_tree_extension( welcome.into_welcome().expect("Unexpected message type."), None, ) + .await .expect("Error creating group from Welcome"); // === Negative case: not using the ratchet tree extension === @@ -1036,14 +1113,16 @@ fn mls_group_ratchet_tree_extension( b"Alice", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; let (bob_credential, bob_signer) = new_credential( backend, b"Bob", CredentialType::Basic, ciphersuite.signature_algorithm(), - ); + ) + .await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -1052,7 +1131,8 @@ fn mls_group_ratchet_tree_extension( backend, bob_credential, &bob_signer, - ); + ) + .await; let mls_group_config = MlsGroupConfig::test_default(ciphersuite); @@ -1064,11 +1144,13 @@ fn mls_group_ratchet_tree_extension( group_id, alice_credential.clone(), ) + .await .expect("An unexpected error occurred."); // === Alice adds Bob === let (_queued_message, welcome, _group_info) = alice_group .add_members(backend, &alice_signer, &[bob_key_package]) + .await .unwrap(); // === Bob tries to join without the ratchet tree extension === @@ -1078,8 +1160,9 @@ fn mls_group_ratchet_tree_extension( welcome.into_welcome().expect("Unexpected message type."), None, ) + .await .expect_err("Could join a group without a ratchet tree"); - assert_eq!(error, WelcomeError::MissingRatchetTree); + assert!(matches!(error, WelcomeError::MissingRatchetTree)); } } diff --git a/openmls_rust_crypto/Cargo.toml b/openmls_rust_crypto/Cargo.toml index 8294aeec55..506eac8760 100644 --- a/openmls_rust_crypto/Cargo.toml +++ b/openmls_rust_crypto/Cargo.toml @@ -2,7 +2,7 @@ name = "openmls_rust_crypto" authors = ["OpenMLS Authors"] version = "0.1.0" -edition = "2018" +edition = "2021" description = "A crypto backend for OpenMLS implementing openmls_traits using RustCrypto primitives." license = "MIT" documentation = "https://docs.rs/openmls_rust_crypto" @@ -17,14 +17,18 @@ sha2 = { version = "0.10" } aes-gcm = { version = "0.9" } chacha20poly1305 = { version = "0.9" } hmac = { version = "0.12" } -ed25519-dalek = { version = "1.0" } -rand-07 = {version = "0.7", package = "rand" } # only needed because of ed25519-dalek -p256 = { version = "0.11" } +ed25519-dalek = { version = "2.0.0-rc.2", features = ["rand_core"] } +p256 = { version = "0.13" } +p384 = { version = "0.13" } hkdf = { version = "0.12" } -rand = "0.8" +rand_core = "0.6" rand_chacha = { version = "0.3" } -hpke = { version = "0.1.0", package = "hpke-rs", default-features = false, features = ["hazmat", "serialization"] } -hpke-rs-crypto = { version = "0.1.1" } -hpke-rs-rust-crypto = { version = "0.1.1" } tls_codec = { workspace = true } +zeroize = { version = "1.6", features = ["derive"] } +signature = "2.1" thiserror = "1.0" + +[dependencies.hpke] +git = "https://github.com/rozbb/rust-hpke.git" +rev = "2867b0ae90a36f27e2c312fe741f268ad558abbd" +features = ["x25519", "p256", "p384", "serde_impls"] diff --git a/openmls_rust_crypto/src/provider.rs b/openmls_rust_crypto/src/provider.rs index 1905dc5255..5c9cd37f5c 100644 --- a/openmls_rust_crypto/src/provider.rs +++ b/openmls_rust_crypto/src/provider.rs @@ -1,3 +1,4 @@ +use rand_core::{RngCore, SeedableRng}; use std::sync::RwLock; use aes_gcm::{ @@ -5,29 +6,61 @@ use aes_gcm::{ Aes128Gcm, Aes256Gcm, NewAead, }; use chacha20poly1305::ChaCha20Poly1305; -// See https://github.com/rust-analyzer/rust-analyzer/issues/7243 -// for the rust-analyzer issue with the following line. -use ed25519_dalek::Signer as DalekSigner; use hkdf::Hkdf; -use hpke::Hpke; -use hpke_rs_crypto::types as hpke_types; -use hpke_rs_rust_crypto::HpkeRustCrypto; use openmls_traits::{ crypto::OpenMlsCrypto, random::OpenMlsRand, types::{ self, AeadType, Ciphersuite, CryptoError, ExporterSecret, HashType, HpkeAeadType, - HpkeCiphertext, HpkeConfig, HpkeKdfType, HpkeKemType, HpkeKeyPair, SignatureScheme, + HpkeConfig, HpkeKdfType, HpkeKemType, SignatureScheme, }, }; -use p256::{ - ecdsa::{signature::Verifier, Signature, SigningKey, VerifyingKey}, - EncodedPoint, -}; -use rand::{RngCore, SeedableRng}; use sha2::{Digest, Sha256, Sha384, Sha512}; use tls_codec::SecretVLBytes; +/// 32-byte raw entropy seed +pub type RawEntropySeed = ::Seed; + +#[derive(Debug, Clone, Default, PartialEq, Eq, zeroize::ZeroizeOnDrop)] +#[repr(transparent)] +/// Wrapped 32-byte entropy seed with bounds check +pub struct EntropySeed(RawEntropySeed); + +impl EntropySeed { + pub const EXPECTED_LEN: usize = std::mem::size_of::() / std::mem::size_of::(); + + pub fn try_from_slice(data: &[u8]) -> Result { + if data.len() < Self::EXPECTED_LEN { + return Err(RandError::EntropySeedLengthError { + actual: data.len(), + expected: Self::EXPECTED_LEN, + }); + } + + let mut inner = RawEntropySeed::default(); + inner.copy_from_slice(&data[..Self::EXPECTED_LEN]); + + Ok(Self(inner)) + } + + pub fn from_raw(raw: RawEntropySeed) -> Self { + Self(raw) + } +} + +impl std::ops::Deref for EntropySeed { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for EntropySeed { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + #[derive(Debug)] pub struct RustCrypto { rng: RwLock, @@ -41,33 +74,11 @@ impl Default for RustCrypto { } } -#[inline(always)] -fn kem_mode(kem: HpkeKemType) -> hpke_types::KemAlgorithm { - match kem { - HpkeKemType::DhKemP256 => hpke_types::KemAlgorithm::DhKemP256, - HpkeKemType::DhKemP384 => hpke_types::KemAlgorithm::DhKemP384, - HpkeKemType::DhKemP521 => hpke_types::KemAlgorithm::DhKemP521, - HpkeKemType::DhKem25519 => hpke_types::KemAlgorithm::DhKem25519, - HpkeKemType::DhKem448 => hpke_types::KemAlgorithm::DhKem448, - } -} - -#[inline(always)] -fn kdf_mode(kdf: HpkeKdfType) -> hpke_types::KdfAlgorithm { - match kdf { - HpkeKdfType::HkdfSha256 => hpke_types::KdfAlgorithm::HkdfSha256, - HpkeKdfType::HkdfSha384 => hpke_types::KdfAlgorithm::HkdfSha384, - HpkeKdfType::HkdfSha512 => hpke_types::KdfAlgorithm::HkdfSha512, - } -} - -#[inline(always)] -fn aead_mode(aead: HpkeAeadType) -> hpke_types::AeadAlgorithm { - match aead { - HpkeAeadType::AesGcm128 => hpke_types::AeadAlgorithm::Aes128Gcm, - HpkeAeadType::AesGcm256 => hpke_types::AeadAlgorithm::Aes256Gcm, - HpkeAeadType::ChaCha20Poly1305 => hpke_types::AeadAlgorithm::ChaCha20Poly1305, - HpkeAeadType::Export => hpke_types::AeadAlgorithm::HpkeExport, +impl RustCrypto { + pub fn new_with_seed(seed: EntropySeed) -> Self { + Self { + rng: rand_chacha::ChaCha20Rng::from_seed(seed.0).into(), + } } } @@ -76,7 +87,8 @@ impl OpenMlsCrypto for RustCrypto { match ciphersuite { Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 - | Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 => Ok(()), + | Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 + | Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => Ok(()), _ => Err(CryptoError::UnsupportedCiphersuite), } } @@ -86,6 +98,7 @@ impl OpenMlsCrypto for RustCrypto { Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, + Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, ] } @@ -113,25 +126,31 @@ impl OpenMlsCrypto for RustCrypto { HashType::Sha2_256 => { let hkdf = Hkdf::::from_prk(prk) .map_err(|_| CryptoError::HkdfOutputLengthInvalid)?; + let mut okm = vec![0u8; okm_len]; hkdf.expand(info, &mut okm) .map_err(|_| CryptoError::HkdfOutputLengthInvalid)?; + Ok(okm.into()) } HashType::Sha2_512 => { let hkdf = Hkdf::::from_prk(prk) .map_err(|_| CryptoError::HkdfOutputLengthInvalid)?; + let mut okm = vec![0u8; okm_len]; hkdf.expand(info, &mut okm) .map_err(|_| CryptoError::HkdfOutputLengthInvalid)?; + Ok(okm.into()) } HashType::Sha2_384 => { let hkdf = Hkdf::::from_prk(prk) .map_err(|_| CryptoError::HkdfOutputLengthInvalid)?; + let mut okm = vec![0u8; okm_len]; hkdf.expand(info, &mut okm) .map_err(|_| CryptoError::HkdfOutputLengthInvalid)?; + Ok(okm.into()) } } @@ -161,24 +180,27 @@ impl OpenMlsCrypto for RustCrypto { AeadType::Aes128Gcm => { let aes = Aes128Gcm::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?; + aes.encrypt(nonce.into(), Payload { msg: data, aad }) .map(|r| r.as_slice().into()) - .map_err(|_| CryptoError::CryptoLibraryError) + .map_err(|_| CryptoError::AeadEncryptionError) } AeadType::Aes256Gcm => { let aes = - Aes256Gcm::new_from_slice(key).map_err(|_| CryptoError::CryptoLibraryError)?; + Aes256Gcm::new_from_slice(key).map_err(|_| CryptoError::AeadEncryptionError)?; + aes.encrypt(nonce.into(), Payload { msg: data, aad }) .map(|r| r.as_slice().into()) - .map_err(|_| CryptoError::CryptoLibraryError) + .map_err(|_| CryptoError::AeadEncryptionError) } AeadType::ChaCha20Poly1305 => { let chacha_poly = ChaCha20Poly1305::new_from_slice(key) - .map_err(|_| CryptoError::CryptoLibraryError)?; + .map_err(|_| CryptoError::AeadEncryptionError)?; + chacha_poly .encrypt(nonce.into(), Payload { msg: data, aad }) .map(|r| r.as_slice().into()) - .map_err(|_| CryptoError::CryptoLibraryError) + .map_err(|_| CryptoError::AeadEncryptionError) } } } @@ -221,23 +243,30 @@ impl OpenMlsCrypto for RustCrypto { &self, alg: openmls_traits::types::SignatureScheme, ) -> Result<(Vec, Vec), openmls_traits::types::CryptoError> { + let mut rng = self + .rng + .write() + .map_err(|_| CryptoError::InsufficientRandomness)?; + match alg { SignatureScheme::ECDSA_SECP256R1_SHA256 => { + let sk = p256::ecdsa::SigningKey::random(&mut *rng); + let pk = sk.verifying_key().to_encoded_point(false).to_bytes().into(); + Ok((sk.to_bytes().to_vec().into(), pk)) + } + SignatureScheme::ECDSA_SECP384R1_SHA384 => { + let sk = p384::ecdsa::SigningKey::random(&mut *rng); + let pk = sk.verifying_key().to_encoded_point(false).to_bytes().into(); + Ok((sk.to_bytes().to_vec().into(), pk)) + } + SignatureScheme::ED25519 => { let mut rng = self .rng .write() .map_err(|_| CryptoError::InsufficientRandomness)?; - let k = SigningKey::random(&mut *rng); - let pk = k.verifying_key().to_encoded_point(false).as_bytes().into(); - Ok((k.to_bytes().as_slice().into(), pk)) - } - SignatureScheme::ED25519 => { - // XXX: We can't use our RNG here - let k = ed25519_dalek::Keypair::generate(&mut rand_07::rngs::OsRng).to_bytes(); - let pk = k[ed25519_dalek::SECRET_KEY_LENGTH..].to_vec(); - // full key here because we need it to sign... - let sk_pk = k.into(); - Ok((sk_pk, pk)) + let k = ed25519_dalek::SigningKey::generate(&mut *rng); + let pk = k.verifying_key(); + Ok((k.to_bytes().into(), pk.to_bytes().into())) } _ => Err(CryptoError::UnsupportedSignatureScheme), } @@ -250,24 +279,36 @@ impl OpenMlsCrypto for RustCrypto { pk: &[u8], signature: &[u8], ) -> Result<(), openmls_traits::types::CryptoError> { + use signature::Verifier as _; match alg { SignatureScheme::ECDSA_SECP256R1_SHA256 => { - let k = VerifyingKey::from_encoded_point( - &EncodedPoint::from_bytes(pk).map_err(|_| CryptoError::CryptoLibraryError)?, - ) - .map_err(|_| CryptoError::CryptoLibraryError)?; - k.verify( - data, - &Signature::from_der(signature).map_err(|_| CryptoError::InvalidSignature)?, - ) - .map_err(|_| CryptoError::InvalidSignature) + let k = p256::ecdsa::VerifyingKey::from_sec1_bytes(pk) + .map_err(|_| CryptoError::CryptoLibraryError)?; + + let signature = p256::ecdsa::DerSignature::from_bytes(signature) + .map_err(|_| CryptoError::InvalidSignature)?; + + k.verify(data, &signature) + .map_err(|_| CryptoError::InvalidSignature) + } + SignatureScheme::ECDSA_SECP384R1_SHA384 => { + let k = p384::ecdsa::VerifyingKey::from_sec1_bytes(pk) + .map_err(|_| CryptoError::CryptoLibraryError)?; + + let signature = p384::ecdsa::DerSignature::from_bytes(signature) + .map_err(|_| CryptoError::InvalidSignature)?; + + k.verify(data, &signature) + .map_err(|_| CryptoError::InvalidSignature) } SignatureScheme::ED25519 => { - let k = ed25519_dalek::PublicKey::from_bytes(pk) + let k = ed25519_dalek::VerifyingKey::try_from(pk) .map_err(|_| CryptoError::CryptoLibraryError)?; + if signature.len() != ed25519_dalek::SIGNATURE_LENGTH { - return Err(CryptoError::CryptoLibraryError); + return Err(CryptoError::InvalidSignature); } + let mut sig = [0u8; ed25519_dalek::SIGNATURE_LENGTH]; sig.clone_from_slice(signature); k.verify_strict(data, &ed25519_dalek::Signature::from(sig)) @@ -283,16 +324,45 @@ impl OpenMlsCrypto for RustCrypto { data: &[u8], key: &[u8], ) -> Result, openmls_traits::types::CryptoError> { + use signature::Signer as _; + match alg { SignatureScheme::ECDSA_SECP256R1_SHA256 => { - let k = SigningKey::from_bytes(key).map_err(|_| CryptoError::CryptoLibraryError)?; - let signature = k.sign(data); - Ok(signature.to_der().to_bytes().into()) + let k = p256::ecdsa::SigningKey::from_bytes(key.into()) + .map_err(|_| CryptoError::CryptoLibraryError)?; + let signature: p256::ecdsa::DerSignature = k + .try_sign(data) + .map_err(|_| CryptoError::CryptoLibraryError)?; + Ok(signature.to_bytes().into()) + } + SignatureScheme::ECDSA_SECP384R1_SHA384 => { + let k = p384::ecdsa::SigningKey::from_bytes(key.into()) + .map_err(|_| CryptoError::CryptoLibraryError)?; + let signature: p384::ecdsa::DerSignature = k + .try_sign(data) + .map_err(|_| CryptoError::CryptoLibraryError)?; + Ok(signature.to_bytes().into()) } SignatureScheme::ED25519 => { - let k = ed25519_dalek::Keypair::from_bytes(key) + let k = match key.len() { + // Compat layer for legacy keypairs [seed, pk] + ed25519_dalek::KEYPAIR_LENGTH => { + let mut sk = zeroize::Zeroizing::new([0u8; ed25519_dalek::KEYPAIR_LENGTH]); + sk.copy_from_slice(key); + ed25519_dalek::SigningKey::from_keypair_bytes(&sk) + .map_err(|_| CryptoError::CryptoLibraryError)? + } + ed25519_dalek::SECRET_KEY_LENGTH => { + let mut sk = + zeroize::Zeroizing::new([0u8; ed25519_dalek::SECRET_KEY_LENGTH]); + sk.copy_from_slice(key); + ed25519_dalek::SigningKey::from_bytes(&sk) + } + _ => return Err(CryptoError::CryptoLibraryError), + }; + let signature = k + .try_sign(data) .map_err(|_| CryptoError::CryptoLibraryError)?; - let signature = k.sign(data); Ok(signature.to_bytes().into()) } _ => Err(CryptoError::UnsupportedSignatureScheme), @@ -306,13 +376,50 @@ impl OpenMlsCrypto for RustCrypto { info: &[u8], aad: &[u8], ptxt: &[u8], - ) -> types::HpkeCiphertext { - let (kem_output, ciphertext) = hpke_from_config(config) - .seal(&pk_r.into(), info, aad, ptxt, None, None, None) - .unwrap(); - HpkeCiphertext { - kem_output: kem_output.into(), - ciphertext: ciphertext.into(), + ) -> Result { + let mut rng = self + .rng + .write() + .map_err(|_| CryptoError::InsufficientRandomness)?; + + match config { + HpkeConfig( + HpkeKemType::DhKem25519, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_seal::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::X25519HkdfSha256, + >(pk_r, info, aad, ptxt, &mut *rng), + HpkeConfig( + HpkeKemType::DhKem25519, + HpkeKdfType::HkdfSha256, + HpkeAeadType::ChaCha20Poly1305, + ) => hpke_core::hpke_seal::< + hpke::aead::ChaCha20Poly1305, + hpke::kdf::HkdfSha256, + hpke::kem::X25519HkdfSha256, + >(pk_r, info, aad, ptxt, &mut *rng), + HpkeConfig( + HpkeKemType::DhKemP256, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_seal::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::DhP256HkdfSha256, + >(pk_r, info, aad, ptxt, &mut *rng), + HpkeConfig( + HpkeKemType::DhKemP384, + HpkeKdfType::HkdfSha384, + HpkeAeadType::AesGcm256, + ) => hpke_core::hpke_seal::< + hpke::aead::AesGcm256, + hpke::kdf::HkdfSha384, + hpke::kem::DhP384HkdfSha384, + >(pk_r, info, aad, ptxt, &mut *rng), + _ => return Err(CryptoError::UnsupportedKem), } } @@ -324,18 +431,71 @@ impl OpenMlsCrypto for RustCrypto { info: &[u8], aad: &[u8], ) -> Result, CryptoError> { - hpke_from_config(config) - .open( + let plaintext = match config { + HpkeConfig( + HpkeKemType::DhKem25519, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_open::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::X25519HkdfSha256, + >( + sk_r, input.kem_output.as_slice(), - &sk_r.into(), info, aad, input.ciphertext.as_slice(), - None, - None, - None, - ) - .map_err(|_| CryptoError::HpkeDecryptionError) + )?, + HpkeConfig( + HpkeKemType::DhKem25519, + HpkeKdfType::HkdfSha256, + HpkeAeadType::ChaCha20Poly1305, + ) => hpke_core::hpke_open::< + hpke::aead::ChaCha20Poly1305, + hpke::kdf::HkdfSha256, + hpke::kem::X25519HkdfSha256, + >( + sk_r, + input.kem_output.as_slice(), + info, + aad, + input.ciphertext.as_slice(), + )?, + HpkeConfig( + HpkeKemType::DhKemP256, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_open::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::DhP256HkdfSha256, + >( + sk_r, + input.kem_output.as_slice(), + info, + aad, + input.ciphertext.as_slice(), + )?, + HpkeConfig( + HpkeKemType::DhKemP384, + HpkeKdfType::HkdfSha384, + HpkeAeadType::AesGcm256, + ) => hpke_core::hpke_open::< + hpke::aead::AesGcm256, + hpke::kdf::HkdfSha384, + hpke::kem::DhP384HkdfSha384, + >( + sk_r, + input.kem_output.as_slice(), + info, + aad, + input.ciphertext.as_slice(), + )?, + _ => return Err(CryptoError::UnsupportedKem), + }; + + Ok(plaintext) } fn hpke_setup_sender_and_export( @@ -346,13 +506,54 @@ impl OpenMlsCrypto for RustCrypto { exporter_context: &[u8], exporter_length: usize, ) -> Result<(Vec, ExporterSecret), CryptoError> { - let (kem_output, context) = hpke_from_config(config) - .setup_sender(&pk_r.into(), info, None, None, None) - .map_err(|_| CryptoError::SenderSetupError)?; - let exported_secret = context - .export(exporter_context, exporter_length) - .map_err(|_| CryptoError::ExporterError)?; - Ok((kem_output, exported_secret.into())) + let mut rng = self + .rng + .write() + .map_err(|_| CryptoError::InsufficientRandomness)?; + + let (kem_output, export) = match config { + HpkeConfig( + HpkeKemType::DhKem25519, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_export_tx::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::X25519HkdfSha256, + >(pk_r, info, exporter_context, exporter_length, &mut *rng)?, + HpkeConfig( + HpkeKemType::DhKem25519, + HpkeKdfType::HkdfSha256, + HpkeAeadType::ChaCha20Poly1305, + ) => hpke_core::hpke_export_tx::< + hpke::aead::ChaCha20Poly1305, + hpke::kdf::HkdfSha256, + hpke::kem::X25519HkdfSha256, + >(pk_r, info, exporter_context, exporter_length, &mut *rng)?, + HpkeConfig( + HpkeKemType::DhKemP256, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_export_tx::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::DhP256HkdfSha256, + >(pk_r, info, exporter_context, exporter_length, &mut *rng)?, + HpkeConfig( + HpkeKemType::DhKemP384, + HpkeKdfType::HkdfSha384, + HpkeAeadType::AesGcm256, + ) => hpke_core::hpke_export_tx::< + hpke::aead::AesGcm256, + hpke::kdf::HkdfSha384, + hpke::kem::DhP384HkdfSha384, + >(pk_r, info, exporter_context, exporter_length, &mut *rng)?, + _ => return Err(CryptoError::UnsupportedKem), + }; + + debug_assert_eq!(export.len(), exporter_length); + + Ok((kem_output, export.into())) } fn hpke_setup_receiver_and_export( @@ -364,41 +565,203 @@ impl OpenMlsCrypto for RustCrypto { exporter_context: &[u8], exporter_length: usize, ) -> Result { - let context = hpke_from_config(config) - .setup_receiver(enc, &sk_r.into(), info, None, None, None) - .map_err(|_| CryptoError::ReceiverSetupError)?; - let exported_secret = context - .export(exporter_context, exporter_length) - .map_err(|_| CryptoError::ExporterError)?; - Ok(exported_secret.into()) + let export = match config { + HpkeConfig( + HpkeKemType::DhKem25519, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_export_rx::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::X25519HkdfSha256, + >(enc, sk_r, info, exporter_context, exporter_length)?, + HpkeConfig( + HpkeKemType::DhKem25519, + HpkeKdfType::HkdfSha256, + HpkeAeadType::ChaCha20Poly1305, + ) => hpke_core::hpke_export_rx::< + hpke::aead::ChaCha20Poly1305, + hpke::kdf::HkdfSha256, + hpke::kem::X25519HkdfSha256, + >(enc, sk_r, info, exporter_context, exporter_length)?, + HpkeConfig( + HpkeKemType::DhKemP256, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_export_rx::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::DhP256HkdfSha256, + >(enc, sk_r, info, exporter_context, exporter_length)?, + HpkeConfig( + HpkeKemType::DhKemP384, + HpkeKdfType::HkdfSha384, + HpkeAeadType::AesGcm256, + ) => hpke_core::hpke_export_rx::< + hpke::aead::AesGcm256, + hpke::kdf::HkdfSha384, + hpke::kem::DhP384HkdfSha384, + >(enc, sk_r, info, exporter_context, exporter_length)?, + _ => return Err(CryptoError::UnsupportedKem), + }; + + debug_assert_eq!(export.len(), exporter_length); + + Ok(export.into()) } - fn derive_hpke_keypair(&self, config: HpkeConfig, ikm: &[u8]) -> types::HpkeKeyPair { - let kp = hpke_from_config(config) - .derive_key_pair(ikm) - .unwrap() - .into_keys(); - HpkeKeyPair { - private: kp.0.as_slice().into(), - public: kp.1.as_slice().into(), + fn derive_hpke_keypair( + &self, + config: HpkeConfig, + ikm: &[u8], + ) -> Result { + match config.0 { + HpkeKemType::DhKemP256 => { + hpke_core::hpke_derive_keypair::(ikm) + } + HpkeKemType::DhKemP384 => { + hpke_core::hpke_derive_keypair::(ikm) + } + HpkeKemType::DhKem25519 => { + hpke_core::hpke_derive_keypair::(ikm) + } + _ => return Err(CryptoError::UnsupportedKem), } } } -fn hpke_from_config(config: HpkeConfig) -> Hpke { - Hpke::::new( - hpke::Mode::Base, - kem_mode(config.0), - kdf_mode(config.1), - aead_mode(config.2), - ) +mod hpke_core { + use openmls_traits::types::{CryptoError, HpkeCiphertext, HpkeKeyPair}; + + pub fn hpke_open( + private_key: &[u8], + kem_output: &[u8], + info: &[u8], + aad: &[u8], + ciphertext: &[u8], + ) -> Result, CryptoError> { + use hpke::Deserializable as _; + let encapped_key = Kem::EncappedKey::from_bytes(kem_output) + .map_err(|_| CryptoError::HpkeDecryptionError)?; + let key = Kem::PrivateKey::from_bytes(private_key) + .map_err(|_| CryptoError::HpkeDecryptionError)?; + let plaintext = hpke::single_shot_open::( + &hpke::OpModeR::Base, + &key, + &encapped_key, + info, + ciphertext, + aad, + ) + .map_err(|_| CryptoError::HpkeDecryptionError)?; + + Ok(plaintext) + } + + pub fn hpke_seal( + public_key: &[u8], + info: &[u8], + aad: &[u8], + plaintext: &[u8], + csprng: &mut impl rand_core::CryptoRngCore, + ) -> Result { + use hpke::{Deserializable as _, Serializable as _}; + let key = + Kem::PublicKey::from_bytes(public_key).map_err(|_| CryptoError::HpkeEncryptionError)?; + let (encapped, ciphertext) = hpke::single_shot_seal::( + &hpke::OpModeS::Base, + &key, + info, + plaintext, + aad, + csprng, + ) + .map_err(|_| CryptoError::HpkeEncryptionError)?; + + Ok(HpkeCiphertext { + kem_output: encapped.to_bytes().to_vec().into(), + ciphertext: ciphertext.into(), + }) + } + + #[allow(dead_code)] + pub fn hpke_gen_keypair( + csprng: &mut impl rand_core::CryptoRngCore, + ) -> Result { + use hpke::Serializable as _; + let (sk, pk) = Kem::gen_keypair(csprng); + let (private, public) = (sk.to_bytes().to_vec().into(), pk.to_bytes().to_vec()); + + Ok(HpkeKeyPair { private, public }) + } + + pub fn hpke_derive_keypair(ikm: &[u8]) -> Result { + use hpke::Serializable as _; + let (sk, pk) = Kem::derive_keypair(ikm); + let (private, public) = (sk.to_bytes().to_vec().into(), pk.to_bytes().to_vec()); + + Ok(HpkeKeyPair { private, public }) + } + + pub fn hpke_export_rx( + encapped_key: &[u8], + rx_private_key: &[u8], + info: &[u8], + export_info: &[u8], + export_len: usize, + ) -> Result, CryptoError> { + use hpke::Deserializable as _; + let key = Kem::PrivateKey::from_bytes(rx_private_key) + .map_err(|_| CryptoError::ReceiverSetupError)?; + let encapped_key = Kem::EncappedKey::from_bytes(encapped_key) + .map_err(|_| CryptoError::ReceiverSetupError)?; + let ctx = + hpke::setup_receiver::(&hpke::OpModeR::Base, &key, &encapped_key, info) + .map_err(|_| CryptoError::ReceiverSetupError)?; + + let mut export = vec![0u8; export_len]; + + ctx.export(export_info, &mut export) + .map_err(|_| CryptoError::ExporterError)?; + + Ok(export) + } + + pub fn hpke_export_tx( + tx_public_key: &[u8], + info: &[u8], + export_info: &[u8], + export_len: usize, + csprng: &mut impl rand_core::CryptoRngCore, + ) -> Result<(Vec, Vec), CryptoError> { + use hpke::{Deserializable as _, Serializable as _}; + let key = + Kem::PublicKey::from_bytes(tx_public_key).map_err(|_| CryptoError::SenderSetupError)?; + let (kem_output, ctx) = + hpke::setup_sender::(&hpke::OpModeS::Base, &key, info, csprng) + .map_err(|_| CryptoError::SenderSetupError)?; + + let mut export = vec![0u8; export_len]; + + ctx.export(export_info, &mut export) + .map_err(|_| CryptoError::ExporterError)?; + + Ok((kem_output.to_bytes().to_vec().into(), export.into())) + } } impl OpenMlsRand for RustCrypto { type Error = RandError; + type RandImpl = rand_chacha::ChaCha20Rng; + type BorrowTarget<'a> = std::sync::RwLockWriteGuard<'a, Self::RandImpl>; + + fn borrow_rand(&self) -> Result, Self::Error> { + self.rng.write().map_err(|_| Self::Error::LockPoisoned) + } + fn random_array(&self) -> Result<[u8; N], Self::Error> { - let mut rng = self.rng.write().map_err(|_| Self::Error::LockPoisoned)?; + let mut rng = self.borrow_rand()?; let mut out = [0u8; N]; rng.try_fill_bytes(&mut out) .map_err(|_| Self::Error::NotEnoughRandomness)?; @@ -406,7 +769,7 @@ impl OpenMlsRand for RustCrypto { } fn random_vec(&self, len: usize) -> Result, Self::Error> { - let mut rng = self.rng.write().map_err(|_| Self::Error::LockPoisoned)?; + let mut rng = self.borrow_rand()?; let mut out = vec![0u8; len]; rng.try_fill_bytes(&mut out) .map_err(|_| Self::Error::NotEnoughRandomness)?; @@ -420,4 +783,8 @@ pub enum RandError { LockPoisoned, #[error("Unable to collect enough randomness.")] NotEnoughRandomness, + #[error( + "The provided entropy seed has an incorrect length: expected {expected}, found {actual}" + )] + EntropySeedLengthError { actual: usize, expected: usize }, } diff --git a/traits/Cargo.toml b/traits/Cargo.toml index 607aae1141..76a153645c 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -2,7 +2,7 @@ name = "openmls_traits" version = "0.1.0" authors = ["OpenMLS Authors"] -edition = "2018" +edition = "2021" description = "Traits used by OpenMLS" license = "MIT" documentation = "https://docs.rs/openmls_traits" @@ -18,4 +18,6 @@ test-utils = [] [dependencies] serde = { version = "1.0", features = ["derive"] } +rand_core = "0.6" tls_codec = { workspace = true } +async-trait = { workspace = true } diff --git a/traits/src/crypto.rs b/traits/src/crypto.rs index 662bc9411a..c804972517 100644 --- a/traits/src/crypto.rs +++ b/traits/src/crypto.rs @@ -105,7 +105,7 @@ pub trait OpenMlsCrypto { info: &[u8], aad: &[u8], ptxt: &[u8], - ) -> HpkeCiphertext; + ) -> Result; /// HPKE single-shot decryption of `input` with `sk_r`, using `info` and /// `aad`. @@ -144,5 +144,9 @@ pub trait OpenMlsCrypto { ) -> Result; /// Derive a new HPKE keypair from a given input key material. - fn derive_hpke_keypair(&self, config: HpkeConfig, ikm: &[u8]) -> HpkeKeyPair; + fn derive_hpke_keypair( + &self, + config: HpkeConfig, + ikm: &[u8], + ) -> Result; } diff --git a/traits/src/key_store.rs b/traits/src/key_store.rs index e5dee4c943..813e0be3c7 100644 --- a/traits/src/key_store.rs +++ b/traits/src/key_store.rs @@ -1,6 +1,7 @@ //! # OpenMLS Key Store Trait /// Sealed list of struct openmls manages (create/read/delete) through [OpenMlsKeyStore] +#[derive(PartialEq)] pub enum MlsEntityId { SignatureKeyPair, HpkePrivateKey, @@ -15,6 +16,14 @@ pub trait MlsEntity: serde::Serialize + serde::de::DeserializeOwned { /// Identifier used to downcast the actual entity within an [OpenMlsKeyStore] method. /// In case for example you need to select a SQL table depending on the entity type const ID: MlsEntityId; + + fn downcast(&self) -> Option<&T> { + if T::ID == Self::ID { + self.downcast() + } else { + None + } + } } /// Blanket impl for when you have to lookup a list of entities from the keystore @@ -25,16 +34,17 @@ where const ID: MlsEntityId = T::ID; } +#[async_trait::async_trait(?Send)] /// The Key Store trait pub trait OpenMlsKeyStore: Send + Sync { /// The error type returned by the [`OpenMlsKeyStore`]. - type Error: std::error::Error + std::fmt::Debug + PartialEq; + type Error: std::error::Error + std::fmt::Debug; /// Store a value `v` that implements the [`MlsEntity`] trait for /// serialization for ID `k`. /// /// Returns an error if storing fails. - fn store(&self, k: &[u8], v: &V) -> Result<(), Self::Error> + async fn store(&self, k: &[u8], v: &V) -> Result<(), Self::Error> where Self: Sized; @@ -42,12 +52,12 @@ pub trait OpenMlsKeyStore: Send + Sync { /// [`MlsEntity`] trait for deserialization. /// /// Returns [`None`] if no value is stored for `k` or reading fails. - fn read(&self, k: &[u8]) -> Option + async fn read(&self, k: &[u8]) -> Option where Self: Sized; /// Delete a value stored for ID `k`. /// /// Returns an error if storing fails. - fn delete(&self, k: &[u8]) -> Result<(), Self::Error>; + async fn delete(&self, k: &[u8]) -> Result<(), Self::Error>; } diff --git a/traits/src/random.rs b/traits/src/random.rs index 483890a2a1..5b56c69544 100644 --- a/traits/src/random.rs +++ b/traits/src/random.rs @@ -3,10 +3,14 @@ //! The [`OpenMlsRand`] trait defines the functionality required by OpenMLS to //! source randomness. -use std::fmt::Debug; - pub trait OpenMlsRand { - type Error: std::error::Error + Debug + Clone + PartialEq; + type Error: std::error::Error + std::fmt::Debug; + type RandImpl: rand_core::CryptoRngCore; + type BorrowTarget<'a>: std::ops::DerefMut + where + Self: 'a; + + fn borrow_rand(&self) -> Result, Self::Error>; /// Fill an array with random bytes. fn random_array(&self) -> Result<[u8; N], Self::Error>; diff --git a/traits/src/types.rs b/traits/src/types.rs index 4243becda3..e6de9a60fc 100644 --- a/traits/src/types.rs +++ b/traits/src/types.rs @@ -130,6 +130,8 @@ pub enum Error { pub enum CryptoError { CryptoLibraryError, AeadDecryptionError, + AeadEncryptionError, + HpkeEncryptionError, HpkeDecryptionError, UnsupportedSignatureScheme, KdfLabelTooLarge, @@ -139,6 +141,7 @@ pub enum CryptoError { InvalidSignature, UnsupportedAeadAlgorithm, UnsupportedKdf, + UnsupportedKem, InvalidLength, UnsupportedHashAlgorithm, SignatureEncodingError, From 0fd1f8f5143bac8260cf9945fe640af33750ea78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Augusto=20C=C3=A9sar=20Dias?= Date: Wed, 17 May 2023 11:01:22 +0200 Subject: [PATCH 02/54] feat: Implement Group Context Extensions proposal support --- basic_credential/src/lib.rs | 4 +- cli/src/user.rs | 2 +- interop_client/README.md | 2 +- interop_client/src/main.rs | 111 ++- openmls/src/extensions/mod.rs | 5 + openmls/src/group/core_group/mod.rs | 78 +- openmls/src/group/core_group/proposals.rs | 2 +- openmls/src/group/core_group/staged_commit.rs | 12 +- .../src/group/core_group/test_core_group.rs | 33 +- .../src/group/core_group/test_past_secrets.rs | 5 +- .../src/group/core_group/test_proposals.rs | 42 +- openmls/src/group/errors.rs | 33 +- openmls/src/group/group_context.rs | 5 + openmls/src/group/mls_group/config.rs | 24 +- openmls/src/group/mls_group/creation.rs | 7 + openmls/src/group/mls_group/errors.rs | 45 +- openmls/src/group/mls_group/extension.rs | 112 +++ openmls/src/group/mls_group/mod.rs | 1 + openmls/src/group/mls_group/proposal.rs | 31 - openmls/src/group/public_group/builder.rs | 28 +- openmls/src/group/public_group/diff.rs | 5 + .../public_group/diff/apply_proposals.rs | 38 +- .../group/public_group/diff/compute_path.rs | 5 +- .../src/group/public_group/staged_commit.rs | 3 +- openmls/src/group/public_group/validation.rs | 37 +- .../src/group/tests/external_add_proposal.rs | 12 +- .../group/tests/external_remove_proposal.rs | 6 +- openmls/src/group/tests/mod.rs | 4 + .../src/group/tests/test_commit_validation.rs | 15 +- .../tests/test_external_commit_validation.rs | 9 +- .../group/tests/test_framing_validation.rs | 6 +- openmls/src/group/tests/test_gce_proposals.rs | 833 ++++++++++++++++++ openmls/src/group/tests/test_group.rs | 11 +- openmls/src/group/tests/test_past_secrets.rs | 6 +- .../group/tests/test_proposal_validation.rs | 33 +- .../src/group/tests/test_update_extensions.rs | 103 +++ .../group/tests/test_wire_format_policy.rs | 6 +- openmls/src/key_packages/mod.rs | 92 +- openmls/src/lib.rs | 155 ++-- openmls/src/messages/proposals.rs | 18 +- openmls/src/messages/tests/test_codec.rs | 5 +- .../unit_tests/test_sender_ratchet.rs | 5 +- openmls/src/treesync/errors.rs | 17 +- openmls/src/treesync/mod.rs | 7 + openmls/src/treesync/node/encryption_keys.rs | 6 +- openmls/src/treesync/node/leaf_node.rs | 29 +- .../treesync/node/leaf_node/capabilities.rs | 4 +- openmls/src/treesync/tests_and_kats/tests.rs | 3 +- openmls_rust_crypto/src/provider.rs | 10 +- 49 files changed, 1740 insertions(+), 325 deletions(-) create mode 100644 openmls/src/group/mls_group/extension.rs create mode 100644 openmls/src/group/tests/test_gce_proposals.rs create mode 100644 openmls/src/group/tests/test_update_extensions.rs diff --git a/basic_credential/src/lib.rs b/basic_credential/src/lib.rs index 67a64a6fda..31aef06c37 100644 --- a/basic_credential/src/lib.rs +++ b/basic_credential/src/lib.rs @@ -41,7 +41,7 @@ impl Clone for SignatureKeyPair { Self { private: self.private.expose_secret().clone().into(), public: self.public.clone(), - signature_scheme: self.signature_scheme.clone(), + signature_scheme: self.signature_scheme, } } } @@ -220,6 +220,6 @@ impl SignatureKeyPair { #[cfg(feature = "test-utils")] pub fn private(&self) -> &[u8] { - &self.private.expose_secret() + self.private.expose_secret() } } diff --git a/cli/src/user.rs b/cli/src/user.rs index a64c97c0e6..4d80c85acc 100644 --- a/cli/src/user.rs +++ b/cli/src/user.rs @@ -174,7 +174,7 @@ impl User { ProcessedMessageContent::ApplicationMessage(application_message) => { let application_message = String::from_utf8(application_message.into_bytes()).unwrap(); - if group_name.is_none() || group_name.clone().unwrap() == group.group_name { + if group_name.is_none() || group_name.unwrap() == group.group_name { message_ret.replace(application_message.clone()); } group.conversation.add(application_message); diff --git a/interop_client/README.md b/interop_client/README.md index 1a92cf1ac5..6c79996b96 100644 --- a/interop_client/README.md +++ b/interop_client/README.md @@ -93,7 +93,7 @@ USAGE: interop_client [OPTIONS] OPTIONS: - -h, --host [default: [::1]] + -h, --host [default: [0.0.0.0]] --help Print help information -p, --port [default: 50051] ``` diff --git a/interop_client/src/main.rs b/interop_client/src/main.rs index 4feabbe29e..f778ba1d50 100644 --- a/interop_client/src/main.rs +++ b/interop_client/src/main.rs @@ -15,6 +15,11 @@ use mls_interop_proto::mls_client; use openmls::{ ciphersuite::HpkePrivateKey, credentials::{Credential, CredentialType, CredentialWithKey}, + extensions::{ + ApplicationIdExtension, Extension, ExtensionType, Extensions, ExternalPubExtension, + ExternalSendersExtension, RatchetTreeExtension, RequiredCapabilitiesExtension, + UnknownExtension, + }, framing::{MlsMessageIn, MlsMessageInBody, MlsMessageOut, ProcessedMessageContent}, group::{ GroupEpoch, GroupId, MlsGroup, MlsGroupConfig, WireFormatPolicy, @@ -24,6 +29,7 @@ use openmls::{ prelude::{config::CryptoConfig, Capabilities, ExtensionType, SenderRatchetConfiguration}, schedule::{psk::ResumptionPskUsage, ExternalPsk, PreSharedKeyId, Psk}, treesync::{ + node::leaf_node::Capabilities, test_utils::{read_keys_from_key_store, write_keys_from_key_store}, RatchetTreeIn, }, @@ -164,6 +170,49 @@ fn ratchet_tree_from_config(bytes: Vec) -> Option { } } +fn convert_to_extensions( + rpc_extensions: &[mls_interop_proto::mls_client::Extension], +) -> Extensions { + let mut extensions = Vec::new(); + + for ext in rpc_extensions.iter() { + let kind = ExtensionType::try_from(u16::try_from(ext.extension_type).unwrap()).unwrap(); + + let extension = match kind { + ExtensionType::ApplicationId => Extension::ApplicationId( + ApplicationIdExtension::tls_deserialize_exact(&ext.extension_data).unwrap(), + ), + ExtensionType::RatchetTree => Extension::RatchetTree( + RatchetTreeExtension::tls_deserialize_exact(&ext.extension_data) + .unwrap() + .into(), + ), + ExtensionType::RequiredCapabilities => Extension::RequiredCapabilities( + RequiredCapabilitiesExtension::tls_deserialize_exact(&ext.extension_data) + .unwrap() + .into(), + ), + ExtensionType::ExternalPub => Extension::ExternalPub( + ExternalPubExtension::tls_deserialize_exact(&ext.extension_data) + .unwrap() + .into(), + ), + ExtensionType::ExternalSenders => Extension::ExternalSenders( + ExternalSendersExtension::tls_deserialize_exact(&ext.extension_data) + .unwrap() + .into(), + ), + ExtensionType::Unknown(unknown) => { + Extension::Unknown(unknown, UnknownExtension(vec![0, 1, 2, 3, 255])) + } + }; + + extensions.push(extension); + } + + Extensions::try_from(extensions).unwrap() +} + fn bytes_to_string(bytes: B) -> String where B: AsRef<[u8]>, @@ -240,6 +289,13 @@ impl MlsClient for MlsClientImpl { // Note: We just use some values here that make live testing work. // There is nothing special about the used numbers and they // can be increased (or decreased) depending on the available scenarios. + let kp_capabilities = Capabilities::new( + None, + None, + Some(&EXTENSION_TYPES), + None, + Some(&CREDENTIAL_TYPES), + ); let mls_group_config = MlsGroupConfig::builder() .crypto_config(CryptoConfig::with_default_version(ciphersuite)) .max_past_epochs(32) @@ -247,6 +303,7 @@ impl MlsClient for MlsClientImpl { .sender_ratchet_configuration(SenderRatchetConfiguration::default()) .use_ratchet_tree_extension(true) .wire_format_policy(wire_format_policy) + .leaf_capabilities(kp_capabilities) .build(); let group = MlsGroup::new_with_group_id( &backend, @@ -1112,9 +1169,19 @@ impl MlsClient for MlsClientImpl { (msg_out, proposal_ref) } "groupContextExtensions" => { - return Err(Status::internal( - "Unsupported proposal type (group context extension)", - )) + let (msg_out, proposal_ref) = group + .propose_extensions( + &interop_group.crypto_provider, + &interop_group.signature_keys, + convert_to_extensions(&proposal.extensions), + ) + .map_err(|_| Status::internal("Unable to generate proposal by value"))?; + + debug!("GroupContextExtensions proposal created."); + trace!(proposal = ?msg_out); + trace!(proposal_ref = ?proposal_ref); + + (msg_out, proposal_ref) } _ => return Err(Status::invalid_argument("Invalid proposal type")), }; @@ -1443,6 +1510,44 @@ impl MlsClient for MlsClientImpl { )) } + #[instrument(skip_all)] + async fn group_context_extensions_proposal( + &self, + request: Request, + ) -> Result, Status> { + let request = request.get_ref(); + info!(?request, "Request"); + + let mut groups = self.groups.lock().unwrap(); + let interop_group = groups + .get_mut(request.state_id as usize) + .ok_or_else(|| Status::new(Code::InvalidArgument, "unknown state_id"))?; + let group = &mut interop_group.group; + + let extensions = convert_to_extensions(&request.extensions); + + debug!("Created extensions."); + trace!(?extensions); + + let (msg_out, _) = group + .propose_extensions( + &interop_group.crypto_provider, + &interop_group.signature_keys, + extensions, + ) + .unwrap(); + + // Store the proposal for potential future use. + interop_group.messages_out.push(msg_out.clone().into()); + + let response = ProposalResponse { + proposal: msg_out.tls_serialize_detached().unwrap(), + }; + + info!(?response, "Response"); + Ok(Response::new(response)) + } + async fn re_init_commit( &self, _: Request, diff --git a/openmls/src/extensions/mod.rs b/openmls/src/extensions/mod.rs index d5ee4d2b56..8fc165c87f 100644 --- a/openmls/src/extensions/mod.rs +++ b/openmls/src/extensions/mod.rs @@ -244,6 +244,11 @@ impl Extensions { self.unique.iter() } + /// Checks if the extensions list is empty + pub fn is_empty(&self) -> bool { + self.unique.is_empty() + } + /// Add an extension to the extension list. /// /// Returns an error when there already is an extension with the same extension type. diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 0fbc432ee2..8b7e5ae622 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -47,7 +47,7 @@ use super::{ builder::TempBuilderPG1, errors::{ CoreGroupBuildError, CreateAddProposalError, CreateCommitError, ExporterError, - ValidationError, + ProposeGroupContextExtensionError, ValidationError, }, group_context::*, public_group::{diff::compute_path::PathComputationResult, PublicGroup}, @@ -72,7 +72,10 @@ use crate::{ *, }, tree::{secret_tree::SecretTreeError, sender_ratchet::SenderRatchetConfiguration}, - treesync::{node::encryption_keys::EncryptionKeyPair, *}, + treesync::{ + errors::MemberExtensionValidationError, node::encryption_keys::EncryptionKeyPair, + node::leaf_node::Capabilities, *, + }, versions::ProtocolVersion, }; @@ -195,6 +198,20 @@ impl CoreGroupBuilder { } self } + /// Set the [`Extensions`] of the own leaf in [`CoreGroup`]. + pub(crate) fn with_leaf_extensions(mut self, leaf_extensions: Extensions) -> Self { + self.public_group_builder = self + .public_group_builder + .with_leaf_extensions(leaf_extensions); + self + } + /// Set the [`Capabilities`] of the own leaf in [`CoreGroup`]. + pub(crate) fn with_leaf_capabilities(mut self, leaf_capabilities: Capabilities) -> Self { + self.public_group_builder = self + .public_group_builder + .with_leaf_capabilities(leaf_capabilities); + self + } /// Set the number of past epochs the group should keep secrets. pub fn with_max_past_epoch_secrets(mut self, max_past_epochs: usize) -> Self { self.max_past_epochs = max_past_epochs; @@ -413,16 +430,13 @@ impl CoreGroup { ) } - /// Create a `GroupContextExtensions` proposal. - #[cfg(test)] - pub(crate) fn create_group_context_ext_proposal( + /// Checks if the memebers suuport the provided extensions. Pending proposals have to be passed + /// as parameters as Remove Proposals should be ignored + pub(crate) fn members_support_extensions<'a>( &self, - framing_parameters: FramingParameters, - extensions: Extensions, - signer: &impl Signer, - ) -> Result { - // Ensure that the group supports all the extensions that are wanted. - + extensions: &Extensions, + pending_proposals: impl Iterator, + ) -> Result<(), MemberExtensionValidationError> { let required_extension = extensions .iter() .find(|extension| extension.extension_type() == ExtensionType::RequiredCapabilities); @@ -430,18 +444,38 @@ impl CoreGroup { let required_capabilities = required_extension.as_required_capabilities_extension()?; // Ensure we support all the capabilities. required_capabilities.check_support()?; - // TODO #566/#1361: This needs to be re-enabled once we support GCEs - /* self.own_leaf_node()? - .capabilities() - .supports_required_capabilities(required_capabilities)?; */ + self.own_leaf_node()? + .capabilities() + .supports_required_capabilities(required_capabilities)?; // Ensure that all other leaf nodes support all the required // extensions as well. + let removed = pending_proposals.filter_map(|proposal| { + if let Proposal::Remove(remove) = proposal.proposal() { + Some(remove.removed()) + } else { + None + } + }); self.public_group() - .check_extension_support(required_capabilities.extension_types())?; + .check_extension_support(required_capabilities.extension_types(), removed)?; } - let proposal = GroupContextExtensionProposal::new(extensions); - let proposal = Proposal::GroupContextExtensions(proposal); + Ok(()) + } + + /// Create a `GroupContextExtensions` proposal. + pub(crate) fn create_group_context_ext_proposal<'a>( + &self, + framing_parameters: FramingParameters, + extensions: Extensions, + pending_proposals: impl Iterator, + signer: &impl Signer, + ) -> Result { + // Ensure that the group supports all the extensions that are wanted. + self.members_support_extensions(&extensions, pending_proposals)?; + + let gce_proposal = GroupContextExtensionProposal::new(extensions); + let proposal = Proposal::GroupContextExtensions(gce_proposal); AuthenticatedContent::member_proposal( framing_parameters, self.own_leaf_index(), @@ -451,7 +485,6 @@ impl CoreGroup { ) .map_err(|e| e.into()) } - // Create application message pub(crate) fn create_application_message( &mut self, @@ -869,6 +902,8 @@ impl CoreGroup { .validate_remove_proposals(&proposal_queue)?; self.public_group .validate_pre_shared_key_proposals(&proposal_queue)?; + self.public_group() + .validate_group_context_extensions_proposals(&proposal_queue)?; // Validate update proposals for member commits if let Sender::Member(sender_index) = &sender { // ValSem110 @@ -903,12 +938,13 @@ impl CoreGroup { apply_proposals_values.exclusion_list(), params.commit_type(), signer, - params.take_credential_with_key() + params.take_credential_with_key(), + apply_proposals_values.extensions )? } else { // If path is not needed, update the group context and return // empty path processing results - diff.update_group_context(backend)?; + diff.update_group_context(backend, apply_proposals_values.extensions.cloned())?; PathComputationResult::default() }; diff --git a/openmls/src/group/core_group/proposals.rs b/openmls/src/group/core_group/proposals.rs index 396365b0f2..bcc774bb3f 100644 --- a/openmls/src/group/core_group/proposals.rs +++ b/openmls/src/group/core_group/proposals.rs @@ -479,7 +479,7 @@ impl ProposalQueue { } } Proposal::GroupContextExtensions(_) => { - // TODO: Validate proposal? + valid_proposals.insert(queued_proposal.proposal_reference()); proposal_pool.insert(queued_proposal.proposal_reference(), queued_proposal); } Proposal::AppAck(_) => unimplemented!("See #291"), diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index 05ac541000..c83b900a6c 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -14,7 +14,7 @@ impl CoreGroup { async fn derive_epoch_secrets( &self, backend: &impl OpenMlsCryptoProvider, - apply_proposals_values: ApplyProposalsValues, + apply_proposals_values: ApplyProposalsValues<'_>, epoch_secrets: &GroupEpochSecrets, commit_secret: CommitSecret, serialized_provisional_group_context: &[u8], @@ -29,8 +29,7 @@ impl CoreGroup { .external_secret() .derive_external_keypair(backend.crypto(), self.ciphersuite()) .map_err(LibraryError::unexpected_crypto_error)? - .private - .into(); + .private; let init_secret = InitSecret::from_kem_output( backend, self.ciphersuite(), @@ -61,7 +60,8 @@ impl CoreGroup { backend.key_store(), &self.resumption_psk_store, &apply_proposals_values.presharedkeys, - ).await?; + ) + .await?; PskSecret::new(backend, self.ciphersuite(), psks).await? }; @@ -158,7 +158,7 @@ impl CoreGroup { diff.apply_received_update_path(backend, ciphersuite, sender_index, &path)?; // Update group context - diff.update_group_context(backend)?; + diff.update_group_context(backend, apply_proposals_values.extensions.cloned())?; let decryption_keypairs: Vec<&EncryptionKeyPair> = old_epoch_keypairs .iter() @@ -202,7 +202,7 @@ impl CoreGroup { } // Even if there is no path, we have to update the group context. - diff.update_group_context(backend)?; + diff.update_group_context(backend, apply_proposals_values.extensions.cloned())?; ( CommitSecret::zero_secret(ciphersuite, self.version()), diff --git a/openmls/src/group/core_group/test_core_group.rs b/openmls/src/group/core_group/test_core_group.rs index 2f3fd4be97..be213f8c68 100644 --- a/openmls/src/group/core_group/test_core_group.rs +++ b/openmls/src/group/core_group/test_core_group.rs @@ -13,7 +13,10 @@ use crate::{ messages::{group_info::GroupInfoTBS, *}, schedule::psk::{store::ResumptionPskStore, ExternalPsk, PreSharedKeyId, Psk}, test_utils::*, - treesync::{errors::ApplyUpdatePathError, node::leaf_node::TreeInfoTbs}, + treesync::{ + errors::ApplyUpdatePathError, + node::leaf_node::{Capabilities, TreeInfoTbs}, + }, }; pub(crate) async fn setup_alice_group( @@ -597,10 +600,12 @@ async fn test_own_commit_processing( assert_eq!(error, StageCommitError::OwnCommit); } -pub(crate) async fn setup_client( +pub(crate) async fn setup_client_with_extensions( id: &str, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, + extensions: Extensions, + capabilities: Capabilities, ) -> ( CredentialWithKey, KeyPackageBundle, @@ -621,16 +626,38 @@ pub(crate) async fn setup_client( .unwrap(); // Generate the KeyPackage - let key_package_bundle = KeyPackageBundle::new( + let key_package_bundle = KeyPackageBundle::new_with_extensions( backend, &signature_keys, ciphersuite, credential_with_key.clone(), + extensions, + capabilities, ) .await; (credential_with_key, key_package_bundle, signature_keys, pk) } +pub(crate) async fn setup_client( + id: &str, + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) -> ( + CredentialWithKey, + KeyPackageBundle, + SignatureKeyPair, + OpenMlsSignaturePublicKey, +) { + setup_client_with_extensions( + id, + ciphersuite, + backend, + Extensions::default(), + Capabilities::default(), + ) + .await +} + #[apply(ciphersuites_and_backends)] async fn test_proposal_application_after_self_was_removed( ciphersuite: Ciphersuite, diff --git a/openmls/src/group/core_group/test_past_secrets.rs b/openmls/src/group/core_group/test_past_secrets.rs index 60969eedab..bc50d726f9 100644 --- a/openmls/src/group/core_group/test_past_secrets.rs +++ b/openmls/src/group/core_group/test_past_secrets.rs @@ -45,7 +45,10 @@ async fn test_secret_tree_store(ciphersuite: Ciphersuite, backend: &impl OpenMls } #[apply(ciphersuites_and_backends)] -async fn test_empty_secret_tree_store(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_empty_secret_tree_store( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { // Create a store that keeps no epochs let mut message_secrets_store = MessageSecretsStore::new_with_secret( 0, diff --git a/openmls/src/group/core_group/test_proposals.rs b/openmls/src/group/core_group/test_proposals.rs index 5cd6e139e7..112bd0ded2 100644 --- a/openmls/src/group/core_group/test_proposals.rs +++ b/openmls/src/group/core_group/test_proposals.rs @@ -15,14 +15,14 @@ use crate::{ errors::*, proposals::{ProposalQueue, ProposalStore, QueuedProposal}, public_group::errors::PublicGroupBuildError, - test_core_group::setup_client, + test_core_group::{setup_client, setup_client_with_extensions}, CreateCommitParams, GroupContext, GroupId, }, key_packages::{KeyPackageBundle, KeyPackageIn}, messages::proposals::{AddProposal, Proposal, ProposalOrRef, ProposalType}, schedule::psk::store::ResumptionPskStore, test_utils::*, - treesync::errors::LeafNodeValidationError, + treesync::{errors::LeafNodeValidationError, node::leaf_node::Capabilities}, versions::ProtocolVersion, }; @@ -376,13 +376,6 @@ async fn test_group_context_extensions( let group_aad = b"Alice's test group"; let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - let (alice_credential, _, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend).await; - let (_bob_credential_with_key, bob_key_package_bundle, _, _) = - setup_client("Bob", ciphersuite, backend).await; - - let bob_key_package = bob_key_package_bundle.key_package(); - // Set required capabilities let extensions = &[ExtensionType::ApplicationId]; let proposals = &[ @@ -392,15 +385,39 @@ async fn test_group_context_extensions( ProposalType::Update, ]; let credentials = &[CredentialType::Basic]; - let required_capabilities = - RequiredCapabilitiesExtension::new(extensions, proposals, credentials); + let leaf_capabilities = Capabilities::new( + None, + None, + Some(extensions), + Some(proposals), + Some(credentials), + ); + // create clients + let (alice_credential, _, alice_signer, _alice_pk) = setup_client_with_extensions( + "Alice", + ciphersuite, + backend, + Extensions::empty(), + leaf_capabilities.clone(), + ) + .await; + let (_bob_credential_bundle, bob_key_package_bundle, _, _) = setup_client_with_extensions( + "Bob", + ciphersuite, + backend, + Extensions::empty(), + leaf_capabilities.clone(), + ) + .await; + + let bob_key_package = bob_key_package_bundle.key_package(); let mut alice_group = CoreGroup::builder( GroupId::random(backend), CryptoConfig::with_default_version(ciphersuite), alice_credential, ) - .with_required_capabilities(required_capabilities) + .with_leaf_capabilities(leaf_capabilities) .build(backend, &alice_signer) .await .expect("Error creating CoreGroup."); @@ -641,6 +658,7 @@ async fn test_group_context_extension_proposal( .create_group_context_ext_proposal( framing_parameters, Extensions::single(required_application_id), + proposal_store.proposals(), &alice_signer, ) .expect("Error creating gce proposal."); diff --git a/openmls/src/group/errors.rs b/openmls/src/group/errors.rs index 1432c494a0..1e8316139c 100644 --- a/openmls/src/group/errors.rs +++ b/openmls/src/group/errors.rs @@ -9,10 +9,11 @@ use super::public_group::errors::{CreationFromExternalError, PublicGroupBuildErr use crate::{ ciphersuite::signable::SignatureError, error::LibraryError, - extensions::errors::{ExtensionError, InvalidExtensionError}, + extensions::errors::InvalidExtensionError, framing::errors::{MessageDecryptionError, SenderError}, - key_packages::errors::KeyPackageVerifyError, - key_packages::errors::{KeyPackageExtensionSupportError, KeyPackageNewError}, + key_packages::errors::{ + KeyPackageExtensionSupportError, KeyPackageNewError, KeyPackageVerifyError, + }, messages::{group_info::GroupInfoError, GroupSecretsError}, schedule::errors::PskError, treesync::errors::*, @@ -361,6 +362,9 @@ pub enum ProposalValidationError { /// See [`PskError`] for more details. #[error(transparent)] Psk(#[from] PskError), + /// There cannot be more than 1 GroupContextExtensions proposal in a commit + #[error("Expected at most 1 GroupContextExtensions proposal, found {0}")] + TooManyGroupContextExtensions(usize), } /// External Commit validaton error @@ -466,6 +470,9 @@ pub(crate) enum ApplyProposalsError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), + /// See [`KeyPackageExtensionSupportError`] for more details. + #[error(transparent)] + KeyPackageExtensionSupportError(#[from] KeyPackageExtensionSupportError), } // Core group build error @@ -498,26 +505,6 @@ pub(crate) enum CoreGroupParseMessageError { ValidationError(#[from] ValidationError), } -/// Create group context ext proposal error -#[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum CreateGroupContextExtProposalError { - /// See [`LibraryError`] for more details. - #[error(transparent)] - LibraryError(#[from] LibraryError), - /// See [`KeyPackageExtensionSupportError`] for more details. - #[error(transparent)] - KeyPackageExtensionSupport(#[from] KeyPackageExtensionSupportError), - /// See [`TreeSyncError`] for more details. - #[error(transparent)] - TreeSyncError(#[from] TreeSyncError), - /// See [`ExtensionError`] for more details. - #[error(transparent)] - Extension(#[from] ExtensionError), - /// See [`LeafNodeValidationError`] for more details. - #[error(transparent)] - LeafNodeValidation(#[from] LeafNodeValidationError), -} - /// Error merging a commit. #[derive(Error, Debug, PartialEq, Clone)] pub enum MergeCommitError { diff --git a/openmls/src/group/group_context.rs b/openmls/src/group/group_context.rs index ba9f1617ae..6d62664879 100644 --- a/openmls/src/group/group_context.rs +++ b/openmls/src/group/group_context.rs @@ -74,6 +74,11 @@ impl GroupContext { self.epoch.increment() } + /// Overrides the extensions + pub(crate) fn set_extensions(&mut self, extensions: Extensions) { + self.extensions = extensions; + } + /// Update the current tree hash to the new value pub(crate) fn update_tree_hash(&mut self, new_tree_hash: Vec) { self.tree_hash = new_tree_hash.into() diff --git a/openmls/src/group/mls_group/config.rs b/openmls/src/group/mls_group/config.rs index edadd4bf8c..723c271153 100644 --- a/openmls/src/group/mls_group/config.rs +++ b/openmls/src/group/mls_group/config.rs @@ -30,7 +30,7 @@ use super::*; use crate::{ group::config::CryptoConfig, key_packages::Lifetime, - tree::sender_ratchet::SenderRatchetConfiguration, + tree::sender_ratchet::SenderRatchetConfiguration, treesync::node::leaf_node::Capabilities, }; use serde::{Deserialize, Serialize}; @@ -60,6 +60,10 @@ pub struct MlsGroupConfig { pub(crate) lifetime: Lifetime, /// Ciphersuite and protocol version pub(crate) crypto_config: CryptoConfig, + /// Extensions to be added to the own leaf node + pub(crate) leaf_extensions: Extensions, + /// Capabilities of the own leaf node + pub(crate) leaf_capabilities: Option, } impl MlsGroupConfig { @@ -113,6 +117,11 @@ impl MlsGroupConfig { &self.crypto_config } + /// Returns the [`MlsGroupConfig`] leaf extensions configuration. + pub fn leaf_extensions(&self) -> &Extensions { + &self.leaf_extensions + } + #[cfg(any(feature = "test-utils", test))] pub fn test_default(ciphersuite: Ciphersuite) -> Self { Self::builder() @@ -206,6 +215,19 @@ impl MlsGroupConfigBuilder { self } + /// Sets the group creator's leaf extensions + pub fn leaf_extensions(mut self, leaf_extensions: Extensions) -> Self { + self.config.leaf_extensions = leaf_extensions; + + self + } + + /// Sets the group creator's leaf capabilities + pub fn leaf_capabilities(mut self, leaf_capabilities: Capabilities) -> Self { + self.config.leaf_capabilities = Some(leaf_capabilities); + self + } + /// Finalizes the builder and retursn an `[MlsGroupConfig`]. pub fn build(self) -> MlsGroupConfig { self.config diff --git a/openmls/src/group/mls_group/creation.rs b/openmls/src/group/mls_group/creation.rs index e79e8d77c1..ce8c06dc0b 100644 --- a/openmls/src/group/mls_group/creation.rs +++ b/openmls/src/group/mls_group/creation.rs @@ -60,6 +60,13 @@ impl MlsGroup { .with_external_senders(mls_group_config.external_senders.clone()) .with_max_past_epoch_secrets(mls_group_config.max_past_epochs) .with_lifetime(*mls_group_config.lifetime()) + .with_leaf_extensions(mls_group_config.leaf_extensions().clone()) + .with_leaf_capabilities( + mls_group_config + .leaf_capabilities + .clone() + .unwrap_or_default(), + ) .build(backend, signer) .await .map_err(|e| match e { diff --git a/openmls/src/group/mls_group/errors.rs b/openmls/src/group/mls_group/errors.rs index 906f6d1d0e..a11824935f 100644 --- a/openmls/src/group/mls_group/errors.rs +++ b/openmls/src/group/mls_group/errors.rs @@ -9,13 +9,14 @@ use thiserror::Error; use crate::{ error::LibraryError, - extensions::errors::InvalidExtensionError, + extensions::errors::{ExtensionError, InvalidExtensionError}, group::errors::{ CreateAddProposalError, CreateCommitError, MergeCommitError, StageCommitError, ValidationError, }, + prelude::KeyPackageExtensionSupportError, schedule::errors::PskError, - treesync::errors::{LeafNodeValidationError, PublicTreeError}, + treesync::errors::{LeafNodeValidationError, MemberExtensionValidationError, PublicTreeError}, }; /// New group error @@ -240,6 +241,46 @@ pub enum ProposeSelfUpdateError { PublicTreeError(#[from] PublicTreeError), } +/// Create group context ext proposal error +#[derive(Error, Debug, PartialEq, Clone)] +pub enum UpdateExtensionsError { + /// See [`LibraryError`] for more details. + #[error(transparent)] + LibraryError(#[from] LibraryError), + /// See [`MemberExtensionValidationError`] for more details. + #[error(transparent)] + MemberExtensionValidationError(#[from] MemberExtensionValidationError), + /// See [`CreateCommitError`] for more details. + #[error(transparent)] + CreateCommitError(#[from] CreateCommitError), + /// See [`MlsGroupStateError`] for more details. + #[error(transparent)] + GroupStateError(#[from] MlsGroupStateError), +} + +/// Create group context ext proposal error +#[derive(Error, Debug, PartialEq, Clone)] +pub enum ProposeGroupContextExtensionError { + /// See [`LibraryError`] for more details. + #[error(transparent)] + LibraryError(#[from] LibraryError), + /// See [`KeyPackageExtensionSupportError`] for more details. + #[error(transparent)] + KeyPackageExtensionSupport(#[from] KeyPackageExtensionSupportError), + /// See [`ExtensionError`] for more details. + #[error(transparent)] + Extension(#[from] ExtensionError), + /// The own CredentialBundle could not be found in the key store. + #[error("The own CredentialBundle could not be found in the key store.")] + NoMatchingCredentialBundle, + /// See [`MlsGroupStateError`] for more details. + #[error(transparent)] + GroupStateError(#[from] MlsGroupStateError), + /// See [`MemberExtensionValidationError`] for more details. + #[error(transparent)] + MemberExtensionValidationError(#[from] MemberExtensionValidationError), +} + /// Commit to pending proposals error #[derive(Error, Debug, PartialEq, Clone)] pub enum CommitToPendingProposalsError { diff --git a/openmls/src/group/mls_group/extension.rs b/openmls/src/group/mls_group/extension.rs new file mode 100644 index 0000000000..a585988b56 --- /dev/null +++ b/openmls/src/group/mls_group/extension.rs @@ -0,0 +1,112 @@ +//! MLS group context extensions +//! +//! Contains all the methods related to modifying a group's extensions. + +use std::iter; + +use openmls_traits::signatures::Signer; + +use crate::{ + messages::group_info::GroupInfo, + prelude::{ + create_commit_params::CreateCommitParams, hash_ref::ProposalRef, + ProposeGroupContextExtensionError, + }, +}; + +use super::*; + +impl MlsGroup { + /// Propose to update the group context extensions. This replaces the existing extensions + /// of the group but does not merge them yet. + /// + /// Returns an error if there is a pending commit. + pub fn propose_extensions( + &mut self, + backend: &impl OpenMlsCryptoProvider, + signer: &impl Signer, + extensions: Extensions, + ) -> Result<(MlsMessageOut, ProposalRef), ProposeGroupContextExtensionError> { + self.is_operational()?; + + let gce_proposal = self.group.create_group_context_ext_proposal( + self.framing_parameters(), + extensions, + self.pending_proposals(), + signer, + )?; + let proposal = QueuedProposal::from_authenticated_content( + self.ciphersuite(), + backend, + gce_proposal.clone(), + ProposalOrRefType::Proposal, + )?; + let reference = proposal.proposal_reference(); + + self.proposal_store.add(proposal); + + let mls_message = self.content_to_mls_message(gce_proposal, backend)?; + + // Since the state of the group might be changed, arm the state flag + self.flag_state_change(); + + Ok((mls_message, reference)) + } + + /// Updates the extensions of the group + /// + /// This operation results in a Commit with a `path`, i.e. it includes an + /// update of the committer's leaf [KeyPackage]. + /// + /// If successful, it returns a triple where the first element + /// contains the commit, the second one the [Welcome] and the third an optional [GroupInfo] that + /// will be [Some] if the group has the `use_ratchet_tree_extension` flag set. + /// + /// Returns an error if there is a pending commit. + pub async fn update_extensions( + &mut self, + backend: &impl OpenMlsCryptoProvider, + signer: &impl Signer, + extensions: Extensions, + ) -> Result< + (MlsMessageOut, Option, Option), + UpdateExtensionsError, + > { + self.is_operational()?; + self.group + .members_support_extensions(&extensions, iter::empty())?; + let proposal = + Proposal::GroupContextExtensions(GroupContextExtensionProposal::new(extensions)); + let params = CreateCommitParams::builder() + .framing_parameters(self.framing_parameters()) + .proposal_store(&self.proposal_store) + .inline_proposals(vec![proposal]) + .build(); + let create_commit_result = self.group.create_commit(params, backend, signer).await?; + + // Convert PublicMessage messages to MLSMessage and encrypt them if required by + // the configuration + let mls_messages = self.content_to_mls_message(create_commit_result.commit, backend)?; + + // Set the current group state to [`MlsGroupState::PendingCommit`], + // storing the current [`StagedCommit`] from the commit results + self.group_state = MlsGroupState::PendingCommit(Box::new(PendingCommitState::Member( + create_commit_result.staged_commit, + ))); + + // Since the state of the group might be changed, arm the state flag + self.flag_state_change(); + Ok(( + mls_messages, + create_commit_result + .welcome_option + .map(|w| MlsMessageOut::from_welcome(w, self.group.version())), + create_commit_result.group_info, + )) + } + + /// Get the group's [`Extensions`]. + pub fn group_context_extensions(&self) -> &Extensions { + self.group.context().extensions() + } +} diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index bb4254572d..8d7ea2bdcd 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -29,6 +29,7 @@ use errors::*; // Crate pub(crate) mod config; pub(crate) mod errors; +pub(crate) mod extension; pub(crate) mod membership; pub(crate) mod processing; pub(crate) mod proposal; diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index 5e1755fb8e..4ed26dabe1 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -320,35 +320,4 @@ impl MlsGroup { )) } } - - #[cfg(test)] - pub fn propose_group_context_extensions( - &mut self, - backend: &impl OpenMlsCryptoProvider, - extensions: Extensions, - signer: &impl Signer, - ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<()>> { - self.is_operational()?; - - let proposal = self - .group - .create_group_context_ext_proposal(self.framing_parameters(), extensions, signer) - .unwrap(); - - let queued_proposal = QueuedProposal::from_authenticated_content_by_ref( - self.ciphersuite(), - backend, - proposal.clone(), - )?; - - let proposal_ref = queued_proposal.proposal_reference(); - self.proposal_store.add(queued_proposal); - - let mls_message = self.content_to_mls_message(proposal, backend)?; - - // Since the state of the group might be changed, arm the state flag - self.flag_state_change(); - - Ok((mls_message, proposal_ref)) - } } diff --git a/openmls/src/group/public_group/builder.rs b/openmls/src/group/public_group/builder.rs index 78a62a4982..4607ea17a1 100644 --- a/openmls/src/group/public_group/builder.rs +++ b/openmls/src/group/public_group/builder.rs @@ -26,6 +26,7 @@ pub(crate) struct TempBuilderPG1 { required_capabilities: Option, external_senders: Option, leaf_extensions: Option, + leaf_capabilities: Option, } impl TempBuilderPG1 { @@ -52,29 +53,31 @@ impl TempBuilderPG1 { self } + pub(crate) fn with_leaf_extensions(mut self, leaf_extensions: Extensions) -> Self { + if !leaf_extensions.is_empty() { + self.leaf_extensions = Some(leaf_extensions); + } + self + } + + pub(crate) fn with_leaf_capabilities(mut self, leaf_capabilities: Capabilities) -> Self { + self.leaf_capabilities = Some(leaf_capabilities); + self + } + pub(crate) fn get_secrets( self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, ) -> Result<(TempBuilderPG2, CommitSecret, EncryptionKeyPair), PublicGroupBuildError> { - let capabilities = self - .required_capabilities - .as_ref() - .map(|re| re.extension_types()); let (treesync, commit_secret, leaf_keypair) = TreeSync::new( backend, signer, self.crypto_config, self.credential_with_key, self.lifetime.unwrap_or_default(), - Capabilities::new( - Some(&[self.crypto_config.version]), // TODO: Allow more versions - Some(&[self.crypto_config.ciphersuite]), // TODO: allow more ciphersuites - capabilities, - None, - None, - ), - self.leaf_extensions.unwrap_or(Extensions::empty()), + self.leaf_capabilities.unwrap_or_default(), + self.leaf_extensions.unwrap_or_default(), )?; let required_capabilities = self.required_capabilities.unwrap_or_default(); required_capabilities.check_support().map_err(|e| match e { @@ -172,6 +175,7 @@ impl PublicGroup { required_capabilities: None, external_senders: None, leaf_extensions: None, + leaf_capabilities: None, } } } diff --git a/openmls/src/group/public_group/diff.rs b/openmls/src/group/public_group/diff.rs index b5aacb53de..ea4d41c62a 100644 --- a/openmls/src/group/public_group/diff.rs +++ b/openmls/src/group/public_group/diff.rs @@ -12,6 +12,7 @@ use super::PublicGroup; use crate::{ binary_tree::{array_representation::TreeSize, LeafNodeIndex}, error::LibraryError, + extensions::Extensions, framing::{mls_auth_content::AuthenticatedContent, public_message::InterimTranscriptHashInput}, group::GroupContext, messages::{proposals::AddProposal, ConfirmationTag, EncryptedGroupSecrets}, @@ -200,6 +201,7 @@ impl<'a> PublicGroupDiff<'a> { pub(crate) fn update_group_context( &mut self, backend: &impl OpenMlsCryptoProvider, + extensions: Option, ) -> Result<(), LibraryError> { // Calculate tree hash let new_tree_hash = self @@ -207,6 +209,9 @@ impl<'a> PublicGroupDiff<'a> { .compute_tree_hashes(backend, self.group_context().ciphersuite())?; self.group_context.update_tree_hash(new_tree_hash); self.group_context.increment_epoch(); + if let Some(extensions) = extensions { + self.group_context.set_extensions(extensions); + } Ok(()) } diff --git a/openmls/src/group/public_group/diff/apply_proposals.rs b/openmls/src/group/public_group/diff/apply_proposals.rs index cf25af9d94..5cbe32a85d 100644 --- a/openmls/src/group/public_group/diff/apply_proposals.rs +++ b/openmls/src/group/public_group/diff/apply_proposals.rs @@ -12,15 +12,16 @@ use crate::{ use super::*; /// This struct contain the return values of the `apply_proposals()` function -pub(crate) struct ApplyProposalsValues { +pub(crate) struct ApplyProposalsValues<'a> { pub(crate) path_required: bool, pub(crate) self_removed: bool, pub(crate) invitation_list: Vec<(LeafNodeIndex, AddProposal)>, pub(crate) presharedkeys: Vec, pub(crate) external_init_proposal_option: Option, + pub(crate) extensions: Option<&'a Extensions>, } -impl ApplyProposalsValues { +impl ApplyProposalsValues<'_> { /// This function creates a `HashSet` of node indexes of the new nodes that /// were added to the tree. The `HashSet` will be querried by the /// `resolve()` function to filter out those nodes from the resolution. @@ -50,12 +51,31 @@ impl ApplyProposalsValues { impl<'a> PublicGroupDiff<'a> { pub(crate) fn apply_proposals( &mut self, - proposal_queue: &ProposalQueue, + proposal_queue: &'a ProposalQueue, own_leaf_index: impl Into>, - ) -> Result { + ) -> Result, LibraryError> { log::debug!("Applying proposal"); let mut self_removed = false; + // Process GroupContextExtensions first because they have to be used to validate other proposals. + // For example, an add proposal will have to fulfill the required capabilities present in those extensions + let extensions = proposal_queue + .filtered_by_type(ProposalType::GroupContextExtensions) + .find_map(|queued_proposal| { + if let Proposal::GroupContextExtensions(ext_proposal) = queued_proposal.proposal() { + Some(ext_proposal.extensions()) + } else { + None + } + }); + + let required_extensions = extensions + .and_then(|exts| { + exts.iter() + .find_map(|i| i.as_required_capabilities_extension().ok()) + }) + .map(|e| e.extension_types()); + // Process external init proposals. We do this before the removes, so we // know that removing "ourselves" (i.e. removing the group member in the // same leaf as we are in) is valid in this case. We only care about the @@ -119,10 +139,15 @@ impl<'a> PublicGroupDiff<'a> { let mut invitation_list = Vec::new(); for add_proposal in add_proposals { // XXX: There are too many clones here. - let leaf_node = add_proposal.key_package.leaf_node(); + let kp = add_proposal.key_package(); + if let Some(required) = required_extensions { + kp.check_extension_support(required).map_err(|_| { + LibraryError::custom("Keypackage doens't support required capability") + })?; + } let leaf_index = self .diff - .add_leaf(leaf_node.clone()) + .add_leaf(kp.leaf_node().clone()) // TODO #810 .map_err(|_| LibraryError::custom("Tree full: cannot add more members"))?; invitation_list.push((leaf_index, add_proposal.clone())) @@ -159,6 +184,7 @@ impl<'a> PublicGroupDiff<'a> { invitation_list, presharedkeys, external_init_proposal_option, + extensions, }) } } diff --git a/openmls/src/group/public_group/diff/compute_path.rs b/openmls/src/group/public_group/diff/compute_path.rs index 351f4b63f5..66264fed5f 100644 --- a/openmls/src/group/public_group/diff/compute_path.rs +++ b/openmls/src/group/public_group/diff/compute_path.rs @@ -7,6 +7,7 @@ use crate::{ binary_tree::LeafNodeIndex, credentials::CredentialWithKey, error::LibraryError, + extensions::Extensions, group::{ config::CryptoConfig, core_group::create_commit_params::CommitType, errors::CreateCommitError, @@ -35,6 +36,7 @@ pub(crate) struct PathComputationResult { } impl<'a> PublicGroupDiff<'a> { + #[allow(clippy::too_many_arguments)] pub(crate) fn compute_path( &mut self, backend: &impl OpenMlsCryptoProvider, @@ -43,6 +45,7 @@ impl<'a> PublicGroupDiff<'a> { commit_type: CommitType, signer: &impl Signer, credential_with_key: Option, + extensions: Option<&Extensions>, ) -> Result> { let version = self.group_context().protocol_version(); let ciphersuite = self.group_context().ciphersuite(); @@ -101,7 +104,7 @@ impl<'a> PublicGroupDiff<'a> { // After we've processed the path, we can update the group context s.t. // the updated group context is used for path secret encryption. Note // that we have not yet updated the confirmed transcript hash. - self.update_group_context(backend)?; + self.update_group_context(backend, extensions.cloned())?; let serialized_group_context = self .group_context() diff --git a/openmls/src/group/public_group/staged_commit.rs b/openmls/src/group/public_group/staged_commit.rs index 4d234d537e..3419b456a8 100644 --- a/openmls/src/group/public_group/staged_commit.rs +++ b/openmls/src/group/public_group/staged_commit.rs @@ -91,6 +91,7 @@ impl PublicGroup { // ValSem402 // ValSem403 self.validate_pre_shared_key_proposals(&proposal_queue)?; + self.validate_group_context_extensions_proposals(&proposal_queue)?; match sender { Sender::Member(leaf_index) => { @@ -226,7 +227,7 @@ impl PublicGroup { }; // Update group context - diff.update_group_context(backend)?; + diff.update_group_context(backend, apply_proposals_values.extensions.cloned())?; // Update the confirmed transcript hash before we compute the confirmation tag. diff.update_confirmed_transcript_hash(backend, mls_content)?; diff --git a/openmls/src/group/public_group/validation.rs b/openmls/src/group/public_group/validation.rs index 6db6328592..a8369000fe 100644 --- a/openmls/src/group/public_group/validation.rs +++ b/openmls/src/group/public_group/validation.rs @@ -6,8 +6,6 @@ use std::collections::{BTreeSet, HashSet}; use openmls_traits::types::VerifiableCiphersuite; use super::PublicGroup; -#[cfg(test)] -use crate::treesync::errors::LeafNodeValidationError; use crate::{ binary_tree::array_representation::LeafNodeIndex, framing::{ @@ -24,7 +22,7 @@ use crate::{ Commit, }, schedule::errors::PskError, - treesync::node::leaf_node::LeafNode, + treesync::{errors::LeafNodeValidationError, node::leaf_node::LeafNode}, }; impl PublicGroup { @@ -315,7 +313,7 @@ impl PublicGroup { } // Check that all extensions are contained in the capabilities. - if !capabilities.contain_extensions(leaf_node.extensions()) { + if !capabilities.are_extensions_supported(leaf_node.extensions()) { return Err(ProposalValidationError::InsufficientCapabilities); } @@ -512,16 +510,41 @@ impl PublicGroup { } Ok(()) } + /// Validate GroupContextExtensions proposals. + /// + /// There must be at most one GroupContextExtensions proposal. + pub(crate) fn validate_group_context_extensions_proposals( + &self, + proposal_queue: &ProposalQueue, + ) -> Result<(), ProposalValidationError> { + let gce_count = proposal_queue + .queued_proposals() + .filter(|p| matches!(p.proposal(), Proposal::GroupContextExtensions(_))) + .count(); + + if gce_count > 1 { + return Err(ProposalValidationError::TooManyGroupContextExtensions( + gce_count, + )); + } + + Ok(()) + } /// Returns a [`LeafNodeValidationError`] if an [`ExtensionType`] /// in `extensions` is not supported by a leaf in this tree. - #[cfg(test)] + /// A list of leaves proposed to be removed must be provided as they + /// should be ignored by this validation pub(crate) fn check_extension_support( &self, extensions: &[crate::extensions::ExtensionType], + removed: impl Iterator, ) -> Result<(), LeafNodeValidationError> { - for leaf in self.treesync().full_leaves() { - leaf.check_extension_support(extensions)?; + let removed = removed.collect::>(); + for (index, leaf) in self.treesync().full_leaves_indexed() { + if !removed.contains(&index) { + leaf.check_extension_support(extensions)?; + } } Ok(()) } diff --git a/openmls/src/group/tests/external_add_proposal.rs b/openmls/src/group/tests/external_add_proposal.rs index c2492bcc3f..5dcb1e8cc7 100644 --- a/openmls/src/group/tests/external_add_proposal.rs +++ b/openmls/src/group/tests/external_add_proposal.rs @@ -33,7 +33,8 @@ async fn new_test_group( // Generate credentials with keys let credential_with_keys = - generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend) + .await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() @@ -66,7 +67,8 @@ async fn validation_test_setup( new_test_group("Alice", wire_format_policy, ciphersuite, backend).await; let bob_credential_with_key = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend) + .await; let bob_key_package = generate_key_package( ciphersuite, @@ -243,7 +245,8 @@ async fn external_add_proposal_should_be_signed_by_key_package_it_references( // A new client, Charlie, will now ask joining with an external Add proposal let charlie_credential = - generate_credential_with_key("Charlie".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Charlie".into(), ciphersuite.signature_algorithm(), backend) + .await; let charlie_kp = generate_key_package( ciphersuite, @@ -286,7 +289,8 @@ async fn new_member_proposal_sender_should_be_reserved_for_join_proposals( // Add proposal can have a 'new_member_proposal' sender let any_credential = - generate_credential_with_key("Any".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Any".into(), ciphersuite.signature_algorithm(), backend) + .await; let any_kp = generate_key_package( ciphersuite, diff --git a/openmls/src/group/tests/external_remove_proposal.rs b/openmls/src/group/tests/external_remove_proposal.rs index c4597d0292..808515b049 100644 --- a/openmls/src/group/tests/external_remove_proposal.rs +++ b/openmls/src/group/tests/external_remove_proposal.rs @@ -24,7 +24,8 @@ async fn new_test_group( // Generate credentials with keys let credential_with_keys = - generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend) + .await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() @@ -65,7 +66,8 @@ async fn validation_test_setup( .await; let bob_credential_with_key = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend) + .await; let bob_key_package = generate_key_package( ciphersuite, diff --git a/openmls/src/group/tests/mod.rs b/openmls/src/group/tests/mod.rs index c78c16e778..ae844eccb5 100644 --- a/openmls/src/group/tests/mod.rs +++ b/openmls/src/group/tests/mod.rs @@ -19,6 +19,8 @@ mod test_framing; #[cfg(test)] mod test_framing_validation; #[cfg(test)] +mod test_gce_proposals; +#[cfg(test)] mod test_group; #[cfg(test)] mod test_past_secrets; @@ -27,6 +29,8 @@ mod test_proposal_validation; #[cfg(test)] mod test_remove_operation; #[cfg(test)] +mod test_update_extensions; +#[cfg(test)] mod test_wire_format_policy; #[cfg(test)] pub(crate) mod utils; diff --git a/openmls/src/group/tests/test_commit_validation.rs b/openmls/src/group/tests/test_commit_validation.rs index 16ca6f2b5e..29d51d50bb 100644 --- a/openmls/src/group/tests/test_commit_validation.rs +++ b/openmls/src/group/tests/test_commit_validation.rs @@ -40,13 +40,16 @@ async fn validation_test_setup( // Generate credentials with keys let alice_credential = - generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend) + .await; let bob_credential = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend) + .await; let charlie_credential = - generate_credential_with_key("Charlie".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Charlie".into(), ciphersuite.signature_algorithm(), backend) + .await; // Generate KeyPackages let bob_key_package = @@ -266,7 +269,8 @@ async fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr let add_proposal = || async { let dave_credential = - generate_credential_with_key("Dave".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Dave".into(), ciphersuite.signature_algorithm(), backend) + .await; let dave_key_package = generate_key_package(ciphersuite, Extensions::empty(), backend, dave_credential).await; @@ -333,8 +337,7 @@ async fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr (vec![remove_proposal(), add_proposal().await], true), // path_required + path_required = path_required (vec![update_proposal, remove_proposal()], true), - // TODO: #566 this should work if GCE proposals validation were implemented - // (vec![add_proposal(), gce_proposal()], true), + (vec![add_proposal().await, gce_proposal()], true), ]; for (proposal, is_path_required) in cases { diff --git a/openmls/src/group/tests/test_external_commit_validation.rs b/openmls/src/group/tests/test_external_commit_validation.rs index a4dbce9dd4..2b023fb8ea 100644 --- a/openmls/src/group/tests/test_external_commit_validation.rs +++ b/openmls/src/group/tests/test_external_commit_validation.rs @@ -638,7 +638,8 @@ async fn test_valsem246(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // the path by generating a new credential for bob, putting it in the path // and then re-signing the message with his original credential. let bob_new_credential = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend) + .await; // Generate KeyPackage let bob_new_key_package = generate_key_package( @@ -804,10 +805,12 @@ mod utils { "Alice".into(), ciphersuite.signature_algorithm(), backend, - ).await; + ) + .await; let bob_credential = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend) + .await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() diff --git a/openmls/src/group/tests/test_framing_validation.rs b/openmls/src/group/tests/test_framing_validation.rs index 9daca17908..2d3c901786 100644 --- a/openmls/src/group/tests/test_framing_validation.rs +++ b/openmls/src/group/tests/test_framing_validation.rs @@ -39,10 +39,12 @@ async fn validation_test_setup( // Generate credentials with keys let alice_credential = - generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend) + .await; let bob_credential = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend) + .await; // Generate KeyPackages let alice_key_package = generate_key_package( diff --git a/openmls/src/group/tests/test_gce_proposals.rs b/openmls/src/group/tests/test_gce_proposals.rs new file mode 100644 index 0000000000..b9d0ae49f9 --- /dev/null +++ b/openmls/src/group/tests/test_gce_proposals.rs @@ -0,0 +1,833 @@ +use crate::{ + credentials::CredentialType, + extensions::{ + ApplicationIdExtension, Extension, ExtensionType, Extensions, ExternalSender, + ExternalSendersExtension, RequiredCapabilitiesExtension, SenderExtensionIndex, + }, + framing::{ + validation::ProcessedMessageContent, FramedContentBody, MlsMessageIn, MlsMessageOut, + }, + group::core_group::test_core_group::setup_client, + group::{ + config::CryptoConfig, + errors::*, + mls_group::{config::MlsGroupConfig, MlsGroup}, + test_core_group::setup_client_with_extensions, + PURE_PLAINTEXT_WIRE_FORMAT_POLICY, + }, + messages::{ + external_proposals::{ExternalProposal, JoinProposal}, + proposals::{GroupContextExtensionProposal, Proposal, ProposalOrRef, ProposalType}, + }, + test_utils::*, + treesync::{ + errors::{LeafNodeValidationError, MemberExtensionValidationError}, + node::leaf_node::Capabilities, + }, + versions::ProtocolVersion, +}; +use openmls_basic_credential::SignatureKeyPair; +use openmls_rust_crypto::OpenMlsRustCrypto; +use openmls_traits::{signatures::Signer, types::Ciphersuite, OpenMlsCryptoProvider}; + +use super::utils::resign_message; + +pub const DEFAULT_PROPOSAL_TYPES: [ProposalType; 6] = [ + ProposalType::Add, + ProposalType::Update, + ProposalType::Remove, + ProposalType::PreSharedKey, + ProposalType::Reinit, + ProposalType::GroupContextExtensions, +]; + +pub const DEFAULT_CREDENTIAL_TYPES: [CredentialType; 1] = [CredentialType::Basic]; + +#[apply(ciphersuites_and_backends)] +async fn gce_are_forwarded_in_welcome( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let required_capabilities = RequiredCapabilitiesExtension::new( + &[ExtensionType::ExternalSenders], + &DEFAULT_PROPOSAL_TYPES, + &DEFAULT_CREDENTIAL_TYPES, + ); + let (ds_credential, ..) = setup_client("delivery service", ciphersuite, backend).await; + let external_senders = vec![ExternalSender::new( + ds_credential.signature_key, + ds_credential.credential, + )]; + let kp_capabilities = Capabilities::new( + None, + None, + Some(&[ExtensionType::ExternalSenders]), + None, + Some(&DEFAULT_CREDENTIAL_TYPES), + ); + // Bob has been created from a welcome message + let (alice_group, bob_group, ..) = group_setup( + ciphersuite, + required_capabilities.clone(), + Some(external_senders.clone()), + Extensions::empty(), + kp_capabilities, + backend, + ) + .await; + assert_eq!( + *alice_group.group_context_extensions(), + Extensions::from_vec(vec![ + Extension::RequiredCapabilities(required_capabilities), + Extension::ExternalSenders(external_senders) + ]) + .unwrap() + ); + assert_eq!( + alice_group.group_context_extensions(), + bob_group.group_context_extensions() + ); +} + +#[should_panic] +#[apply(ciphersuites_and_backends)] +async fn cannot_create_group_when_keypackage_lacks_required_capability( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let required_capabilities = RequiredCapabilitiesExtension::new( + // External senders is required... + &[ExtensionType::ExternalSenders], + &DEFAULT_PROPOSAL_TYPES, + &DEFAULT_CREDENTIAL_TYPES, + ); + let _ = group_setup( + ciphersuite, + required_capabilities, + None, + // ...but not present in keypackage extensions + Extensions::empty(), + Capabilities::default(), + backend, + ) + .await; +} + +#[apply(ciphersuites_and_backends)] +async fn gce_fails_when_it_contains_unsupported_extensions( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let required_capabilities = + RequiredCapabilitiesExtension::new(&[], &DEFAULT_PROPOSAL_TYPES, &DEFAULT_CREDENTIAL_TYPES); + // Bob has been created from a welcome message + let (mut alice_group, mut bob_group, alice_signer, bob_signer) = group_setup( + ciphersuite, + required_capabilities, + None, + Extensions::empty(), + Capabilities::default(), + backend, + ) + .await; + // Alice tries to add a required capability she doesn't support herself. + let required_key_id = Extension::RequiredCapabilities(RequiredCapabilitiesExtension::new( + &[ExtensionType::ExternalSenders], + &[], + &[], + )); + let e = alice_group.propose_extensions(backend, &alice_signer, Extensions::single(required_key_id.clone())) + .expect_err("Alice was able to create a gce proposal with a required extensions she doesn't support."); + assert_eq!( + e, + ProposeGroupContextExtensionError::MemberExtensionValidationError( + MemberExtensionValidationError::LeafNodeValidationError( + LeafNodeValidationError::UnsupportedExtensions + ) + ) + ); + // Now Bob wants the ExternalSenders extension to be required. + // This should fail because Alice doesn't support it. + let e = bob_group + .propose_extensions(backend, &bob_signer, Extensions::single(required_key_id)) + .expect_err("Bob was able to create a gce proposal for an extension not supported by all other parties."); + assert_eq!( + e, + ProposeGroupContextExtensionError::MemberExtensionValidationError( + MemberExtensionValidationError::LeafNodeValidationError( + LeafNodeValidationError::UnsupportedExtensions + ) + ) + ); +} + +#[apply(ciphersuites_and_backends)] +async fn gce_proposal_should_overwrite_previous( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let old_required_capabilities = RequiredCapabilitiesExtension::new( + &[ExtensionType::ExternalSenders], + &[ + ProposalType::Add, + ProposalType::Update, + ProposalType::Remove, + ProposalType::PreSharedKey, + ProposalType::GroupContextExtensions, + ], + &DEFAULT_CREDENTIAL_TYPES, + ); + let new_required_capabilities = RequiredCapabilitiesExtension::new( + &[ExtensionType::RatchetTree, ExtensionType::ApplicationId], + &[ + ProposalType::Add, + ProposalType::Update, + ProposalType::Remove, + ProposalType::Reinit, + ProposalType::GroupContextExtensions, + ], + &DEFAULT_CREDENTIAL_TYPES, + ); + + let kp_extensions = Extensions::from_vec(vec![Extension::ExternalSenders( + ExternalSendersExtension::default(), + )]) + .unwrap(); + + let kp_capabilities = Capabilities::new( + None, + None, + Some(&[ + ExtensionType::ExternalSenders, + ExtensionType::RatchetTree, + ExtensionType::ApplicationId, + ]), + None, + Some(&DEFAULT_CREDENTIAL_TYPES), + ); + let (mut alice_group, _, alice_signer, _) = group_setup( + ciphersuite, + old_required_capabilities, + None, + kp_extensions, + kp_capabilities, + backend, + ) + .await; + + // Alice adds a required capability. + let new_extensions = Extensions::from_vec(vec![ + Extension::RequiredCapabilities(new_required_capabilities), + Extension::ApplicationId(ApplicationIdExtension::new(b"test_mls")), + ]) + .unwrap(); + alice_group + .propose_extensions(backend, &alice_signer, new_extensions.clone()) + .unwrap(); + alice_group + .commit_to_pending_proposals(backend, &alice_signer) + .await + .unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); + assert_eq!(*alice_group.group_context_extensions(), new_extensions); +} + +#[apply(ciphersuites_and_backends)] +async fn gce_proposal_can_roundtrip( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let required_capabilities = + RequiredCapabilitiesExtension::new(&[], &DEFAULT_PROPOSAL_TYPES, &DEFAULT_CREDENTIAL_TYPES); + let (mut alice_group, mut bob_group, alice_signer, bob_signer) = group_setup( + ciphersuite, + required_capabilities, + None, + Extensions::empty(), + Capabilities::default(), + backend, + ) + .await; + + // Alice adds an extension + let new_extensions = Extensions::single(Extension::ApplicationId(ApplicationIdExtension::new( + b"test_mls", + ))); + let (gce_proposal, _) = alice_group + .propose_extensions(backend, &alice_signer, new_extensions.clone()) + .unwrap(); + let processed_message = bob_group + .process_message(backend, MlsMessageIn::from(gce_proposal)) + .await + .unwrap(); + let ProcessedMessageContent::ProposalMessage(gce_proposal) = processed_message.into_content() else { panic!("Not a remove proposal");}; + bob_group.store_pending_proposal(*gce_proposal); + let (commit, _, _) = bob_group + .commit_to_pending_proposals(backend, &bob_signer) + .await + .unwrap(); + bob_group.merge_pending_commit(backend).await.unwrap(); + assert_eq!(*bob_group.group_context_extensions(), new_extensions); + + let message = alice_group + .process_message(backend, MlsMessageIn::from(commit)) + .await + .unwrap(); + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = message.into_content() { + alice_group + .merge_staged_commit(backend, *staged_commit) + .await + .unwrap() + } + assert_eq!(*alice_group.group_context_extensions(), new_extensions); +} + +#[apply(ciphersuites_and_backends)] +async fn creating_commit_with_more_than_one_gce_proposal_should_fail( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let required_capabilities = + RequiredCapabilitiesExtension::new(&[], &DEFAULT_PROPOSAL_TYPES, &DEFAULT_CREDENTIAL_TYPES); + let (mut alice_group, _, alice_signer, _) = group_setup( + ciphersuite, + required_capabilities, + None, + Extensions::empty(), + Capabilities::default(), + backend, + ) + .await; + + // Alice creates a commit with 2 GroupContextExtension proposals, should fail + let application_id = Extension::ApplicationId(ApplicationIdExtension::new(b"mls_test")); + alice_group + .propose_extensions(backend, &alice_signer, Extensions::single(application_id)) + .unwrap(); + let external_senders = Extension::ExternalSenders(ExternalSendersExtension::default()); + alice_group + .propose_extensions(backend, &alice_signer, Extensions::single(external_senders)) + .unwrap(); + assert_eq!(alice_group.pending_proposals().count(), 2); + let commit = alice_group + .commit_to_pending_proposals(backend, &alice_signer) + .await; + assert!(matches!( + commit.unwrap_err(), + CommitToPendingProposalsError::CreateCommitError( + CreateCommitError::ProposalValidationError( + ProposalValidationError::TooManyGroupContextExtensions(2) + ) + ) + )); +} + +#[apply(ciphersuites_and_backends)] +async fn validating_commit_with_more_than_one_gce_proposal_should_fail( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let required_capabilities = + RequiredCapabilitiesExtension::new(&[], &DEFAULT_PROPOSAL_TYPES, &DEFAULT_CREDENTIAL_TYPES); + let (mut alice_group, mut bob_group, alice_signer, bob_signer) = group_setup( + ciphersuite, + required_capabilities, + None, + Extensions::empty(), + Capabilities::default(), + backend, + ) + .await; + + // Alice creates a commit with 2 GroupContextExtension proposals, should fail + let application_id = Extension::ApplicationId(ApplicationIdExtension::new(b"test_mls")); + let (first_gce_proposal, _) = alice_group + .propose_extensions(backend, &alice_signer, Extensions::single(application_id)) + .unwrap(); + let processed_message = bob_group + .process_message(backend, MlsMessageIn::from(first_gce_proposal)) + .await + .unwrap(); + let ProcessedMessageContent::ProposalMessage(gce_proposal) = processed_message.into_content() else { panic!("Not a proposal");}; + bob_group.store_pending_proposal(*gce_proposal); + + // Bob creates a commit with just 1 GCE proposal + let (commit, _, _) = bob_group + .commit_to_pending_proposals(backend, &bob_signer) + .await + .unwrap(); + + let external_senders = Extension::ExternalSenders(ExternalSendersExtension::default()); + let second_proposal = Proposal::GroupContextExtensions(GroupContextExtensionProposal::new( + Extensions::single(external_senders), + )); + + // We create a fake commit with 2 GCE proposal by rewriting the commit message + // because otherwise the library would prevent us to do so + let commit_with_2_gce_proposal = add_gce_proposal_to_commit( + commit.into(), + &bob_group, + &bob_signer, + second_proposal, + backend, + ); + + let process = alice_group + .process_message(backend, commit_with_2_gce_proposal) + .await + .unwrap_err(); + // Alice does not accept a commit with 2 GCE proposals + assert_eq!( + process, + ProcessMessageError::InvalidCommit(StageCommitError::ProposalValidationError( + ProposalValidationError::TooManyGroupContextExtensions(2) + )) + ); +} + +#[apply(ciphersuites_and_backends)] +async fn gce_proposal_must_be_applied_first_then_used_to_validate_other_add_proposals( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let required_capabilities = + RequiredCapabilitiesExtension::new(&[], &DEFAULT_PROPOSAL_TYPES, &DEFAULT_CREDENTIAL_TYPES); + let kp_extensions = Extensions::from_vec(vec![Extension::ExternalSenders( + ExternalSendersExtension::default(), + )]) + .unwrap(); + let kp_capabilities = Capabilities::new( + None, + None, + Some(&[ExtensionType::ExternalSenders]), + None, + Some(&DEFAULT_CREDENTIAL_TYPES), + ); + // Alice & Bob both support ExternalSenders + let (mut alice_group, mut bob_group, alice_signer, bob_signer) = group_setup( + ciphersuite, + required_capabilities, + None, + kp_extensions, + kp_capabilities, + backend, + ) + .await; + + // Propose to add ExternalSenders to RequiredCapabilities + let new_required_capabilities = + Extension::RequiredCapabilities(RequiredCapabilitiesExtension::new( + &[ExtensionType::ExternalSenders], + &DEFAULT_PROPOSAL_TYPES, + &DEFAULT_CREDENTIAL_TYPES, + )); + let (gce_proposal, _) = alice_group + .propose_extensions( + backend, + &alice_signer, + Extensions::single(new_required_capabilities), + ) + .unwrap(); + + // Charlie does not have ExternalSenders in its extensions, hence it should fail to be added to the group + let (_, charlie_key_package_bundle, ..) = setup_client("Charlie", ciphersuite, backend).await; + let (charlie_add_proposal, _) = alice_group + .propose_add_member( + backend, + &alice_signer, + charlie_key_package_bundle.key_package(), + ) + .unwrap(); + + let processed_message = bob_group + .process_message(backend, MlsMessageIn::from(charlie_add_proposal)) + .await + .unwrap(); + let ProcessedMessageContent::ProposalMessage(add_proposal) = processed_message.into_content() else { panic!("Not a remove proposal");}; + bob_group.store_pending_proposal(*add_proposal); + + let processed_message = bob_group + .process_message(backend, MlsMessageIn::from(gce_proposal)) + .await + .unwrap(); + let ProcessedMessageContent::ProposalMessage(gce_proposal) = processed_message.into_content() else { panic!("Not a remove proposal");}; + bob_group.store_pending_proposal(*gce_proposal); + + assert_eq!(bob_group.pending_proposals().count(), 2); + let commit = bob_group + .commit_to_pending_proposals(backend, &bob_signer) + .await; + // Bob does not accept the commit since adding Charlie would go against GCE proposal + assert!(matches!( + commit.unwrap_err(), + CommitToPendingProposalsError::CreateCommitError( + CreateCommitError::LibraryError( + e + ) + ) if e.to_string().contains( "Error description: Keypackage doens't support required capability") + )); +} + +#[apply(ciphersuites_and_backends)] +async fn gce_proposal_must_be_applied_first_then_used_to_validate_other_external_add_proposals( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let required_capabilities = + RequiredCapabilitiesExtension::new(&[], &DEFAULT_PROPOSAL_TYPES, &DEFAULT_CREDENTIAL_TYPES); + let kp_extensions = Extensions::from_vec(vec![Extension::ExternalSenders( + ExternalSendersExtension::default(), + )]) + .unwrap(); + let kp_capabilities = Capabilities::new( + None, + None, + Some(&[ExtensionType::ExternalSenders]), + None, + Some(&DEFAULT_CREDENTIAL_TYPES), + ); + // Alice support ExternalSenders + let (mut alice_group, _, alice_signer, _) = group_setup( + ciphersuite, + required_capabilities, + None, + kp_extensions, + kp_capabilities, + backend, + ) + .await; + + // Propose to add ExternalSenders to RequiredCapabilities + let new_required_capabilities = + Extension::RequiredCapabilities(RequiredCapabilitiesExtension::new( + &[ExtensionType::ExternalSenders], + &DEFAULT_PROPOSAL_TYPES, + &DEFAULT_CREDENTIAL_TYPES, + )); + alice_group + .propose_extensions( + backend, + &alice_signer, + Extensions::single(new_required_capabilities), + ) + .unwrap(); + + // Charlie does not have ExternalSenders in its extensions, hence it should fail to be added to the group + let (_, charlie_key_package_bundle, charlie_signer, _) = + setup_client("Charlie", ciphersuite, backend).await; + + let charlie_add_proposal = JoinProposal::new( + charlie_key_package_bundle.key_package, + alice_group.group_id().clone(), + alice_group.epoch(), + &charlie_signer, + ) + .unwrap(); + + let processed_message = alice_group + .process_message(backend, MlsMessageIn::from(charlie_add_proposal)) + .await + .unwrap(); + let ProcessedMessageContent::ExternalJoinProposalMessage(charlie_add_proposal) = processed_message.into_content() else { panic!("Not a proposal");}; + alice_group.store_pending_proposal(*charlie_add_proposal); + + assert_eq!(alice_group.pending_proposals().count(), 2); + let commit = alice_group + .commit_to_pending_proposals(backend, &alice_signer) + .await; + // Alice refuses to add Charlie because it does not satisfy GCE proposal + assert!(matches!( + commit.unwrap_err(), + CommitToPendingProposalsError::CreateCommitError( + CreateCommitError::LibraryError( + e + ) + ) if e.to_string().contains( "Error description: Keypackage doens't support required capability") + )); +} + +#[apply(ciphersuites_and_backends)] +async fn gce_proposal_must_be_applied_first_but_ignored_for_remove_proposals( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let required_capabilities = + RequiredCapabilitiesExtension::new(&[], &DEFAULT_PROPOSAL_TYPES, &DEFAULT_CREDENTIAL_TYPES); + // Alice & Bob have ExternalSenders support even though it is not required + let external_senders = Extension::ExternalSenders(ExternalSendersExtension::default()); + let kp_capabilities = Capabilities::new( + None, + None, + Some(&[ExtensionType::ExternalSenders]), + None, + Some(&DEFAULT_CREDENTIAL_TYPES), + ); + let (mut alice_group, mut bob_group, alice_signer, _) = group_setup( + ciphersuite, + required_capabilities, + None, + Extensions::from_vec(vec![external_senders]).unwrap(), + kp_capabilities, + backend, + ) + .await; + + // Charlie does not have ExternalSenders in its extensions + let (_, charlie_key_package_bundle, ..) = setup_client("Charlie", ciphersuite, backend).await; + let (commit, ..) = alice_group + .add_members( + backend, + &alice_signer, + &[charlie_key_package_bundle.key_package().clone()], + ) + .await + .unwrap(); + let commit = bob_group + .process_message(backend, MlsMessageIn::from(commit)) + .await + .unwrap(); + if let ProcessedMessageContent::StagedCommitMessage(commit) = commit.into_content() { + bob_group + .merge_staged_commit(backend, *commit) + .await + .unwrap(); + } + alice_group.merge_pending_commit(backend).await.unwrap(); + + // Propose requiring ExternalSenders, which Charlie does not support + let new_required_capabilities = + Extension::RequiredCapabilities(RequiredCapabilitiesExtension::new( + &[ExtensionType::ExternalSenders], + &DEFAULT_PROPOSAL_TYPES, + &DEFAULT_CREDENTIAL_TYPES, + )); + + let extension_proposal = alice_group.propose_extensions( + backend, + &alice_signer, + Extensions::single(new_required_capabilities.clone()), + ); + // because group contains Charlie which is incompatible with new extensions + assert!(extension_proposal.is_err()); + alice_group.clear_pending_proposals(); + + let charlie_index = alice_group + .members() + .find(|member| member.credential.identity() == b"Charlie") + .map(|member| member.index) + .unwrap(); + let (charlie_remove_proposal, _) = alice_group + .propose_remove_member(backend, &alice_signer, charlie_index) + .unwrap(); + + // Bob is able to process remove proposal + bob_group + .process_message(backend, MlsMessageIn::from(charlie_remove_proposal)) + .await + .unwrap(); + + let (extension_proposal, _) = alice_group + .propose_extensions( + backend, + &alice_signer, + Extensions::single(new_required_capabilities), + ) + .unwrap(); + assert_eq!(alice_group.pending_proposals().count(), 2); + + // Charlie does not support this extension. But since Charlie is proposed for removal it should not fail. + + // Bob is able to process GCE proposal + bob_group + .process_message(backend, MlsMessageIn::from(extension_proposal)) + .await + .unwrap(); + // Bob accepts GCE proposal since it also has one for removing Charlie + + // Once validating proposals, it should not fail as even though Charlie does not support the new + // required extensions, he is going to be removed from the group + alice_group + .commit_to_pending_proposals(backend, &alice_signer) + .await + .unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); + assert_eq!(alice_group.members().count(), 2); +} + +#[apply(ciphersuites_and_backends)] +async fn gce_proposal_must_be_applied_first_but_ignored_for_external_remove_proposals( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let (ds_credential_bundle, _, ds_signer, _) = setup_client("DS", ciphersuite, backend).await; + + let required_capabilities = + RequiredCapabilitiesExtension::new(&[], &DEFAULT_PROPOSAL_TYPES, &DEFAULT_CREDENTIAL_TYPES); + // Alice & Bob have ExternalSenders support even though it is not required + let external_sender = ExternalSender::new( + ds_credential_bundle.signature_key, + ds_credential_bundle.credential, + ); + let external_senders = Extension::ExternalSenders(vec![external_sender.clone()]); + let kp_extensions = Extensions::from_vec(vec![external_senders]).unwrap(); + let kp_capabilities = Capabilities::new( + None, + None, + Some(&[ExtensionType::ExternalSenders]), + None, + Some(&DEFAULT_CREDENTIAL_TYPES), + ); + let (mut alice_group, _, alice_signer, _) = group_setup( + ciphersuite, + required_capabilities, + Some(vec![external_sender]), + kp_extensions, + kp_capabilities, + backend, + ) + .await; + + // Charlie does not have ExternalSenders in its extensions + let (_, charlie_key_package_bundle, ..) = setup_client("Charlie", ciphersuite, backend).await; + alice_group + .add_members( + backend, + &alice_signer, + &[charlie_key_package_bundle.key_package().clone()], + ) + .await + .unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); + assert_eq!(alice_group.members().count(), 3); + + let charlie_index = alice_group + .members() + .find(|member| member.credential.identity() == b"Charlie") + .map(|member| member.index) + .unwrap(); + let charlie_ext_remove_proposal = ExternalProposal::new_remove( + charlie_index, + alice_group.group_id().clone(), + alice_group.epoch(), + &ds_signer, + SenderExtensionIndex::new(0), + ) + .unwrap(); + + let processed_message = alice_group + .process_message(backend, MlsMessageIn::from(charlie_ext_remove_proposal)) + .await + .unwrap(); + let ProcessedMessageContent::ProposalMessage(charlie_ext_remove_proposal) = processed_message.into_content() else { panic!("Not a remove proposal");}; + alice_group.store_pending_proposal(*charlie_ext_remove_proposal); + + // Propose requiring ExternalSenders, which Charlie does not support + let new_required_capabilities = + Extension::RequiredCapabilities(RequiredCapabilitiesExtension::new( + &[ExtensionType::ExternalSenders], + &DEFAULT_PROPOSAL_TYPES, + &DEFAULT_CREDENTIAL_TYPES, + )); + alice_group + .propose_extensions( + backend, + &alice_signer, + Extensions::single(new_required_capabilities), + ) + .unwrap(); + // Once validating proposals, it should not fail as even though Charlie does not support the new + // required extensions, he is going to be removed from the group + let commit = alice_group + .commit_to_pending_proposals(backend, &alice_signer) + .await; + assert!(commit.is_ok()); + alice_group.merge_pending_commit(backend).await.unwrap(); + assert_eq!(alice_group.members().count(), 2); +} + +pub async fn group_setup( + ciphersuite: Ciphersuite, + required_capabilities: RequiredCapabilitiesExtension, + external_senders: Option, + kp_extensions: Extensions, + kp_capabilities: Capabilities, + backend: &impl OpenMlsCryptoProvider, +) -> (MlsGroup, MlsGroup, SignatureKeyPair, SignatureKeyPair) { + // Basic group setup. + let (alice_credential_bundle, _kpb, alice_signer, _pk) = setup_client_with_extensions( + "Alice", + ciphersuite, + backend, + kp_extensions.clone(), + kp_capabilities.clone(), + ) + .await; + let (_, bob_key_package_bundle, bob_signer, _pk) = setup_client_with_extensions( + "Bob", + ciphersuite, + backend, + kp_extensions.clone(), + kp_capabilities.clone(), + ) + .await; + + let external_senders = external_senders.unwrap_or_default(); + let crypto_config = CryptoConfig { + ciphersuite, + version: ProtocolVersion::default(), + }; + + let cfg = MlsGroupConfig { + wire_format_policy: PURE_PLAINTEXT_WIRE_FORMAT_POLICY, + required_capabilities, + external_senders, + crypto_config, + leaf_extensions: kp_extensions, + leaf_capabilities: Some(kp_capabilities), + ..Default::default() + }; + let mut alice_group = MlsGroup::new(backend, &alice_signer, &cfg, alice_credential_bundle) + .await + .unwrap(); + + let (_, welcome, _) = alice_group + .add_members( + backend, + &alice_signer, + &[bob_key_package_bundle.key_package().clone()], + ) + .await + .unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); + let bob_group = MlsGroup::new_from_welcome( + backend, + &cfg, + welcome.into_welcome().unwrap(), + Some(alice_group.export_ratchet_tree().into()), + ) + .await + .unwrap(); + (alice_group, bob_group, alice_signer, bob_signer) +} + +fn add_gce_proposal_to_commit( + commit: MlsMessageIn, + group: &MlsGroup, + signer: &impl Signer, + proposal: Proposal, + backend: &impl OpenMlsCryptoProvider, +) -> MlsMessageIn { + let original_pub_msg = commit.into_plaintext().unwrap(); + let mut new_pub_msg = original_pub_msg.clone(); + + let mut commit = if let FramedContentBody::Commit(commit) = original_pub_msg.content() { + commit.clone() + } else { + panic!("Unexpected content type."); + }; + commit.proposals.push(ProposalOrRef::Proposal(proposal)); + new_pub_msg.set_content(FramedContentBody::Commit(commit)); + + let pub_msg = resign_message(group, new_pub_msg, &original_pub_msg, backend, signer); + MlsMessageIn::from(Into::::into(pub_msg)) +} diff --git a/openmls/src/group/tests/test_group.rs b/openmls/src/group/tests/test_group.rs index a778479274..e911bbe617 100644 --- a/openmls/src/group/tests/test_group.rs +++ b/openmls/src/group/tests/test_group.rs @@ -32,7 +32,8 @@ async fn create_commit_optional_path( ) .await; let bob_credential_with_keys = - generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend) + .await; // Generate Bob's KeyPackage let bob_key_package = generate_key_package( @@ -153,7 +154,7 @@ async fn create_commit_optional_path( backend, ResumptionPskStore::new(1024), ) - .await + .await { Ok(group) => group, Err(e) => panic!("Error creating group from Welcome: {e:?}"), @@ -234,7 +235,8 @@ async fn basic_group_setup(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypt ) .await; let bob_credential_with_keys = - generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend) + .await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -313,7 +315,8 @@ async fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto ) .await; let bob_credential_with_keys = - generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend) + .await; // Generate KeyPackages let bob_key_package_bundle = KeyPackageBundle::new( diff --git a/openmls/src/group/tests/test_past_secrets.rs b/openmls/src/group/tests/test_past_secrets.rs index fbfb821a08..3a1cb60b7b 100644 --- a/openmls/src/group/tests/test_past_secrets.rs +++ b/openmls/src/group/tests/test_past_secrets.rs @@ -26,12 +26,14 @@ async fn test_past_secrets_in_group( b"Alice".to_vec(), ciphersuite.signature_algorithm(), backend, - ).await; + ) + .await; let bob_credential_with_keys = generate_credential_with_key( b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend, - ).await; + ) + .await; // Generate KeyPackages let bob_key_package = generate_key_package( diff --git a/openmls/src/group/tests/test_proposal_validation.rs b/openmls/src/group/tests/test_proposal_validation.rs index 8794b5a8dd..b3e739f7f1 100644 --- a/openmls/src/group/tests/test_proposal_validation.rs +++ b/openmls/src/group/tests/test_proposal_validation.rs @@ -106,7 +106,8 @@ async fn new_test_group( // Generate credentials with keys let credential_with_key_and_signer = - generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key(identity.into(), ciphersuite.signature_algorithm(), backend) + .await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() @@ -139,7 +140,8 @@ async fn validation_test_setup( new_test_group("Alice", wire_format_policy, ciphersuite, backend).await; let bob_credential_with_key_and_signer = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend) + .await; let bob_key_package = generate_key_package( ciphersuite, @@ -245,14 +247,16 @@ async fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP ] { // 0. Initialize Alice let (alice_credential_with_keys, _) = - generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend).await; + generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend) + .await; // 1. Initialize Bob and Charlie let bob_credential_with_keys = generate_credential_with_key( b"Bob".to_vec(), ciphersuite.signature_algorithm(), backend, - ).await; + ) + .await; let mut charlie_credential_with_keys = generate_credential_with_key( b"Charlie".to_vec(), ciphersuite.signature_algorithm(), @@ -425,11 +429,13 @@ async fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr ] { // 0. Initialize Alice, Bob, and Charlie let (alice_credential_with_key, _) = - generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend).await; + generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend) + .await; let (bob_credential_with_key, mut bob_key_package) = generate_credential_with_key_and_key_package("Bob".into(), ciphersuite, backend).await; let (_charlie_credential_with_key, charlie_key_package) = - generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend).await; + generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend) + .await; match bob_and_charlie_share_keys { KeyUniqueness::NegativeSameKey => { @@ -622,13 +628,15 @@ async fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP Extensions::empty(), backend, bob_credential_with_key.clone(), - ).await; + ) + .await; let target_key_package = generate_key_package( ciphersuite, Extensions::empty(), backend, target_credential_with_key.clone(), - ).await; + ) + .await; // 1. Alice creates a group and tries to add Bob to it let mut alice_group = MlsGroup::new_with_group_id( @@ -846,7 +854,8 @@ async fn test_valsem103_valsem104(ciphersuite: Ciphersuite, backend: &impl OpenM ] { // 0. Initialize Alice and Bob let (alice_credential_with_key, _) = - generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend).await; + generate_credential_with_key_and_key_package("Alice".into(), ciphersuite, backend) + .await; let (bob_credential_with_key, mut bob_key_package) = generate_credential_with_key_and_key_package("Bob".into(), ciphersuite, backend).await; @@ -1062,7 +1071,8 @@ async fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr } = validation_test_setup(PURE_PLAINTEXT_WIRE_FORMAT_POLICY, ciphersuite, backend).await; let (charlie_credential_with_key, mut charlie_key_package) = - generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend).await; + generate_credential_with_key_and_key_package("Charlie".into(), ciphersuite, backend) + .await; let kpi = KeyPackageIn::from(charlie_key_package.clone()); kpi.validate(backend.crypto(), ProtocolVersion::Mls10) @@ -1110,7 +1120,8 @@ async fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr "Charlie".into(), ciphersuite, backend, - ).await; + ) + .await; // Let's just pick a ciphersuite that's not the one we're testing right now. let wrong_ciphersuite = match ciphersuite { diff --git a/openmls/src/group/tests/test_update_extensions.rs b/openmls/src/group/tests/test_update_extensions.rs new file mode 100644 index 0000000000..0e8d23164d --- /dev/null +++ b/openmls/src/group/tests/test_update_extensions.rs @@ -0,0 +1,103 @@ +use openmls_traits::{types::Ciphersuite, OpenMlsCryptoProvider}; + +use crate::{ + extensions::{ + ApplicationIdExtension, Extension, ExtensionType, Extensions, RequiredCapabilitiesExtension, + }, + framing::{validation::ProcessedMessageContent, MlsMessageIn}, + group::errors::UpdateExtensionsError, + test_utils::*, + treesync::{ + errors::{LeafNodeValidationError, MemberExtensionValidationError}, + node::leaf_node::Capabilities, + }, +}; + +use super::test_gce_proposals::{group_setup, DEFAULT_CREDENTIAL_TYPES, DEFAULT_PROPOSAL_TYPES}; + +#[apply(ciphersuites_and_backends)] +async fn gce_fails_when_it_contains_unsupported_extensions( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { + let required_capabilities = + RequiredCapabilitiesExtension::new(&[], &DEFAULT_PROPOSAL_TYPES, &DEFAULT_CREDENTIAL_TYPES); + // Bob has been created from a welcome message + let (mut alice_group, mut bob_group, alice_signer, bob_signer) = group_setup( + ciphersuite, + required_capabilities, + None, + Extensions::empty(), + Capabilities::default(), + backend, + ) + .await; + // Alice tries to add a required capability she doesn't support herself. + let required_key_id = Extension::RequiredCapabilities(RequiredCapabilitiesExtension::new( + &[ExtensionType::ExternalSenders], + &[], + &[], + )); + let e = alice_group.update_extensions(backend, &alice_signer, Extensions::single(required_key_id.clone())).await + .expect_err("Alice was able to create a gce proposal with a required extensions she doesn't support."); + assert!(matches!( + e, + UpdateExtensionsError::MemberExtensionValidationError( + MemberExtensionValidationError::LeafNodeValidationError( + LeafNodeValidationError::UnsupportedExtensions + ) + ) + )); + // Now Bob wants the ExternalSenders extension to be required. + // This should fail because Alice doesn't support it. + let e = bob_group + .update_extensions(backend, &bob_signer, Extensions::single(required_key_id)) + .await + .expect_err("Bob was able to create a gce proposal for an extension not supported by all other parties."); + assert!(matches!( + e, + UpdateExtensionsError::MemberExtensionValidationError( + MemberExtensionValidationError::LeafNodeValidationError( + LeafNodeValidationError::UnsupportedExtensions + ) + ) + )); +} + +#[apply(ciphersuites_and_backends)] +async fn gce_commit_can_roundtrip(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { + let required_capabilities = + RequiredCapabilitiesExtension::new(&[], &DEFAULT_PROPOSAL_TYPES, &DEFAULT_CREDENTIAL_TYPES); + let (mut alice_group, mut bob_group, alice_signer, _) = group_setup( + ciphersuite, + required_capabilities, + None, + Extensions::empty(), + Capabilities::default(), + backend, + ) + .await; + + // Alice adds an extension + let new_extensions = Extensions::single(Extension::ApplicationId(ApplicationIdExtension::new( + b"test_mls", + ))); + let (gce_commit, _, _) = alice_group + .update_extensions(backend, &alice_signer, new_extensions.clone()) + .await + .unwrap(); + alice_group.merge_pending_commit(backend).await.unwrap(); + assert_eq!(*alice_group.group_context_extensions(), new_extensions); + + // bob should be able to process the commit + let processed_message = bob_group + .process_message(backend, MlsMessageIn::from(gce_commit)) + .await + .unwrap(); + let ProcessedMessageContent::StagedCommitMessage(gce_commit) = processed_message.into_content() else { panic!("Not a remove proposal");}; + bob_group + .merge_staged_commit(backend, *gce_commit) + .await + .unwrap(); + assert_eq!(*bob_group.group_context_extensions(), new_extensions); +} diff --git a/openmls/src/group/tests/test_wire_format_policy.rs b/openmls/src/group/tests/test_wire_format_policy.rs index cc9db46cf3..92a784b10d 100644 --- a/openmls/src/group/tests/test_wire_format_policy.rs +++ b/openmls/src/group/tests/test_wire_format_policy.rs @@ -25,7 +25,8 @@ async fn create_group( // Generate credentials with keys let credential_with_key_and_signer = - generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), backend) + .await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::builder() @@ -57,7 +58,8 @@ async fn receive_message( ) -> MlsMessageIn { // Generate credentials with keys let bob_credential_with_key_and_signer = - generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend).await; + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), backend) + .await; // Generate KeyPackages let bob_key_package = generate_key_package( diff --git a/openmls/src/key_packages/mod.rs b/openmls/src/key_packages/mod.rs index 6294c0e75c..1099a58eb7 100644 --- a/openmls/src/key_packages/mod.rs +++ b/openmls/src/key_packages/mod.rs @@ -32,63 +32,34 @@ //! use openmls::prelude::*; //! use openmls_rust_crypto::OpenMlsRustCrypto; //! use openmls_basic_credential::SignatureKeyPair; +//! use tokio::runtime::Runtime; //! //! let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519; //! let backend = OpenMlsRustCrypto::default(); //! //! let credential = Credential::new("identity".into(), CredentialType::Basic).unwrap(); //! let signer = -//! SignatureKeyPair::new(ciphersuite.signature_algorithm()) +//! SignatureKeyPair::new(ciphersuite.signature_algorithm(), &mut *backend.rand().borrow_rand().unwrap()) //! .expect("Error generating a signature key pair."); //! let credential_with_key = CredentialWithKey { //! credential, //! signature_key: signer.public().into(), //! }; -//! let key_package = KeyPackage::builder() -//! .build( -//! CryptoConfig { -//! ciphersuite, -//! version: ProtocolVersion::default(), -//! }, -//! &backend, -//! &signer, -//! credential_with_key, -//! ) -//! .unwrap(); -//! ``` -//! -//! See [`KeyPackage`] for more details and other ways to create key packages. -//! -//! ## Loading key packages -//! -//! When getting key packages from another user the serialized bytes are parsed -//! as follows; -//! -//! ``` -//! use openmls::prelude::*; -//! use openmls::test_utils::hex_to_bytes; -//! use openmls_rust_crypto::OpenMlsRustCrypto; -//! -//! let backend = OpenMlsRustCrypto::default(); -//! let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519; -//! -//! let key_package_bytes = hex_to_bytes( -//! "00010003205D1DEB647BB5BDBB417022D1161076275ED861BD427B10DD07AC0233ED\ -//! D1D54F20428F4A2DAE538BD64154892A1A2C85D0F6888CE9977E09AC53DCB3471DECD\ -//! F6120DFC735AD28400BD79EFC8043B326D90A75A7B66F2A36EEDE79E3620A885E1825\ -//! 0001055361736861020001060001000200030200010C0001000200030004000500070\ -//! 200010100000000643170770000000064A03C87004040033B4FCBCC85D1AFE86241E8\ -//! 5CECB2B496C9D7F8CE98BEDDBDFFC02EB2CA54BC881A84CA800345005BB2622EDC377\ -//! B54F223E160BFC872D5FEA2287E16A44703004040D33B9F1DA0F86C78855036B5FADD\ -//! 35983E14188A0798B455211654E594E3955026CD1AD275D284CA8FBEA5356A0BA4C6F\ -//! 799707C0046C05FBF52FF1FC1E9640A"); -//! -//! let key_package_in = KeyPackageIn::tls_deserialize(&mut key_package_bytes.as_slice()) -//! .expect("Could not deserialize KeyPackage"); -//! -//! let key_package = key_package_in -//! .validate(backend.crypto(), ProtocolVersion::Mls10) -//! .expect("Invalid KeyPackage"); +//! let rt = Runtime::new().unwrap(); +//! rt.block_on(async move { +//! let key_package = KeyPackage::builder() +//! .build( +//! CryptoConfig { +//! ciphersuite, +//! version: ProtocolVersion::default(), +//! }, +//! &backend, +//! &signer, +//! credential_with_key, +//! ) +//! .await +//! .unwrap(); +//! }); //! ``` //! //! See [`KeyPackage`] for more details on how to use key packages. @@ -461,7 +432,7 @@ impl KeyPackage { // The key is the public key. backend .key_store() - .store::(&init_key.public, &init_key.private.into()) + .store::(&init_key.public, &init_key.private) .await .map_err(KeyPackageNewError::KeyStoreError)?; @@ -663,10 +634,7 @@ impl KeyPackageBuilder { // The key is the public key. backend .key_store() - .store::( - key_package.hpke_init_key().as_slice(), - &init_private_key.into(), - ) + .store::(key_package.hpke_init_key().as_slice(), &init_private_key) .await .map_err(KeyPackageNewError::KeyStoreError)?; @@ -698,13 +666,17 @@ impl KeyPackageBundle { #[cfg(test)] impl KeyPackageBundle { - pub(crate) async fn new( + pub(crate) async fn new_with_extensions( backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, ciphersuite: Ciphersuite, credential_with_key: CredentialWithKey, + extensions: Extensions, + capabilities: Capabilities, ) -> Self { let key_package = KeyPackage::builder() + .leaf_node_extensions(extensions) + .leaf_node_capabilities(capabilities) .build( CryptoConfig { ciphersuite, @@ -726,4 +698,20 @@ impl KeyPackageBundle { private_key, } } + pub(crate) async fn new( + backend: &impl OpenMlsCryptoProvider, + signer: &impl Signer, + ciphersuite: Ciphersuite, + credential_with_key: CredentialWithKey, + ) -> Self { + Self::new_with_extensions( + backend, + signer, + ciphersuite, + credential_with_key, + Extensions::default(), + Capabilities::default(), + ) + .await + } } diff --git a/openmls/src/lib.rs b/openmls/src/lib.rs index aed85590a1..fc657d72e2 100644 --- a/openmls/src/lib.rs +++ b/openmls/src/lib.rs @@ -7,6 +7,7 @@ //! use openmls::prelude::{*, config::CryptoConfig}; //! use openmls_rust_crypto::{OpenMlsRustCrypto}; //! use openmls_basic_credential::SignatureKeyPair; +//! use tokio::runtime::Runtime; //! //! // Define ciphersuite ... //! let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519; @@ -16,7 +17,7 @@ //! // Now let's create two participants. //! //! // A helper to create and store credentials. -//! fn generate_credential_with_key( +//! async fn generate_credential_with_key( //! identity: Vec, //! credential_type: CredentialType, //! signature_algorithm: SignatureScheme, @@ -24,15 +25,16 @@ //! ) -> (CredentialWithKey, SignatureKeyPair) { //! let credential = Credential::new(identity, credential_type).unwrap(); //! let signature_keys = -//! SignatureKeyPair::new(signature_algorithm) +//! SignatureKeyPair::new(signature_algorithm, &mut *backend.rand().borrow_rand().unwrap()) //! .expect("Error generating a signature key pair."); //! //! // Store the signature key into the key store so OpenMLS has access //! // to it. //! signature_keys //! .store(backend.key_store()) +//! .await //! .expect("Error storing signature keys in key store."); -//! +//! //! ( //! CredentialWithKey { //! credential, @@ -43,7 +45,7 @@ //! } //! //! // A helper to create key package bundles. -//! fn generate_key_package( +//! async fn generate_key_package( //! ciphersuite: Ciphersuite, //! backend: &impl OpenMlsCryptoProvider, //! signer: &SignatureKeyPair, @@ -60,77 +62,86 @@ //! signer, //! credential_with_key, //! ) +//! .await //! .unwrap() //! } //! -//! // First they need credentials to identify them -//! let (sasha_credential_with_key, sasha_signer) = generate_credential_with_key( -//! "Sasha".into(), -//! CredentialType::Basic, -//! ciphersuite.signature_algorithm(), -//! backend, -//! ); -//! -//! let (maxim_credential_with_key, maxim_signer) = generate_credential_with_key( -//! "Maxim".into(), -//! CredentialType::Basic, -//! ciphersuite.signature_algorithm(), -//! backend, -//! ); -//! -//! // Then they generate key packages to facilitate the asynchronous handshakes -//! // in MLS -//! -//! // Generate KeyPackages -//! let maxim_key_package = generate_key_package(ciphersuite, backend, &maxim_signer, maxim_credential_with_key); -//! -//! // Now Sasha starts a new group ... -//! let mut sasha_group = MlsGroup::new( -//! backend, -//! &sasha_signer, -//! &MlsGroupConfig::default(), -//! sasha_credential_with_key, -//! ) -//! .expect("An unexpected error occurred."); -//! -//! // ... and invites Maxim. -//! // The key package has to be retrieved from Maxim in some way. Most likely -//! // via a server storing key packages for users. -//! let (mls_message_out, welcome_out, group_info) = sasha_group -//! .add_members(backend, &sasha_signer, &[maxim_key_package]) -//! .expect("Could not add members."); -//! -//! // Sasha merges the pending commit that adds Maxim. -//! sasha_group -//! .merge_pending_commit(backend) -//! .expect("error merging pending commit"); -//! -//! // Sascha serializes the [`MlsMessageOut`] containing the [`Welcome`]. -//! let serialized_welcome = welcome_out -//! .tls_serialize_detached() -//! .expect("Error serializing welcome"); -//! -//! // Maxim can now de-serialize the message as an [`MlsMessageIn`] ... -//! let mls_message_in = MlsMessageIn::tls_deserialize(&mut serialized_welcome.as_slice()) -//! .expect("An unexpected error occurred."); -//! -//! // ... and inspect the message. -//! let welcome = match mls_message_in.extract() { -//! MlsMessageInBody::Welcome(welcome) => welcome, -//! // We know it's a welcome message, so we ignore all other cases. -//! _ => unreachable!("Unexpected message type."), -//! }; -//! -//! // Now Maxim can join the group. -//! let mut maxim_group = MlsGroup::new_from_welcome( -//! backend, -//! &MlsGroupConfig::default(), -//! welcome, -//! // The public tree is need and transferred out of band. -//! // It is also possible to use the [`RatchetTreeExtension`] -//! Some(sasha_group.export_ratchet_tree().into()), -//! ) -//! .expect("Error joining group from Welcome"); +//! let rt = Runtime::new().unwrap(); +//! rt.block_on(async move { +//! +//! // First they need credentials to identify them +//! let (sasha_credential_with_key, sasha_signer) = generate_credential_with_key( +//! "Sasha".into(), +//! CredentialType::Basic, +//! ciphersuite.signature_algorithm(), +//! backend, +//! ).await; +//! +//! let (maxim_credential_with_key, maxim_signer) = generate_credential_with_key( +//! "Maxim".into(), +//! CredentialType::Basic, +//! ciphersuite.signature_algorithm(), +//! backend, +//! ).await; +//! +//! // Then they generate key packages to facilitate the asynchronous handshakes +//! // in MLS +//! +//! // Generate KeyPackages +//! let maxim_key_package = generate_key_package(ciphersuite, backend, &maxim_signer, maxim_credential_with_key).await; +//! +//! // Now Sasha starts a new group ... +//! let mut sasha_group = MlsGroup::new( +//! backend, +//! &sasha_signer, +//! &MlsGroupConfig::default(), +//! sasha_credential_with_key, +//! ) +//! .await +//! .expect("An unexpected error occurred."); +//! +//! // ... and invites Maxim. +//! // The key package has to be retrieved from Maxim in some way. Most likely +//! // via a server storing key packages for users. +//! let (mls_message_out, welcome_out, group_info) = sasha_group +//! .add_members(backend, &sasha_signer, &[maxim_key_package]) +//! .await +//! .expect("Could not add members."); +//! +//! // Sasha merges the pending commit that adds Maxim. +//! sasha_group +//! .merge_pending_commit(backend) +//! .await +//! .expect("error merging pending commit"); +//! +//! // Sascha serializes the [`MlsMessageOut`] containing the [`Welcome`]. +//! let serialized_welcome = welcome_out +//! .tls_serialize_detached() +//! .expect("Error serializing welcome"); +//! +//! // Maxim can now de-serialize the message as an [`MlsMessageIn`] ... +//! let mls_message_in = MlsMessageIn::tls_deserialize(&mut serialized_welcome.as_slice()) +//! .expect("An unexpected error occurred."); +//! +//! // ... and inspect the message. +//! let welcome = match mls_message_in.extract() { +//! MlsMessageInBody::Welcome(welcome) => welcome, +//! // We know it's a welcome message, so we ignore all other cases. +//! _ => unreachable!("Unexpected message type."), +//! }; +//! +//! // Now Maxim can join the group. +//! let mut maxim_group = MlsGroup::new_from_welcome( +//! backend, +//! &MlsGroupConfig::default(), +//! welcome, +//! // The public tree is need and transferred out of band. +//! // It is also possible to use the [`RatchetTreeExtension`] +//! Some(sasha_group.export_ratchet_tree().into()), +//! ) +//! .await +//! .expect("Error joining group from Welcome"); +//! }); //! ``` //! //! [//]: # "links and badges" diff --git a/openmls/src/messages/proposals.rs b/openmls/src/messages/proposals.rs index b67dce46d0..67aab34186 100644 --- a/openmls/src/messages/proposals.rs +++ b/openmls/src/messages/proposals.rs @@ -124,6 +124,19 @@ impl ProposalType { ) } + /// Returns a slice of all supported proposal types by OpenMls + pub const fn supported_types() -> &'static [ProposalType] { + &[ + ProposalType::Add, + ProposalType::Update, + ProposalType::Remove, + ProposalType::PreSharedKey, + ProposalType::Reinit, + ProposalType::ExternalInit, + ProposalType::GroupContextExtensions, + ] + } + /// Returns `true` if the proposal type requires a path and `false` pub fn is_path_required(&self) -> bool { matches!( @@ -426,10 +439,13 @@ pub struct GroupContextExtensionProposal { impl GroupContextExtensionProposal { /// Create a new [`GroupContextExtensionProposal`]. - #[cfg(test)] pub(crate) fn new(extensions: Extensions) -> Self { Self { extensions } } + + pub(crate) fn extensions(&self) -> &Extensions { + &self.extensions + } } // Crate-only types diff --git a/openmls/src/messages/tests/test_codec.rs b/openmls/src/messages/tests/test_codec.rs index 4e4eaacd86..b2b9edc574 100644 --- a/openmls/src/messages/tests/test_codec.rs +++ b/openmls/src/messages/tests/test_codec.rs @@ -80,7 +80,10 @@ async fn test_pre_shared_key_proposal_codec(backend: &impl OpenMlsCryptoProvider /// Test the encoding for ReInitProposal, that also covers some of the /// other PSK-related structs #[apply(ciphersuites_and_backends)] -async fn test_reinit_proposal_codec(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_reinit_proposal_codec( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { let orig = ReInitProposal { group_id: GroupId::random(backend), version: ProtocolVersion::default(), diff --git a/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs b/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs index bc7de1d6d8..a3e5d7a7f3 100644 --- a/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs +++ b/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs @@ -45,7 +45,10 @@ async fn test_max_forward_distance(ciphersuite: Ciphersuite, backend: &impl Open // Test out-of-order generations #[apply(ciphersuites_and_backends)] -async fn test_out_of_order_generations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_out_of_order_generations( + ciphersuite: Ciphersuite, + backend: &impl OpenMlsCryptoProvider, +) { let configuration = &SenderRatchetConfiguration::default(); let secret = Secret::random(ciphersuite, backend, ProtocolVersion::Mls10) .expect("Not enough randomness."); diff --git a/openmls/src/treesync/errors.rs b/openmls/src/treesync/errors.rs index 27ab8a9cfa..77714379fa 100644 --- a/openmls/src/treesync/errors.rs +++ b/openmls/src/treesync/errors.rs @@ -6,7 +6,8 @@ use thiserror::Error; use super::*; use crate::{ - binary_tree::MlsBinaryTreeDiffError, ciphersuite::signable::SignatureError, error::LibraryError, + binary_tree::MlsBinaryTreeDiffError, ciphersuite::signable::SignatureError, + error::LibraryError, extensions::errors::ExtensionError, }; // === Public errors === @@ -203,6 +204,20 @@ pub(crate) enum TreeKemError { PathSecretError(#[from] PathSecretError), } +/// Errors that can happen during leaf node extension support validation. +#[derive(Clone, Debug, Error, PartialEq)] +pub enum MemberExtensionValidationError { + /// See [`LibraryError`] for more details. + #[error(transparent)] + LibraryError(#[from] LibraryError), + /// See [`LeafNodeValidationError`] for more details. + #[error(transparent)] + LeafNodeValidationError(#[from] LeafNodeValidationError), + /// See [`ExtensionError`] for more details. + #[error(transparent)] + ExtensionError(#[from] ExtensionError), +} + /// Errors that can happen during leaf node validation. #[derive(Clone, Debug, Error, Eq, PartialEq)] pub enum LeafNodeValidationError { diff --git a/openmls/src/treesync/mod.rs b/openmls/src/treesync/mod.rs index ee874bb7e8..3d7d1493e3 100644 --- a/openmls/src/treesync/mod.rs +++ b/openmls/src/treesync/mod.rs @@ -533,6 +533,13 @@ impl TreeSync { .filter_map(|(_, tsn)| tsn.node().as_ref()) } + /// Returns an indexed list of [`LeafNodeIndex`]es containing only full nodes. + pub(crate) fn full_leaves_indexed(&self) -> impl Iterator { + self.tree + .leaves() + .filter_map(|(index, tsn)| tsn.node().as_ref().map(|node| (index, node))) + } + /// Returns the index of the last full leaf in the tree. fn rightmost_full_leaf(&self) -> LeafNodeIndex { let mut index = LeafNodeIndex::new(0); diff --git a/openmls/src/treesync/node/encryption_keys.rs b/openmls/src/treesync/node/encryption_keys.rs index c8918c1006..513a21f7c5 100644 --- a/openmls/src/treesync/node/encryption_keys.rs +++ b/openmls/src/treesync/node/encryption_keys.rs @@ -147,7 +147,7 @@ impl EncryptionKeyPair { ) -> Result<(), KeyStore::Error> { backend .key_store() - .store(&self.public_key().as_slice(), self) + .store(self.public_key().as_slice(), self) .await } @@ -160,7 +160,7 @@ impl EncryptionKeyPair { backend: &impl OpenMlsCryptoProvider, encryption_key: &EncryptionKey, ) -> Option { - backend.key_store().read(&encryption_key.as_slice()).await + backend.key_store().read(encryption_key.as_slice()).await } /// Delete the [`EncryptionKeyPair`] from the key store of the `backend`. @@ -174,7 +174,7 @@ impl EncryptionKeyPair { ) -> Result<(), KeyStore::Error> { backend .key_store() - .delete::(&self.public_key().as_slice()) + .delete::(self.public_key().as_slice()) .await } diff --git a/openmls/src/treesync/node/leaf_node.rs b/openmls/src/treesync/node/leaf_node.rs index 0900b1ac15..94377177bf 100644 --- a/openmls/src/treesync/node/leaf_node.rs +++ b/openmls/src/treesync/node/leaf_node.rs @@ -24,7 +24,6 @@ use crate::{ versions::ProtocolVersion, }; -#[cfg(test)] use crate::treesync::errors::LeafNodeValidationError; mod capabilities; @@ -387,6 +386,20 @@ impl LeafNode { .contains(extension_type) || default_extensions().iter().any(|et| et == extension_type) } + + /// Check whether this leaf node supports all the required extensions + /// in the provided list. + pub(crate) fn check_extension_support( + &self, + extensions: &[ExtensionType], + ) -> Result<(), LeafNodeValidationError> { + for required in extensions.iter() { + if !self.supports_extension(required) { + return Err(LeafNodeValidationError::UnsupportedExtensions); + } + } + Ok(()) + } } #[cfg(test)] @@ -416,20 +429,6 @@ impl LeafNode { pub fn capabilities_mut(&mut self) -> &mut Capabilities { &mut self.payload.capabilities } - - /// Check whether the this leaf node supports all the required extensions - /// in the provided list. - pub(crate) fn check_extension_support( - &self, - extensions: &[ExtensionType], - ) -> Result<(), LeafNodeValidationError> { - for required in extensions.iter() { - if !self.supports_extension(required) { - return Err(LeafNodeValidationError::UnsupportedExtensions); - } - } - Ok(()) - } } #[cfg(any(feature = "test-utils", test))] diff --git a/openmls/src/treesync/node/leaf_node/capabilities.rs b/openmls/src/treesync/node/leaf_node/capabilities.rs index d33b1f61d0..3f40c757f7 100644 --- a/openmls/src/treesync/node/leaf_node/capabilities.rs +++ b/openmls/src/treesync/node/leaf_node/capabilities.rs @@ -64,7 +64,7 @@ impl Capabilities { }, proposals: match proposals { Some(p) => p.into(), - None => vec![], + None => ProposalType::supported_types().into(), }, credentials: match credentials { Some(c) => c.into(), @@ -152,7 +152,7 @@ impl Capabilities { } /// Check if these [`Capabilities`] contain all the extensions. - pub(crate) fn contain_extensions(&self, extension: &Extensions) -> bool { + pub(crate) fn are_extensions_supported(&self, extension: &Extensions) -> bool { extension .iter() .map(Extension::extension_type) diff --git a/openmls/src/treesync/tests_and_kats/tests.rs b/openmls/src/treesync/tests_and_kats/tests.rs index 14e8cc50f8..cd14121b56 100644 --- a/openmls/src/treesync/tests_and_kats/tests.rs +++ b/openmls/src/treesync/tests_and_kats/tests.rs @@ -42,7 +42,8 @@ async fn that_commit_secret_is_derived_from_end_of_update_path_not_root( name: Vec, ) -> Member { let credential_with_key_and_signer = - generate_credential_with_key(name.clone(), ciphersuite.signature_algorithm(), &backend).await; + generate_credential_with_key(name.clone(), ciphersuite.signature_algorithm(), &backend) + .await; let key_package = KeyPackage::builder() .build( CryptoConfig::with_default_version(ciphersuite), diff --git a/openmls_rust_crypto/src/provider.rs b/openmls_rust_crypto/src/provider.rs index 5c9cd37f5c..f9cf51491a 100644 --- a/openmls_rust_crypto/src/provider.rs +++ b/openmls_rust_crypto/src/provider.rs @@ -252,12 +252,12 @@ impl OpenMlsCrypto for RustCrypto { SignatureScheme::ECDSA_SECP256R1_SHA256 => { let sk = p256::ecdsa::SigningKey::random(&mut *rng); let pk = sk.verifying_key().to_encoded_point(false).to_bytes().into(); - Ok((sk.to_bytes().to_vec().into(), pk)) + Ok((sk.to_bytes().to_vec(), pk)) } SignatureScheme::ECDSA_SECP384R1_SHA384 => { let sk = p384::ecdsa::SigningKey::random(&mut *rng); let pk = sk.verifying_key().to_encoded_point(false).to_bytes().into(); - Ok((sk.to_bytes().to_vec().into(), pk)) + Ok((sk.to_bytes().to_vec(), pk)) } SignatureScheme::ED25519 => { let mut rng = self @@ -419,7 +419,7 @@ impl OpenMlsCrypto for RustCrypto { hpke::kdf::HkdfSha384, hpke::kem::DhP384HkdfSha384, >(pk_r, info, aad, ptxt, &mut *rng), - _ => return Err(CryptoError::UnsupportedKem), + _ => Err(CryptoError::UnsupportedKem), } } @@ -625,7 +625,7 @@ impl OpenMlsCrypto for RustCrypto { HpkeKemType::DhKem25519 => { hpke_core::hpke_derive_keypair::(ikm) } - _ => return Err(CryptoError::UnsupportedKem), + _ => Err(CryptoError::UnsupportedKem), } } } @@ -746,7 +746,7 @@ mod hpke_core { ctx.export(export_info, &mut export) .map_err(|_| CryptoError::ExporterError)?; - Ok((kem_output.to_bytes().to_vec().into(), export.into())) + Ok((kem_output.to_bytes().to_vec(), export)) } } From e3930d962e4821e0d5caa52614a2ac34e2782426 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Mon, 22 May 2023 16:58:41 +0200 Subject: [PATCH 03/54] fix: Make LeafNode lifetime visible --- openmls/src/key_packages/lifetime.rs | 2 +- openmls/src/treesync/node/leaf_node.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openmls/src/key_packages/lifetime.rs b/openmls/src/key_packages/lifetime.rs index 9da1f46573..4a75a91dcb 100644 --- a/openmls/src/key_packages/lifetime.rs +++ b/openmls/src/key_packages/lifetime.rs @@ -61,7 +61,7 @@ impl Lifetime { } /// Returns true if this lifetime is valid. - pub(crate) fn is_valid(&self) -> bool { + pub fn is_valid(&self) -> bool { match SystemTime::now() .duration_since(UNIX_EPOCH) .map(|duration| duration.as_secs()) diff --git a/openmls/src/treesync/node/leaf_node.rs b/openmls/src/treesync/node/leaf_node.rs index 94377177bf..8123ee92b8 100644 --- a/openmls/src/treesync/node/leaf_node.rs +++ b/openmls/src/treesync/node/leaf_node.rs @@ -355,7 +355,7 @@ impl LeafNode { /// Returns the [`Lifetime`] if present. /// `None` otherwise. - pub(crate) fn life_time(&self) -> Option<&Lifetime> { + pub fn life_time(&self) -> Option<&Lifetime> { if let LeafNodeSource::KeyPackage(life_time) = &self.payload.leaf_node_source { Some(life_time) } else { From fb2b49c5b18c50d5a7074d9d57a2b8d5c2d4fec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Augusto=20C=C3=A9sar=20Dias?= Date: Tue, 23 May 2023 09:11:05 +0200 Subject: [PATCH 04/54] fix: Pass pending proposals to check member support on gce extensions --- openmls/src/group/mls_group/extension.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmls/src/group/mls_group/extension.rs b/openmls/src/group/mls_group/extension.rs index a585988b56..a68155da24 100644 --- a/openmls/src/group/mls_group/extension.rs +++ b/openmls/src/group/mls_group/extension.rs @@ -74,7 +74,7 @@ impl MlsGroup { > { self.is_operational()?; self.group - .members_support_extensions(&extensions, iter::empty())?; + .members_support_extensions(&extensions, self.pending_proposals())?; let proposal = Proposal::GroupContextExtensions(GroupContextExtensionProposal::new(extensions)); let params = CreateCommitParams::builder() From aae07a2ce7717c3162430699978878a88a28f84e Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Tue, 23 May 2023 17:15:28 +0200 Subject: [PATCH 05/54] Opened up API --- .../binary_tree/array_representation/diff.rs | 2 +- .../binary_tree/array_representation/tree.rs | 2 +- openmls/src/binary_tree/mod.rs | 4 +- openmls/src/ciphersuite/hpke.rs | 2 +- openmls/src/ciphersuite/mac.rs | 9 ++- openmls/src/ciphersuite/secret.rs | 2 +- openmls/src/error.rs | 6 +- .../extensions/external_sender_extension.rs | 4 +- .../src/extensions/required_capabilities.rs | 6 +- openmls/src/framing/errors.rs | 2 +- openmls/src/framing/message_out.rs | 13 +++- openmls/src/framing/mls_auth_content.rs | 36 +++++------ openmls/src/framing/mls_auth_content_in.rs | 22 +++---- openmls/src/framing/mls_content.rs | 4 +- openmls/src/framing/mls_content_in.rs | 12 ++-- openmls/src/framing/mod.rs | 6 +- openmls/src/framing/public_message.rs | 4 +- openmls/src/framing/public_message_in.rs | 27 +++++---- openmls/src/framing/validation.rs | 4 +- openmls/src/group/core_group/proposals.rs | 26 ++++---- openmls/src/group/core_group/staged_commit.rs | 17 ++++++ openmls/src/group/errors.rs | 14 ++--- openmls/src/group/public_group/diff.rs | 7 +++ openmls/src/group/public_group/mod.rs | 2 +- openmls/src/messages/mod.rs | 60 +++++++++---------- openmls/src/messages/proposals.rs | 2 +- openmls/src/schedule/errors.rs | 4 +- openmls/src/schedule/mod.rs | 4 +- openmls/src/treesync/errors.rs | 12 ++-- openmls/src/treesync/node/encryption_keys.rs | 4 +- .../treesync/node/leaf_node/capabilities.rs | 6 +- 31 files changed, 181 insertions(+), 144 deletions(-) diff --git a/openmls/src/binary_tree/array_representation/diff.rs b/openmls/src/binary_tree/array_representation/diff.rs index 36e69d6140..6d58cbf915 100644 --- a/openmls/src/binary_tree/array_representation/diff.rs +++ b/openmls/src/binary_tree/array_representation/diff.rs @@ -400,7 +400,7 @@ impl<'a, L: Clone + Debug + Default, P: Clone + Debug + Default> AbDiff<'a, L, P /// Binary Tree Diff error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum ABinaryTreeDiffError { +pub enum ABinaryTreeDiffError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), diff --git a/openmls/src/binary_tree/array_representation/tree.rs b/openmls/src/binary_tree/array_representation/tree.rs index c0cb943d6e..f8a8dd69df 100644 --- a/openmls/src/binary_tree/array_representation/tree.rs +++ b/openmls/src/binary_tree/array_representation/tree.rs @@ -226,7 +226,7 @@ impl ABinaryTree { /// Binary Tree error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum ABinaryTreeError { +pub enum ABinaryTreeError { /// Adding nodes exceeds the maximum possible size of the tree. #[error("Adding nodes exceeds the maximum possible size of the tree.")] OutOfRange, diff --git a/openmls/src/binary_tree/mod.rs b/openmls/src/binary_tree/mod.rs index 2d21396147..dedcd8573e 100644 --- a/openmls/src/binary_tree/mod.rs +++ b/openmls/src/binary_tree/mod.rs @@ -23,5 +23,5 @@ mod test_binary_tree; pub(crate) type MlsBinaryTree = ABinaryTree; pub(crate) type MlsBinaryTreeDiff<'a, L, P> = AbDiff<'a, L, P>; pub(crate) type StagedMlsBinaryTreeDiff = StagedAbDiff; -pub(crate) type MlsBinaryTreeError = ABinaryTreeError; -pub(crate) type MlsBinaryTreeDiffError = ABinaryTreeDiffError; +pub type MlsBinaryTreeError = ABinaryTreeError; +pub type MlsBinaryTreeDiffError = ABinaryTreeDiffError; diff --git a/openmls/src/ciphersuite/hpke.rs b/openmls/src/ciphersuite/hpke.rs index cb2ae8320f..df6418324c 100644 --- a/openmls/src/ciphersuite/hpke.rs +++ b/openmls/src/ciphersuite/hpke.rs @@ -45,7 +45,7 @@ use super::LABEL_PREFIX; /// HPKE labeled encryption errors. #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum Error { +pub enum Error { /// Error while serializing content. This should only happen if a bounds check was missing. #[error( "Error while serializing content. This should only happen if a bounds check was missing." diff --git a/openmls/src/ciphersuite/mac.rs b/openmls/src/ciphersuite/mac.rs index c0cf956088..c1ec86ec83 100644 --- a/openmls/src/ciphersuite/mac.rs +++ b/openmls/src/ciphersuite/mac.rs @@ -4,7 +4,7 @@ use super::*; /// /// opaque MAC; #[derive(Debug, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize)] -pub(crate) struct Mac { +pub struct Mac { pub(crate) mac_value: VLBytes, } @@ -43,3 +43,10 @@ impl Mac { self.mac_value.push(last_bits); } } + +impl std::ops::Deref for Mac { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + self.mac_value.as_slice() + } +} diff --git a/openmls/src/ciphersuite/secret.rs b/openmls/src/ciphersuite/secret.rs index f2ad827416..ce59d6189d 100644 --- a/openmls/src/ciphersuite/secret.rs +++ b/openmls/src/ciphersuite/secret.rs @@ -11,7 +11,7 @@ use super::{kdf_label::KdfLabel, *}; /// Note: This has a hand-written `Debug` implementation. /// Please update as well when changing this struct. #[derive(Clone, Serialize, Deserialize, Eq)] -pub(crate) struct Secret { +pub struct Secret { pub(in crate::ciphersuite) ciphersuite: Ciphersuite, pub(in crate::ciphersuite) value: SecretVLBytes, pub(in crate::ciphersuite) mls_version: ProtocolVersion, diff --git a/openmls/src/error.rs b/openmls/src/error.rs index 919d2f2f5a..864044b6b1 100644 --- a/openmls/src/error.rs +++ b/openmls/src/error.rs @@ -74,7 +74,7 @@ pub struct LibraryError { impl LibraryError { /// A custom error (typically to avoid an unwrap()) - pub(crate) fn custom(s: &'static str) -> Self { + pub fn custom(s: &'static str) -> Self { let bt = Backtrace::new(); let display_string = format!("Error description: {s}\n Backtrace:\n{bt:?}"); Self { @@ -83,14 +83,14 @@ impl LibraryError { } /// Used when encoding doesn't work because of missing bound checks - pub(crate) fn missing_bound_check(e: TlsCodecError) -> Self { + pub fn missing_bound_check(e: TlsCodecError) -> Self { Self { internal: InternalLibraryError::MissingBoundsCheck(e), } } /// Used when the crypto provider returns an unexpected error - pub(crate) fn unexpected_crypto_error(e: CryptoError) -> Self { + pub fn unexpected_crypto_error(e: CryptoError) -> Self { Self { internal: InternalLibraryError::CryptoError(e), } diff --git a/openmls/src/extensions/external_sender_extension.rs b/openmls/src/extensions/external_sender_extension.rs index 0df444c85e..522262c332 100644 --- a/openmls/src/extensions/external_sender_extension.rs +++ b/openmls/src/extensions/external_sender_extension.rs @@ -29,11 +29,11 @@ impl ExternalSender { } } - pub(crate) fn credential(&self) -> &Credential { + pub fn credential(&self) -> &Credential { &self.credential } - pub(crate) fn signature_key(&self) -> &SignaturePublicKey { + pub fn signature_key(&self) -> &SignaturePublicKey { &self.signature_key } } diff --git a/openmls/src/extensions/required_capabilities.rs b/openmls/src/extensions/required_capabilities.rs index 00d128f072..d8624093d5 100644 --- a/openmls/src/extensions/required_capabilities.rs +++ b/openmls/src/extensions/required_capabilities.rs @@ -60,18 +60,18 @@ impl RequiredCapabilitiesExtension { } /// Get a slice with the required extension types. - pub(crate) fn extension_types(&self) -> &[ExtensionType] { + pub fn extension_types(&self) -> &[ExtensionType] { self.extension_types.as_slice() } /// Get a slice with the required proposal types. - pub(crate) fn proposal_types(&self) -> &[ProposalType] { + pub fn proposal_types(&self) -> &[ProposalType] { self.proposal_types.as_slice() } /// Get a slice with the required credential types. #[allow(unused)] - pub(crate) fn credential_types(&self) -> &[CredentialType] { + pub fn credential_types(&self) -> &[CredentialType] { self.credential_types.as_slice() } diff --git a/openmls/src/framing/errors.rs b/openmls/src/framing/errors.rs index 55f5683cc6..0001217671 100644 --- a/openmls/src/framing/errors.rs +++ b/openmls/src/framing/errors.rs @@ -38,7 +38,7 @@ pub enum MessageDecryptionError { /// Message encryption error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum MessageEncryptionError { +pub enum MessageEncryptionError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), diff --git a/openmls/src/framing/message_out.rs b/openmls/src/framing/message_out.rs index a0dfe1f154..386487e1c5 100644 --- a/openmls/src/framing/message_out.rs +++ b/openmls/src/framing/message_out.rs @@ -87,6 +87,15 @@ impl From for MlsMessageOut { } } +impl From for MlsMessageOut { + fn from(priv_message: PrivateMessage) -> Self { + Self { + version: ProtocolVersion::default(), + body: MlsMessageOutBody::PrivateMessage(priv_message), + } + } +} + impl From for MlsMessageOut { fn from(group_info: GroupInfo) -> Self { Self { @@ -108,7 +117,7 @@ impl From for MlsMessageOut { impl MlsMessageOut { /// Create an [`MlsMessageOut`] from a [`PrivateMessage`], as well as the /// currently used [`ProtocolVersion`]. - pub(crate) fn from_private_message( + pub fn from_private_message( private_message: PrivateMessage, version: ProtocolVersion, ) -> Self { @@ -120,7 +129,7 @@ impl MlsMessageOut { /// Create an [`MlsMessageOut`] from a [`Welcome`] message and the currently /// used [`ProtocolVersion`]. - pub(crate) fn from_welcome(welcome: Welcome, version: ProtocolVersion) -> Self { + pub fn from_welcome(welcome: Welcome, version: ProtocolVersion) -> Self { MlsMessageOut { version, body: MlsMessageOutBody::Welcome(welcome), diff --git a/openmls/src/framing/mls_auth_content.rs b/openmls/src/framing/mls_auth_content.rs index 5dd099512a..670950c2f2 100644 --- a/openmls/src/framing/mls_auth_content.rs +++ b/openmls/src/framing/mls_auth_content.rs @@ -45,9 +45,9 @@ use crate::{ ///} FramedContentAuthData; /// ``` #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] -pub(crate) struct FramedContentAuthData { - pub(super) signature: Signature, - pub(super) confirmation_tag: Option, +pub struct FramedContentAuthData { + pub signature: Signature, + pub confirmation_tag: Option, } impl FramedContentAuthData { @@ -80,7 +80,7 @@ impl FramedContentAuthData { /// } AuthenticatedContent; /// ``` #[derive(PartialEq, Debug, Clone, TlsSerialize, TlsSize)] -pub(crate) struct AuthenticatedContent { +pub struct AuthenticatedContent { pub(super) wire_format: WireFormat, pub(super) content: FramedContent, pub(super) auth: FramedContentAuthData, @@ -119,7 +119,7 @@ impl AuthenticatedContent { /// This constructor builds an `AuthenticatedContent` containing an application /// message. The sender type is always `SenderType::Member`. - pub(crate) fn new_application( + pub fn new_application( sender_leaf_index: LeafNodeIndex, authenticated_data: &[u8], application_message: &[u8], @@ -139,7 +139,7 @@ impl AuthenticatedContent { /// This constructor builds an `PublicMessage` containing a Proposal. /// The sender type is always `SenderType::Member`. - pub(crate) fn member_proposal( + pub fn member_proposal( framing_parameters: FramingParameters, sender_leaf_index: LeafNodeIndex, proposal: Proposal, @@ -156,7 +156,7 @@ impl AuthenticatedContent { } /// This constructor builds an `PublicMessage` containing an External Proposal. - pub(crate) fn new_join_proposal( + pub fn new_join_proposal( proposal: Proposal, group_id: GroupId, epoch: GroupEpoch, @@ -179,7 +179,7 @@ impl AuthenticatedContent { } /// This constructor builds an `PublicMessage` containing an External Proposal. - pub(crate) fn new_external_proposal( + pub fn new_external_proposal( proposal: Proposal, group_id: GroupId, epoch: GroupEpoch, @@ -207,7 +207,7 @@ impl AuthenticatedContent { /// it's an `External` commit, the `SenderType` is `NewMemberCommit`. If it is an /// `External` commit, the context is not signed along with the rest of the /// commit. - pub(crate) fn commit( + pub fn commit( framing_parameters: FramingParameters, sender: Sender, commit: Commit, @@ -224,41 +224,41 @@ impl AuthenticatedContent { } /// Get the signature. - pub(crate) fn signature(&self) -> &Signature { + pub fn signature(&self) -> &Signature { &self.auth.signature } /// Get the signature. - pub(crate) fn confirmation_tag(&self) -> Option<&ConfirmationTag> { + pub fn confirmation_tag(&self) -> Option<&ConfirmationTag> { self.auth.confirmation_tag.as_ref() } /// Set the confirmation tag. - pub(crate) fn set_confirmation_tag(&mut self, tag: ConfirmationTag) { + pub fn set_confirmation_tag(&mut self, tag: ConfirmationTag) { self.auth.confirmation_tag = Some(tag) } /// Get the content body of the message. - pub(crate) fn content(&self) -> &FramedContentBody { + pub fn content(&self) -> &FramedContentBody { &self.content.body } /// Get the wire format. - pub(crate) fn wire_format(&self) -> WireFormat { + pub fn wire_format(&self) -> WireFormat { self.wire_format } - pub(crate) fn authenticated_data(&self) -> &[u8] { + pub fn authenticated_data(&self) -> &[u8] { self.content.authenticated_data.as_slice() } /// Get the group id as [`GroupId`]. - pub(crate) fn group_id(&self) -> &GroupId { + pub fn group_id(&self) -> &GroupId { &self.content.group_id } /// Get the epoch. - pub(crate) fn epoch(&self) -> GroupEpoch { + pub fn epoch(&self) -> GroupEpoch { self.content.epoch } @@ -270,7 +270,7 @@ impl AuthenticatedContent { #[cfg(test)] impl AuthenticatedContent { - pub(crate) fn new( + pub fn new( wire_format: WireFormat, content: FramedContent, auth: FramedContentAuthData, diff --git a/openmls/src/framing/mls_auth_content_in.rs b/openmls/src/framing/mls_auth_content_in.rs index c9db02a412..a2a0d61483 100644 --- a/openmls/src/framing/mls_auth_content_in.rs +++ b/openmls/src/framing/mls_auth_content_in.rs @@ -24,11 +24,8 @@ use crate::{ #[cfg(doc)] use super::{PrivateMessageIn, PublicMessageIn}; -/// Private module to ensure protection of [`AuthenticatedContent`]. -mod private_mod { - #[derive(Default)] - pub(crate) struct Seal; -} +#[derive(Default)] +pub struct Seal; /// 6 Message Framing /// @@ -42,15 +39,15 @@ mod private_mod { /// } AuthenticatedContent; /// ``` #[derive(PartialEq, Debug, Clone, TlsSize)] -pub(crate) struct AuthenticatedContentIn { - pub(super) wire_format: WireFormat, - pub(super) content: FramedContentIn, - pub(super) auth: FramedContentAuthData, +pub struct AuthenticatedContentIn { + pub wire_format: WireFormat, + pub content: FramedContentIn, + pub auth: FramedContentAuthData, } impl AuthenticatedContentIn { /// Returns a [`AuthenticatedContent`] after successful validation. - pub(crate) fn validate( + pub fn validate( self, ciphersuite: Ciphersuite, crypto: &impl OpenMlsCrypto, @@ -70,7 +67,6 @@ impl AuthenticatedContentIn { } } -#[cfg(any(feature = "test-utils", test))] impl AuthenticatedContentIn { /// Get the content body of the message. pub(crate) fn content(&self) -> &FramedContentBodyIn { @@ -112,7 +108,7 @@ impl From for AuthenticatedContentIn { /// Wrapper struct around [`AuthenticatedContent`] to enforce signature verification /// before content can be accessed. #[derive(PartialEq, Debug, Clone)] -pub(crate) struct VerifiableAuthenticatedContentIn { +pub struct VerifiableAuthenticatedContentIn { tbs: FramedContentTbsIn, auth: FramedContentAuthData, } @@ -216,7 +212,7 @@ impl VerifiedStruct for AuthenticatedContentIn } } - type SealingType = private_mod::Seal; + type SealingType = Seal; } impl SignedStruct for AuthenticatedContentIn { diff --git a/openmls/src/framing/mls_content.rs b/openmls/src/framing/mls_content.rs index fff459e171..d0dc310310 100644 --- a/openmls/src/framing/mls_content.rs +++ b/openmls/src/framing/mls_content.rs @@ -31,7 +31,7 @@ use tls_codec::{Serialize as TlsSerializeTrait, Size, TlsSerialize, TlsSize, VLB /// } FramedContent; /// ``` #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, TlsSerialize, TlsSize)] -pub(crate) struct FramedContent { +pub struct FramedContent { pub(super) group_id: GroupId, pub(super) epoch: GroupEpoch, pub(super) sender: Sender, @@ -64,7 +64,7 @@ impl From for FramedContent { /// ``` #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, TlsSerialize, TlsSize)] #[repr(u8)] -pub(crate) enum FramedContentBody { +pub enum FramedContentBody { #[tls_codec(discriminant = 1)] Application(VLBytes), #[tls_codec(discriminant = 2)] diff --git a/openmls/src/framing/mls_content_in.rs b/openmls/src/framing/mls_content_in.rs index 74d6e2a0cf..61b367cf43 100644 --- a/openmls/src/framing/mls_content_in.rs +++ b/openmls/src/framing/mls_content_in.rs @@ -39,7 +39,7 @@ use tls_codec::{ #[derive( Debug, PartialEq, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, )] -pub(crate) struct FramedContentIn { +pub struct FramedContentIn { pub(super) group_id: GroupId, pub(super) epoch: GroupEpoch, pub(super) sender: Sender, @@ -49,7 +49,7 @@ pub(crate) struct FramedContentIn { impl FramedContentIn { /// Returns a [`FramedContent`] after successful validation. - pub(crate) fn validate( + pub fn validate( self, ciphersuite: Ciphersuite, crypto: &impl OpenMlsCrypto, @@ -94,7 +94,7 @@ impl From for FramedContentIn { Debug, PartialEq, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, )] #[repr(u8)] -pub(crate) enum FramedContentBodyIn { +pub enum FramedContentBodyIn { #[tls_codec(discriminant = 1)] Application(VLBytes), #[tls_codec(discriminant = 2)] @@ -105,7 +105,7 @@ pub(crate) enum FramedContentBodyIn { impl FramedContentBodyIn { /// Returns the [`ContentType`]. - pub(crate) fn content_type(&self) -> ContentType { + pub fn content_type(&self) -> ContentType { match self { FramedContentBodyIn::Application(_) => ContentType::Application, FramedContentBodyIn::Proposal(_) => ContentType::Proposal, @@ -113,7 +113,7 @@ impl FramedContentBodyIn { } } - pub(super) fn deserialize_without_type( + pub fn deserialize_without_type( bytes: &mut R, content_type: ContentType, ) -> Result { @@ -129,7 +129,7 @@ impl FramedContentBodyIn { } /// Returns a [`FramedContentBody`] after successful validation. - pub(crate) fn validate( + pub fn validate( self, ciphersuite: Ciphersuite, crypto: &impl OpenMlsCrypto, diff --git a/openmls/src/framing/mod.rs b/openmls/src/framing/mod.rs index 17910d381f..66b7a96b86 100644 --- a/openmls/src/framing/mod.rs +++ b/openmls/src/framing/mod.rs @@ -128,7 +128,7 @@ pub enum WireFormat { /// This struct is used to group common framing parameters /// in order to reduce the number of arguments in function calls. #[derive(Clone, Copy, PartialEq, Debug)] -pub(crate) struct FramingParameters<'a> { +pub struct FramingParameters<'a> { aad: &'a [u8], wire_format: WireFormat, } @@ -141,10 +141,10 @@ impl<'a> FramingParameters<'a> { } } - pub(crate) fn aad(&self) -> &'a [u8] { + pub fn aad(&self) -> &'a [u8] { self.aad } - pub(crate) fn wire_format(&self) -> WireFormat { + pub fn wire_format(&self) -> WireFormat { self.wire_format } } diff --git a/openmls/src/framing/public_message.rs b/openmls/src/framing/public_message.rs index 0d9f1a293b..7982f9ee14 100644 --- a/openmls/src/framing/public_message.rs +++ b/openmls/src/framing/public_message.rs @@ -19,7 +19,7 @@ use crate::{error::LibraryError, versions::ProtocolVersion}; #[derive( Debug, PartialEq, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, )] -pub(crate) struct MembershipTag(pub(crate) Mac); +pub struct MembershipTag(pub(crate) Mac); /// [`PublicMessage`] is a framing structure for MLS messages. It can contain /// Proposals, Commits and application messages. @@ -44,7 +44,7 @@ pub struct PublicMessage { #[cfg(test)] impl PublicMessage { - pub(crate) fn content(&self) -> &crate::framing::mls_content::FramedContentBody { + pub fn content(&self) -> &crate::framing::mls_content::FramedContentBody { &self.content.body } diff --git a/openmls/src/framing/public_message_in.rs b/openmls/src/framing/public_message_in.rs index 0d4aee19f2..ce794619fb 100644 --- a/openmls/src/framing/public_message_in.rs +++ b/openmls/src/framing/public_message_in.rs @@ -68,10 +68,6 @@ impl PublicMessageIn { self.content.epoch = epoch.into(); } - pub fn confirmation_tag(&self) -> Option<&ConfirmationTag> { - self.auth.confirmation_tag.as_ref() - } - /// Set the sender. pub(crate) fn set_sender(&mut self, sender: Sender) { self.content.sender = sender; @@ -90,7 +86,7 @@ impl From for PublicMessageIn { impl PublicMessageIn { /// Build an [`PublicMessageIn`]. - pub(crate) fn new( + pub fn new( content: FramedContentIn, auth: FramedContentAuthData, membership_tag: Option, @@ -103,17 +99,17 @@ impl PublicMessageIn { } /// Returns the [`ContentType`] of the message. - pub(crate) fn content_type(&self) -> ContentType { + pub fn content_type(&self) -> ContentType { self.content.body.content_type() } /// Get the sender of this message. - pub(crate) fn sender(&self) -> &Sender { + pub fn sender(&self) -> &Sender { &self.content.sender } #[cfg(test)] - pub(crate) fn set_membership_tag( + pub fn set_membership_tag( &mut self, backend: &impl OpenMlsCryptoProvider, membership_key: &MembershipKey, @@ -138,7 +134,7 @@ impl PublicMessageIn { /// member. Returns `Ok(())` if successful or [`ValidationError`] otherwise. /// Note, that the context must have been set before calling this function. // TODO #133: Include this in the validation - pub(crate) fn verify_membership( + pub fn verify_membership( &self, backend: &impl OpenMlsCryptoProvider, membership_key: &MembershipKey, @@ -171,17 +167,17 @@ impl PublicMessageIn { } /// Get the group epoch. - pub(crate) fn epoch(&self) -> GroupEpoch { + pub fn epoch(&self) -> GroupEpoch { self.content.epoch } /// Get the [`GroupId`]. - pub(crate) fn group_id(&self) -> &GroupId { + pub fn group_id(&self) -> &GroupId { &self.content.group_id } /// Turn this [`PublicMessageIn`] into a [`VerifiableAuthenticatedContent`]. - pub(crate) fn into_verifiable_content( + pub fn into_verifiable_content( self, serialized_context: impl Into>>, ) -> VerifiableAuthenticatedContentIn { @@ -194,9 +190,14 @@ impl PublicMessageIn { } /// Get the [`MembershipTag`]. - pub(crate) fn membership_tag(&self) -> Option<&MembershipTag> { + pub fn membership_tag(&self) -> Option<&MembershipTag> { self.membership_tag.as_ref() } + + /// Get the [`ConfirmationTag`] + pub fn confirmation_tag(&self) -> Option<&ConfirmationTag> { + self.auth.confirmation_tag.as_ref() + } } #[cfg(test)] diff --git a/openmls/src/framing/validation.rs b/openmls/src/framing/validation.rs index 0b43bbdd51..f91b052be2 100644 --- a/openmls/src/framing/validation.rs +++ b/openmls/src/framing/validation.rs @@ -153,7 +153,7 @@ impl DecryptedMessage { /// of ValSem246 is validated as part of ValSem010. /// /// Returns the [`Credential`] and the leaf's [`SignaturePublicKey`]. - pub(crate) fn credential( + pub fn credential( &self, treesync: &TreeSync, old_leaves: &[Member], @@ -231,7 +231,7 @@ impl DecryptedMessage { /// Context that is needed to verify the signature of a the leaf node of an /// UpdatePath or an update proposal. #[derive(Debug, Clone)] -pub(crate) enum SenderContext { +pub enum SenderContext { Member((GroupId, LeafNodeIndex)), ExternalCommit((GroupId, LeafNodeIndex)), } diff --git a/openmls/src/group/core_group/proposals.rs b/openmls/src/group/core_group/proposals.rs index bcc774bb3f..70fb4a31a4 100644 --- a/openmls/src/group/core_group/proposals.rs +++ b/openmls/src/group/core_group/proposals.rs @@ -39,22 +39,22 @@ impl ProposalStore { queued_proposals: vec![queued_proposal], } } - pub(crate) fn add(&mut self, queued_proposal: QueuedProposal) { + pub fn add(&mut self, queued_proposal: QueuedProposal) { self.queued_proposals.push(queued_proposal); } - pub(crate) fn proposals(&self) -> impl Iterator { + pub fn proposals(&self) -> impl Iterator { self.queued_proposals.iter() } - pub(crate) fn is_empty(&self) -> bool { + pub fn is_empty(&self) -> bool { self.queued_proposals.is_empty() } - pub(crate) fn empty(&mut self) { + pub fn empty(&mut self) { self.queued_proposals.clear(); } /// Removes a proposal from the store using its reference. It will return None if it wasn't /// found in the store. - pub(crate) fn remove(&mut self, proposal_ref: ProposalRef) -> Option<()> { + pub fn remove(&mut self, proposal_ref: ProposalRef) -> Option<()> { let index = self .queued_proposals .iter() @@ -153,11 +153,11 @@ impl QueuedProposal { &self.proposal } /// Returns the `ProposalRef`. - pub(crate) fn proposal_reference(&self) -> ProposalRef { + pub fn proposal_reference(&self) -> ProposalRef { self.proposal_reference.clone() } /// Returns the `ProposalOrRefType`. - pub(crate) fn proposal_or_ref_type(&self) -> ProposalOrRefType { + pub fn proposal_or_ref_type(&self) -> ProposalOrRefType { self.proposal_or_ref_type } /// Returns the `Sender` as a reference @@ -184,7 +184,7 @@ pub(crate) struct ProposalQueue { impl ProposalQueue { /// Returns `true` if the [`ProposalQueue`] is empty. Otherwise returns `false`. - pub(crate) fn is_empty(&self) -> bool { + pub fn is_empty(&self) -> bool { self.proposal_references.is_empty() } /// Returns a new `QueuedProposalQueue` from proposals that were committed and @@ -294,7 +294,7 @@ impl ProposalQueue { /// Returns an iterator over all `QueuedProposal` in the queue /// in the order of the the Commit message - pub(crate) fn queued_proposals(&self) -> impl Iterator { + pub fn queued_proposals(&self) -> impl Iterator { // Iterate over the reference to extract the proposals in the right order self.proposal_references .iter() @@ -303,7 +303,7 @@ impl ProposalQueue { /// Returns an iterator over all Add proposals in the queue /// in the order of the the Commit message - pub(crate) fn add_proposals(&self) -> impl Iterator { + pub fn add_proposals(&self) -> impl Iterator { self.queued_proposals().filter_map(|queued_proposal| { if let Proposal::Add(add_proposal) = queued_proposal.proposal() { let sender = queued_proposal.sender(); @@ -319,7 +319,7 @@ impl ProposalQueue { /// Returns an iterator over all Remove proposals in the queue /// in the order of the the Commit message - pub(crate) fn remove_proposals(&self) -> impl Iterator { + pub fn remove_proposals(&self) -> impl Iterator { self.queued_proposals().filter_map(|queued_proposal| { if let Proposal::Remove(remove_proposal) = queued_proposal.proposal() { let sender = queued_proposal.sender(); @@ -335,7 +335,7 @@ impl ProposalQueue { /// Returns an iterator over all Update in the queue /// in the order of the the Commit message - pub(crate) fn update_proposals(&self) -> impl Iterator { + pub fn update_proposals(&self) -> impl Iterator { self.queued_proposals().filter_map(|queued_proposal| { if let Proposal::Update(update_proposal) = queued_proposal.proposal() { let sender = queued_proposal.sender(); @@ -351,7 +351,7 @@ impl ProposalQueue { /// Returns an iterator over all PresharedKey proposals in the queue /// in the order of the the Commit message - pub(crate) fn psk_proposals(&self) -> impl Iterator { + pub fn psk_proposals(&self) -> impl Iterator { self.queued_proposals().filter_map(|queued_proposal| { if let Proposal::PreSharedKey(psk_proposal) = queued_proposal.proposal() { let sender = queued_proposal.sender(); diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index c83b900a6c..8436d0e5dd 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -420,12 +420,24 @@ impl StagedCommit { self.staged_proposal_queue.psk_proposals() } + /// Returns an interator over all [`QueuedProposal`]s + pub fn queued_proposals(&self) -> impl Iterator { + self.staged_proposal_queue.queued_proposals() + } + /// Returns `true` if the member was removed through a proposal covered by this Commit message /// and `false` otherwise. pub fn self_removed(&self) -> bool { matches!(self.state, StagedCommitState::PublicState(_)) } + pub fn staged_context(&self) -> &GroupContext { + match &self.state { + StagedCommitState::PublicState(ps) => ps.group_context(), + StagedCommitState::GroupMember(gm) => gm.group_context(), + } + } + /// Consume this [`StagedCommit`] and return the internal [`StagedCommitState`]. pub(crate) fn into_state(self) -> StagedCommitState { self.state @@ -458,4 +470,9 @@ impl MemberStagedCommitState { new_leaf_keypair_option, } } + + /// Get the staged [`GroupContext`] + pub(crate) fn group_context(&self) -> &GroupContext { + self.staged_diff.group_context() + } } diff --git a/openmls/src/group/errors.rs b/openmls/src/group/errors.rs index 1e8316139c..a63d0886bc 100644 --- a/openmls/src/group/errors.rs +++ b/openmls/src/group/errors.rs @@ -416,7 +416,7 @@ pub enum CreateAddProposalError { /// Exporter error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum ExporterError { +pub enum ExporterError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -426,7 +426,7 @@ pub(crate) enum ExporterError { /// Proposal queue error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum ProposalQueueError { +pub enum ProposalQueueError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -441,7 +441,7 @@ pub(crate) enum ProposalQueueError { /// Errors that can arise when creating a [`ProposalQueue`] from committed /// proposals. #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum FromCommittedProposalsError { +pub enum FromCommittedProposalsError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -455,7 +455,7 @@ pub(crate) enum FromCommittedProposalsError { /// Creation proposal queue error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum CreationProposalQueueError { +pub enum CreationProposalQueueError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -466,7 +466,7 @@ pub(crate) enum CreationProposalQueueError { // Apply proposals error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum ApplyProposalsError { +pub enum ApplyProposalsError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -477,7 +477,7 @@ pub(crate) enum ApplyProposalsError { // Core group build error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum CoreGroupBuildError { +pub enum CoreGroupBuildError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -494,7 +494,7 @@ pub(crate) enum CoreGroupBuildError { // CoreGroup parse message error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum CoreGroupParseMessageError { +pub enum CoreGroupParseMessageError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), diff --git a/openmls/src/group/public_group/diff.rs b/openmls/src/group/public_group/diff.rs index ea4d41c62a..430ca24449 100644 --- a/openmls/src/group/public_group/diff.rs +++ b/openmls/src/group/public_group/diff.rs @@ -244,3 +244,10 @@ pub(crate) struct StagedPublicGroupDiff { pub(super) interim_transcript_hash: Vec, pub(super) confirmation_tag: ConfirmationTag, } + +impl StagedPublicGroupDiff { + /// Get the staged [`GroupContext`]. + pub(crate) fn group_context(&self) -> &GroupContext { + &self.group_context + } +} diff --git a/openmls/src/group/public_group/mod.rs b/openmls/src/group/public_group/mod.rs index c7792814e8..59eeb9d921 100644 --- a/openmls/src/group/public_group/mod.rs +++ b/openmls/src/group/public_group/mod.rs @@ -176,7 +176,7 @@ impl PublicGroup { /// of the sender. /// /// The proposals must be validated before calling this function. - pub(crate) fn free_leaf_index_after_remove<'a>( + pub fn free_leaf_index_after_remove<'a>( &self, mut inline_proposals: impl Iterator>, ) -> Result { diff --git a/openmls/src/messages/mod.rs b/openmls/src/messages/mod.rs index a406613f39..2a3396173f 100644 --- a/openmls/src/messages/mod.rs +++ b/openmls/src/messages/mod.rs @@ -65,7 +65,7 @@ pub struct Welcome { impl Welcome { /// Create a new welcome message from the provided data. /// Note that secrets and the encrypted group info are consumed. - pub(crate) fn new( + pub fn new( cipher_suite: Ciphersuite, secrets: Vec, encrypted_group_info: Vec, @@ -78,7 +78,7 @@ impl Welcome { } /// Returns a reference to the ciphersuite in this Welcome message. - pub(crate) fn ciphersuite(&self) -> Ciphersuite { + pub fn ciphersuite(&self) -> Ciphersuite { self.cipher_suite } @@ -88,7 +88,7 @@ impl Welcome { } /// Returns a reference to the encrypted group info. - pub(crate) fn encrypted_group_info(&self) -> &[u8] { + pub fn encrypted_group_info(&self) -> &[u8] { self.encrypted_group_info.as_slice() } @@ -125,7 +125,7 @@ impl EncryptedGroupSecrets { } /// Returns a reference to the encrypted group secrets' encrypted group secrets. - pub(crate) fn encrypted_group_secrets(&self) -> &HpkeCiphertext { + pub fn encrypted_group_secrets(&self) -> &HpkeCiphertext { &self.encrypted_group_secrets } } @@ -149,9 +149,9 @@ impl EncryptedGroupSecrets { /// } Commit; /// ``` #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, TlsSerialize, TlsSize)] -pub(crate) struct Commit { - pub(crate) proposals: Vec, - pub(crate) path: Option, +pub struct Commit { + pub proposals: Vec, + pub path: Option, } impl Commit { @@ -162,7 +162,7 @@ impl Commit { } /// Returns the update path of the Commit if it has one. - pub(crate) fn path(&self) -> &Option { + pub fn path(&self) -> &Option { &self.path } } @@ -170,13 +170,13 @@ impl Commit { #[derive( Debug, PartialEq, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize, )] -pub(crate) struct CommitIn { +pub struct CommitIn { proposals: Vec, path: Option, } impl CommitIn { - pub(crate) fn unverified_credential(&self) -> Option { + pub fn unverified_credential(&self) -> Option { self.path.as_ref().map(|p| { let credential = p.leaf_node().credential().clone(); let pk = p.leaf_node().signature_key().clone(); @@ -188,7 +188,7 @@ impl CommitIn { } /// Returns a [`Commit`] after successful validation. - pub(crate) fn validate( + pub fn validate( self, ciphersuite: Ciphersuite, crypto: &impl OpenMlsCrypto, @@ -266,7 +266,7 @@ impl From for CommitIn { #[derive( Debug, PartialEq, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize, )] -pub struct ConfirmationTag(pub(crate) Mac); +pub struct ConfirmationTag(pub Mac); /// PathSecret /// @@ -279,8 +279,8 @@ pub struct ConfirmationTag(pub(crate) Mac); /// ``` #[derive(Debug, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize)] #[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))] -pub(crate) struct PathSecret { - pub(crate) path_secret: Secret, +pub struct PathSecret { + pub path_secret: Secret, } impl From for PathSecret { @@ -291,7 +291,7 @@ impl From for PathSecret { impl PathSecret { /// Derives a node secret which in turn is used to derive an HpkeKeyPair. - pub(crate) fn derive_key_pair( + pub fn derive_key_pair( &self, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, @@ -309,7 +309,7 @@ impl PathSecret { } /// Derives a path secret. - pub(crate) fn derive_path_secret( + pub fn derive_path_secret( &self, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, @@ -323,7 +323,7 @@ impl PathSecret { /// Encrypt the path secret under the given `HpkePublicKey` using the given /// `group_context`. - pub(crate) fn encrypt( + pub fn encrypt( &self, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, @@ -339,7 +339,7 @@ impl PathSecret { } /// Consume the `PathSecret`, returning the internal `Secret` value. - pub(crate) fn secret(self) -> Secret { + pub fn secret(self) -> Secret { self.path_secret } @@ -349,7 +349,7 @@ impl PathSecret { /// was unsuccessful. /// /// ValSem203: Path secrets must decrypt correctly - pub(crate) fn decrypt( + pub fn decrypt( backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, version: ProtocolVersion, @@ -367,7 +367,7 @@ impl PathSecret { /// Path secret error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum PathSecretError { +pub enum PathSecretError { /// See [`hpke::Error`] for more details. #[error(transparent)] DecryptionError(#[from] hpke::Error), @@ -384,17 +384,17 @@ pub(crate) enum PathSecretError { /// } GroupSecrets; /// ``` #[derive(Debug, TlsDeserialize, TlsSize)] -pub(crate) struct GroupSecrets { - pub(crate) joiner_secret: JoinerSecret, - pub(crate) path_secret: Option, - pub(crate) psks: Vec, +pub struct GroupSecrets { + pub joiner_secret: JoinerSecret, + pub path_secret: Option, + pub psks: Vec, } #[derive(TlsSerialize, TlsSize)] struct EncodedGroupSecrets<'a> { - pub(crate) joiner_secret: &'a JoinerSecret, - pub(crate) path_secret: Option<&'a PathSecret>, - pub(crate) psks: &'a [PreSharedKeyId], + pub joiner_secret: &'a JoinerSecret, + pub path_secret: Option<&'a PathSecret>, + pub psks: &'a [PreSharedKeyId], } /// Error related to group secrets. @@ -410,7 +410,7 @@ pub enum GroupSecretsError { impl GroupSecrets { /// Try to decrypt (and parse) a ciphertext into group secrets. - pub(crate) fn try_from_ciphertext( + pub fn try_from_ciphertext( skey: &HpkePrivateKey, ciphertext: &HpkeCiphertext, context: &[u8], @@ -431,7 +431,7 @@ impl GroupSecrets { } /// Create new encoded group secrets. - pub(crate) fn new_encoded<'a>( + pub fn new_encoded<'a>( joiner_secret: &JoinerSecret, path_secret: Option<&'a PathSecret>, psks: &'a [PreSharedKeyId], @@ -445,7 +445,7 @@ impl GroupSecrets { } /// Set the config for the secrets, i.e. ciphersuite and MLS version. - pub(crate) fn config( + pub fn config( mut self, ciphersuite: Ciphersuite, mls_version: ProtocolVersion, diff --git a/openmls/src/messages/proposals.rs b/openmls/src/messages/proposals.rs index 67aab34186..2e75f53c45 100644 --- a/openmls/src/messages/proposals.rs +++ b/openmls/src/messages/proposals.rs @@ -486,7 +486,7 @@ pub enum ProposalOrRefType { #[repr(u8)] #[allow(missing_docs)] #[allow(clippy::large_enum_variant)] -pub(crate) enum ProposalOrRef { +pub enum ProposalOrRef { #[tls_codec(discriminant = 1)] Proposal(Proposal), Reference(ProposalRef), diff --git a/openmls/src/schedule/errors.rs b/openmls/src/schedule/errors.rs index 7519a4c398..ca38629c4e 100644 --- a/openmls/src/schedule/errors.rs +++ b/openmls/src/schedule/errors.rs @@ -59,7 +59,7 @@ pub enum PskError { /// Key schedule state error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum ErrorState { +pub enum ErrorState { /// Expected to be in initial state. #[error("Expected to be in initial state.")] Init, @@ -70,7 +70,7 @@ pub(crate) enum ErrorState { /// Key schedule error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum KeyScheduleError { +pub enum KeyScheduleError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), diff --git a/openmls/src/schedule/mod.rs b/openmls/src/schedule/mod.rs index c27d8dbcc9..9655029dda 100644 --- a/openmls/src/schedule/mod.rs +++ b/openmls/src/schedule/mod.rs @@ -355,7 +355,7 @@ impl InitSecret { } #[derive(Debug, TlsDeserialize, TlsSerialize, TlsSize)] -pub(crate) struct JoinerSecret { +pub struct JoinerSecret { secret: Secret, } @@ -847,7 +847,7 @@ impl ConfirmationKey { /// The membership key is used to calculate the `MembershipTag`. #[derive(Debug, Serialize, Deserialize)] #[cfg_attr(test, derive(PartialEq, Clone))] -pub(crate) struct MembershipKey { +pub struct MembershipKey { secret: Secret, } diff --git a/openmls/src/treesync/errors.rs b/openmls/src/treesync/errors.rs index 77714379fa..ec8d659b4e 100644 --- a/openmls/src/treesync/errors.rs +++ b/openmls/src/treesync/errors.rs @@ -82,7 +82,7 @@ pub enum ApplyUpdatePathError { #[allow(dead_code)] /// TreeSync error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum TreeSyncError { +pub enum TreeSyncError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -120,7 +120,7 @@ pub(crate) enum TreeSyncError { /// Derive path error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum DerivePathError { +pub enum DerivePathError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -131,7 +131,7 @@ pub(crate) enum DerivePathError { /// TreeSync set path error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum TreeSyncAddLeaf { +pub enum TreeSyncAddLeaf { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -156,7 +156,7 @@ pub enum TreeSyncFromNodesError { /// TreeSync parent hash error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum TreeSyncParentHashError { +pub enum TreeSyncParentHashError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -167,7 +167,7 @@ pub(crate) enum TreeSyncParentHashError { /// TreeSync parent hash error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum TreeSyncDiffError { +pub enum TreeSyncDiffError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -189,7 +189,7 @@ pub(crate) enum TreeSyncDiffError { /// TreeKem error #[derive(Error, Debug, PartialEq, Clone)] #[allow(clippy::enum_variant_names)] -pub(crate) enum TreeKemError { +pub enum TreeKemError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), diff --git a/openmls/src/treesync/node/encryption_keys.rs b/openmls/src/treesync/node/encryption_keys.rs index 513a21f7c5..7536427eb7 100644 --- a/openmls/src/treesync/node/encryption_keys.rs +++ b/openmls/src/treesync/node/encryption_keys.rs @@ -58,7 +58,7 @@ impl EncryptionKey { #[derive(Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize)] #[cfg_attr(test, derive(PartialEq, Eq))] -pub(crate) struct EncryptionPrivateKey { +pub struct EncryptionPrivateKey { key: HpkePrivateKey, } @@ -130,7 +130,7 @@ impl From for EncryptionKey { #[derive(Debug, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize)] #[cfg_attr(test, derive(PartialEq, Eq))] -pub(crate) struct EncryptionKeyPair { +pub struct EncryptionKeyPair { public_key: EncryptionKey, private_key: EncryptionPrivateKey, } diff --git a/openmls/src/treesync/node/leaf_node/capabilities.rs b/openmls/src/treesync/node/leaf_node/capabilities.rs index 3f40c757f7..04f60b3ac4 100644 --- a/openmls/src/treesync/node/leaf_node/capabilities.rs +++ b/openmls/src/treesync/node/leaf_node/capabilities.rs @@ -120,7 +120,7 @@ impl Capabilities { /// /// Returns a [`LeafNodeValidationError`] error if any of the required /// capabilities is not supported. - pub(crate) fn supports_required_capabilities( + pub fn supports_required_capabilities( &self, required_capabilities: &RequiredCapabilitiesExtension, ) -> Result<(), LeafNodeValidationError> { @@ -152,7 +152,7 @@ impl Capabilities { } /// Check if these [`Capabilities`] contain all the extensions. - pub(crate) fn are_extensions_supported(&self, extension: &Extensions) -> bool { + pub fn are_extensions_supported(&self, extension: &Extensions) -> bool { extension .iter() .map(Extension::extension_type) @@ -160,7 +160,7 @@ impl Capabilities { } /// Check if these [`Capabilities`] contain all the credentials. - pub(crate) fn contains_credential(&self, credential_type: &CredentialType) -> bool { + pub fn contains_credential(&self, credential_type: &CredentialType) -> bool { self.credentials().contains(credential_type) } } From 97b506857f02833c6af3fb0b29d4c4cee7966b81 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Wed, 24 May 2023 16:56:04 +0200 Subject: [PATCH 06/54] Added deref impl for RatchetTree --- openmls/src/treesync/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openmls/src/treesync/mod.rs b/openmls/src/treesync/mod.rs index 3d7d1493e3..85f9c970c6 100644 --- a/openmls/src/treesync/mod.rs +++ b/openmls/src/treesync/mod.rs @@ -100,6 +100,13 @@ pub mod tests_and_kats; #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, TlsSerialize, TlsSize)] pub struct RatchetTree(Vec>); +impl std::ops::Deref for RatchetTree { + type Target = [Option]; + fn deref(&self) -> &Self::Target { + self.0.as_slice() + } +} + /// An error during processing of an incoming ratchet tree. #[derive(Error, Debug, PartialEq, Clone)] pub enum RatchetTreeError { From dd60193164f001f56c94c65c9bf06e2306e6a0a6 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Wed, 24 May 2023 17:09:01 +0200 Subject: [PATCH 07/54] API Opening --- openmls/src/group/core_group/mod.rs | 8 ++++---- openmls/src/group/mls_group/extension.rs | 2 -- openmls/src/group/mls_group/mod.rs | 19 +++++-------------- openmls/src/treesync/mod.rs | 1 - 4 files changed, 9 insertions(+), 21 deletions(-) diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 8b7e5ae622..144a7921ab 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -1128,6 +1128,10 @@ impl CoreGroup { pub(crate) fn own_tree_position(&self) -> TreePosition { TreePosition::new(self.group_id().clone(), self.own_leaf_index()) } + + pub(crate) fn print_ratchet_tree(&self, message: &str) { + println!("{}: {}", message, self.public_group().export_ratchet_tree()); + } } /// Composite key for key material of a client within an epoch @@ -1156,10 +1160,6 @@ impl CoreGroup { self.message_secrets_store.message_secrets_mut() } - pub(crate) fn print_ratchet_tree(&self, message: &str) { - println!("{}: {}", message, self.public_group().export_ratchet_tree()); - } - #[cfg(test)] pub(crate) fn message_secrets_store(&self) -> &MessageSecretsStore { &self.message_secrets_store diff --git a/openmls/src/group/mls_group/extension.rs b/openmls/src/group/mls_group/extension.rs index a68155da24..4a2a4802a1 100644 --- a/openmls/src/group/mls_group/extension.rs +++ b/openmls/src/group/mls_group/extension.rs @@ -2,8 +2,6 @@ //! //! Contains all the methods related to modifying a group's extensions. -use std::iter; - use openmls_traits::signatures::Signer; use crate::{ diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 8d7ea2bdcd..bfb571bba3 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -392,34 +392,21 @@ impl MlsGroup { MlsGroupState::Operational => Ok(()), } } -} - -// Methods used in tests -impl MlsGroup { - #[cfg(any(feature = "test-utils", test))] pub fn export_group_context(&self) -> &GroupContext { self.group.context() } - #[cfg(any(feature = "test-utils", test))] pub fn tree_hash(&self) -> &[u8] { self.group.public_group().group_context().tree_hash() } - #[cfg(any(feature = "test-utils", test))] - pub fn print_ratchet_tree(&self, message: &str) { - self.group.print_ratchet_tree(message) - } - /// Returns the underlying [CoreGroup]. - #[cfg(test)] pub(crate) fn group(&self) -> &CoreGroup { &self.group } /// Clear the pending proposals. - #[cfg(test)] - pub(crate) fn clear_pending_proposals(&mut self) { + pub fn clear_pending_proposals(&mut self) { self.proposal_store.empty() } @@ -432,6 +419,10 @@ impl MlsGroup { .remove(proposal_ref) .ok_or(MlsGroupStateError::PendingProposalNotFound) } + + pub fn print_ratchet_tree(&self, message: &str) { + self.group.print_ratchet_tree(message) + } } /// `Enum` that indicates whether the inner group state has been modified since the last time it was persisted. diff --git a/openmls/src/treesync/mod.rs b/openmls/src/treesync/mod.rs index 85f9c970c6..5715c356be 100644 --- a/openmls/src/treesync/mod.rs +++ b/openmls/src/treesync/mod.rs @@ -276,7 +276,6 @@ impl From for RatchetTree { } } -#[cfg(any(feature = "test-utils", test))] impl fmt::Display for RatchetTree { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let factor = 3; From f4b9d95f60854a772576a5dbb6c263285fee9a08 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Thu, 25 May 2023 16:16:22 +0200 Subject: [PATCH 08/54] More fixes --- openmls/Cargo.toml | 1 + .../binary_tree/array_representation/mod.rs | 7 ++--- .../array_representation/treemath.rs | 5 ++-- openmls/src/lib.rs | 1 - openmls/src/test_utils/mod.rs | 15 ++-------- openmls/src/treesync/mod.rs | 29 +++++++------------ 6 files changed, 18 insertions(+), 40 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 3f72e9f263..b042d7b06c 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -17,6 +17,7 @@ tls_codec = { workspace = true } rayon = "^1.5.0" thiserror = "^1.0" backtrace = "0.3" +hex = "0.4" async-trait = { workspace = true } # Only required for tests. rand = { version = "0.8", optional = true } diff --git a/openmls/src/binary_tree/array_representation/mod.rs b/openmls/src/binary_tree/array_representation/mod.rs index dac5c93223..204a5afb48 100644 --- a/openmls/src/binary_tree/array_representation/mod.rs +++ b/openmls/src/binary_tree/array_representation/mod.rs @@ -19,13 +19,10 @@ pub(crate) mod sorted_iter; pub(crate) mod tree; pub(crate) use treemath::{ - direct_path, is_node_in_tree, left, right, root, ParentNodeIndex, TreeNodeIndex, TreeSize, - MIN_TREE_SIZE, + direct_path, is_node_in_tree, left, level, right, root, ParentNodeIndex, TreeNodeIndex, + TreeSize, MIN_TREE_SIZE, }; -#[cfg(any(feature = "test-utils", test))] -pub(crate) use treemath::level; - mod treemath; // Tests diff --git a/openmls/src/binary_tree/array_representation/treemath.rs b/openmls/src/binary_tree/array_representation/treemath.rs index decc0a1a78..219455ec18 100644 --- a/openmls/src/binary_tree/array_representation/treemath.rs +++ b/openmls/src/binary_tree/array_representation/treemath.rs @@ -142,7 +142,7 @@ impl TreeNodeIndex { } /// Return the inner value as `u32`. - fn u32(&self) -> u32 { + pub fn u32(&self) -> u32 { match self { TreeNodeIndex::Leaf(index) => index.to_tree_index(), TreeNodeIndex::Parent(index) => index.to_tree_index(), @@ -156,8 +156,7 @@ impl TreeNodeIndex { } /// Return the inner value as `usize`. - #[cfg(any(feature = "test-utils", test))] - fn usize(&self) -> usize { + pub fn usize(&self) -> usize { self.u32() as usize } diff --git a/openmls/src/lib.rs b/openmls/src/lib.rs index fc657d72e2..b725406524 100644 --- a/openmls/src/lib.rs +++ b/openmls/src/lib.rs @@ -148,7 +148,6 @@ //! [user Manual]: https://openmls.tech/book #![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(not(test), forbid(unsafe_code))] -#![cfg_attr(not(feature = "test-utils"), deny(missing_docs))] #![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::private_intra_doc_links)] #![cfg(any( diff --git a/openmls/src/test_utils/mod.rs b/openmls/src/test_utils/mod.rs index 11dff0786c..3b6449d1c0 100644 --- a/openmls/src/test_utils/mod.rs +++ b/openmls/src/test_utils/mod.rs @@ -55,23 +55,12 @@ pub(crate) fn read(file_name: &str) -> T { /// Convert `bytes` to a hex string. pub fn bytes_to_hex(bytes: &[u8]) -> String { - let mut hex = String::new(); - for &b in bytes { - write!(&mut hex, "{b:02X}").expect("Unable to write to string"); - } - hex + hex::encode(bytes) } /// Convert a hex string to a byte vector. pub fn hex_to_bytes(hex: &str) -> Vec { - 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).expect("An unexpected error occurred."), - ); - } - bytes + hex::decode(hex).unwrap() } /// Convert a hex string to a byte vector. diff --git a/openmls/src/treesync/mod.rs b/openmls/src/treesync/mod.rs index 5715c356be..8180135155 100644 --- a/openmls/src/treesync/mod.rs +++ b/openmls/src/treesync/mod.rs @@ -24,8 +24,6 @@ use openmls_rust_crypto::OpenMlsRustCrypto; use rstest::*; #[cfg(test)] use rstest_reuse::apply; -#[cfg(any(feature = "test-utils", test))] -use std::fmt; use openmls_traits::{ crypto::OpenMlsCrypto, @@ -50,11 +48,7 @@ use self::{ }; #[cfg(test)] use crate::binary_tree::array_representation::ParentNodeIndex; -#[cfg(any(feature = "test-utils", test))] -use crate::{ - binary_tree::array_representation::level, group::tests::tree_printing::root, - test_utils::bytes_to_hex, -}; + use crate::{ binary_tree::{ array_representation::{is_node_in_tree, tree::TreeNode, LeafNodeIndex, TreeSize}, @@ -276,11 +270,12 @@ impl From for RatchetTree { } } -impl fmt::Display for RatchetTree { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl std::fmt::Display for RatchetTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use crate::binary_tree::array_representation::{level, root}; let factor = 3; let nodes = &self.0; - let tree_size = nodes.len() as u32; + let tree_size = TreeSize::new(nodes.len() as u32); for (i, node) in nodes.iter().enumerate() { let level = level(i as u32); @@ -290,27 +285,25 @@ impl fmt::Display for RatchetTree { Node::LeafNode(leaf_node) => { write!(f, "\tL ")?; let key_bytes = leaf_node.encryption_key().as_slice(); - let parent_hash_bytes = leaf_node - .parent_hash() - .map(bytes_to_hex) - .unwrap_or_default(); + let parent_hash_bytes = + leaf_node.parent_hash().map(hex::encode).unwrap_or_default(); (key_bytes, parent_hash_bytes) } Node::ParentNode(parent_node) => { - if root(tree_size) == i as u32 { + if root(tree_size).usize() == i { write!(f, "\tP (*) ")?; } else { write!(f, "\tP ")?; } let key_bytes = parent_node.public_key().as_slice(); - let parent_hash_string = bytes_to_hex(parent_node.parent_hash()); + let parent_hash_string = hex::encode(parent_node.parent_hash()); (key_bytes, parent_hash_string) } }; write!( f, "PK: {} PH: {} | ", - bytes_to_hex(key_bytes), + hex::encode(key_bytes), if !parent_hash_bytes.is_empty() { parent_hash_bytes } else { @@ -320,7 +313,7 @@ impl fmt::Display for RatchetTree { write!(f, "{}◼︎", str::repeat(" ", level * factor))?; } else { - if root(tree_size) == i as u32 { + if root(tree_size).usize() == i { write!( f, "\t_ (*) PK: {} PH: {} | ", From 8c6ac9c26a1e992c84b7c003f70fb644d1c4cc05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Augusto=20C=C3=A9sar=20Dias?= Date: Tue, 23 May 2023 09:08:08 +0200 Subject: [PATCH 09/54] feat: Implement x509 credentials support - CL-171 --- Cargo.toml | 3 +- basic_credential/Cargo.toml | 2 - basic_credential/src/lib.rs | 51 +-- book/src/user_manual/identity.md | 5 +- cli/src/identity.rs | 2 +- delivery-service/ds-lib/tests/test_codec.rs | 3 +- delivery-service/ds/src/test.rs | 2 +- openmls/Cargo.toml | 3 + openmls/benches/benchmark.rs | 2 +- openmls/src/credentials/codec.rs | 12 +- openmls/src/credentials/errors.rs | 9 + openmls/src/credentials/mod.rs | 112 ++++-- .../extensions/external_sender_extension.rs | 4 +- openmls/src/extensions/test_extensions.rs | 18 +- openmls/src/framing/message_out.rs | 5 +- openmls/src/framing/test_framing.rs | 81 +---- openmls/src/framing/validation.rs | 46 ++- .../src/group/core_group/test_core_group.rs | 54 +-- openmls/src/group/mls_group/errors.rs | 8 + .../group/tests/test_proposal_validation.rs | 4 +- openmls/src/group/tests/utils.rs | 2 +- openmls/src/key_packages/mod.rs | 2 +- openmls/src/key_packages/test_key_packages.rs | 5 +- openmls/src/lib.rs | 5 +- openmls/src/test_utils/mod.rs | 2 +- openmls/src/test_utils/test_framework/mod.rs | 2 +- .../tests_and_kats/kats/kat_encryption.rs | 15 +- .../kats/kat_message_protection.rs | 20 +- .../tests_and_kats/tests/test_diff.rs | 27 +- openmls/tests/book_code.rs | 49 +-- openmls/tests/key_store.rs | 2 +- openmls/tests/test_external_commit.rs | 45 +-- openmls/tests/test_mls_group.rs | 72 +--- traits/Cargo.toml | 7 + traits/src/key_store.rs | 1 + traits/src/signatures.rs | 59 +++ traits/src/types.rs | 5 + x509_credential/Cargo.toml | 25 ++ x509_credential/Readme.md | 4 + x509_credential/src/lib.rs | 343 ++++++++++++++++++ 40 files changed, 714 insertions(+), 404 deletions(-) create mode 100644 x509_credential/Cargo.toml create mode 100644 x509_credential/Readme.md create mode 100644 x509_credential/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 6bd8e37287..0c1990c14f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,8 @@ members = [ "memory_keystore", "delivery-service/ds", "delivery-service/ds-lib", - "basic_credential" + "basic_credential", + "x509_credential" ] resolver = "2" diff --git a/basic_credential/Cargo.toml b/basic_credential/Cargo.toml index aefbaa6b75..19321f5fac 100644 --- a/basic_credential/Cargo.toml +++ b/basic_credential/Cargo.toml @@ -20,9 +20,7 @@ ed25519-dalek = { version = "2.0.0-rc.2", features = ["rand_core"] } p256 = "0.13" p384 = "0.13" secrecy = { version = "0.8", features = ["serde"] } -zeroize = "1.6" rand_core = "0.6" -signature = "2.1" [features] clonable = [] # Make the keys clonable diff --git a/basic_credential/src/lib.rs b/basic_credential/src/lib.rs index 31aef06c37..dfae218859 100644 --- a/basic_credential/src/lib.rs +++ b/basic_credential/src/lib.rs @@ -5,12 +5,11 @@ //! For now this credential uses only RustCrypto. use secrecy::{ExposeSecret, SecretVec}; -use signature::Signer; use std::fmt::Debug; use openmls_traits::{ key_store::{MlsEntity, MlsEntityId, OpenMlsKeyStore}, - types::{CryptoError, Error, SignatureScheme}, + types::{CryptoError, SignatureScheme}, }; fn expose_sk(data: &SecretVec, ser: S) -> Result { @@ -91,51 +90,9 @@ impl Debug for SignatureKeyPair { } } -impl openmls_traits::signatures::Signer for SignatureKeyPair { - fn sign(&self, payload: &[u8]) -> Result, Error> { - match self.signature_scheme { - SignatureScheme::ECDSA_SECP256R1_SHA256 => { - let k = p256::ecdsa::SigningKey::from_bytes( - self.private.expose_secret().as_slice().into(), - ) - .map_err(|_| Error::SigningError)?; - let signature: p256::ecdsa::Signature = - k.try_sign(payload).map_err(|_| Error::SigningError)?; - Ok(signature.to_der().to_bytes().into()) - } - SignatureScheme::ECDSA_SECP384R1_SHA384 => { - let k = p384::ecdsa::SigningKey::from_bytes( - self.private.expose_secret().as_slice().into(), - ) - .map_err(|_| Error::SigningError)?; - let signature: p384::ecdsa::Signature = - k.try_sign(payload).map_err(|_| Error::SigningError)?; - Ok(signature.to_der().to_bytes().into()) - } - SignatureScheme::ED25519 => { - let exposed = self.private.expose_secret(); - let k = match exposed.len() { - // Compat layer for legacy keypairs [seed, pk] - ed25519_dalek::KEYPAIR_LENGTH => { - let mut sk = zeroize::Zeroizing::new([0u8; ed25519_dalek::KEYPAIR_LENGTH]); - sk.copy_from_slice(exposed.as_slice()); - ed25519_dalek::SigningKey::from_keypair_bytes(&sk) - .map_err(|_| Error::SigningError)? - } - ed25519_dalek::SECRET_KEY_LENGTH => { - let mut sk = - zeroize::Zeroizing::new([0u8; ed25519_dalek::SECRET_KEY_LENGTH]); - sk.copy_from_slice(exposed.as_slice()); - ed25519_dalek::SigningKey::from_bytes(&sk) - } - _ => return Err(Error::SigningError), - }; - - let signature = k.try_sign(payload).map_err(|_| Error::SigningError)?; - Ok(signature.to_bytes().into()) - } - _ => Err(Error::SigningError), - } +impl openmls_traits::signatures::DefaultSigner for SignatureKeyPair { + fn private_key(&self) -> &[u8] { + self.private.expose_secret().as_slice() } fn signature_scheme(&self) -> SignatureScheme { diff --git a/book/src/user_manual/identity.md b/book/src/user_manual/identity.md index eef110e35c..1180a5e58b 100644 --- a/book/src/user_manual/identity.md +++ b/book/src/user_manual/identity.md @@ -15,11 +15,10 @@ Note that the implementation of the Authentication Service and, thus, the detail ## Creating and using credentials OpenMLS allows clients to create `Credentials`. -A `BasicCredential`, currently the only credential type supported by OpenMLS, consists only of the `identity`. -Thus, to create a fresh `Credential`, the following inputs are required: +A `BasicCredential`, consists only of the `identity`. +Thus, to create a fresh `Credential`, the following input is required: - `identity: Vec`: An octet string that uniquely identifies the client. -- `credential_type: CredentialType`: The type of the credential, in this case `CredentialType::Basic`. ```rust,no_run,noplayground {{#include ../../../openmls/tests/book_code.rs:create_basic_credential}} diff --git a/cli/src/identity.rs b/cli/src/identity.rs index 8004d48e60..1f87b1a032 100644 --- a/cli/src/identity.rs +++ b/cli/src/identity.rs @@ -15,7 +15,7 @@ impl Identity { crypto: &OpenMlsRustCrypto, id: &[u8], ) -> Self { - let credential = Credential::new(id.to_vec(), CredentialType::Basic).unwrap(); + let credential = Credential::new_basic(id.to_vec()); let signature_keys = SignatureKeyPair::new( ciphersuite.signature_algorithm(), &mut *crypto.rand().borrow_rand().unwrap(), diff --git a/delivery-service/ds-lib/tests/test_codec.rs b/delivery-service/ds-lib/tests/test_codec.rs index 85ff68409c..a72ffd3a98 100644 --- a/delivery-service/ds-lib/tests/test_codec.rs +++ b/delivery-service/ds-lib/tests/test_codec.rs @@ -11,8 +11,7 @@ async fn test_client_info() { let client_name = "Client1"; let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519; - let credential = - Credential::new(client_name.as_bytes().to_vec(), CredentialType::Basic).unwrap(); + let credential = Credential::new_basic(client_name.as_bytes().to_vec()); let signature_keys = SignatureKeyPair::new( ciphersuite.signature_algorithm(), &mut *crypto.rand().borrow_rand().unwrap(), diff --git a/delivery-service/ds/src/test.rs b/delivery-service/ds/src/test.rs index 14cc13c3f9..20d65b81e6 100644 --- a/delivery-service/ds/src/test.rs +++ b/delivery-service/ds/src/test.rs @@ -12,7 +12,7 @@ fn generate_credential( signature_scheme: SignatureScheme, crypto_backend: &impl OpenMlsCryptoProvider, ) -> (CredentialWithKey, SignatureKeyPair) { - let credential = Credential::new(identity, CredentialType::Basic).unwrap(); + let credential = Credential::new_basic(identity); let signature_keys = SignatureKeyPair::new( signature_scheme, &mut *crypto_backend.rand().borrow_rand().unwrap(), diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index b042d7b06c..7d13df8066 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -26,9 +26,11 @@ serde_json = { version = "1.0", optional = true } itertools = { version = "0.10", optional = true } openmls_rust_crypto = { version = "0.1.0", path = "../openmls_rust_crypto", optional = true } openmls_basic_credential = { version = "0.1.0", path = "../basic_credential", optional = true, features = ["clonable", "test-utils"] } +openmls_x509_credential = { version = "0.1.0", path = "../x509_credential", optional = true, features = ["test-utils"] } rstest = { version = "^0.16", optional = true } rstest_reuse = { version = "0.4", optional = true } tokio = { version = "1.24", features = ["full"], optional = true } +x509-cert = "0.2" [features] default = [] @@ -42,6 +44,7 @@ test-utils = [ "dep:rstest", "dep:rstest_reuse", "dep:openmls_basic_credential", + "dep:openmls_x509_credential", ] crypto-debug = [] # ☣️ Enable logging of sensitive cryptographic information content-debug = [] # ☣️ Enable logging of sensitive message content diff --git a/openmls/benches/benchmark.rs b/openmls/benches/benchmark.rs index 85a3c7ac95..97f47e36a8 100644 --- a/openmls/benches/benchmark.rs +++ b/openmls/benches/benchmark.rs @@ -9,7 +9,7 @@ use openmls_traits::{crypto::OpenMlsCrypto, OpenMlsCryptoProvider}; fn criterion_benchmark(c: &mut Criterion) { let backend = OpenMlsRustCrypto::default(); for &ciphersuite in backend.crypto().supported_ciphersuites().iter() { - let credential = Credential::new(vec![1, 2, 3], CredentialType::Basic).unwrap(); + let credential = Credential::new_basic(vec![1, 2, 3]); let signer = SignatureKeyPair::new( ciphersuite.signature_algorithm(), &mut *backend.rand().borrow_rand().unwrap(), diff --git a/openmls/src/credentials/codec.rs b/openmls/src/credentials/codec.rs index c4169936d4..a54ae144be 100644 --- a/openmls/src/credentials/codec.rs +++ b/openmls/src/credentials/codec.rs @@ -8,7 +8,7 @@ impl tls_codec::Size for Credential { self.credential_type.tls_serialized_len() + match &self.credential { MlsCredentialType::Basic(c) => c.tls_serialized_len(), - MlsCredentialType::X509(_) => unimplemented!(), + MlsCredentialType::X509(c) => c.tls_serialized_len(), } } } @@ -21,9 +21,10 @@ impl tls_codec::Serialize for Credential { basic_credential.tls_serialize(writer).map(|l| l + written) } // TODO #134: implement encoding for X509 certificates - MlsCredentialType::X509(_) => Err(tls_codec::Error::EncodingError( - "X509 certificates are not yet implemented.".to_string(), - )), + MlsCredentialType::X509(credential) => { + let written = CredentialType::X509.tls_serialize(writer)?; + credential.tls_serialize(writer).map(|l| l + written) + } } } } @@ -37,6 +38,9 @@ impl tls_codec::Deserialize for Credential { CredentialType::Basic => Ok(Credential::from(MlsCredentialType::Basic( BasicCredential::tls_deserialize(bytes)?, ))), + CredentialType::X509 => Ok(Credential::from(MlsCredentialType::X509( + Certificate::tls_deserialize(bytes)?, + ))), _ => Err(tls_codec::Error::DecodingError(format!( "{credential_type:?} can not be deserialized." ))), diff --git a/openmls/src/credentials/errors.rs b/openmls/src/credentials/errors.rs index 4c868cc880..f1a566c986 100644 --- a/openmls/src/credentials/errors.rs +++ b/openmls/src/credentials/errors.rs @@ -17,4 +17,13 @@ pub enum CredentialError { /// Verifying the signature with this credential failed. #[error("Invalid signature.")] InvalidSignature, + /// Incomplete x509 certificate chain + #[error("x509 certificate chain is either empty or contains a single self-signed certificate which is not allowed.")] + IncompleteCertificateChain, + /// Failed to decode certificate data + #[error("Failed to decode certificate data: {0}")] + CertificateDecodingError(#[from] x509_cert::der::Error), + /// x509 certificate chain is either unordered or a child is missigned by its issuer + #[error("Invalid x509 certificate chain.")] + InvalidCertificateChain, } diff --git a/openmls/src/credentials/mod.rs b/openmls/src/credentials/mod.rs index fbd4f3340e..d5d88d6f45 100644 --- a/openmls/src/credentials/mod.rs +++ b/openmls/src/credentials/mod.rs @@ -35,6 +35,7 @@ mod codec; #[cfg(test)] mod tests; use errors::*; +use x509_cert::{der::Decode, PkiPath}; use crate::ciphersuite::SignaturePublicKey; @@ -139,9 +140,24 @@ impl From for u16 { /// opaque cert_data; /// } Certificate; /// ``` -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[derive( + Debug, PartialEq, Eq, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, +)] pub struct Certificate { - cert_data: Vec, + identity: VLBytes, + cert_data: Vec, +} + +impl Certificate { + pub(crate) fn pki_path(&self) -> Result { + self.cert_data.iter().try_fold( + PkiPath::new(), + |mut acc, cert_data| -> Result { + acc.push(x509_cert::Certificate::from_der(cert_data.as_slice())?); + Ok(acc) + }, + ) + } } /// MlsCredentialType. @@ -184,27 +200,36 @@ impl Credential { self.credential_type } - /// Creates and returns a new [`Credential`] of the given - /// [`CredentialType`] for the given identity. + pub(crate) fn mls_credential(&self) -> &MlsCredentialType { + &self.credential + } + + /// Creates and returns a new basic [`Credential`] for the given identity. /// If the credential holds key material, this is generated and stored in /// the key store. - /// - /// Returns an error if the given [`CredentialType`] is not supported. - pub fn new( - identity: Vec, - credential_type: CredentialType, - ) -> Result { - let mls_credential = match credential_type { - CredentialType::Basic => BasicCredential { + pub fn new_basic(identity: Vec) -> Self { + Self { + credential_type: CredentialType::Basic, + credential: MlsCredentialType::Basic(BasicCredential { identity: identity.into(), - }, - _ => return Err(CredentialError::UnsupportedCredentialType), - }; - let credential = Credential { - credential_type, - credential: MlsCredentialType::Basic(mls_credential), - }; - Ok(credential) + }), + } + } + + /// Creates and returns a new X509 [`Credential`] for the given identity. + /// If the credential holds key material, this is generated and stored in + /// the key store. + pub fn new_x509(identity: Vec, cert_data: Vec>) -> Result { + if cert_data.len() < 2 { + return Err(CredentialError::IncompleteCertificateChain); + } + Ok(Self { + credential_type: CredentialType::X509, + credential: MlsCredentialType::X509(Certificate { + identity: identity.into(), + cert_data: cert_data.into_iter().map(|c| c.into()).collect(), + }), + }) } /// Returns the identity of a given credential. @@ -212,7 +237,7 @@ impl Credential { match &self.credential { MlsCredentialType::Basic(basic_credential) => basic_credential.identity.as_slice(), // TODO: implement getter for identity for X509 certificates. See issue #134. - MlsCredentialType::X509(_) => panic!("X509 certificates are not yet implemented."), + MlsCredentialType::X509(cred) => cred.identity.as_slice(), } } } @@ -272,18 +297,61 @@ pub mod test_utils { use super::{Credential, CredentialType, CredentialWithKey}; + /// Convenience function that generates a new credential and a key pair for + /// it (using the x509 credential crate). + /// The signature keys are stored in the key store. + /// + /// Returns the [`Credential`] and the [`SignatureKeyPair`]. + pub async fn new_x509_credential( + backend: &impl OpenMlsCryptoProvider, + identity: &[u8], + signature_scheme: SignatureScheme, + cert_data: Vec>, + ) -> (CredentialWithKey, SignatureKeyPair) { + build_credential( + backend, + identity, + CredentialType::X509, + signature_scheme, + Some(cert_data), + ) + .await + } + /// Convenience function that generates a new credential and a key pair for /// it (using the basic credential crate). /// The signature keys are stored in the key store. /// /// Returns the [`Credential`] and the [`SignatureKeyPair`]. pub async fn new_credential( + backend: &impl OpenMlsCryptoProvider, + identity: &[u8], + signature_scheme: SignatureScheme, + ) -> (CredentialWithKey, SignatureKeyPair) { + build_credential( + backend, + identity, + CredentialType::Basic, + signature_scheme, + None, + ) + .await + } + + async fn build_credential( backend: &impl OpenMlsCryptoProvider, identity: &[u8], credential_type: CredentialType, signature_scheme: SignatureScheme, + cert_data: Option>>, ) -> (CredentialWithKey, SignatureKeyPair) { - let credential = Credential::new(identity.into(), credential_type).unwrap(); + let credential = match credential_type { + CredentialType::Basic => Credential::new_basic(identity.into()), + CredentialType::X509 => { + Credential::new_x509(identity.into(), cert_data.unwrap()).unwrap() + } + CredentialType::Unknown(_) => unimplemented!(), + }; let signature_keys = SignatureKeyPair::new( signature_scheme, &mut *backend.rand().borrow_rand().unwrap(), diff --git a/openmls/src/extensions/external_sender_extension.rs b/openmls/src/extensions/external_sender_extension.rs index 522262c332..22033ee9af 100644 --- a/openmls/src/extensions/external_sender_extension.rs +++ b/openmls/src/extensions/external_sender_extension.rs @@ -70,7 +70,7 @@ mod test { use tls_codec::{Deserialize, Serialize}; use super::*; - use crate::{credentials::CredentialType, test_utils::*}; + use crate::test_utils::*; #[apply(ciphersuites)] async fn test_serialize_deserialize(ciphersuite: Ciphersuite) { @@ -79,7 +79,7 @@ mod test { let mut external_sender_extensions = Vec::new(); for _ in 0..8 { - let credential = Credential::new(b"Alice".to_vec(), CredentialType::Basic).unwrap(); + let credential = Credential::new_basic(b"Alice".to_vec()); let signature_keys = SignatureKeyPair::new(ciphersuite.signature_algorithm(), &mut rng).unwrap(); diff --git a/openmls/src/extensions/test_extensions.rs b/openmls/src/extensions/test_extensions.rs index 317b5970b0..2f585a3515 100644 --- a/openmls/src/extensions/test_extensions.rs +++ b/openmls/src/extensions/test_extensions.rs @@ -41,20 +41,10 @@ async fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMls let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); // Create credentials and keys - let (alice_credential_with_key, alice_signature_keys) = test_utils::new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; - let (bob_credential_with_key, bob_signature_keys) = test_utils::new_credential( - backend, - b"Bob", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (alice_credential_with_key, alice_signature_keys) = + test_utils::new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; + let (bob_credential_with_key, bob_signature_keys) = + test_utils::new_credential(backend, b"Bob", ciphersuite.signature_algorithm()).await; // Generate KeyPackages let bob_key_package_bundle = KeyPackageBundle::new( diff --git a/openmls/src/framing/message_out.rs b/openmls/src/framing/message_out.rs index 386487e1c5..f40edf7c0d 100644 --- a/openmls/src/framing/message_out.rs +++ b/openmls/src/framing/message_out.rs @@ -117,10 +117,7 @@ impl From for MlsMessageOut { impl MlsMessageOut { /// Create an [`MlsMessageOut`] from a [`PrivateMessage`], as well as the /// currently used [`ProtocolVersion`]. - pub fn from_private_message( - private_message: PrivateMessage, - version: ProtocolVersion, - ) -> Self { + pub fn from_private_message(private_message: PrivateMessage, version: ProtocolVersion) -> Self { Self { version, body: MlsMessageOutBody::PrivateMessage(private_message), diff --git a/openmls/src/framing/test_framing.rs b/openmls/src/framing/test_framing.rs index d08100ba4c..b54f916e93 100644 --- a/openmls/src/framing/test_framing.rs +++ b/openmls/src/framing/test_framing.rs @@ -27,13 +27,8 @@ use crate::{ /// This tests serializing/deserializing PublicMessage #[apply(ciphersuites_and_backends)] async fn codec_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - let (_credential, signature_keys) = test_utils::new_credential( - backend, - b"Creator", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (_credential, signature_keys) = + test_utils::new_credential(backend, b"Creator", ciphersuite.signature_algorithm()).await; let sender = Sender::build_member(LeafNodeIndex::new(987543210)); let group_context = GroupContext::new( ciphersuite, @@ -80,13 +75,8 @@ async fn codec_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP /// This tests serializing/deserializing PrivateMessage #[apply(ciphersuites_and_backends)] async fn codec_ciphertext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - let (_credential, signature_keys) = test_utils::new_credential( - backend, - b"Creator", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (_credential, signature_keys) = + test_utils::new_credential(backend, b"Creator", ciphersuite.signature_algorithm()).await; let sender = Sender::build_member(LeafNodeIndex::new(0)); let group_context = GroupContext::new( ciphersuite, @@ -297,13 +287,8 @@ async fn create_content( wire_format: WireFormat, backend: &impl OpenMlsCryptoProvider, ) -> (AuthenticatedContent, CredentialWithKey, SignatureKeyPair) { - let (credential, signature_keys) = test_utils::new_credential( - backend, - b"Creator", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (credential, signature_keys) = + test_utils::new_credential(backend, b"Creator", ciphersuite.signature_algorithm()).await; let sender = Sender::build_member(LeafNodeIndex::new(0)); let group_context = GroupContext::new( ciphersuite, @@ -334,13 +319,8 @@ async fn create_content( #[apply(ciphersuites_and_backends)] async fn membership_tag(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - let (_credential, signature_keys) = test_utils::new_credential( - backend, - b"Creator", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (_credential, signature_keys) = + test_utils::new_credential(backend, b"Creator", ciphersuite.signature_algorithm()).await; let group_context = GroupContext::new( ciphersuite, GroupId::random(backend), @@ -396,27 +376,12 @@ async fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr let configuration = &SenderRatchetConfiguration::default(); // Define credentials with keys - let (alice_credential, alice_signature_keys) = test_utils::new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; - let (bob_credential, bob_signature_keys) = test_utils::new_credential( - backend, - b"Bob", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; - let (charlie_credential, charlie_signature_keys) = test_utils::new_credential( - backend, - b"Charlie", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (alice_credential, alice_signature_keys) = + test_utils::new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; + let (bob_credential, bob_signature_keys) = + test_utils::new_credential(backend, b"Bob", ciphersuite.signature_algorithm()).await; + let (charlie_credential, charlie_signature_keys) = + test_utils::new_credential(backend, b"Charlie", ciphersuite.signature_algorithm()).await; // Generate KeyPackages let bob_key_package_bundle = @@ -651,20 +616,10 @@ pub(crate) async fn setup_alice_bob_group( let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); // Create credentials and keys - let (alice_credential, alice_signature_keys) = test_utils::new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; - let (bob_credential, bob_signature_keys) = test_utils::new_credential( - backend, - b"Bob", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (alice_credential, alice_signature_keys) = + test_utils::new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; + let (bob_credential, bob_signature_keys) = + test_utils::new_credential(backend, b"Bob", ciphersuite.signature_algorithm()).await; // Generate KeyPackages let bob_key_package_bundle = KeyPackageBundle::new( diff --git a/openmls/src/framing/validation.rs b/openmls/src/framing/validation.rs index f91b052be2..d68afe20e7 100644 --- a/openmls/src/framing/validation.rs +++ b/openmls/src/framing/validation.rs @@ -23,7 +23,12 @@ //! ``` // TODO #106/#151: Update the above diagram -use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite, OpenMlsCryptoProvider}; +use openmls_traits::{ + crypto::OpenMlsCrypto, + types::{Ciphersuite, CryptoError}, + OpenMlsCryptoProvider, +}; +use openmls_x509_credential::X509Ext; use crate::{ binary_tree::LeafNodeIndex, @@ -273,10 +278,41 @@ impl UnverifiedMessage { crypto: &impl OpenMlsCrypto, protocol_version: ProtocolVersion, ) -> Result<(AuthenticatedContent, Credential), ProcessMessageError> { - let content: AuthenticatedContentIn = self - .verifiable_content - .verify(crypto, &self.sender_pk) - .map_err(|_| ProcessMessageError::InvalidSignature)?; + let content: AuthenticatedContentIn = match self.credential.mls_credential() { + MlsCredentialType::Basic(_) => self + .verifiable_content + .verify(crypto, &self.sender_pk) + .map_err(|_| ProcessMessageError::InvalidSignature)?, + MlsCredentialType::X509(certificate_chain) => { + certificate_chain + .pki_path()? + .iter() + .enumerate() + .map(Ok) + .reduce( + |a, b| -> Result<(usize, &x509_cert::Certificate), CryptoError> { + let (_, child_cert) = a?; + let (parent_idx, parent_cert) = b?; + // verify not expired + child_cert.is_valid()?; + + // verify that child is signed by parent + child_cert.is_signed_by(crypto, parent_cert)?; + + Ok((parent_idx, parent_cert)) + }, + ) + .ok_or_else(|| { + ProcessMessageError::LibraryError(LibraryError::custom( + "Cannot have validated an empty certificate chain", + )) + })??; + self.verifiable_content + // sender pk should be the leaf certificate + .verify(crypto, &self.sender_pk) + .map_err(|_| ProcessMessageError::InvalidSignature)? + } + }; let content = content.validate(ciphersuite, crypto, self.sender_context, protocol_version)?; Ok((content, self.credential)) diff --git a/openmls/src/group/core_group/test_core_group.rs b/openmls/src/group/core_group/test_core_group.rs index be213f8c68..340bde85e8 100644 --- a/openmls/src/group/core_group/test_core_group.rs +++ b/openmls/src/group/core_group/test_core_group.rs @@ -29,13 +29,8 @@ pub(crate) async fn setup_alice_group( OpenMlsSignaturePublicKey, ) { // Create credentials and keys - let (alice_credential_with_key, alice_signature_keys) = test_utils::new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (alice_credential_with_key, alice_signature_keys) = + test_utils::new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; let pk = OpenMlsSignaturePublicKey::new( alice_signature_keys.to_public_vec().into(), ciphersuite.signature_algorithm(), @@ -100,13 +95,8 @@ async fn test_failed_groupinfo_decryption( }); // Create credentials and keys - let (alice_credential_with_key, alice_signature_keys) = test_utils::new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (alice_credential_with_key, alice_signature_keys) = + test_utils::new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; let key_package_bundle = KeyPackageBundle::new( backend, @@ -320,20 +310,10 @@ async fn setup_alice_bob( SignatureKeyPair, ) { // Create credentials and keys - let (alice_credential_with_key, alice_signer) = test_utils::new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; - let (bob_credential_with_key, bob_signer) = test_utils::new_credential( - backend, - b"Bob", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (alice_credential_with_key, alice_signer) = + test_utils::new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; + let (bob_credential_with_key, bob_signer) = + test_utils::new_credential(backend, b"Bob", ciphersuite.signature_algorithm()).await; // Generate Bob's KeyPackage let bob_key_package_bundle = @@ -562,13 +542,8 @@ async fn test_own_commit_processing( let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); // Create credentials and keys - let (alice_credential_with_key, alice_signature_keys) = test_utils::new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (alice_credential_with_key, alice_signature_keys) = + test_utils::new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; // === Alice creates a group === let alice_group = CoreGroup::builder( @@ -612,13 +587,8 @@ pub(crate) async fn setup_client_with_extensions( SignatureKeyPair, OpenMlsSignaturePublicKey, ) { - let (credential_with_key, signature_keys) = test_utils::new_credential( - backend, - id.as_bytes(), - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (credential_with_key, signature_keys) = + test_utils::new_credential(backend, id.as_bytes(), ciphersuite.signature_algorithm()).await; let pk = OpenMlsSignaturePublicKey::new( signature_keys.to_public_vec().into(), ciphersuite.signature_algorithm(), diff --git a/openmls/src/group/mls_group/errors.rs b/openmls/src/group/mls_group/errors.rs index a11824935f..556402ba25 100644 --- a/openmls/src/group/mls_group/errors.rs +++ b/openmls/src/group/mls_group/errors.rs @@ -5,9 +5,11 @@ // These errors are exposed through `crate::group::errors`. +use openmls_traits::types::CryptoError; use thiserror::Error; use crate::{ + credentials::errors::CredentialError, error::LibraryError, extensions::errors::{ExtensionError, InvalidExtensionError}, group::errors::{ @@ -114,6 +116,12 @@ pub enum ProcessMessageError { /// The proposal is invalid for the Sender of type [External](crate::prelude::Sender::External) #[error("The proposal is invalid for the Sender of type External")] UnsupportedProposalType, + /// Error parsing the certificate chain + #[error("Error parsing the X509 certificate chain: {0}")] + CredentialError(#[from] CredentialError), + /// Error validating the certificate chain + #[error("Error validating certificate chain")] + CryptoError(#[from] CryptoError), } /// Create message error diff --git a/openmls/src/group/tests/test_proposal_validation.rs b/openmls/src/group/tests/test_proposal_validation.rs index b3e739f7f1..27147001c1 100644 --- a/openmls/src/group/tests/test_proposal_validation.rs +++ b/openmls/src/group/tests/test_proposal_validation.rs @@ -370,7 +370,7 @@ async fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP backend, &charlie_credential_with_key.signer, CredentialWithKey { - credential: Credential::new(b"Dave".to_vec(), CredentialType::Basic).unwrap(), + credential: Credential::new_basic(b"Dave".to_vec()), signature_key: charlie_credential_with_key .credential_with_key .signature_key, @@ -617,7 +617,7 @@ async fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP } .map(|(name, keypair)| CredentialWithKeyAndSigner { credential_with_key: CredentialWithKey { - credential: Credential::new(name.into(), CredentialType::Basic).unwrap(), + credential: Credential::new_basic(name.into()), signature_key: keypair.to_public_vec().into(), }, signer: keypair, diff --git a/openmls/src/group/tests/utils.rs b/openmls/src/group/tests/utils.rs index cefe0fea03..c86bda81e9 100644 --- a/openmls/src/group/tests/utils.rs +++ b/openmls/src/group/tests/utils.rs @@ -344,7 +344,7 @@ pub(crate) async fn generate_credential_with_key( backend: &impl OpenMlsCryptoProvider, ) -> CredentialWithKeyAndSigner { let (credential, signer) = { - let credential = Credential::new(identity, CredentialType::Basic).unwrap(); + let credential = Credential::new_basic(identity); let signature_keys = SignatureKeyPair::new( signature_scheme, &mut *backend.rand().borrow_rand().unwrap(), diff --git a/openmls/src/key_packages/mod.rs b/openmls/src/key_packages/mod.rs index 1099a58eb7..41e15e5d6f 100644 --- a/openmls/src/key_packages/mod.rs +++ b/openmls/src/key_packages/mod.rs @@ -37,7 +37,7 @@ //! let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519; //! let backend = OpenMlsRustCrypto::default(); //! -//! let credential = Credential::new("identity".into(), CredentialType::Basic).unwrap(); +//! let credential = Credential::new_basic("identity".into()); //! let signer = //! SignatureKeyPair::new(ciphersuite.signature_algorithm(), &mut *backend.rand().borrow_rand().unwrap()) //! .expect("Error generating a signature key pair."); diff --git a/openmls/src/key_packages/test_key_packages.rs b/openmls/src/key_packages/test_key_packages.rs index 1211fd13d5..fcc8e45e78 100644 --- a/openmls/src/key_packages/test_key_packages.rs +++ b/openmls/src/key_packages/test_key_packages.rs @@ -11,7 +11,7 @@ pub(crate) async fn key_package( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) -> (KeyPackage, Credential, SignatureKeyPair) { - let credential = Credential::new(b"Sasha".to_vec(), CredentialType::Basic).unwrap(); + let credential = Credential::new_basic(b"Sasha".to_vec()); let signer = SignatureKeyPair::new( ciphersuite.signature_algorithm(), &mut *backend.rand().borrow_rand().unwrap(), @@ -65,8 +65,7 @@ async fn serialization(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPro #[apply(ciphersuites_and_backends)] async fn application_id_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - let credential = Credential::new(b"Sasha".to_vec(), CredentialType::Basic) - .expect("An unexpected error occurred."); + let credential = Credential::new_basic(b"Sasha".to_vec()); let signature_keys = SignatureKeyPair::new( ciphersuite.signature_algorithm(), &mut *backend.rand().borrow_rand().unwrap(), diff --git a/openmls/src/lib.rs b/openmls/src/lib.rs index b725406524..f3968af210 100644 --- a/openmls/src/lib.rs +++ b/openmls/src/lib.rs @@ -19,11 +19,10 @@ //! // A helper to create and store credentials. //! async fn generate_credential_with_key( //! identity: Vec, -//! credential_type: CredentialType, //! signature_algorithm: SignatureScheme, //! backend: &impl OpenMlsCryptoProvider, //! ) -> (CredentialWithKey, SignatureKeyPair) { -//! let credential = Credential::new(identity, credential_type).unwrap(); +//! let credential = Credential::new_basic(identity); //! let signature_keys = //! SignatureKeyPair::new(signature_algorithm, &mut *backend.rand().borrow_rand().unwrap()) //! .expect("Error generating a signature key pair."); @@ -72,14 +71,12 @@ //! // First they need credentials to identify them //! let (sasha_credential_with_key, sasha_signer) = generate_credential_with_key( //! "Sasha".into(), -//! CredentialType::Basic, //! ciphersuite.signature_algorithm(), //! backend, //! ).await; //! //! let (maxim_credential_with_key, maxim_signer) = generate_credential_with_key( //! "Maxim".into(), -//! CredentialType::Basic, //! ciphersuite.signature_algorithm(), //! backend, //! ).await; diff --git a/openmls/src/test_utils/mod.rs b/openmls/src/test_utils/mod.rs index 3b6449d1c0..1cc9094035 100644 --- a/openmls/src/test_utils/mod.rs +++ b/openmls/src/test_utils/mod.rs @@ -94,7 +94,7 @@ pub(crate) async fn generate_group_candidate( use openmls_traits::random::OpenMlsRand; let credential_with_key_and_signer = { - let credential = Credential::new(identity.to_vec(), CredentialType::Basic).unwrap(); + let credential = Credential::new_basic(identity.to_vec()); let signature_keypair = SignatureKeyPair::new( ciphersuite.signature_algorithm(), diff --git a/openmls/src/test_utils/test_framework/mod.rs b/openmls/src/test_utils/test_framework/mod.rs index 42b8a38c92..713aabda0b 100644 --- a/openmls/src/test_utils/test_framework/mod.rs +++ b/openmls/src/test_utils/test_framework/mod.rs @@ -148,7 +148,7 @@ impl MlsGroupTestSetup { let crypto = OpenMlsRustCrypto::default(); let mut credentials = HashMap::new(); for ciphersuite in crypto.crypto().supported_ciphersuites().iter() { - let credential = Credential::new(identity.clone(), CredentialType::Basic).unwrap(); + let credential = Credential::new_basic(identity.clone()); let signature_keys = SignatureKeyPair::new( ciphersuite.signature_algorithm(), &mut *crypto.rand().borrow_rand().unwrap(), diff --git a/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs b/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs index 74a69b827a..20bfbafe80 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs @@ -92,7 +92,7 @@ use thiserror::Error; use crate::{ binary_tree::array_representation::LeafNodeIndex, - credentials::{Credential, CredentialType, CredentialWithKey}, + credentials::{Credential, CredentialWithKey}, framing::{ mls_auth_content::AuthenticatedContent, mls_auth_content_in::AuthenticatedContentIn, mls_content_in::FramedContentBodyIn, *, @@ -143,11 +143,10 @@ pub struct EncryptionTestVector { async fn generate_credential( identity: Vec, - credential_type: CredentialType, signature_algorithm: SignatureScheme, backend: &impl OpenMlsCryptoProvider, ) -> (CredentialWithKey, SignatureKeyPair) { - let credential = Credential::new(identity, credential_type).unwrap(); + let credential = Credential::new_basic(identity); let signature_keys = SignatureKeyPair::new( signature_algorithm, &mut *backend.rand().borrow_rand().unwrap(), @@ -171,13 +170,8 @@ async fn group( ) -> (CoreGroup, CredentialWithKey, SignatureKeyPair) { use crate::group::config::CryptoConfig; - let (credential_with_key, signer) = generate_credential( - "Kreator".into(), - CredentialType::Basic, - ciphersuite.signature_algorithm(), - backend, - ) - .await; + let (credential_with_key, signer) = + generate_credential("Kreator".into(), ciphersuite.signature_algorithm(), backend).await; let group = CoreGroup::builder( GroupId::random(backend), @@ -201,7 +195,6 @@ async fn receiver_group( let (credential_with_key, signer) = generate_credential( "Receiver".into(), - CredentialType::Basic, ciphersuite.signature_algorithm(), backend, ) diff --git a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs index 749ca730b8..43935a8664 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs @@ -69,7 +69,7 @@ use serde::{self, Deserialize, Serialize}; use crate::{ binary_tree::array_representation::LeafNodeIndex, - credentials::{Credential, CredentialType, CredentialWithKey}, + credentials::{Credential, CredentialWithKey}, framing::{mls_auth_content::AuthenticatedContent, mls_content::FramedContentBody, *}, group::*, schedule::{EncryptionSecret, SenderDataSecret}, @@ -108,11 +108,10 @@ pub struct MessageProtectionTest { async fn generate_credential( identity: Vec, - credential_type: CredentialType, signature_algorithm: SignatureScheme, backend: &impl OpenMlsCryptoProvider, ) -> (CredentialWithKey, SignatureKeyPair) { - let credential = Credential::new(identity, credential_type).unwrap(); + let credential = Credential::new_basic(identity); let signature_keys = SignatureKeyPair::new( signature_algorithm, &mut *backend.rand().borrow_rand().unwrap(), @@ -136,13 +135,8 @@ async fn group( ) -> (CoreGroup, CredentialWithKey, SignatureKeyPair) { use crate::group::config::CryptoConfig; - let (credential_with_key, signer) = generate_credential( - "Kreator".into(), - CredentialType::Basic, - ciphersuite.signature_algorithm(), - backend, - ) - .await; + let (credential_with_key, signer) = + generate_credential("Kreator".into(), ciphersuite.signature_algorithm(), backend).await; let group = CoreGroup::builder( GroupId::random(backend), @@ -166,7 +160,6 @@ async fn receiver_group( let (credential_with_key, signer) = generate_credential( "Receiver".into(), - CredentialType::Basic, ciphersuite.signature_algorithm(), backend, ) @@ -266,8 +259,7 @@ pub async fn run_test_vector( ); // Set up the group, unfortunately we can't do without. - let credential = - Credential::new(b"This is not needed".to_vec(), CredentialType::Basic).unwrap(); + let credential = Credential::new_basic(b"This is not needed".to_vec()); let signature_private_key = match ciphersuite { Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => { @@ -305,7 +297,7 @@ pub async fn run_test_vector( .await .unwrap(); - let credential = Credential::new("Fake user".into(), CredentialType::Basic).unwrap(); + let credential = Credential::new_basic("Fake user".into()); let signature_keys = SignatureKeyPair::new( ciphersuite.signature_algorithm(), &mut *backend.rand().borrow_rand().unwrap(), diff --git a/openmls/src/treesync/tests_and_kats/tests/test_diff.rs b/openmls/src/treesync/tests_and_kats/tests/test_diff.rs index a49cd09cb9..1058a1245e 100644 --- a/openmls/src/treesync/tests_and_kats/tests/test_diff.rs +++ b/openmls/src/treesync/tests_and_kats/tests/test_diff.rs @@ -4,7 +4,7 @@ use rstest::*; use rstest_reuse::apply; use crate::{ - credentials::{test_utils::new_credential, CredentialType}, + credentials::test_utils::new_credential, key_packages::KeyPackageBundle, treesync::{node::Node, RatchetTree, TreeSync}, }; @@ -15,23 +15,11 @@ async fn test_free_leaf_computation( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, ) { - let (c_0, sk_0) = new_credential( - backend, - b"leaf0", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (c_0, sk_0) = new_credential(backend, b"leaf0", ciphersuite.signature_algorithm()).await; let kpb_0 = KeyPackageBundle::new(backend, &sk_0, ciphersuite, c_0).await; - let (c_3, sk_3) = new_credential( - backend, - b"leaf3", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (c_3, sk_3) = new_credential(backend, b"leaf3", ciphersuite.signature_algorithm()).await; let kpb_3 = KeyPackageBundle::new(backend, &sk_3, ciphersuite, c_3).await; // Build a rudimentary tree with two populated and two empty leaf nodes. @@ -51,13 +39,8 @@ async fn test_free_leaf_computation( // Create and add a new leaf. It should go to leaf index 1 - let (c_2, signer_2) = new_credential( - backend, - b"leaf2", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (c_2, signer_2) = + new_credential(backend, b"leaf2", ciphersuite.signature_algorithm()).await; let kpb_2 = KeyPackageBundle::new(backend, &signer_2, ciphersuite, c_2).await; let mut diff = tree.empty_diff(); diff --git a/openmls/tests/book_code.rs b/openmls/tests/book_code.rs index edaec7335c..64d8e44e18 100644 --- a/openmls/tests/book_code.rs +++ b/openmls/tests/book_code.rs @@ -21,12 +21,11 @@ fn create_backend_rust_crypto() { async fn generate_credential( identity: Vec, - credential_type: CredentialType, signature_algorithm: SignatureScheme, backend: &impl OpenMlsCryptoProvider, ) -> (CredentialWithKey, SignatureKeyPair) { // ANCHOR: create_basic_credential - let credential = Credential::new(identity, credential_type).unwrap(); + let credential = Credential::new_basic(identity); // ANCHOR_END: create_basic_credential // ANCHOR: create_credential_keys let signature_keys = SignatureKeyPair::new( @@ -85,37 +84,17 @@ async fn generate_key_package( #[apply(ciphersuites_and_backends)] async fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Generate credentials with keys - let (alice_credential, alice_signature_keys) = generate_credential( - "Alice".into(), - CredentialType::Basic, - ciphersuite.signature_algorithm(), - backend, - ) - .await; + let (alice_credential, alice_signature_keys) = + generate_credential("Alice".into(), ciphersuite.signature_algorithm(), backend).await; - let (bob_credential, bob_signature_keys) = generate_credential( - "Bob".into(), - CredentialType::Basic, - ciphersuite.signature_algorithm(), - backend, - ) - .await; + let (bob_credential, bob_signature_keys) = + generate_credential("Bob".into(), ciphersuite.signature_algorithm(), backend).await; - let (charlie_credential, charlie_signature_keys) = generate_credential( - "Charlie".into(), - CredentialType::Basic, - ciphersuite.signature_algorithm(), - backend, - ) - .await; + let (charlie_credential, charlie_signature_keys) = + generate_credential("Charlie".into(), ciphersuite.signature_algorithm(), backend).await; - let (dave_credential, dave_signature_keys) = generate_credential( - "Dave".into(), - CredentialType::Basic, - ciphersuite.signature_algorithm(), - backend, - ) - .await; + let (dave_credential, dave_signature_keys) = + generate_credential("Dave".into(), ciphersuite.signature_algorithm(), backend).await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -131,7 +110,6 @@ async fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP // delivery service credentials let (ds_credential_with_key, ds_signature_keys) = generate_credential( "delivery-service".into(), - CredentialType::Basic, ciphersuite.signature_algorithm(), backend, ) @@ -1337,13 +1315,8 @@ async fn test_empty_input_errors(ciphersuite: Ciphersuite, backend: &impl OpenMl let group_id = GroupId::from_slice(b"Test Group"); // Generate credentials with keys - let (alice_credential, alice_signature_keys) = generate_credential( - "Alice".into(), - CredentialType::Basic, - ciphersuite.signature_algorithm(), - backend, - ) - .await; + let (alice_credential, alice_signature_keys) = + generate_credential("Alice".into(), ciphersuite.signature_algorithm(), backend).await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::test_default(ciphersuite); diff --git a/openmls/tests/key_store.rs b/openmls/tests/key_store.rs index ffb9817d98..522f327a81 100644 --- a/openmls/tests/key_store.rs +++ b/openmls/tests/key_store.rs @@ -6,7 +6,7 @@ use openmls_basic_credential::SignatureKeyPair; async fn test_store_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // ANCHOR: key_store_store // First we generate a credential and key package for our user. - let credential = Credential::new(b"User ID".to_vec(), CredentialType::Basic).unwrap(); + let credential = Credential::new_basic(b"User ID".to_vec()); let signature_keys = SignatureKeyPair::new( ciphersuite.into(), &mut *backend.rand().borrow_rand().unwrap(), diff --git a/openmls/tests/test_external_commit.rs b/openmls/tests/test_external_commit.rs index d3edacf221..c21e27fa49 100644 --- a/openmls/tests/test_external_commit.rs +++ b/openmls/tests/test_external_commit.rs @@ -14,13 +14,8 @@ async fn create_alice_group( .crypto_config(CryptoConfig::with_default_version(ciphersuite)) .build(); - let (credential_with_key, signature_keys) = new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (credential_with_key, signature_keys) = + new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; let group = MlsGroup::new( backend, @@ -78,13 +73,8 @@ async fn test_external_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr // Now, Bob wants to join Alice' group by an external commit. (Positive case.) { - let (bob_credential, bob_signature_keys) = new_credential( - backend, - b"Bob", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (bob_credential, bob_signature_keys) = + new_credential(backend, b"Bob", ciphersuite.signature_algorithm()).await; let (_bob_group, _, _) = MlsGroup::join_by_external_commit( backend, @@ -103,13 +93,8 @@ async fn test_external_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr // Now, Bob wants to join Alice' group by an external commit. (Negative case, broken signature.) { - let (bob_credential, bob_signature_keys) = new_credential( - backend, - b"Bob", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (bob_credential, bob_signature_keys) = + new_credential(backend, b"Bob", ciphersuite.signature_algorithm()).await; let got_error = MlsGroup::join_by_external_commit( backend, @@ -147,13 +132,8 @@ async fn test_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP alice_group.merge_pending_commit(backend).await.unwrap(); // Bob wants to join - let (bob_credential, bob_signature_keys) = new_credential( - backend, - b"Bob", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (bob_credential, bob_signature_keys) = + new_credential(backend, b"Bob", ciphersuite.signature_algorithm()).await; let verifiable_group_info = { let serialized_group_info = group_info.unwrap().tls_serialize_detached().unwrap(); @@ -207,13 +187,8 @@ async fn test_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP // check that the returned group info from the external join is valid // Bob wants to join with another client - let (bob_credential, bob_signature_keys) = new_credential( - backend, - b"Bob 2", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (bob_credential, bob_signature_keys) = + new_credential(backend, b"Bob 2", ciphersuite.signature_algorithm()).await; let verifiable_group_info = { let serialized_group_info = group_info.unwrap().tls_serialize_detached().unwrap(); diff --git a/openmls/tests/test_mls_group.rs b/openmls/tests/test_mls_group.rs index 8d8ce03223..c382bc998f 100644 --- a/openmls/tests/test_mls_group.rs +++ b/openmls/tests/test_mls_group.rs @@ -48,29 +48,14 @@ async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr let group_id = GroupId::from_slice(b"Test Group"); // Generate credentials with keys - let (alice_credential, alice_signer) = new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (alice_credential, alice_signer) = + new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; - let (bob_credential, bob_signer) = new_credential( - backend, - b"Bob", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (bob_credential, bob_signer) = + new_credential(backend, b"Bob", ciphersuite.signature_algorithm()).await; - let (charlie_credential, charlie_signer) = new_credential( - backend, - b"Charlie", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (charlie_credential, charlie_signer) = + new_credential(backend, b"Charlie", ciphersuite.signature_algorithm()).await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -994,13 +979,8 @@ async fn test_empty_input_errors(ciphersuite: Ciphersuite, backend: &impl OpenMl let group_id = GroupId::from_slice(b"Test Group"); // Generate credentials with keys - let (alice_credential, alice_signer) = new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (alice_credential, alice_signer) = + new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; // Define the MlsGroup configuration let mls_group_config = MlsGroupConfig::test_default(ciphersuite); @@ -1046,21 +1026,11 @@ async fn mls_group_ratchet_tree_extension( // === Positive case: using the ratchet tree extension === // Generate credentials - let (alice_credential, alice_signer) = new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (alice_credential, alice_signer) = + new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; - let (bob_credential, bob_signer) = new_credential( - backend, - b"Bob", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (bob_credential, bob_signer) = + new_credential(backend, b"Bob", ciphersuite.signature_algorithm()).await; // Generate KeyPackages let bob_key_package = generate_key_package( @@ -1108,21 +1078,11 @@ async fn mls_group_ratchet_tree_extension( // === Negative case: not using the ratchet tree extension === // Generate credentials with keys - let (alice_credential, alice_signer) = new_credential( - backend, - b"Alice", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (alice_credential, alice_signer) = + new_credential(backend, b"Alice", ciphersuite.signature_algorithm()).await; - let (bob_credential, bob_signer) = new_credential( - backend, - b"Bob", - CredentialType::Basic, - ciphersuite.signature_algorithm(), - ) - .await; + let (bob_credential, bob_signer) = + new_credential(backend, b"Bob", ciphersuite.signature_algorithm()).await; // Generate KeyPackages let bob_key_package = generate_key_package( diff --git a/traits/Cargo.toml b/traits/Cargo.toml index 76a153645c..e03e8ff7dc 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -21,3 +21,10 @@ serde = { version = "1.0", features = ["derive"] } rand_core = "0.6" tls_codec = { workspace = true } async-trait = { workspace = true } +# for the default signer +ed25519-dalek = { version = "2.0.0-rc.2", features = ["rand_core"] } +p256 = "0.13" +p384 = "0.13" +zeroize = "1.6" +signature = "2.1" +secrecy = { version = "0.8", features = ["serde"] } diff --git a/traits/src/key_store.rs b/traits/src/key_store.rs index 813e0be3c7..a59603e7cb 100644 --- a/traits/src/key_store.rs +++ b/traits/src/key_store.rs @@ -4,6 +4,7 @@ #[derive(PartialEq)] pub enum MlsEntityId { SignatureKeyPair, + CertificateKeyPair, HpkePrivateKey, KeyPackage, PskBundle, diff --git a/traits/src/signatures.rs b/traits/src/signatures.rs index d9ceaee8bc..df5c591b3f 100644 --- a/traits/src/signatures.rs +++ b/traits/src/signatures.rs @@ -10,3 +10,62 @@ pub trait Signer { /// The [`SignatureScheme`] of this signer. fn signature_scheme(&self) -> SignatureScheme; } + +/// Implement a default signer using the signature crate and: +/// * p256 crate for [SignatureScheme::ECDSA_SECP256R1_SHA256] +/// * p384 crate for [SignatureScheme::ECDSA_SECP384R1_SHA384] +/// * ed25519-dalek crate for [SignatureScheme::ED25519] +pub trait DefaultSigner { + /// Provides the private key to sign the payload + fn private_key(&self) -> &[u8]; + /// The [`SignatureScheme`] of this signer. + fn signature_scheme(&self) -> SignatureScheme; +} + +impl Signer for T { + fn sign(&self, payload: &[u8]) -> Result, Error> { + use signature::Signer; + match self.signature_scheme() { + SignatureScheme::ECDSA_SECP256R1_SHA256 => { + let k = p256::ecdsa::SigningKey::from_bytes(self.private_key().into()) + .map_err(|_| Error::SigningError)?; + let signature: p256::ecdsa::Signature = + k.try_sign(payload).map_err(|_| Error::SigningError)?; + Ok(signature.to_der().to_bytes().into()) + } + SignatureScheme::ECDSA_SECP384R1_SHA384 => { + let k = p384::ecdsa::SigningKey::from_bytes(self.private_key().into()) + .map_err(|_| Error::SigningError)?; + let signature: p384::ecdsa::Signature = + k.try_sign(payload).map_err(|_| Error::SigningError)?; + Ok(signature.to_der().to_bytes().into()) + } + SignatureScheme::ED25519 => { + let k = match self.private_key().len() { + // Compat layer for legacy keypairs [seed, pk] + ed25519_dalek::KEYPAIR_LENGTH => { + let mut sk = zeroize::Zeroizing::new([0u8; ed25519_dalek::KEYPAIR_LENGTH]); + sk.copy_from_slice(self.private_key()); + ed25519_dalek::SigningKey::from_keypair_bytes(&sk) + .map_err(|_| Error::SigningError)? + } + ed25519_dalek::SECRET_KEY_LENGTH => { + let mut sk = + zeroize::Zeroizing::new([0u8; ed25519_dalek::SECRET_KEY_LENGTH]); + sk.copy_from_slice(self.private_key()); + ed25519_dalek::SigningKey::from_bytes(&sk) + } + _ => return Err(Error::SigningError), + }; + + let signature = k.try_sign(payload).map_err(|_| Error::SigningError)?; + Ok(signature.to_bytes().into()) + } + _ => Err(Error::SigningError), + } + } + + fn signature_scheme(&self) -> SignatureScheme { + self.signature_scheme() + } +} diff --git a/traits/src/types.rs b/traits/src/types.rs index e6de9a60fc..3803c2f69b 100644 --- a/traits/src/types.rs +++ b/traits/src/types.rs @@ -151,6 +151,11 @@ pub enum CryptoError { ExporterError, UnsupportedCiphersuite, TlsSerializationError, + IncompleteCertificateChain, + CertificateDecodingError, + CertificateEncodingError, + IncompleteCertificate(&'static str), + InvalidCertificate, } impl std::fmt::Display for CryptoError { diff --git a/x509_credential/Cargo.toml b/x509_credential/Cargo.toml new file mode 100644 index 0000000000..9c31212ff7 --- /dev/null +++ b/x509_credential/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "openmls_x509_credential" +version = "0.1.0" +edition = "2021" +description = "A Basic Credential implementation for OpenMLS" +license = "MIT" +readme = "README.md" + +[dependencies] +openmls_traits = { version = "0.1.0", path = "../traits" } +tls_codec = { workspace = true } +async-trait = { workspace = true } +serde = "1.0" + +# Rust Crypto +secrecy = { version = "0.8", features = ["serde"] } +x509-cert = "0.2" +oid-registry = "0.6" + +[dev-dependencies] +rcgen = "0.10" +serde_json = "1.0" + +[features] +test-utils = [] # Only use for tests! diff --git a/x509_credential/Readme.md b/x509_credential/Readme.md new file mode 100644 index 0000000000..611c422e20 --- /dev/null +++ b/x509_credential/Readme.md @@ -0,0 +1,4 @@ +# Signature Keys for the x509 Credential + +This crate implements a simple signature key pair for x509 credential and +implements the `Signer` trait required by the OpenMLS APIs. diff --git a/x509_credential/src/lib.rs b/x509_credential/src/lib.rs new file mode 100644 index 0000000000..294adc1b75 --- /dev/null +++ b/x509_credential/src/lib.rs @@ -0,0 +1,343 @@ +//! # X509 Credential +//! +//! An implementation of the x509 credential from the MLS spec. + +use secrecy::{ExposeSecret, SecretVec}; +use serde::{Deserialize, Serialize}; +use std::{convert::From, fmt::Debug}; +use x509_cert::{ + der::{Decode, Encode}, + Certificate, PkiPath, +}; + +use openmls_traits::{ + crypto::OpenMlsCrypto, + key_store::{MlsEntity, MlsEntityId, OpenMlsKeyStore}, + types::{CryptoError, SignatureScheme}, +}; + +fn expose_sk(data: &SecretVec, ser: S) -> Result { + use serde::ser::SerializeSeq as _; + let exposed = data.expose_secret(); + let mut seq = ser.serialize_seq(Some(exposed.len()))?; + for b in exposed.iter() { + seq.serialize_element(b)?; + } + seq.end() +} + +mod serde_certificate { + use serde::{ + de::Error as _, + ser::{Error as _, SerializeSeq}, + Deserialize, Deserializer, Serializer, + }; + use x509_cert::{ + der::{Decode, Encode}, + PkiPath, + }; + + pub(super) fn serialize(cert: &PkiPath, ser: S) -> Result { + let mut cert_data = Vec::new(); + cert.encode_to_vec(&mut cert_data) + .map_err(S::Error::custom)?; + let mut seq = ser.serialize_seq(Some(cert_data.len()))?; + cert_data + .iter() + .try_for_each(|b| seq.serialize_element(b))?; + seq.end() + } + + pub(super) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let cert_data = Vec::::deserialize(deserializer)?; + PkiPath::from_der(&cert_data).map_err(D::Error::custom) + } + + #[cfg(test)] + mod tests { + use openmls_traits::types::SignatureScheme; + use rcgen::generate_simple_self_signed; + use x509_cert::{der::Decode, Certificate, PkiPath}; + + use crate::CertificateKeyPair; + + #[test] + fn should_serialize_ceritifcate() { + let cert_generator = |quantity: usize| -> PkiPath { + (0..quantity) + .map(|_| { + let subject_alt_names = + vec!["hello.world.example".to_string(), "localhost".to_string()]; + let cert = generate_simple_self_signed(subject_alt_names).unwrap(); + let cert_der = cert.serialize_der().unwrap(); + Certificate::from_der(&cert_der).unwrap() + }) + .collect() + }; + let certs = cert_generator(3); + let kp = CertificateKeyPair { + private: vec![].into(), + certificate_chain: certs, + signature_scheme: SignatureScheme::ED25519, + }; + let serialized = serde_json::to_value(&kp).unwrap(); + let deserialized = serde_json::from_value::(serialized).unwrap(); + assert_eq!(deserialized.certificate_chain, kp.certificate_chain); + } + } +} + +/// A signature key pair for the x509 credential. +/// +/// This can be used as keys to implement the MLS x509 credential. It simple +/// stores the private key and certificate chain. +#[derive(Serialize, Deserialize)] +pub struct CertificateKeyPair { + #[serde(serialize_with = "expose_sk")] + private: SecretVec, + #[serde(with = "serde_certificate")] + certificate_chain: PkiPath, + signature_scheme: SignatureScheme, +} + +impl secrecy::SerializableSecret for CertificateKeyPair {} + +impl tls_codec::Size for CertificateKeyPair { + fn tls_serialized_len(&self) -> usize { + let cert_len: usize = self + .certificate_chain + .encoded_len() + .unwrap() + .try_into() + .unwrap(); + self.private.expose_secret().tls_serialized_len() + + cert_len + + self.signature_scheme.tls_serialized_len() + } +} + +impl tls_codec::Deserialize for CertificateKeyPair { + fn tls_deserialize(bytes: &mut R) -> Result + where + Self: Sized, + { + let private = Vec::::tls_deserialize(bytes)?.into(); + let cert_data = Vec::::tls_deserialize(bytes)?; + let certificate = PkiPath::from_der(&cert_data).map_err(|e| { + tls_codec::Error::DecodingError(format!("Error decoding certificate: {e}")) + })?; + + let signature_scheme = SignatureScheme::tls_deserialize(bytes)?; + Ok(Self { + private, + certificate_chain: certificate, + signature_scheme, + }) + } +} + +impl tls_codec::Serialize for CertificateKeyPair { + fn tls_serialize(&self, writer: &mut W) -> Result { + let mut written = self.private.expose_secret().tls_serialize(writer)?; + let mut cert_data = Vec::new(); + self.certificate_chain + .encode_to_vec(&mut cert_data) + .map_err(|e| { + tls_codec::Error::EncodingError(format!("Error encoding certificate: {e}")) + })?; + written += cert_data.tls_serialize(writer)?; + written += self.signature_scheme.tls_serialize(writer)?; + Ok(written) + } +} + +impl Debug for CertificateKeyPair { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SignatureKeyPair") + .field("private", &"***".to_string()) + .field("certificate", &self.certificate_chain) + .field("signature_scheme", &self.signature_scheme) + .finish() + } +} + +impl openmls_traits::signatures::DefaultSigner for CertificateKeyPair { + fn private_key(&self) -> &[u8] { + self.private.expose_secret().as_slice() + } + + fn signature_scheme(&self) -> SignatureScheme { + self.signature_scheme + } +} + +impl MlsEntity for CertificateKeyPair { + const ID: MlsEntityId = MlsEntityId::CertificateKeyPair; +} + +impl CertificateKeyPair { + /// Constructs the `CertificateKeyPair` from a private key and a der encoded certificate chain + pub fn new(private: SecretVec, cert_chain: Vec>) -> Result { + if cert_chain.len() < 2 { + return Err(CryptoError::IncompleteCertificateChain); + } + let pki_path = cert_chain.into_iter().try_fold( + PkiPath::new(), + |mut acc, cert_data| -> Result { + acc.push( + Certificate::from_der(&cert_data) + .map_err(|_| CryptoError::CertificateDecodingError)?, + ); + Ok(acc) + }, + )?; + + let signature_scheme = pki_path[0].signature_scheme()?; + + pki_path + .iter() + .try_for_each(|certificate| certificate.is_valid())?; + + Ok(Self { + private, + certificate_chain: pki_path, + signature_scheme, + }) + } + + /// Store this signature key pair in the key store. + pub async fn store(&self, key_store: &T) -> Result<(), ::Error> + where + T: OpenMlsKeyStore, + ::Error: From, + { + key_store.store(self.public()?, self).await + } + + /// Read a signature key pair from the key store. + pub async fn read(key_store: &impl OpenMlsKeyStore, public_key: &[u8]) -> Option { + key_store.read(public_key).await + } + + /// Get the public key as byte slice. + pub fn public(&self) -> Result<&[u8], CryptoError> { + self.certificate_chain + .get(0) + .ok_or(CryptoError::IncompleteCertificateChain)? + .public_key() + } + + /// Get the public key as byte vector. + pub fn to_public_vec(&self) -> Result, CryptoError> { + self.public().map(|p| p.to_owned()) + } + + /// Get the [`SignatureScheme`] of this signature key. + pub fn signature_scheme(&self) -> SignatureScheme { + self.signature_scheme + } + + #[cfg(feature = "test-utils")] + pub fn private(&self) -> &[u8] { + self.private.expose_secret() + } +} + +pub trait X509Ext { + fn is_valid(&self) -> Result<(), CryptoError>; + + fn is_time_valid(&self) -> Result; + + fn public_key(&self) -> Result<&[u8], CryptoError>; + + fn signature_scheme(&self) -> Result; + + fn is_signed_by( + &self, + backend: &impl OpenMlsCrypto, + issuer: &Certificate, + ) -> Result<(), CryptoError>; +} + +impl X509Ext for Certificate { + fn is_valid(&self) -> Result<(), CryptoError> { + if !self.is_time_valid()? { + return Err(CryptoError::InvalidCertificate); + } + Ok(()) + } + + fn is_time_valid(&self) -> Result { + // 'not_before' < now < 'not_after' + let x509_cert::time::Validity { + not_before, + not_after, + } = self.tbs_certificate.validity; + let x509_cert::time::Validity { + not_before: now, .. + } = x509_cert::time::Validity::from_now(core::time::Duration::default()) + .map_err(|_| CryptoError::CryptoLibraryError)?; + + let now = now.to_unix_duration(); + let is_nbf = now > not_before.to_unix_duration(); + let is_naf = now < not_after.to_unix_duration(); + Ok(is_nbf && is_naf) + } + + fn public_key(&self) -> Result<&[u8], CryptoError> { + self.tbs_certificate + .subject_public_key_info + .subject_public_key + .as_bytes() + .ok_or(CryptoError::IncompleteCertificate("spki")) + } + + fn signature_scheme(&self) -> Result { + let alg = self.tbs_certificate.subject_public_key_info.algorithm.oid; + let alg = oid_registry::Oid::new(std::borrow::Cow::Borrowed(alg.as_bytes())); + + let scheme = if alg == oid_registry::OID_SIG_ED25519 { + SignatureScheme::ED25519 + } else if alg == oid_registry::OID_SIG_ED448 { + SignatureScheme::ED448 + } else if alg == oid_registry::OID_SIG_ECDSA_WITH_SHA256 { + SignatureScheme::ECDSA_SECP256R1_SHA256 + } else if alg == oid_registry::OID_SIG_ECDSA_WITH_SHA384 { + SignatureScheme::ECDSA_SECP384R1_SHA384 + } else if alg == oid_registry::OID_SIG_ECDSA_WITH_SHA512 { + SignatureScheme::ECDSA_SECP521R1_SHA512 + } else { + return Err(CryptoError::UnsupportedSignatureScheme); + }; + Ok(scheme) + } + + fn is_signed_by( + &self, + backend: &impl OpenMlsCrypto, + issuer: &Certificate, + ) -> Result<(), CryptoError> { + let issuer_pk = issuer.public_key()?; + let cert_signature = self + .signature + .as_bytes() + .ok_or(CryptoError::InvalidCertificate)?; + + use x509_cert::der::Encode as _; + let mut raw_tbs: Vec = vec![]; + self.tbs_certificate + .encode(&mut raw_tbs) + .map_err(|_| CryptoError::CertificateEncodingError)?; + backend + .verify_signature( + self.signature_scheme()?, + &raw_tbs, + issuer_pk, + cert_signature, + ) + .map_err(|_| CryptoError::InvalidSignature) + } +} From a4055668035d5db920e7ce46fb68405d8b56e75c Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Tue, 30 May 2023 17:28:12 +0200 Subject: [PATCH 10/54] Fixed compilation issues --- openmls/Cargo.toml | 8 ++++---- openmls/src/framing/validation.rs | 2 +- openmls/src/treesync/node/leaf_node/capabilities.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 7d13df8066..a2e8057425 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -19,14 +19,14 @@ thiserror = "^1.0" backtrace = "0.3" hex = "0.4" async-trait = { workspace = true } +openmls_basic_credential = { version = "0.1.0", path = "../basic_credential", features = ["clonable", "test-utils"] } +openmls_x509_credential = { version = "0.1.0", path = "../x509_credential", features = ["test-utils"] } # Only required for tests. rand = { version = "0.8", optional = true } serde_json = { version = "1.0", optional = true } # Crypto backends required for KAT and testing - "test-utils" feature itertools = { version = "0.10", optional = true } openmls_rust_crypto = { version = "0.1.0", path = "../openmls_rust_crypto", optional = true } -openmls_basic_credential = { version = "0.1.0", path = "../basic_credential", optional = true, features = ["clonable", "test-utils"] } -openmls_x509_credential = { version = "0.1.0", path = "../x509_credential", optional = true, features = ["test-utils"] } rstest = { version = "^0.16", optional = true } rstest_reuse = { version = "0.4", optional = true } tokio = { version = "1.24", features = ["full"], optional = true } @@ -43,8 +43,8 @@ test-utils = [ "dep:tokio", "dep:rstest", "dep:rstest_reuse", - "dep:openmls_basic_credential", - "dep:openmls_x509_credential", + "openmls_basic_credential/test-utils", + "openmls_x509_credential/test-utils", ] crypto-debug = [] # ☣️ Enable logging of sensitive cryptographic information content-debug = [] # ☣️ Enable logging of sensitive message content diff --git a/openmls/src/framing/validation.rs b/openmls/src/framing/validation.rs index d68afe20e7..e549521496 100644 --- a/openmls/src/framing/validation.rs +++ b/openmls/src/framing/validation.rs @@ -28,7 +28,6 @@ use openmls_traits::{ types::{Ciphersuite, CryptoError}, OpenMlsCryptoProvider, }; -use openmls_x509_credential::X509Ext; use crate::{ binary_tree::LeafNodeIndex, @@ -291,6 +290,7 @@ impl UnverifiedMessage { .map(Ok) .reduce( |a, b| -> Result<(usize, &x509_cert::Certificate), CryptoError> { + use openmls_x509_credential::X509Ext; let (_, child_cert) = a?; let (parent_idx, parent_cert) = b?; // verify not expired diff --git a/openmls/src/treesync/node/leaf_node/capabilities.rs b/openmls/src/treesync/node/leaf_node/capabilities.rs index 04f60b3ac4..aaf18685a7 100644 --- a/openmls/src/treesync/node/leaf_node/capabilities.rs +++ b/openmls/src/treesync/node/leaf_node/capabilities.rs @@ -225,7 +225,7 @@ pub(super) fn default_proposals() -> Vec { // TODO(#1231) pub(super) fn default_credentials() -> Vec { - vec![CredentialType::Basic] + vec![CredentialType::Basic, CredentialType::X509] } #[cfg(test)] From 8781a4830db13175a1f0e0b15122a090e9d515c6 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Wed, 31 May 2023 12:04:28 +0200 Subject: [PATCH 11/54] Derive Clone where needed --- .../binary_tree/array_representation/diff.rs | 2 +- openmls/src/ciphersuite/aead.rs | 4 +-- openmls/src/group/core_group/proposals.rs | 2 +- openmls/src/group/core_group/staged_commit.rs | 6 ++-- openmls/src/group/public_group/diff.rs | 2 +- openmls/src/schedule/message_secrets.rs | 3 +- openmls/src/schedule/mod.rs | 30 +++++++++---------- openmls/src/tree/secret_tree.rs | 8 ++--- openmls/src/tree/sender_ratchet.rs | 12 ++++---- openmls/src/treesync/diff.rs | 2 +- 10 files changed, 34 insertions(+), 37 deletions(-) diff --git a/openmls/src/binary_tree/array_representation/diff.rs b/openmls/src/binary_tree/array_representation/diff.rs index 6d58cbf915..a4d284fc50 100644 --- a/openmls/src/binary_tree/array_representation/diff.rs +++ b/openmls/src/binary_tree/array_representation/diff.rs @@ -41,7 +41,7 @@ use super::{ /// original content, it can't provide the same information as the [`AbDiff`] it /// was created from. However, the lack of the internal reference means that its /// lifetime is not tied to that of the original tree. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct StagedAbDiff { leaf_diff: BTreeMap, parent_diff: BTreeMap, diff --git a/openmls/src/ciphersuite/aead.rs b/openmls/src/ciphersuite/aead.rs index 8ea69e156c..084138608d 100644 --- a/openmls/src/ciphersuite/aead.rs +++ b/openmls/src/ciphersuite/aead.rs @@ -6,8 +6,8 @@ use super::*; pub(crate) const NONCE_BYTES: usize = 12; /// AEAD keys holding the plain key value and the AEAD algorithm type. -#[derive(Serialize, Deserialize)] -#[cfg_attr(any(feature = "test-utils", test), derive(Clone, PartialEq, Eq))] +#[derive(Clone, Serialize, Deserialize)] +#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Eq))] #[cfg_attr(feature = "crypto-debug", derive(Debug))] pub struct AeadKey { aead_mode: AeadType, diff --git a/openmls/src/group/core_group/proposals.rs b/openmls/src/group/core_group/proposals.rs index 70fb4a31a4..551d0846cc 100644 --- a/openmls/src/group/core_group/proposals.rs +++ b/openmls/src/group/core_group/proposals.rs @@ -171,7 +171,7 @@ impl QueuedProposal { /// references to Proposals, such that, given a reference, a proposal can be /// accessed efficiently. To enable iteration over the queue in order, the /// `ProposalQueue` also contains a vector of `ProposalRef`s. -#[derive(Default, Debug, Serialize, Deserialize)] +#[derive(Default, Clone, Debug, Serialize, Deserialize)] pub(crate) struct ProposalQueue { /// `proposal_references` holds references to the proposals in the queue and /// determines the order of the queue. diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index 8436d0e5dd..2398b6eef8 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -377,14 +377,14 @@ impl CoreGroup { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) enum StagedCommitState { PublicState(Box), GroupMember(Box), } /// Contains the changes from a commit to the group state. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct StagedCommit { staged_proposal_queue: ProposalQueue, state: StagedCommitState, @@ -445,7 +445,7 @@ impl StagedCommit { } /// This struct is used internally by [StagedCommit] to encapsulate all the modified group state. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct MemberStagedCommitState { group_epoch_secrets: GroupEpochSecrets, message_secrets: MessageSecrets, diff --git a/openmls/src/group/public_group/diff.rs b/openmls/src/group/public_group/diff.rs index 430ca24449..20eaecfc38 100644 --- a/openmls/src/group/public_group/diff.rs +++ b/openmls/src/group/public_group/diff.rs @@ -237,7 +237,7 @@ impl<'a> PublicGroupDiff<'a> { /// The staged version of a [`PublicGroupDiff`], which means it can no longer be /// modified. Its only use is to merge it into the original [`PublicGroup`]. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct StagedPublicGroupDiff { pub(super) staged_diff: StagedTreeSyncDiff, pub(super) group_context: GroupContext, diff --git a/openmls/src/schedule/message_secrets.rs b/openmls/src/schedule/message_secrets.rs index b3600cd3e6..9f19197739 100644 --- a/openmls/src/schedule/message_secrets.rs +++ b/openmls/src/schedule/message_secrets.rs @@ -3,8 +3,7 @@ use super::*; /// Combined message secrets that need to be stored for later decryption/verification -#[derive(Serialize, Deserialize)] -#[cfg_attr(test, derive(Clone))] +#[derive(Serialize, Clone, Deserialize)] #[cfg_attr(feature = "crypto-debug", derive(Debug))] pub(crate) struct MessageSecrets { sender_data_secret: SenderDataSecret, diff --git a/openmls/src/schedule/mod.rs b/openmls/src/schedule/mod.rs index 9655029dda..834efe862c 100644 --- a/openmls/src/schedule/mod.rs +++ b/openmls/src/schedule/mod.rs @@ -185,8 +185,8 @@ impl ResumptionPskSecret { /// A secret that can be used among members to make sure everyone has the same /// group state. -#[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(Eq, PartialEq, Clone))] +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(test, derive(Eq, PartialEq))] pub struct EpochAuthenticator { secret: Secret, } @@ -250,8 +250,8 @@ impl CommitSecret { } /// The `InitSecret` is used to connect the next epoch to the current one. -#[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(test, derive(PartialEq))] pub(crate) struct InitSecret { secret: Secret, } @@ -700,8 +700,8 @@ impl EncryptionSecret { } /// A secret that we can derive secrets from, that are used outside of OpenMLS. -#[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(test, derive(PartialEq))] pub(crate) struct ExporterSecret { secret: Secret, } @@ -745,8 +745,7 @@ impl ExporterSecret { } /// A secret used when joining a group with an external Commit. -#[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[derive(Debug, Serialize, Deserialize, Clone)] pub(crate) struct ExternalSecret { secret: Secret, } @@ -777,8 +776,8 @@ impl ExternalSecret { } /// The confirmation key is used to calculate the `ConfirmationTag`. -#[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(test, derive(PartialEq))] pub(crate) struct ConfirmationKey { secret: Secret, } @@ -845,8 +844,8 @@ impl ConfirmationKey { } /// The membership key is used to calculate the `MembershipTag`. -#[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(test, derive(PartialEq))] pub struct MembershipKey { secret: Secret, } @@ -916,9 +915,9 @@ fn ciphertext_sample(ciphersuite: Ciphersuite, ciphertext: &[u8]) -> &[u8] { } /// A key that can be used to derive an `AeadKey` and an `AeadNonce`. -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] #[cfg_attr(test, derive(PartialEq))] -#[cfg_attr(any(feature = "test-utils", test), derive(Debug, Clone))] +#[cfg_attr(any(feature = "test-utils", test), derive(Debug))] pub(crate) struct SenderDataSecret { secret: Secret, } @@ -1199,8 +1198,7 @@ impl EpochSecrets { } } -#[derive(Serialize, Deserialize)] -#[cfg_attr(test, derive(Clone))] +#[derive(Serialize, Clone, Deserialize)] pub(crate) struct GroupEpochSecrets { init_secret: InitSecret, exporter_secret: ExporterSecret, diff --git a/openmls/src/tree/secret_tree.rs b/openmls/src/tree/secret_tree.rs index 3e35e8d523..0bf34cd9de 100644 --- a/openmls/src/tree/secret_tree.rs +++ b/openmls/src/tree/secret_tree.rs @@ -98,14 +98,14 @@ pub(crate) struct TreeContext { pub(crate) generation: u32, } -#[derive(Debug, Serialize, Deserialize, TlsSerialize, TlsSize)] -#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))] +#[derive(Debug, Serialize, Deserialize, TlsSerialize, TlsSize, Clone)] +#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq))] pub(crate) struct SecretTreeNode { pub(crate) secret: Secret, } -#[derive(Serialize, Deserialize)] -#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))] +#[derive(Serialize, Deserialize, Clone)] +#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq))] #[cfg_attr(any(feature = "crypto-debug", test), derive(Debug))] pub(crate) struct SecretTree { own_index: LeafNodeIndex, diff --git a/openmls/src/tree/sender_ratchet.rs b/openmls/src/tree/sender_ratchet.rs index 20752d6310..1a6d1dd1e1 100644 --- a/openmls/src/tree/sender_ratchet.rs +++ b/openmls/src/tree/sender_ratchet.rs @@ -69,8 +69,8 @@ pub(crate) type RatchetKeyMaterial = (AeadKey, AeadNonce); /// `out_of_order_tolerance` and a `maximum_forward_distance` (see /// [`SenderRatchetConfiguration`]) while an Encryption Ratchet never keeps past /// secrets around. -#[derive(Serialize, Deserialize)] -#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))] +#[derive(Serialize, Deserialize, Clone)] +#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq))] #[cfg_attr(any(feature = "crypto-debug", test), derive(Debug))] pub(crate) enum SenderRatchet { EncryptionRatchet(RatchetSecret), @@ -91,8 +91,8 @@ impl SenderRatchet { /// the ratchet chain, as well as its current [`Generation`]. It can be /// initialized with a given secret and then ratcheted forward, outputting /// [`RatchetKeyMaterial`] and increasing its [`Generation`] each time. -#[derive(Debug, Serialize, Deserialize, Default)] -#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))] +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq,))] pub(crate) struct RatchetSecret { secret: Secret, generation: Generation, @@ -166,8 +166,8 @@ impl RatchetSecret { /// [`RatchetKeyMaterial`] of epochs around until they are retrieved. This /// behaviour can be configured via the `out_of_order_tolerance` and /// `maximum_forward_distance` of the given [`SenderRatchetConfiguration`]. -#[derive(Serialize, Deserialize)] -#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))] +#[derive(Serialize, Deserialize, Clone)] +#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq))] #[cfg_attr(any(feature = "crypto-debug", test), derive(Debug))] pub struct DecryptionRatchet { past_secrets: VecDeque>, diff --git a/openmls/src/treesync/diff.rs b/openmls/src/treesync/diff.rs index ad23db039c..b5d26cab7d 100644 --- a/openmls/src/treesync/diff.rs +++ b/openmls/src/treesync/diff.rs @@ -57,7 +57,7 @@ pub(crate) type UpdatePathResult = ( /// The [`StagedTreeSyncDiff`] can be created from a [`TreeSyncDiff`], examined /// and later merged into a [`TreeSync`] instance. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct StagedTreeSyncDiff { diff: StagedMlsBinaryTreeDiff, new_tree_hash: Vec, From 5a48731eac48bfc7425896fbb7b132c7b980acde Mon Sep 17 00:00:00 2001 From: beltram Date: Wed, 31 May 2023 14:23:51 +0200 Subject: [PATCH 12/54] impl from bytes for HashReference --- openmls/src/ciphersuite/hash_ref.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/openmls/src/ciphersuite/hash_ref.rs b/openmls/src/ciphersuite/hash_ref.rs index 43e1d3f51d..b329f2c9d7 100644 --- a/openmls/src/ciphersuite/hash_ref.rs +++ b/openmls/src/ciphersuite/hash_ref.rs @@ -55,6 +55,22 @@ pub struct HashReference { value: VLBytes, } +impl From> for HashReference { + fn from(value: Vec) -> Self { + Self { + value: value.into(), + } + } +} + +impl From<&[u8]> for HashReference { + fn from(value: &[u8]) -> Self { + Self { + value: value.into(), + } + } +} + /// A reference to a key package. /// This value uniquely identifies a key package. pub type KeyPackageRef = HashReference; From 23249b25b1e61c92675115cbf1e61e21ba6aea80 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Wed, 31 May 2023 15:08:51 +0200 Subject: [PATCH 13/54] Allow hashref from slice --- openmls/src/ciphersuite/hash_ref.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/openmls/src/ciphersuite/hash_ref.rs b/openmls/src/ciphersuite/hash_ref.rs index b329f2c9d7..cdba404827 100644 --- a/openmls/src/ciphersuite/hash_ref.rs +++ b/openmls/src/ciphersuite/hash_ref.rs @@ -129,7 +129,6 @@ impl HashReference { self.value.as_slice() } - #[cfg(any(feature = "test-utils", test))] pub fn from_slice(slice: &[u8]) -> Self { Self { value: VLBytes::from(slice), From 1ac46ddcfeeda63e8d9b8adf8ffe7e7a8a66c08f Mon Sep 17 00:00:00 2001 From: beltram Date: Wed, 31 May 2023 15:48:06 +0200 Subject: [PATCH 14/54] expose MlsMessageOut -> In --- openmls/src/framing/message_out.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/openmls/src/framing/message_out.rs b/openmls/src/framing/message_out.rs index f40edf7c0d..0856e26977 100644 --- a/openmls/src/framing/message_out.rs +++ b/openmls/src/framing/message_out.rs @@ -193,7 +193,6 @@ impl From for MlsMessageOut { } } -#[cfg(any(feature = "test-utils", test))] impl From for MlsMessageIn { fn from(mls_message_out: MlsMessageOut) -> Self { let version = mls_message_out.version; From c868028e840b709fc9b6a73048dffbb9bb0cecf7 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Wed, 31 May 2023 15:53:56 +0200 Subject: [PATCH 15/54] impl From for Leaf|ParentNodeIndex --- .../src/binary_tree/array_representation/treemath.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/openmls/src/binary_tree/array_representation/treemath.rs b/openmls/src/binary_tree/array_representation/treemath.rs index 219455ec18..bb499c1ed3 100644 --- a/openmls/src/binary_tree/array_representation/treemath.rs +++ b/openmls/src/binary_tree/array_representation/treemath.rs @@ -30,6 +30,12 @@ impl std::fmt::Display for LeafNodeIndex { } } +impl From for LeafNodeIndex { + fn from(value: u32) -> Self { + Self(value) + } +} + impl LeafNodeIndex { /// Create a new `LeafNodeIndex` from a `u32`. pub fn new(index: u32) -> Self { @@ -62,6 +68,12 @@ impl LeafNodeIndex { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct ParentNodeIndex(u32); +impl From for ParentNodeIndex { + fn from(value: u32) -> Self { + Self(value) + } +} + impl ParentNodeIndex { /// Create a new `ParentNodeIndex` from a `u32`. pub(crate) fn new(index: u32) -> Self { From 020645afd0bc50bf7150500055ecb2d9feb501bd Mon Sep 17 00:00:00 2001 From: beltram Date: Wed, 31 May 2023 16:11:52 +0200 Subject: [PATCH 16/54] fix conversion discrepancy --- openmls/src/framing/message_out.rs | 2 +- openmls/src/framing/mls_content_in.rs | 2 -- openmls/src/framing/private_message_in.rs | 2 -- openmls/src/framing/public_message_in.rs | 2 -- openmls/src/key_packages/key_package_in.rs | 2 -- openmls/src/messages/group_info.rs | 2 -- openmls/src/messages/mod.rs | 1 - openmls/src/messages/proposals_in.rs | 4 ---- openmls/src/treesync/node/leaf_node.rs | 1 - openmls/src/treesync/treekem.rs | 1 - 10 files changed, 1 insertion(+), 18 deletions(-) diff --git a/openmls/src/framing/message_out.rs b/openmls/src/framing/message_out.rs index 0856e26977..13bac0d07c 100644 --- a/openmls/src/framing/message_out.rs +++ b/openmls/src/framing/message_out.rs @@ -178,7 +178,7 @@ impl MlsMessageOut { // The following two `From` implementations break abstraction layers and MUST // NOT be made available outside of tests or "test-utils". -#[cfg(any(feature = "test-utils", test))] +// #[cfg(any(feature = "test-utils", test))] impl From for MlsMessageOut { fn from(mls_message: MlsMessageIn) -> Self { let version = mls_message.version; diff --git a/openmls/src/framing/mls_content_in.rs b/openmls/src/framing/mls_content_in.rs index 61b367cf43..474a84892a 100644 --- a/openmls/src/framing/mls_content_in.rs +++ b/openmls/src/framing/mls_content_in.rs @@ -210,7 +210,6 @@ impl TlsSerializeTrait for FramedContentTbsIn { // The following `From` implementation( breaks abstraction layers and MUST // NOT be made available outside of tests or "test-utils". -#[cfg(any(feature = "test-utils", test))] impl From for FramedContentBody { fn from(body: FramedContentBodyIn) -> Self { match body { @@ -225,7 +224,6 @@ impl From for FramedContentBody { // The following `From` implementation( breaks abstraction layers and MUST // NOT be made available outside of tests or "test-utils". -#[cfg(any(feature = "test-utils", test))] impl From for crate::framing::mls_content::FramedContent { fn from(value: FramedContentIn) -> Self { Self { diff --git a/openmls/src/framing/private_message_in.rs b/openmls/src/framing/private_message_in.rs index 30334e8c5e..7fa1ced39e 100644 --- a/openmls/src/framing/private_message_in.rs +++ b/openmls/src/framing/private_message_in.rs @@ -256,7 +256,6 @@ pub(crate) struct PrivateContentAad<'a> { // The following `From` implementation( breaks abstraction layers and MUST // NOT be made available outside of tests or "test-utils". -#[cfg(any(feature = "test-utils", test))] impl From for PrivateMessage { fn from(value: PrivateMessageIn) -> Self { Self { @@ -270,7 +269,6 @@ impl From for PrivateMessage { } } -#[cfg(any(feature = "test-utils", test))] impl From for PrivateMessageIn { fn from(value: PrivateMessage) -> Self { Self { diff --git a/openmls/src/framing/public_message_in.rs b/openmls/src/framing/public_message_in.rs index ce794619fb..5bb6765336 100644 --- a/openmls/src/framing/public_message_in.rs +++ b/openmls/src/framing/public_message_in.rs @@ -297,7 +297,6 @@ impl TlsSerializeTrait for PublicMessageIn { // The following `From` implementation( breaks abstraction layers and MUST // NOT be made available outside of tests or "test-utils". -#[cfg(any(feature = "test-utils", test))] impl From for PublicMessage { fn from(v: PublicMessageIn) -> Self { PublicMessage { @@ -308,7 +307,6 @@ impl From for PublicMessage { } } -#[cfg(any(feature = "test-utils", test))] impl From for PublicMessageIn { fn from(v: PublicMessage) -> Self { PublicMessageIn { diff --git a/openmls/src/key_packages/key_package_in.rs b/openmls/src/key_packages/key_package_in.rs index 712804d947..2fb54bc286 100644 --- a/openmls/src/key_packages/key_package_in.rs +++ b/openmls/src/key_packages/key_package_in.rs @@ -186,7 +186,6 @@ impl KeyPackageIn { } } -#[cfg(any(feature = "test-utils", test))] impl From for KeyPackageTbs { fn from(value: KeyPackageTbsIn) -> Self { KeyPackageTbs { @@ -220,7 +219,6 @@ impl From for KeyPackageIn { } } -#[cfg(any(feature = "test-utils", test))] impl From for KeyPackage { fn from(value: KeyPackageIn) -> Self { Self { diff --git a/openmls/src/messages/group_info.rs b/openmls/src/messages/group_info.rs index fc6e16dd41..3318f920e8 100644 --- a/openmls/src/messages/group_info.rs +++ b/openmls/src/messages/group_info.rs @@ -107,7 +107,6 @@ impl VerifiableGroupInfo { } } -#[cfg(any(feature = "test-utils", test))] impl From for GroupInfo { fn from(vgi: VerifiableGroupInfo) -> Self { GroupInfo { @@ -156,7 +155,6 @@ impl GroupInfo { &self.payload.confirmation_tag } - #[cfg(any(feature = "test-utils", test))] pub(crate) fn into_verifiable_group_info(self) -> VerifiableGroupInfo { VerifiableGroupInfo { payload: GroupInfoTBS { diff --git a/openmls/src/messages/mod.rs b/openmls/src/messages/mod.rs index 2a3396173f..a2bff79ece 100644 --- a/openmls/src/messages/mod.rs +++ b/openmls/src/messages/mod.rs @@ -242,7 +242,6 @@ impl CommitIn { // The following `From` implementation( breaks abstraction layers and MUST // NOT be made available outside of tests or "test-utils". -#[cfg(any(feature = "test-utils", test))] impl From for Commit { fn from(commit: CommitIn) -> Self { Self { diff --git a/openmls/src/messages/proposals_in.rs b/openmls/src/messages/proposals_in.rs index b5f41308c5..1f289d390a 100644 --- a/openmls/src/messages/proposals_in.rs +++ b/openmls/src/messages/proposals_in.rs @@ -244,7 +244,6 @@ impl ProposalOrRefIn { // The following `From` implementation breaks abstraction layers and MUST // NOT be made available outside of tests or "test-utils". -#[cfg(any(feature = "test-utils", test))] impl From for crate::messages::proposals::AddProposal { fn from(value: AddProposalIn) -> Self { Self { @@ -263,7 +262,6 @@ impl From for AddProposalIn { // The following `From` implementation( breaks abstraction layers and MUST // NOT be made available outside of tests or "test-utils". -#[cfg(any(feature = "test-utils", test))] impl From for crate::messages::proposals::UpdateProposal { fn from(value: UpdateProposalIn) -> Self { Self { @@ -280,7 +278,6 @@ impl From for UpdateProposalIn { } } -#[cfg(any(feature = "test-utils", test))] impl From for crate::messages::proposals::Proposal { fn from(proposal: ProposalIn) -> Self { match proposal { @@ -315,7 +312,6 @@ impl From for ProposalIn { } } -#[cfg(any(feature = "test-utils", test))] impl From for crate::messages::proposals::ProposalOrRef { fn from(proposal: ProposalOrRefIn) -> Self { match proposal { diff --git a/openmls/src/treesync/node/leaf_node.rs b/openmls/src/treesync/node/leaf_node.rs index 8123ee92b8..d17bd5c9ce 100644 --- a/openmls/src/treesync/node/leaf_node.rs +++ b/openmls/src/treesync/node/leaf_node.rs @@ -715,7 +715,6 @@ impl From for LeafNodeIn { } } -#[cfg(any(feature = "test-utils", test))] impl From for LeafNode { fn from(deserialized: LeafNodeIn) -> Self { Self { diff --git a/openmls/src/treesync/treekem.rs b/openmls/src/treesync/treekem.rs index 6bea69f647..c242a9b03b 100644 --- a/openmls/src/treesync/treekem.rs +++ b/openmls/src/treesync/treekem.rs @@ -409,7 +409,6 @@ impl UpdatePathIn { // The following `From` implementation( breaks abstraction layers and MUST // NOT be made available outside of tests or "test-utils". -#[cfg(any(feature = "test-utils", test))] impl From for UpdatePath { fn from(update_path_in: UpdatePathIn) -> Self { Self { From d38d720b46200064ca308848ae868b9b3579871f Mon Sep 17 00:00:00 2001 From: beltram Date: Wed, 31 May 2023 16:50:37 +0200 Subject: [PATCH 17/54] expose private message epoch() --- openmls/src/framing/private_message_in.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmls/src/framing/private_message_in.rs b/openmls/src/framing/private_message_in.rs index 7fa1ced39e..d63adf0157 100644 --- a/openmls/src/framing/private_message_in.rs +++ b/openmls/src/framing/private_message_in.rs @@ -198,7 +198,7 @@ impl PrivateMessageIn { } /// Get the `epoch` in the `PrivateMessage`. - pub(crate) fn epoch(&self) -> GroupEpoch { + pub fn epoch(&self) -> GroupEpoch { self.epoch } From 42a93997d6552be24dd21a9f1dff7075fbf6ff52 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Wed, 31 May 2023 17:55:28 +0200 Subject: [PATCH 18/54] Added way to externally change state --- openmls/src/group/mls_group/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index bfb571bba3..52080ca614 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -323,6 +323,11 @@ impl MlsGroup { self.state_changed } + /// Meh + pub fn set_state(&mut self, state: InnerState) { + self.state_changed = state; + } + // === Extensions === /// Exports the Ratchet Tree. From f49aec997f5958ba687858b6c56b80aded9060c0 Mon Sep 17 00:00:00 2001 From: beltram Date: Thu, 1 Jun 2023 10:58:23 +0200 Subject: [PATCH 19/54] expose x509 required fields --- openmls/src/credentials/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openmls/src/credentials/mod.rs b/openmls/src/credentials/mod.rs index d5d88d6f45..585e8705e8 100644 --- a/openmls/src/credentials/mod.rs +++ b/openmls/src/credentials/mod.rs @@ -144,8 +144,8 @@ impl From for u16 { Debug, PartialEq, Eq, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, )] pub struct Certificate { - identity: VLBytes, - cert_data: Vec, + pub identity: VLBytes, + pub cert_data: Vec, } impl Certificate { @@ -200,7 +200,7 @@ impl Credential { self.credential_type } - pub(crate) fn mls_credential(&self) -> &MlsCredentialType { + pub fn mls_credential(&self) -> &MlsCredentialType { &self.credential } From 51611f2ecc3c24bbb04e61e175b660e5e5734a68 Mon Sep 17 00:00:00 2001 From: beltram Date: Thu, 1 Jun 2023 16:08:32 +0200 Subject: [PATCH 20/54] fix external commit bug happening when the joiner merges the external commit --- openmls/src/group/core_group/mod.rs | 11 +- openmls/src/group/core_group/staged_commit.rs | 132 ++++++++++-------- openmls/src/group/mls_group/processing.rs | 24 +++- .../src/group/public_group/staged_commit.rs | 2 +- openmls/src/schedule/mod.rs | 2 +- 5 files changed, 104 insertions(+), 67 deletions(-) diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 144a7921ab..8471377b1c 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -1111,10 +1111,13 @@ impl CoreGroup { // proposal, so there is no extra keypair to store here. None, ); - let staged_commit = StagedCommit::new( - proposal_queue, - StagedCommitState::GroupMember(Box::new(staged_commit_state)), - ); + let staged_commit_state = match params.commit_type() { + CommitType::Member => StagedCommitState::GroupMember(Box::new(staged_commit_state)), + CommitType::External => { + StagedCommitState::ExternalMember(Box::new(staged_commit_state)) + } + }; + let staged_commit = StagedCommit::new(proposal_queue, staged_commit_state); Ok(CreateCommitResult { commit: authenticated_content, diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index 2398b6eef8..f5d91d93be 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -288,68 +288,86 @@ impl CoreGroup { Ok(None) } StagedCommitState::GroupMember(state) => { - self.group_epoch_secrets = state.group_epoch_secrets; - - // Replace the previous message secrets with the new ones and return the previous message secrets - let mut message_secrets = state.message_secrets; - mem::swap( - &mut message_secrets, - self.message_secrets_store.message_secrets_mut(), - ); - - self.public_group.merge_diff(state.staged_diff); - - // TODO #1194: Group storage and key storage should be - // correlated s.t. there is no divergence between key material - // and group state. - - let leaf_keypair = if let Some(keypair) = &state.new_leaf_keypair_option { - vec![keypair.clone()] - } else { - vec![] - }; + self.merge_member_commit(backend, old_epoch_keypairs, state, true) + .await + } + StagedCommitState::ExternalMember(state) => { + self.merge_member_commit(backend, old_epoch_keypairs, state, false) + .await + } + } + } - // Figure out which keys we need in the new epoch. - let new_owned_encryption_keys = self - .public_group() - .owned_encryption_keys(self.own_leaf_index()); - - // From the old and new keys, keep the ones that are still relevant in the new epoch. - let epoch_keypairs: Vec = old_epoch_keypairs - .into_iter() - .chain(state.new_keypairs.into_iter()) - .chain(leaf_keypair.into_iter()) - .filter(|keypair| new_owned_encryption_keys.contains(keypair.public_key())) - .collect(); - // We should have private keys for all owned encryption keys. - debug_assert_eq!(new_owned_encryption_keys.len(), epoch_keypairs.len()); - if new_owned_encryption_keys.len() != epoch_keypairs.len() { - return Err(LibraryError::custom( - "We should have all the private key material we need.", - ) - .into()); - } + async fn merge_member_commit( + &mut self, + backend: &impl OpenMlsCryptoProvider, + old_epoch_keypairs: Vec, + state: Box, + is_member: bool, + ) -> Result, MergeCommitError<::Error>> + { + self.group_epoch_secrets = state.group_epoch_secrets; + + // Replace the previous message secrets with the new ones and return the previous message secrets + let mut message_secrets = state.message_secrets; + mem::swap( + &mut message_secrets, + self.message_secrets_store.message_secrets_mut(), + ); + + self.public_group.merge_diff(state.staged_diff); + + // TODO #1194: Group storage and key storage should be + // correlated s.t. there is no divergence between key material + // and group state. + + let leaf_keypair = if let Some(keypair) = &state.new_leaf_keypair_option { + vec![keypair.clone()] + } else { + vec![] + }; - // Store the relevant keys under the new epoch - self.store_epoch_keypairs(backend, epoch_keypairs.as_slice()) - .await - .map_err(MergeCommitError::KeyStoreError)?; + // Figure out which keys we need in the new epoch. + let new_owned_encryption_keys = self + .public_group() + .owned_encryption_keys(self.own_leaf_index()); + + // From the old and new keys, keep the ones that are still relevant in the new epoch. + let epoch_keypairs: Vec = old_epoch_keypairs + .into_iter() + .chain(state.new_keypairs.into_iter()) + .chain(leaf_keypair.into_iter()) + .filter(|keypair| new_owned_encryption_keys.contains(keypair.public_key())) + .collect(); + // We should have private keys for all owned encryption keys. + debug_assert_eq!(new_owned_encryption_keys.len(), epoch_keypairs.len()); + if new_owned_encryption_keys.len() != epoch_keypairs.len() { + return Err(LibraryError::custom( + "We should have all the private key material we need.", + ) + .into()); + } - // Delete the old keys. - self.delete_previous_epoch_keypairs(backend) - .await - .map_err(MergeCommitError::KeyStoreError)?; + // Store the relevant keys under the new epoch + self.store_epoch_keypairs(backend, epoch_keypairs.as_slice()) + .await + .map_err(MergeCommitError::KeyStoreError)?; - if let Some(keypair) = state.new_leaf_keypair_option { - keypair - .delete_from_key_store(backend) - .await - .map_err(MergeCommitError::KeyStoreError)?; - } + // Delete the old keys. + if is_member { + self.delete_previous_epoch_keypairs(backend) + .await + .map_err(MergeCommitError::KeyStoreError)?; + } - Ok(Some(message_secrets)) - } + if let Some(keypair) = state.new_leaf_keypair_option { + keypair + .delete_from_key_store(backend) + .await + .map_err(MergeCommitError::KeyStoreError)?; } + + Ok(Some(message_secrets)) } #[cfg(test)] @@ -381,6 +399,7 @@ impl CoreGroup { pub(crate) enum StagedCommitState { PublicState(Box), GroupMember(Box), + ExternalMember(Box), } /// Contains the changes from a commit to the group state. @@ -435,6 +454,7 @@ impl StagedCommit { match &self.state { StagedCommitState::PublicState(ps) => ps.group_context(), StagedCommitState::GroupMember(gm) => gm.group_context(), + StagedCommitState::ExternalMember(gm) => gm.group_context(), } } diff --git a/openmls/src/group/mls_group/processing.rs b/openmls/src/group/mls_group/processing.rs index 2c193c0995..54d35e2071 100644 --- a/openmls/src/group/mls_group/processing.rs +++ b/openmls/src/group/mls_group/processing.rs @@ -1,6 +1,7 @@ //! Processing functions of an [`MlsGroup`] for incoming messages. use std::mem; +use std::ops::Deref; use core_group::staged_commit::StagedCommit; use openmls_traits::signatures::Signer; @@ -166,11 +167,24 @@ impl MlsGroup { backend: &impl OpenMlsCryptoProvider, ) -> Result<(), MergePendingCommitError> { match &self.group_state { - MlsGroupState::PendingCommit(_) => { - let old_state = mem::replace(&mut self.group_state, MlsGroupState::Operational); - if let MlsGroupState::PendingCommit(pending_commit_state) = old_state { - self.merge_staged_commit(backend, (*pending_commit_state).into()) - .await?; + MlsGroupState::PendingCommit(state) => { + match state.deref() { + PendingCommitState::Member(_) => { + let old_state = + mem::replace(&mut self.group_state, MlsGroupState::Operational); + if let MlsGroupState::PendingCommit(pending_commit_state) = old_state { + self.merge_staged_commit(backend, (*pending_commit_state).into()) + .await?; + } + } + PendingCommitState::External(_) => { + let old_state = + mem::replace(&mut self.group_state, MlsGroupState::Operational); + if let MlsGroupState::PendingCommit(pending_commit_state) = old_state { + self.merge_staged_commit(backend, (*pending_commit_state).into()) + .await?; + } + } } Ok(()) } diff --git a/openmls/src/group/public_group/staged_commit.rs b/openmls/src/group/public_group/staged_commit.rs index 3419b456a8..e6ceb837fa 100644 --- a/openmls/src/group/public_group/staged_commit.rs +++ b/openmls/src/group/public_group/staged_commit.rs @@ -256,7 +256,7 @@ impl PublicGroup { pub fn merge_commit(&mut self, staged_commit: StagedCommit) { match staged_commit.into_state() { StagedCommitState::PublicState(staged_diff) => self.merge_diff(*staged_diff), - StagedCommitState::GroupMember(_) => (), + StagedCommitState::GroupMember(_) | StagedCommitState::ExternalMember(_) => (), } self.proposal_store.empty() } diff --git a/openmls/src/schedule/mod.rs b/openmls/src/schedule/mod.rs index 834efe862c..8c87fd1966 100644 --- a/openmls/src/schedule/mod.rs +++ b/openmls/src/schedule/mod.rs @@ -745,7 +745,7 @@ impl ExporterSecret { } /// A secret used when joining a group with an external Commit. -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] pub(crate) struct ExternalSecret { secret: Secret, } From 2c9a429b8bbc87f00d1c53cd0e7bb461537b695f Mon Sep 17 00:00:00 2001 From: beltram Date: Fri, 2 Jun 2023 10:25:58 +0200 Subject: [PATCH 21/54] clean unused methods --- openmls/src/group/mls_group/mod.rs | 1 + openmls/src/group/tests/mod.rs | 2 -- openmls/src/group/tests/tree_printing.rs | 22 ---------------------- 3 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 openmls/src/group/tests/tree_printing.rs diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 52080ca614..7dd68cf4b9 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -406,6 +406,7 @@ impl MlsGroup { } /// Returns the underlying [CoreGroup]. + #[cfg(test)] pub(crate) fn group(&self) -> &CoreGroup { &self.group } diff --git a/openmls/src/group/tests/mod.rs b/openmls/src/group/tests/mod.rs index ae844eccb5..eadaf4edef 100644 --- a/openmls/src/group/tests/mod.rs +++ b/openmls/src/group/tests/mod.rs @@ -34,5 +34,3 @@ mod test_update_extensions; mod test_wire_format_policy; #[cfg(test)] pub(crate) mod utils; - -pub(crate) mod tree_printing; diff --git a/openmls/src/group/tests/tree_printing.rs b/openmls/src/group/tests/tree_printing.rs deleted file mode 100644 index 6ecf49ba34..0000000000 --- a/openmls/src/group/tests/tree_printing.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! A framework to create integration tests of the "raw" core_group API. -//! # Test utils -//! -//! Most tests require to set up groups, clients, credentials, and identities. -//! This module implements helpers to do that. - -#[cfg(any(feature = "test-utils", test))] -fn log2(x: u32) -> usize { - if x == 0 { - return 0; - } - let mut k = 0; - while (x >> k) > 0 { - k += 1 - } - k - 1 -} - -#[cfg(any(feature = "test-utils", test))] -pub(crate) fn root(size: u32) -> u32 { - (1 << log2(size)) - 1 -} From a74963250b655cadc7576c13fdc13715bf6355c8 Mon Sep 17 00:00:00 2001 From: beltram Date: Fri, 2 Jun 2023 11:05:26 +0200 Subject: [PATCH 22/54] quiet rust 1.70 lints --- openmls/src/prelude.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/openmls/src/prelude.rs b/openmls/src/prelude.rs index 4e215d1865..a27d336512 100644 --- a/openmls/src/prelude.rs +++ b/openmls/src/prelude.rs @@ -2,6 +2,7 @@ //! Include this to get access to all the public functions of OpenMLS. // MlsGroup +#[allow(ambiguous_glob_reexports)] pub use crate::group::{config::CryptoConfig, core_group::Member, errors::*, ser::*, *}; pub use crate::group::public_group::{errors::*, process::*, *}; From da7dff2447ccaf5e0f5045780e63df49cb3a0813 Mon Sep 17 00:00:00 2001 From: beltram Date: Fri, 2 Jun 2023 11:06:59 +0200 Subject: [PATCH 23/54] fix AuthenticatedContentIn::content visibility --- openmls/src/framing/mls_auth_content_in.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmls/src/framing/mls_auth_content_in.rs b/openmls/src/framing/mls_auth_content_in.rs index a2a0d61483..06b4a0af03 100644 --- a/openmls/src/framing/mls_auth_content_in.rs +++ b/openmls/src/framing/mls_auth_content_in.rs @@ -69,7 +69,7 @@ impl AuthenticatedContentIn { impl AuthenticatedContentIn { /// Get the content body of the message. - pub(crate) fn content(&self) -> &FramedContentBodyIn { + pub fn content(&self) -> &FramedContentBodyIn { &self.content.body } } From 12bd342316eb5122249225af6a7062bcce3c95b2 Mon Sep 17 00:00:00 2001 From: beltram Date: Fri, 2 Jun 2023 11:09:18 +0200 Subject: [PATCH 24/54] quiet clippy lints --- openmls/src/prelude.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openmls/src/prelude.rs b/openmls/src/prelude.rs index a27d336512..3437085439 100644 --- a/openmls/src/prelude.rs +++ b/openmls/src/prelude.rs @@ -1,8 +1,9 @@ //! Prelude for OpenMLS. //! Include this to get access to all the public functions of OpenMLS. +#![allow(ambiguous_glob_reexports)] + // MlsGroup -#[allow(ambiguous_glob_reexports)] pub use crate::group::{config::CryptoConfig, core_group::Member, errors::*, ser::*, *}; pub use crate::group::public_group::{errors::*, process::*, *}; From 752b5a80f03fb5c246d6479d20ed1eb70120d276 Mon Sep 17 00:00:00 2001 From: beltram Date: Mon, 5 Jun 2023 12:07:25 +0200 Subject: [PATCH 25/54] fix: replace home-baked constant-time implementation by subtle --- openmls/Cargo.toml | 1 + openmls/src/ciphersuite/mac.rs | 5 ++++- openmls/src/ciphersuite/mod.rs | 12 +----------- openmls/src/ciphersuite/secret.rs | 2 +- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index a2e8057425..4d7661e47f 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -31,6 +31,7 @@ rstest = { version = "^0.16", optional = true } rstest_reuse = { version = "0.4", optional = true } tokio = { version = "1.24", features = ["full"], optional = true } x509-cert = "0.2" +subtle = "2.5" [features] default = [] diff --git a/openmls/src/ciphersuite/mac.rs b/openmls/src/ciphersuite/mac.rs index c1ec86ec83..ed61b0ec59 100644 --- a/openmls/src/ciphersuite/mac.rs +++ b/openmls/src/ciphersuite/mac.rs @@ -11,7 +11,10 @@ pub struct Mac { impl PartialEq for Mac { // Constant time comparison. fn eq(&self, other: &Mac) -> bool { - equal_ct(self.mac_value.as_slice(), other.mac_value.as_slice()) + self.mac_value + .as_slice() + .ct_eq(other.mac_value.as_slice()) + .into() } } diff --git a/openmls/src/ciphersuite/mod.rs b/openmls/src/ciphersuite/mod.rs index 2b4af22ea7..38bd47b5c3 100644 --- a/openmls/src/ciphersuite/mod.rs +++ b/openmls/src/ciphersuite/mod.rs @@ -36,6 +36,7 @@ pub(crate) use secret::*; pub(crate) use signature::*; pub(crate) use serde::{Deserialize, Serialize}; +use subtle::ConstantTimeEq; #[cfg(test)] mod tests; @@ -45,14 +46,3 @@ const LABEL_PREFIX: &str = "MLS 1.0 "; /// A simple type for HPKE public keys using [`VLBytes`] for (de)serializing. pub type HpkePublicKey = VLBytes; pub use openmls_traits::types::HpkePrivateKey; - -/// Compare two byte slices in a way that's hopefully not optimised out by the -/// compiler. -#[inline(never)] -fn equal_ct(a: &[u8], b: &[u8]) -> bool { - let mut diff = 0u8; - for (l, r) in a.iter().zip(b.iter()) { - diff |= l ^ r; - } - diff == 0 -} diff --git a/openmls/src/ciphersuite/secret.rs b/openmls/src/ciphersuite/secret.rs index ce59d6189d..1fe68d63fe 100644 --- a/openmls/src/ciphersuite/secret.rs +++ b/openmls/src/ciphersuite/secret.rs @@ -65,7 +65,7 @@ impl PartialEq for Secret { ); return false; } - equal_ct(self.value.as_slice(), other.value.as_slice()) + self.value.as_slice().ct_eq(other.value.as_slice()).into() } } From 0148ed9124bbc1f783b013ab3e16b23ce6b217cc Mon Sep 17 00:00:00 2001 From: beltram Date: Mon, 5 Jun 2023 14:13:18 +0200 Subject: [PATCH 26/54] chore: remove already resolved TODO --- openmls/src/framing/public_message_in.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/openmls/src/framing/public_message_in.rs b/openmls/src/framing/public_message_in.rs index 5bb6765336..e695bd02fc 100644 --- a/openmls/src/framing/public_message_in.rs +++ b/openmls/src/framing/public_message_in.rs @@ -156,7 +156,6 @@ impl PublicMessageIn { // Verify the membership tag if let Some(membership_tag) = &self.membership_tag { - // TODO #133: make this a constant-time comparison if membership_tag != expected_membership_tag { return Err(ValidationError::InvalidMembershipTag); } From 322138a889b6a3bc5706b7bbb02955bb19b33fe7 Mon Sep 17 00:00:00 2001 From: beltram Date: Mon, 5 Jun 2023 14:28:36 +0200 Subject: [PATCH 27/54] chore: remove some panicking code from `hkdf_extract` --- openmls/src/ciphersuite/secret.rs | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/openmls/src/ciphersuite/secret.rs b/openmls/src/ciphersuite/secret.rs index 1fe68d63fe..ce6a910166 100644 --- a/openmls/src/ciphersuite/secret.rs +++ b/openmls/src/ciphersuite/secret.rs @@ -129,21 +129,11 @@ impl Secret { let ikm = ikm_option.into().unwrap_or(&zero_secret); log_crypto!(trace, " ikm: {:x?}", ikm.value); - // We don't return an error here to keep the error propagation from - // blowing up. If this fails, something in the library is really wrong - // and we can't recover from it. - assert!( - self.mls_version == ikm.mls_version, - "{} != {}", - self.mls_version, - ikm.mls_version - ); - assert!( - self.ciphersuite == ikm.ciphersuite, - "{} != {}", - self.ciphersuite, - ikm.ciphersuite - ); + let different_versions = self.mls_version != ikm.mls_version; + let different_ciphersuites = self.ciphersuite != ikm.ciphersuite; + if different_versions || different_ciphersuites { + return Err(CryptoError::CryptoLibraryError); + } Ok(Self { value: backend.crypto().hkdf_extract( From 849242607719883ea3e54a6be327523cd470310a Mon Sep 17 00:00:00 2001 From: beltram Date: Mon, 5 Jun 2023 17:36:37 +0200 Subject: [PATCH 28/54] fix: review x509 --- traits/src/key_store.rs | 1 - x509_credential/Cargo.toml | 1 + x509_credential/src/lib.rs | 242 +++++++------------------------------ 3 files changed, 42 insertions(+), 202 deletions(-) diff --git a/traits/src/key_store.rs b/traits/src/key_store.rs index a59603e7cb..813e0be3c7 100644 --- a/traits/src/key_store.rs +++ b/traits/src/key_store.rs @@ -4,7 +4,6 @@ #[derive(PartialEq)] pub enum MlsEntityId { SignatureKeyPair, - CertificateKeyPair, HpkePrivateKey, KeyPackage, PskBundle, diff --git a/x509_credential/Cargo.toml b/x509_credential/Cargo.toml index 9c31212ff7..0477b49b88 100644 --- a/x509_credential/Cargo.toml +++ b/x509_credential/Cargo.toml @@ -11,6 +11,7 @@ openmls_traits = { version = "0.1.0", path = "../traits" } tls_codec = { workspace = true } async-trait = { workspace = true } serde = "1.0" +openmls_basic_credential = { version = "0.1.0", path = "../basic_credential" } # Rust Crypto secrecy = { version = "0.8", features = ["serde"] } diff --git a/x509_credential/src/lib.rs b/x509_credential/src/lib.rs index 294adc1b75..9d0526f40f 100644 --- a/x509_credential/src/lib.rs +++ b/x509_credential/src/lib.rs @@ -2,248 +2,88 @@ //! //! An implementation of the x509 credential from the MLS spec. -use secrecy::{ExposeSecret, SecretVec}; -use serde::{Deserialize, Serialize}; -use std::{convert::From, fmt::Debug}; -use x509_cert::{ - der::{Decode, Encode}, - Certificate, PkiPath, -}; +use openmls_basic_credential::SignatureKeyPair; +use x509_cert::der::Decode; +use x509_cert::Certificate; use openmls_traits::{ crypto::OpenMlsCrypto, - key_store::{MlsEntity, MlsEntityId, OpenMlsKeyStore}, + key_store::{MlsEntity, MlsEntityId}, types::{CryptoError, SignatureScheme}, }; -fn expose_sk(data: &SecretVec, ser: S) -> Result { - use serde::ser::SerializeSeq as _; - let exposed = data.expose_secret(); - let mut seq = ser.serialize_seq(Some(exposed.len()))?; - for b in exposed.iter() { - seq.serialize_element(b)?; - } - seq.end() -} - -mod serde_certificate { - use serde::{ - de::Error as _, - ser::{Error as _, SerializeSeq}, - Deserialize, Deserializer, Serializer, - }; - use x509_cert::{ - der::{Decode, Encode}, - PkiPath, - }; +#[derive(std::fmt::Debug, serde::Serialize, serde::Deserialize)] +#[serde(transparent)] +pub struct CertificateKeyPair(pub SignatureKeyPair); - pub(super) fn serialize(cert: &PkiPath, ser: S) -> Result { - let mut cert_data = Vec::new(); - cert.encode_to_vec(&mut cert_data) - .map_err(S::Error::custom)?; - let mut seq = ser.serialize_seq(Some(cert_data.len()))?; - cert_data - .iter() - .try_for_each(|b| seq.serialize_element(b))?; - seq.end() - } +impl CertificateKeyPair { + /// Constructs the `CertificateKeyPair` from a private key and a der encoded certificate chain + pub fn new(sk: Vec, cert_chain: Vec>) -> Result { + if cert_chain.len() < 2 { + return Err(CryptoError::IncompleteCertificateChain); + } + let pki_path = cert_chain.into_iter().try_fold( + x509_cert::PkiPath::new(), + |mut acc, cert_data| -> Result { + let cert = Certificate::from_der(&cert_data) + .map_err(|_| CryptoError::CertificateDecodingError)?; + cert.is_valid()?; + acc.push(cert); + Ok(acc) + }, + )?; - pub(super) fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let cert_data = Vec::::deserialize(deserializer)?; - PkiPath::from_der(&cert_data).map_err(D::Error::custom) - } + let leaf = pki_path.get(0).ok_or(CryptoError::CryptoLibraryError)?; - #[cfg(test)] - mod tests { - use openmls_traits::types::SignatureScheme; - use rcgen::generate_simple_self_signed; - use x509_cert::{der::Decode, Certificate, PkiPath}; + let signature_scheme = leaf.signature_scheme()?; + let pk = leaf.public_key()?; - use crate::CertificateKeyPair; + let kp = SignatureKeyPair::from_raw(signature_scheme, sk, pk.to_vec()); - #[test] - fn should_serialize_ceritifcate() { - let cert_generator = |quantity: usize| -> PkiPath { - (0..quantity) - .map(|_| { - let subject_alt_names = - vec!["hello.world.example".to_string(), "localhost".to_string()]; - let cert = generate_simple_self_signed(subject_alt_names).unwrap(); - let cert_der = cert.serialize_der().unwrap(); - Certificate::from_der(&cert_der).unwrap() - }) - .collect() - }; - let certs = cert_generator(3); - let kp = CertificateKeyPair { - private: vec![].into(), - certificate_chain: certs, - signature_scheme: SignatureScheme::ED25519, - }; - let serialized = serde_json::to_value(&kp).unwrap(); - let deserialized = serde_json::from_value::(serialized).unwrap(); - assert_eq!(deserialized.certificate_chain, kp.certificate_chain); - } + Ok(Self(kp)) } } -/// A signature key pair for the x509 credential. -/// -/// This can be used as keys to implement the MLS x509 credential. It simple -/// stores the private key and certificate chain. -#[derive(Serialize, Deserialize)] -pub struct CertificateKeyPair { - #[serde(serialize_with = "expose_sk")] - private: SecretVec, - #[serde(with = "serde_certificate")] - certificate_chain: PkiPath, - signature_scheme: SignatureScheme, +impl std::ops::Deref for CertificateKeyPair { + type Target = SignatureKeyPair; + + fn deref(&self) -> &Self::Target { + &self.0 + } } impl secrecy::SerializableSecret for CertificateKeyPair {} impl tls_codec::Size for CertificateKeyPair { fn tls_serialized_len(&self) -> usize { - let cert_len: usize = self - .certificate_chain - .encoded_len() - .unwrap() - .try_into() - .unwrap(); - self.private.expose_secret().tls_serialized_len() - + cert_len - + self.signature_scheme.tls_serialized_len() + self.0.tls_serialized_len() } } impl tls_codec::Deserialize for CertificateKeyPair { - fn tls_deserialize(bytes: &mut R) -> Result - where - Self: Sized, - { - let private = Vec::::tls_deserialize(bytes)?.into(); - let cert_data = Vec::::tls_deserialize(bytes)?; - let certificate = PkiPath::from_der(&cert_data).map_err(|e| { - tls_codec::Error::DecodingError(format!("Error decoding certificate: {e}")) - })?; - - let signature_scheme = SignatureScheme::tls_deserialize(bytes)?; - Ok(Self { - private, - certificate_chain: certificate, - signature_scheme, - }) + fn tls_deserialize(bytes: &mut R) -> Result { + SignatureKeyPair::tls_deserialize(bytes).map(Self) } } impl tls_codec::Serialize for CertificateKeyPair { fn tls_serialize(&self, writer: &mut W) -> Result { - let mut written = self.private.expose_secret().tls_serialize(writer)?; - let mut cert_data = Vec::new(); - self.certificate_chain - .encode_to_vec(&mut cert_data) - .map_err(|e| { - tls_codec::Error::EncodingError(format!("Error encoding certificate: {e}")) - })?; - written += cert_data.tls_serialize(writer)?; - written += self.signature_scheme.tls_serialize(writer)?; - Ok(written) - } -} - -impl Debug for CertificateKeyPair { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("SignatureKeyPair") - .field("private", &"***".to_string()) - .field("certificate", &self.certificate_chain) - .field("signature_scheme", &self.signature_scheme) - .finish() + self.0.tls_serialize(writer) } } impl openmls_traits::signatures::DefaultSigner for CertificateKeyPair { fn private_key(&self) -> &[u8] { - self.private.expose_secret().as_slice() + self.0.private_key() } fn signature_scheme(&self) -> SignatureScheme { - self.signature_scheme + self.0.signature_scheme() } } impl MlsEntity for CertificateKeyPair { - const ID: MlsEntityId = MlsEntityId::CertificateKeyPair; -} - -impl CertificateKeyPair { - /// Constructs the `CertificateKeyPair` from a private key and a der encoded certificate chain - pub fn new(private: SecretVec, cert_chain: Vec>) -> Result { - if cert_chain.len() < 2 { - return Err(CryptoError::IncompleteCertificateChain); - } - let pki_path = cert_chain.into_iter().try_fold( - PkiPath::new(), - |mut acc, cert_data| -> Result { - acc.push( - Certificate::from_der(&cert_data) - .map_err(|_| CryptoError::CertificateDecodingError)?, - ); - Ok(acc) - }, - )?; - - let signature_scheme = pki_path[0].signature_scheme()?; - - pki_path - .iter() - .try_for_each(|certificate| certificate.is_valid())?; - - Ok(Self { - private, - certificate_chain: pki_path, - signature_scheme, - }) - } - - /// Store this signature key pair in the key store. - pub async fn store(&self, key_store: &T) -> Result<(), ::Error> - where - T: OpenMlsKeyStore, - ::Error: From, - { - key_store.store(self.public()?, self).await - } - - /// Read a signature key pair from the key store. - pub async fn read(key_store: &impl OpenMlsKeyStore, public_key: &[u8]) -> Option { - key_store.read(public_key).await - } - - /// Get the public key as byte slice. - pub fn public(&self) -> Result<&[u8], CryptoError> { - self.certificate_chain - .get(0) - .ok_or(CryptoError::IncompleteCertificateChain)? - .public_key() - } - - /// Get the public key as byte vector. - pub fn to_public_vec(&self) -> Result, CryptoError> { - self.public().map(|p| p.to_owned()) - } - - /// Get the [`SignatureScheme`] of this signature key. - pub fn signature_scheme(&self) -> SignatureScheme { - self.signature_scheme - } - - #[cfg(feature = "test-utils")] - pub fn private(&self) -> &[u8] { - self.private.expose_secret() - } + const ID: MlsEntityId = MlsEntityId::SignatureKeyPair; } pub trait X509Ext { From 531d567fb388892aba32b58560e365140bdaa649 Mon Sep 17 00:00:00 2001 From: beltram Date: Tue, 6 Jun 2023 17:50:46 +0200 Subject: [PATCH 29/54] remove rayon --- openmls/Cargo.toml | 1 - openmls/src/test_utils/test_framework/mod.rs | 1 - openmls/src/treesync/node/parent_node.rs | 5 ++--- openmls/src/treesync/treekem.rs | 5 ++--- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 4d7661e47f..92cf723dec 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -14,7 +14,6 @@ openmls_traits = { version = "0.1.0", path = "../traits" } serde = { version = "^1.0", features = ["derive"] } log = { version = "0.4", features = ["std"] } tls_codec = { workspace = true } -rayon = "^1.5.0" thiserror = "^1.0" backtrace = "0.3" hex = "0.4" diff --git a/openmls/src/test_utils/test_framework/mod.rs b/openmls/src/test_utils/test_framework/mod.rs index 713aabda0b..2b6b3aed4d 100644 --- a/openmls/src/test_utils/test_framework/mod.rs +++ b/openmls/src/test_utils/test_framework/mod.rs @@ -41,7 +41,6 @@ use openmls_traits::{ types::{Ciphersuite, HpkeKeyPair, SignatureScheme}, OpenMlsCryptoProvider, }; -use rayon::prelude::*; use std::{collections::HashMap, sync::RwLock}; use tls_codec::*; diff --git a/openmls/src/treesync/node/parent_node.rs b/openmls/src/treesync/node/parent_node.rs index f334c4c5f5..6669cfaddf 100644 --- a/openmls/src/treesync/node/parent_node.rs +++ b/openmls/src/treesync/node/parent_node.rs @@ -5,7 +5,6 @@ use openmls_traits::{ types::{Ciphersuite, HpkeCiphertext}, OpenMlsCryptoProvider, }; -use rayon::prelude::*; use serde::{Deserialize, Serialize}; use thiserror::*; use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize, VLBytes}; @@ -60,7 +59,7 @@ impl PlainUpdatePathNode { group_context: &[u8], ) -> Result { public_keys - .par_iter() + .iter() .map(|pk| { self.path_secret .encrypt(backend, ciphersuite, pk, group_context) @@ -125,7 +124,7 @@ impl ParentNode { // Iterate over the path secrets and derive a key pair let (path_with_keypairs, update_path_nodes): PathDerivationResults = path_secrets - .into_par_iter() + .into_iter() .zip(path_indices) .map(|(path_secret, index)| { // Derive a key pair from the path secret. This includes the diff --git a/openmls/src/treesync/treekem.rs b/openmls/src/treesync/treekem.rs index c242a9b03b..72f52c0121 100644 --- a/openmls/src/treesync/treekem.rs +++ b/openmls/src/treesync/treekem.rs @@ -11,7 +11,6 @@ use openmls_traits::{ types::{Ciphersuite, HpkeCiphertext}, OpenMlsCryptoProvider, }; -use rayon::prelude::*; use serde::{Deserialize, Serialize}; use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize}; @@ -71,8 +70,8 @@ impl<'a> TreeSyncDiff<'a> { debug_assert_eq!(copath_resolutions.len(), path.len()); // Encrypt the secrets - path.par_iter() - .zip(copath_resolutions.par_iter()) + path.iter() + .zip(copath_resolutions.iter()) .map(|(node, resolution)| node.encrypt(backend, ciphersuite, resolution, group_context)) .collect::, LibraryError>>() } From f2eac36d773521e7d1ed5316a9d2f69ed77fa2b7 Mon Sep 17 00:00:00 2001 From: beltram Date: Wed, 7 Jun 2023 11:18:33 +0200 Subject: [PATCH 30/54] fix wasm compilation and add wasm-bindgen --- basic_credential/Cargo.toml | 1 + delivery-service/ds/Cargo.toml | 5 +++-- openmls/Cargo.toml | 16 +++++++++------- openmls/src/group/core_group/test_core_group.rs | 9 +++++++++ .../core_group/test_create_commit_params.rs | 3 +++ .../src/group/core_group/test_external_init.rs | 5 +++++ .../src/group/core_group/test_past_secrets.rs | 4 ++++ openmls/src/group/core_group/test_proposals.rs | 9 +++++++++ .../src/group/tests/test_commit_validation.rs | 9 +++++++++ openmls/src/group/tests/test_encoding.rs | 7 +++++++ .../tests/test_external_commit_validation.rs | 10 ++++++++++ openmls/src/group/tests/test_framing.rs | 3 +++ .../src/group/tests/test_framing_validation.rs | 11 +++++++++++ openmls/src/group/tests/test_gce_proposals.rs | 13 +++++++++++++ openmls/src/group/tests/test_group.rs | 5 +++++ openmls/src/group/tests/test_past_secrets.rs | 3 +++ .../src/group/tests/test_proposal_validation.rs | 13 +++++++++++++ openmls/src/group/tests/test_remove_operation.rs | 3 +++ .../src/group/tests/test_update_extensions.rs | 4 ++++ .../src/group/tests/test_wire_format_policy.rs | 4 ++++ traits/Cargo.toml | 1 - 21 files changed, 128 insertions(+), 10 deletions(-) diff --git a/basic_credential/Cargo.toml b/basic_credential/Cargo.toml index 19321f5fac..06347cb47d 100644 --- a/basic_credential/Cargo.toml +++ b/basic_credential/Cargo.toml @@ -21,6 +21,7 @@ p256 = "0.13" p384 = "0.13" secrecy = { version = "0.8", features = ["serde"] } rand_core = "0.6" +getrandom = { version = "0.2", features = ["js"] } [features] clonable = [] # Make the keys clonable diff --git a/delivery-service/ds/Cargo.toml b/delivery-service/ds/Cargo.toml index f42a0bf92f..92f0b6f111 100644 --- a/delivery-service/ds/Cargo.toml +++ b/delivery-service/ds/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -actix-rt = "2.0" actix-web = "3" futures-core = "0.3" futures-util = "0.3" @@ -15,7 +14,6 @@ serde_json = "1.0" log = "0.4" pretty_env_logger = "0.4" serde = {version = "1.0", features = ["derive"]} -uuid = { version = "0.8", features = ["serde", "v4"] } clap = "2.33" base64 = "0.13" tls_codec = { workspace = true } @@ -26,3 +24,6 @@ ds-lib = { path = "../ds-lib/" } openmls_rust_crypto = { path = "../../openmls_rust_crypto" } openmls_traits = { path = "../../traits" } openmls_basic_credential = { path = "../../basic_credential" } + +[dev-dependencies] +actix-rt = "2.8" \ No newline at end of file diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 92cf723dec..384fd6ab27 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -21,14 +21,15 @@ async-trait = { workspace = true } openmls_basic_credential = { version = "0.1.0", path = "../basic_credential", features = ["clonable", "test-utils"] } openmls_x509_credential = { version = "0.1.0", path = "../x509_credential", features = ["test-utils"] } # Only required for tests. -rand = { version = "0.8", optional = true } +rand = { version = "0.8", optional = true, features = ["getrandom"] } +getrandom = { version = "0.2", optional = true, features = ["js"] } serde_json = { version = "1.0", optional = true } # Crypto backends required for KAT and testing - "test-utils" feature itertools = { version = "0.10", optional = true } openmls_rust_crypto = { version = "0.1.0", path = "../openmls_rust_crypto", optional = true } rstest = { version = "^0.16", optional = true } rstest_reuse = { version = "0.4", optional = true } -tokio = { version = "1.24", features = ["full"], optional = true } +tokio = { version = "1.24", optional = true, features = ["macros", "rt"] } x509-cert = "0.2" subtle = "2.5" @@ -40,6 +41,7 @@ test-utils = [ "dep:itertools", "dep:openmls_rust_crypto", "dep:rand", + "dep:getrandom", "dep:tokio", "dep:rstest", "dep:rstest_reuse", @@ -51,7 +53,6 @@ content-debug = [] # ☣️ Enable logging of sensitive message content [dev-dependencies] backtrace = "0.3" -criterion = { version = "0.4", features = ["async_futures"] } hex = { version = "0.4", features = ["serde"] } itertools = "0.10" lazy_static = "1.4" @@ -61,11 +62,12 @@ pretty_env_logger = "0.4" rstest = "^0.16" rstest_reuse = "0.4" tempfile = "3" +wasm-bindgen = "0.2" +wasm-bindgen-futures = "0.4" +wasm-bindgen-test = "0.3" -# x64 targets get evercrypt compiled into dev-dependencies. -[target.'cfg(target_arch = "x86_64")'.dev-dependencies.openmls] -path = "." -features = ["test-utils"] +[target.'cfg(not(target_family = "wasm"))'.dev-dependencies] +criterion = { version = "0.4", features = ["async_futures"] } [[bench]] name = "benchmark" diff --git a/openmls/src/group/core_group/test_core_group.rs b/openmls/src/group/core_group/test_core_group.rs index 340bde85e8..07bfab1630 100644 --- a/openmls/src/group/core_group/test_core_group.rs +++ b/openmls/src/group/core_group/test_core_group.rs @@ -19,6 +19,8 @@ use crate::{ }, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + pub(crate) async fn setup_alice_group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -50,6 +52,7 @@ pub(crate) async fn setup_alice_group( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_core_group_persistence( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -81,6 +84,7 @@ pub fn flip_last_byte(ctxt: &mut HpkeCiphertext) { } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_failed_groupinfo_decryption( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -199,6 +203,7 @@ async fn test_failed_groupinfo_decryption( /// Test what happens if the KEM ciphertext for the receiver in the UpdatePath /// is broken. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_update_path(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // === Alice creates a group with her and Bob === let ( @@ -329,6 +334,7 @@ async fn setup_alice_bob( // Test several scenarios when PSKs are used in a group #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Basic group setup. let group_aad = b"Alice's test group"; @@ -458,6 +464,7 @@ async fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // Test several scenarios when PSKs are used in a group #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_staged_commit_creation( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -533,6 +540,7 @@ async fn test_staged_commit_creation( // Test processing of own commits #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_own_commit_processing( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -629,6 +637,7 @@ pub(crate) async fn setup_client( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_proposal_application_after_self_was_removed( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/group/core_group/test_create_commit_params.rs b/openmls/src/group/core_group/test_create_commit_params.rs index 0b1eac4d52..c37fe45056 100644 --- a/openmls/src/group/core_group/test_create_commit_params.rs +++ b/openmls/src/group/core_group/test_create_commit_params.rs @@ -2,8 +2,11 @@ use crate::test_utils::*; use super::*; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + // Tests that the builder for CreateCommitParams works as expected #[apply(backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn build_create_commit_params(backend: &impl OpenMlsCryptoProvider) { let _ = backend; let framing_parameters: FramingParameters = diff --git a/openmls/src/group/core_group/test_external_init.rs b/openmls/src/group/core_group/test_external_init.rs index a303855d44..a0836849b1 100644 --- a/openmls/src/group/core_group/test_external_init.rs +++ b/openmls/src/group/core_group/test_external_init.rs @@ -17,7 +17,10 @@ use openmls_traits::{types::Ciphersuite, OpenMlsCryptoProvider}; use super::{proposals::ProposalStore, CoreGroup}; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_external_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ( framing_parameters, @@ -197,6 +200,7 @@ async fn test_external_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_external_init_single_member_group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -263,6 +267,7 @@ async fn test_external_init_single_member_group( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_external_init_broken_signature( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/group/core_group/test_past_secrets.rs b/openmls/src/group/core_group/test_past_secrets.rs index bc50d726f9..1b4e38df8c 100644 --- a/openmls/src/group/core_group/test_past_secrets.rs +++ b/openmls/src/group/core_group/test_past_secrets.rs @@ -5,7 +5,10 @@ use crate::{ schedule::message_secrets::MessageSecrets, test_utils::*, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_secret_tree_store(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Create a store that keeps up to 3 epochs let mut message_secrets_store = MessageSecretsStore::new_with_secret( @@ -45,6 +48,7 @@ async fn test_secret_tree_store(ciphersuite: Ciphersuite, backend: &impl OpenMls } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_empty_secret_tree_store( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/group/core_group/test_proposals.rs b/openmls/src/group/core_group/test_proposals.rs index 112bd0ded2..d6ec7e98a2 100644 --- a/openmls/src/group/core_group/test_proposals.rs +++ b/openmls/src/group/core_group/test_proposals.rs @@ -26,10 +26,13 @@ use crate::{ versions::ProtocolVersion, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// This test makes sure ProposalQueue works as intended. This functionality is /// used in `create_commit` to filter the epoch proposals. Expected result: /// `filtered_queued_proposals` returns only proposals of a certain type #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn proposal_queue_functions(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Framing parameters let framing_parameters = FramingParameters::new(&[], WireFormat::PublicMessage); @@ -174,6 +177,7 @@ async fn proposal_queue_functions(ciphersuite: Ciphersuite, backend: &impl OpenM /// Test, that we QueuedProposalQueue is iterated in the right order. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn proposal_queue_order(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Framing parameters let framing_parameters = FramingParameters::new(&[], WireFormat::PublicMessage); @@ -282,6 +286,7 @@ async fn proposal_queue_order(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_required_unsupported_proposals( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -315,6 +320,7 @@ async fn test_required_unsupported_proposals( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_required_extension_key_package_mismatch( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -368,6 +374,7 @@ async fn test_required_extension_key_package_mismatch( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_group_context_extensions( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -465,6 +472,7 @@ async fn test_group_context_extensions( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_group_context_extension_proposal_fails( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -582,6 +590,7 @@ async fn test_group_context_extension_proposal_fails( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_group_context_extension_proposal( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/group/tests/test_commit_validation.rs b/openmls/src/group/tests/test_commit_validation.rs index 29d51d50bb..a19867b25b 100644 --- a/openmls/src/group/tests/test_commit_validation.rs +++ b/openmls/src/group/tests/test_commit_validation.rs @@ -23,6 +23,8 @@ use crate::{ versions::ProtocolVersion, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + struct CommitValidationTestSetup { alice_group: MlsGroup, alice_credential: CredentialWithKeyAndSigner, @@ -125,6 +127,7 @@ async fn validation_test_setup( // ValSem200: Commit must not cover inline self Remove proposal #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem200(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let CommitValidationTestSetup { @@ -246,6 +249,7 @@ async fn test_valsem200(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem201: Path must be present, if at least one proposal requires a path #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let wire_format_policy = PURE_PLAINTEXT_WIRE_FORMAT_POLICY; // Test with PublicMessage @@ -432,6 +436,7 @@ fn erase_path( // ValSem202: Path must be the right length #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem202(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let CommitValidationTestSetup { @@ -506,6 +511,7 @@ async fn test_valsem202(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem203: Path secrets must decrypt correctly #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem203(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let CommitValidationTestSetup { @@ -582,6 +588,7 @@ async fn test_valsem203(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem204: Public keys from Path must be verified and match the private keys from the direct path #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem204(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let CommitValidationTestSetup { @@ -706,6 +713,7 @@ async fn test_valsem204(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem205: Confirmation tag must be successfully verified #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem205(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let CommitValidationTestSetup { @@ -775,6 +783,7 @@ async fn test_valsem205(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // this ensures that a member can process commits not containing all the stored proposals #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_partial_proposal_commit( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/group/tests/test_encoding.rs b/openmls/src/group/tests/test_encoding.rs index 2c5fc78fb8..1b3f1a3c26 100644 --- a/openmls/src/group/tests/test_encoding.rs +++ b/openmls/src/group/tests/test_encoding.rs @@ -7,6 +7,7 @@ use crate::{ binary_tree::LeafNodeIndex, framing::*, group::*, key_packages::*, messages::*, schedule::psk::store::ResumptionPskStore, test_utils::*, *, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); /// Creates a simple test setup for various encoding tests. async fn create_encoding_test_setup(backend: &impl OpenMlsCryptoProvider) -> TestSetup { @@ -52,6 +53,7 @@ async fn create_encoding_test_setup(backend: &impl OpenMlsCryptoProvider) -> Tes /// This test tests encoding and decoding of application messages. #[apply(backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_application_message_encoding(backend: &impl OpenMlsCryptoProvider) { let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); @@ -94,6 +96,7 @@ async fn test_application_message_encoding(backend: &impl OpenMlsCryptoProvider) /// This test tests encoding and decoding of update proposals. #[apply(backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_update_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); @@ -148,6 +151,7 @@ async fn test_update_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { /// This test tests encoding and decoding of add proposals. #[apply(backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_add_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); @@ -199,6 +203,7 @@ async fn test_add_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { /// This test tests encoding and decoding of remove proposals. #[apply(backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_remove_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); @@ -242,6 +247,7 @@ async fn test_remove_proposal_encoding(backend: &impl OpenMlsCryptoProvider) { /// This test tests encoding and decoding of commit messages. #[apply(backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_commit_encoding(backend: &impl OpenMlsCryptoProvider) { let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); @@ -344,6 +350,7 @@ async fn test_commit_encoding(backend: &impl OpenMlsCryptoProvider) { } #[apply(backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_welcome_message_encoding(backend: &impl OpenMlsCryptoProvider) { let test_setup = create_encoding_test_setup(backend).await; let test_clients = test_setup.clients.borrow(); diff --git a/openmls/src/group/tests/test_external_commit_validation.rs b/openmls/src/group/tests/test_external_commit_validation.rs index 2b023fb8ea..a9d7406f25 100644 --- a/openmls/src/group/tests/test_external_commit_validation.rs +++ b/openmls/src/group/tests/test_external_commit_validation.rs @@ -31,8 +31,11 @@ use crate::{ }, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + // ValSem240: External Commit, inline Proposals: There MUST be at least one ExternalInit proposal. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem240(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ECValidationTestSetup { mut alice_group, @@ -102,6 +105,7 @@ async fn test_valsem240(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem241: External Commit, inline Proposals: There MUST be at most one ExternalInit proposal. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem241(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let ECValidationTestSetup { @@ -167,6 +171,7 @@ async fn test_valsem241(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem242: External Commit must only cover inline proposal in allowlist (ExternalInit, Remove, PreSharedKey) #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem242(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let ECValidationTestSetup { @@ -323,6 +328,7 @@ async fn test_valsem242(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem243: External Commit, inline Remove Proposal: The identity of the // removed leaf are identical to the ones in the path KeyPackage. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem243(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ECValidationTestSetup { mut alice_group, @@ -478,6 +484,7 @@ async fn test_valsem243(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem244: External Commit must not include any proposals by reference #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem244(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let ECValidationTestSetup { @@ -558,6 +565,7 @@ async fn test_valsem244(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem245: External Commit: MUST contain a path. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem245(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let ECValidationTestSetup { @@ -616,6 +624,7 @@ async fn test_valsem245(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem246: External Commit: The signature of the PublicMessage MUST be verified with the credential of the KeyPackage in the included `path`. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem246(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PublicMessage let ECValidationTestSetup { @@ -732,6 +741,7 @@ async fn test_valsem246(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // External Commit should work when group use ciphertext WireFormat #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_pure_ciphertest(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Test with PrivateMessage let ECValidationTestSetup { diff --git a/openmls/src/group/tests/test_framing.rs b/openmls/src/group/tests/test_framing.rs index d9bd3bbbde..2e41ba6147 100644 --- a/openmls/src/group/tests/test_framing.rs +++ b/openmls/src/group/tests/test_framing.rs @@ -25,6 +25,8 @@ use crate::{ *, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[apply(backends)] async fn padding(backend: &impl OpenMlsCryptoProvider) { // Create a test config for a single client supporting all possible @@ -99,6 +101,7 @@ async fn padding(backend: &impl OpenMlsCryptoProvider) { /// Check that PrivateMessageContent's padding field is verified to be all-zero. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn bad_padding(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let tests = { // { 2^i } ∪ { 2^i +- 1 } diff --git a/openmls/src/group/tests/test_framing_validation.rs b/openmls/src/group/tests/test_framing_validation.rs index 2d3c901786..ac92f29713 100644 --- a/openmls/src/group/tests/test_framing_validation.rs +++ b/openmls/src/group/tests/test_framing_validation.rs @@ -19,6 +19,8 @@ use super::utils::{ generate_credential_with_key, generate_key_package, CredentialWithKeyAndSigner, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + // Test setup values struct ValidationTestSetup { alice_group: MlsGroup, @@ -116,6 +118,7 @@ async fn validation_test_setup( // ValSem002 Group id #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem002(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, @@ -165,6 +168,7 @@ async fn test_valsem002(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem003 Epoch #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, @@ -268,6 +272,7 @@ async fn test_valsem003(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem004 Sender: Member: check the member exists #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem004(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, @@ -327,6 +332,7 @@ async fn test_valsem004(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem005 Application messages must use ciphertext #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem005(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, @@ -385,6 +391,7 @@ async fn test_valsem005(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem006 Ciphertext: decryption needs to work #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem006(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, @@ -435,6 +442,7 @@ async fn test_valsem006(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem007 Membership tag presence #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem007(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, @@ -484,6 +492,7 @@ async fn test_valsem007(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem008 Membership tag verification #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem008(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, @@ -537,6 +546,7 @@ async fn test_valsem008(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem009 Confirmation tag presence #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem009(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, @@ -595,6 +605,7 @@ async fn test_valsem009(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // ValSem010 Signature verification #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem010(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ValidationTestSetup { mut alice_group, diff --git a/openmls/src/group/tests/test_gce_proposals.rs b/openmls/src/group/tests/test_gce_proposals.rs index b9d0ae49f9..84b01056e6 100644 --- a/openmls/src/group/tests/test_gce_proposals.rs +++ b/openmls/src/group/tests/test_gce_proposals.rs @@ -32,6 +32,8 @@ use openmls_traits::{signatures::Signer, types::Ciphersuite, OpenMlsCryptoProvid use super::utils::resign_message; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + pub const DEFAULT_PROPOSAL_TYPES: [ProposalType; 6] = [ ProposalType::Add, ProposalType::Update, @@ -44,6 +46,7 @@ pub const DEFAULT_PROPOSAL_TYPES: [ProposalType; 6] = [ pub const DEFAULT_CREDENTIAL_TYPES: [CredentialType; 1] = [CredentialType::Basic]; #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn gce_are_forwarded_in_welcome( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -91,6 +94,7 @@ async fn gce_are_forwarded_in_welcome( #[should_panic] #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn cannot_create_group_when_keypackage_lacks_required_capability( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -114,6 +118,7 @@ async fn cannot_create_group_when_keypackage_lacks_required_capability( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn gce_fails_when_it_contains_unsupported_extensions( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -162,6 +167,7 @@ async fn gce_fails_when_it_contains_unsupported_extensions( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn gce_proposal_should_overwrite_previous( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -233,6 +239,7 @@ async fn gce_proposal_should_overwrite_previous( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn gce_proposal_can_roundtrip( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -283,6 +290,7 @@ async fn gce_proposal_can_roundtrip( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn creating_commit_with_more_than_one_gce_proposal_should_fail( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -323,6 +331,7 @@ async fn creating_commit_with_more_than_one_gce_proposal_should_fail( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn validating_commit_with_more_than_one_gce_proposal_should_fail( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -386,6 +395,7 @@ async fn validating_commit_with_more_than_one_gce_proposal_should_fail( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn gce_proposal_must_be_applied_first_then_used_to_validate_other_add_proposals( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -469,6 +479,7 @@ async fn gce_proposal_must_be_applied_first_then_used_to_validate_other_add_prop } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn gce_proposal_must_be_applied_first_then_used_to_validate_other_external_add_proposals( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -547,6 +558,7 @@ async fn gce_proposal_must_be_applied_first_then_used_to_validate_other_external } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn gce_proposal_must_be_applied_first_but_ignored_for_remove_proposals( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -655,6 +667,7 @@ async fn gce_proposal_must_be_applied_first_but_ignored_for_remove_proposals( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn gce_proposal_must_be_applied_first_but_ignored_for_external_remove_proposals( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/group/tests/test_group.rs b/openmls/src/group/tests/test_group.rs index e911bbe617..eabf350d54 100644 --- a/openmls/src/group/tests/test_group.rs +++ b/openmls/src/group/tests/test_group.rs @@ -15,7 +15,10 @@ use crate::{ *, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn create_commit_optional_path( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -222,6 +225,7 @@ async fn create_commit_optional_path( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn basic_group_setup(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_aad = b"Alice's test group"; // Framing parameters @@ -301,6 +305,7 @@ async fn basic_group_setup(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypt /// - Charlie updates and commits /// - Charlie removes Bob #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_aad = b"Alice's test group"; // Framing parameters diff --git a/openmls/src/group/tests/test_past_secrets.rs b/openmls/src/group/tests/test_past_secrets.rs index 3a1cb60b7b..7a3a639b65 100644 --- a/openmls/src/group/tests/test_past_secrets.rs +++ b/openmls/src/group/tests/test_past_secrets.rs @@ -12,7 +12,10 @@ use crate::{ group::{config::CryptoConfig, errors::*, *}, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_past_secrets_in_group( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/group/tests/test_proposal_validation.rs b/openmls/src/group/tests/test_proposal_validation.rs index 27147001c1..29068bf9fe 100644 --- a/openmls/src/group/tests/test_proposal_validation.rs +++ b/openmls/src/group/tests/test_proposal_validation.rs @@ -32,6 +32,8 @@ use crate::{ versions::ProtocolVersion, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// Helper function to generate and output CredentialWithKeyAndSigner and KeyPackage async fn generate_credential_with_key_and_key_package( identity: Vec, @@ -240,6 +242,7 @@ enum KeyUniqueness { /// Add Proposal: /// Signature public key in proposals must be unique among proposals #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { for bob_and_charlie_share_keys in [ KeyUniqueness::NegativeSameKey, @@ -422,6 +425,7 @@ async fn test_valsem101a(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP /// Add Proposal: /// HPKE init key in proposals must be unique among proposals #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { for bob_and_charlie_share_keys in [ KeyUniqueness::NegativeSameKey, @@ -582,6 +586,7 @@ async fn test_valsem102(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr /// Signature public key in proposals must be unique among existing group /// members #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { for alice_and_bob_share_keys in [ KeyUniqueness::NegativeSameKey, @@ -847,6 +852,7 @@ async fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP /// ValSem104: /// Add Proposal: Init key and encryption key must be different #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem103_valsem104(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { for alice_and_bob_share_keys in [ KeyUniqueness::NegativeSameKey, @@ -1031,6 +1037,7 @@ enum ProposalInclusion { /// Add Proposal: /// Ciphersuite & protocol version must match the group #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let _ = pretty_env_logger::try_init(); @@ -1381,6 +1388,7 @@ async fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr /// Remove Proposal: /// Removed member must be unique among proposals #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem107(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Helper function to unwrap a commit with a single proposal from an mls message. fn unwrap_specific_commit(commit_ref_remove: MlsMessageOut) -> Commit { @@ -1530,6 +1538,7 @@ async fn test_valsem107(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr /// Remove Proposal: /// Removed member must be an existing group member #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Before we can test creation or reception of (invalid) proposals, we set // up a new group with Alice and Bob. @@ -1659,6 +1668,7 @@ async fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr /// Update Proposal: /// Encryption key must be unique among existing members #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem110(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Before we can test creation or reception of (invalid) proposals, we set // up a new group with Alice and Bob. @@ -1805,6 +1815,7 @@ async fn test_valsem110(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr /// Update Proposal: /// The sender of a full Commit must not include own update proposals #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Before we can test creation or reception of (invalid) proposals, we set // up a new group with Alice and Bob. @@ -1976,6 +1987,7 @@ async fn test_valsem111(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr /// Update Proposal: /// The sender of a standalone update proposal must be of type member #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem112(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Before we can test creation or reception of (invalid) proposals, we set // up a new group with Alice and Bob. @@ -2042,6 +2054,7 @@ async fn test_valsem112(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // --- PreSharedKey Proposals --- #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_valsem401_valsem402(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let ProposalValidationTestSetup { mut alice_group, diff --git a/openmls/src/group/tests/test_remove_operation.rs b/openmls/src/group/tests/test_remove_operation.rs index c744142475..eb02d66d80 100644 --- a/openmls/src/group/tests/test_remove_operation.rs +++ b/openmls/src/group/tests/test_remove_operation.rs @@ -9,8 +9,11 @@ use crate::{ }; use openmls_rust_crypto::OpenMlsRustCrypto; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + // Tests the different variants of the RemoveOperation enum. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_remove_operation_variants( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/group/tests/test_update_extensions.rs b/openmls/src/group/tests/test_update_extensions.rs index 0e8d23164d..b3c3ed8c36 100644 --- a/openmls/src/group/tests/test_update_extensions.rs +++ b/openmls/src/group/tests/test_update_extensions.rs @@ -15,7 +15,10 @@ use crate::{ use super::test_gce_proposals::{group_setup, DEFAULT_CREDENTIAL_TYPES, DEFAULT_PROPOSAL_TYPES}; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn gce_fails_when_it_contains_unsupported_extensions( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -65,6 +68,7 @@ async fn gce_fails_when_it_contains_unsupported_extensions( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn gce_commit_can_roundtrip(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let required_capabilities = RequiredCapabilitiesExtension::new(&[], &DEFAULT_PROPOSAL_TYPES, &DEFAULT_CREDENTIAL_TYPES); diff --git a/openmls/src/group/tests/test_wire_format_policy.rs b/openmls/src/group/tests/test_wire_format_policy.rs index 92a784b10d..647919a6d7 100644 --- a/openmls/src/group/tests/test_wire_format_policy.rs +++ b/openmls/src/group/tests/test_wire_format_policy.rs @@ -15,6 +15,8 @@ use super::utils::{ generate_credential_with_key, generate_key_package, CredentialWithKeyAndSigner, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + // Creates a group with one member async fn create_group( ciphersuite: Ciphersuite, @@ -103,6 +105,7 @@ async fn receive_message( // Test positive cases with all valid (pure & mixed) policies #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_wire_policy_positive(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { for wire_format_policy in WIRE_FORMAT_POLICIES.iter() { let (mut alice_group, alice_credential_with_key_and_signer) = @@ -123,6 +126,7 @@ async fn test_wire_policy_positive(ciphersuite: Ciphersuite, backend: &impl Open // Test negative cases with only icompatible policies #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_wire_policy_negative(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // All combinations that are not part of WIRE_FORMAT_POLICIES let incompatible_policies = vec![ diff --git a/traits/Cargo.toml b/traits/Cargo.toml index e03e8ff7dc..899805b7eb 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -27,4 +27,3 @@ p256 = "0.13" p384 = "0.13" zeroize = "1.6" signature = "2.1" -secrecy = { version = "0.8", features = ["serde"] } From dd956461a401f7da11749f0f91a2a8020ca2baca Mon Sep 17 00:00:00 2001 From: beltram Date: Wed, 7 Jun 2023 11:44:40 +0200 Subject: [PATCH 31/54] remove tokio and use async_std for test due to integration with rstest --- openmls/Cargo.toml | 11 +++++++---- openmls/src/group/core_group/kat_passive_client.rs | 2 +- openmls/src/group/core_group/kat_welcome.rs | 2 +- openmls/src/group/tests/kat_messages.rs | 2 +- openmls/src/key_packages/lifetime.rs | 2 +- openmls/src/test_utils/mod.rs | 3 --- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 384fd6ab27..b13617163a 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -20,6 +20,10 @@ hex = "0.4" async-trait = { workspace = true } openmls_basic_credential = { version = "0.1.0", path = "../basic_credential", features = ["clonable", "test-utils"] } openmls_x509_credential = { version = "0.1.0", path = "../x509_credential", features = ["test-utils"] } +x509-cert = "0.2" +subtle = "2.5" +fluvio-wasm-timer = "0.2" + # Only required for tests. rand = { version = "0.8", optional = true, features = ["getrandom"] } getrandom = { version = "0.2", optional = true, features = ["js"] } @@ -29,9 +33,7 @@ itertools = { version = "0.10", optional = true } openmls_rust_crypto = { version = "0.1.0", path = "../openmls_rust_crypto", optional = true } rstest = { version = "^0.16", optional = true } rstest_reuse = { version = "0.4", optional = true } -tokio = { version = "1.24", optional = true, features = ["macros", "rt"] } -x509-cert = "0.2" -subtle = "2.5" +#tokio = { version = "1.24", optional = true, features = ["macros", "rt"] } [features] default = [] @@ -42,7 +44,7 @@ test-utils = [ "dep:openmls_rust_crypto", "dep:rand", "dep:getrandom", - "dep:tokio", +# "dep:tokio", "dep:rstest", "dep:rstest_reuse", "openmls_basic_credential/test-utils", @@ -65,6 +67,7 @@ tempfile = "3" wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" wasm-bindgen-test = "0.3" +async-std = { version = "1.12", features = ["attributes"] } [target.'cfg(not(target_family = "wasm"))'.dev-dependencies] criterion = { version = "0.4", features = ["async_futures"] } diff --git a/openmls/src/group/core_group/kat_passive_client.rs b/openmls/src/group/core_group/kat_passive_client.rs index 94810080de..f22a9596a2 100644 --- a/openmls/src/group/core_group/kat_passive_client.rs +++ b/openmls/src/group/core_group/kat_passive_client.rs @@ -92,7 +92,7 @@ pub struct TestEpoch { #[derive(Serialize, Deserialize, Debug, Clone, Default)] pub struct TestProposal(#[serde(with = "hex::serde")] Vec); -#[tokio::test] +#[async_std::test] async fn test_read_vectors() { for file in TEST_VECTORS_PATH_READ { let scenario: Vec = read(file); diff --git a/openmls/src/group/core_group/kat_welcome.rs b/openmls/src/group/core_group/kat_welcome.rs index 423a960756..9652a99773 100644 --- a/openmls/src/group/core_group/kat_welcome.rs +++ b/openmls/src/group/core_group/kat_welcome.rs @@ -67,7 +67,7 @@ pub struct WelcomeTestVector { welcome: Vec, } -#[tokio::test] +#[async_std::test] async fn test_read_vectors() { let test_vectors: Vec = read(TEST_VECTOR_PATH_READ); diff --git a/openmls/src/group/tests/kat_messages.rs b/openmls/src/group/tests/kat_messages.rs index 2213384720..fc56a9e9a7 100644 --- a/openmls/src/group/tests/kat_messages.rs +++ b/openmls/src/group/tests/kat_messages.rs @@ -334,7 +334,7 @@ pub async fn run_test_vector(tv: MessagesTestVector) -> Result<(), EncodingMisma Ok(()) } -#[tokio::test] +#[async_std::test] async fn read_test_vectors_messages() { let tests: Vec = read("test_vectors/messages.json"); diff --git a/openmls/src/key_packages/lifetime.rs b/openmls/src/key_packages/lifetime.rs index 4a75a91dcb..9b8c09199e 100644 --- a/openmls/src/key_packages/lifetime.rs +++ b/openmls/src/key_packages/lifetime.rs @@ -1,4 +1,4 @@ -use std::time::{SystemTime, UNIX_EPOCH}; +use fluvio_wasm_timer::{SystemTime, UNIX_EPOCH}; use serde::{Deserialize, Serialize}; use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize}; diff --git a/openmls/src/test_utils/mod.rs b/openmls/src/test_utils/mod.rs index 1cc9094035..b8706b4dd4 100644 --- a/openmls/src/test_utils/mod.rs +++ b/openmls/src/test_utils/mod.rs @@ -209,7 +209,6 @@ pub use openmls_rust_crypto::OpenMlsRustCrypto; case::rust_crypto(&OpenMlsRustCrypto::default()), ) ] -#[tokio::test] #[allow(non_snake_case)] pub async fn backends(backend: &impl OpenMlsCryptoProvider) {} @@ -235,7 +234,6 @@ pub async fn backends(backend: &impl OpenMlsCryptoProvider) {} ) )] #[allow(non_snake_case)] -#[tokio::test] pub async fn ciphersuites(ciphersuite: Ciphersuite) {} // === Ciphersuites & backends === @@ -249,7 +247,6 @@ pub async fn ciphersuites(ciphersuite: Ciphersuite) {} case::rust_crypto_MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519(Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, &OpenMlsRustCrypto::default()), ) ] -#[tokio::test] #[allow(non_snake_case)] pub async fn ciphersuites_and_backends( ciphersuite: Ciphersuite, From 06a51788485e047b62a1ae79bbb151461fd9d677 Mon Sep 17 00:00:00 2001 From: beltram Date: Wed, 7 Jun 2023 12:03:51 +0200 Subject: [PATCH 32/54] fix failing test `secret_incompatible` --- openmls/Cargo.toml | 4 ++-- openmls/src/ciphersuite/tests/test_secrets.rs | 5 ++--- openmls/tests/test_external_commit.rs | 3 ++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index b13617163a..74ffe19644 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -33,7 +33,7 @@ itertools = { version = "0.10", optional = true } openmls_rust_crypto = { version = "0.1.0", path = "../openmls_rust_crypto", optional = true } rstest = { version = "^0.16", optional = true } rstest_reuse = { version = "0.4", optional = true } -#tokio = { version = "1.24", optional = true, features = ["macros", "rt"] } +tokio = { version = "1.24", optional = true, features = ["macros", "rt"] } [features] default = [] @@ -44,7 +44,7 @@ test-utils = [ "dep:openmls_rust_crypto", "dep:rand", "dep:getrandom", -# "dep:tokio", + "dep:tokio", "dep:rstest", "dep:rstest_reuse", "openmls_basic_credential/test-utils", diff --git a/openmls/src/ciphersuite/tests/test_secrets.rs b/openmls/src/ciphersuite/tests/test_secrets.rs index 806a0271e8..b955150f72 100644 --- a/openmls/src/ciphersuite/tests/test_secrets.rs +++ b/openmls/src/ciphersuite/tests/test_secrets.rs @@ -19,9 +19,8 @@ async fn secret_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi assert_ne!(derived_default_secret, derived_draft_secret); } -#[should_panic] #[apply(ciphersuites_and_backends)] -async fn secret_incompatible(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +pub async fn secret_incompatible(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // These two secrets must be incompatible let default_secret = Secret::random(ciphersuite, backend, None).expect("Not enough randomness."); @@ -29,5 +28,5 @@ async fn secret_incompatible(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry .expect("Not enough randomness."); // This must panic because the two secrets have incompatible MLS versions. - let _default_extracted = default_secret.hkdf_extract(backend, &draft_secret); + assert!(default_secret.hkdf_extract(backend, &draft_secret).is_err()); } diff --git a/openmls/tests/test_external_commit.rs b/openmls/tests/test_external_commit.rs index c21e27fa49..25ac28369c 100644 --- a/openmls/tests/test_external_commit.rs +++ b/openmls/tests/test_external_commit.rs @@ -76,7 +76,7 @@ async fn test_external_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr let (bob_credential, bob_signature_keys) = new_credential(backend, b"Bob", ciphersuite.signature_algorithm()).await; - let (_bob_group, _, _) = MlsGroup::join_by_external_commit( + let (mut bob_group, _, _) = MlsGroup::join_by_external_commit( backend, &bob_signature_keys, None, @@ -89,6 +89,7 @@ async fn test_external_commit(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr ) .await .unwrap(); + bob_group.merge_pending_commit(backend).await.unwrap(); } // Now, Bob wants to join Alice' group by an external commit. (Negative case, broken signature.) From 40eb73473e6f71abb095a2e511ef76578d59dd18 Mon Sep 17 00:00:00 2001 From: beltram Date: Wed, 7 Jun 2023 13:09:41 +0200 Subject: [PATCH 33/54] add more wasm-bindgen tests and fix the one related to keypackage lifetime --- openmls/Cargo.toml | 2 ++ openmls/src/binary_tree/test_binary_tree.rs | 8 ++++++++ .../src/ciphersuite/tests/test_ciphersuite.rs | 3 +++ openmls/src/ciphersuite/tests/test_secrets.rs | 4 ++++ openmls/src/credentials/tests.rs | 4 ++++ openmls/src/extensions/test_extensions.rs | 5 +++++ openmls/src/framing/test_framing.rs | 9 +++++++++ openmls/src/group/mls_group/test_mls_group.rs | 9 +++++++++ .../src/group/tests/external_add_proposal.rs | 5 +++++ .../group/tests/external_remove_proposal.rs | 6 ++++++ openmls/src/key_packages/lifetime.rs | 20 ++++++++++++++++++- openmls/src/key_packages/test_key_packages.rs | 6 ++++++ openmls/src/messages/tests/test_codec.rs | 4 ++++ .../messages/tests/test_export_group_info.rs | 3 +++ openmls/src/messages/tests/test_proposals.rs | 3 +++ openmls/src/messages/tests/test_welcome.rs | 5 +++++ openmls/src/schedule/unit_tests.rs | 3 +++ .../unit_tests/test_secret_tree.rs | 5 +++++ .../unit_tests/test_sender_ratchet.rs | 5 +++++ 19 files changed, 108 insertions(+), 1 deletion(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 74ffe19644..d3d74c7eef 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -68,6 +68,8 @@ wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" wasm-bindgen-test = "0.3" async-std = { version = "1.12", features = ["attributes"] } +web-sys = { version = "0.3", features = ["Window"] } +js-sys = "0.3" [target.'cfg(not(target_family = "wasm"))'.dev-dependencies] criterion = { version = "0.4", features = ["async_futures"] } diff --git a/openmls/src/binary_tree/test_binary_tree.rs b/openmls/src/binary_tree/test_binary_tree.rs index b7eae517b9..dc512fddbc 100644 --- a/openmls/src/binary_tree/test_binary_tree.rs +++ b/openmls/src/binary_tree/test_binary_tree.rs @@ -10,7 +10,10 @@ use super::{ LeafNodeIndex, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] +#[wasm_bindgen_test::wasm_bindgen_test] fn test_tree_basics() { // Test tree creation: Wrong number of nodes. let mut nodes = vec![TreeNode::Leaf(1), TreeNode::Parent(0)]; @@ -62,6 +65,7 @@ fn test_tree_basics() { } #[test] +#[wasm_bindgen_test::wasm_bindgen_test] fn test_diff_merging() { let mut tree = MlsBinaryTree::new(vec![ TreeNode::Leaf(2), @@ -154,6 +158,7 @@ fn test_diff_merging() { } #[test] +#[wasm_bindgen_test::wasm_bindgen_test] fn test_new_tree_error() { // Let's test what happens when the tree is getting too large. let mut nodes: Vec> = Vec::new(); @@ -171,6 +176,7 @@ fn test_new_tree_error() { } #[test] +#[wasm_bindgen_test::wasm_bindgen_test] fn test_diff_iter() { let nodes = (0..101) .map(|i| { @@ -203,6 +209,7 @@ fn test_diff_iter() { } #[test] +#[wasm_bindgen_test::wasm_bindgen_test] fn test_diff_mutable_access_after_manipulation() { let nodes = (0..101) .map(|i| { @@ -235,6 +242,7 @@ fn test_diff_mutable_access_after_manipulation() { } #[test] +#[wasm_bindgen_test::wasm_bindgen_test] fn diff_leaf_access() { // We want to test if leaf access works correctly in a diff. In particular, // we want to ensure that if we access outside of the diff (but inside of diff --git a/openmls/src/ciphersuite/tests/test_ciphersuite.rs b/openmls/src/ciphersuite/tests/test_ciphersuite.rs index f13263a52f..b8c8f1ec77 100644 --- a/openmls/src/ciphersuite/tests/test_ciphersuite.rs +++ b/openmls/src/ciphersuite/tests/test_ciphersuite.rs @@ -4,8 +4,11 @@ use openmls_traits::types::HpkeCiphertext; use crate::{ciphersuite::*, test_utils::*}; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + // Spot test to make sure hpke seal/open work. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_hpke_seal_open(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let plaintext = &[1, 2, 3]; let kp = backend diff --git a/openmls/src/ciphersuite/tests/test_secrets.rs b/openmls/src/ciphersuite/tests/test_secrets.rs index b955150f72..8c21f2b692 100644 --- a/openmls/src/ciphersuite/tests/test_secrets.rs +++ b/openmls/src/ciphersuite/tests/test_secrets.rs @@ -6,7 +6,10 @@ use crate::{ versions::ProtocolVersion, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn secret_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // These two secrets must be incompatible let default_secret = @@ -20,6 +23,7 @@ async fn secret_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] pub async fn secret_incompatible(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // These two secrets must be incompatible let default_secret = diff --git a/openmls/src/credentials/tests.rs b/openmls/src/credentials/tests.rs index dd06a95ac7..9a39f936f9 100644 --- a/openmls/src/credentials/tests.rs +++ b/openmls/src/credentials/tests.rs @@ -2,7 +2,10 @@ use tls_codec::{Deserialize, Serialize}; use super::*; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] +#[wasm_bindgen_test::wasm_bindgen_test] fn test_protocol_version() { use crate::versions::ProtocolVersion; let mls10_version = ProtocolVersion::Mls10; @@ -26,6 +29,7 @@ fn test_protocol_version() { } #[test] +#[wasm_bindgen_test::wasm_bindgen_test] fn that_unknown_credential_types_are_de_serialized_correctly() { let credential_types = [0x0000u16, 0x0A0A, 0x7A7A, 0xF000, 0xFFFF]; diff --git a/openmls/src/extensions/test_extensions.rs b/openmls/src/extensions/test_extensions.rs index 2f585a3515..4470f63ea6 100644 --- a/openmls/src/extensions/test_extensions.rs +++ b/openmls/src/extensions/test_extensions.rs @@ -16,7 +16,10 @@ use crate::{ test_utils::*, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] +#[wasm_bindgen_test::wasm_bindgen_test] fn application_id() { // A raw application id extension let data = &[8u8, 1, 2, 3, 4, 5, 6, 6, 6]; @@ -35,6 +38,7 @@ fn application_id() { // This tests the ratchet tree extension to deliver the public ratcheting tree // in-band #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Basic group setup. let group_aad = b"Alice's test group"; @@ -200,6 +204,7 @@ async fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMls } #[test] +#[wasm_bindgen_test::wasm_bindgen_test] fn required_capabilities() { // A raw required capabilities extension with the default values for openmls (none). let extension_bytes = vec![0, 3, 3, 0, 0, 0]; diff --git a/openmls/src/framing/test_framing.rs b/openmls/src/framing/test_framing.rs index b54f916e93..55afdc1dc6 100644 --- a/openmls/src/framing/test_framing.rs +++ b/openmls/src/framing/test_framing.rs @@ -24,8 +24,11 @@ use crate::{ versions::ProtocolVersion, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// This tests serializing/deserializing PublicMessage #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn codec_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (_credential, signature_keys) = test_utils::new_credential(backend, b"Creator", ciphersuite.signature_algorithm()).await; @@ -74,6 +77,7 @@ async fn codec_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP /// This tests serializing/deserializing PrivateMessage #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn codec_ciphertext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (_credential, signature_keys) = test_utils::new_credential(backend, b"Creator", ciphersuite.signature_algorithm()).await; @@ -147,6 +151,7 @@ async fn codec_ciphertext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto /// This tests the correctness of wire format checks #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn wire_format_checks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let configuration = &SenderRatchetConfiguration::default(); let (plaintext, _credential, _keys) = @@ -318,6 +323,7 @@ async fn create_content( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn membership_tag(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (_credential, signature_keys) = test_utils::new_credential(backend, b"Creator", ciphersuite.signature_algorithm()).await; @@ -370,6 +376,7 @@ async fn membership_tag(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_aad = b"Alice's test group"; let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); @@ -574,6 +581,7 @@ async fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn confirmation_tag_presence(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (framing_parameters, group_alice, alice_signature_keys, group_bob, _, _) = setup_alice_bob_group(ciphersuite, backend).await; @@ -705,6 +713,7 @@ pub(crate) async fn setup_alice_bob_group( /// Test divergent protocol versions in KeyPackages #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn key_package_version(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (mut key_package, _, _) = key_package(ciphersuite, backend).await; diff --git a/openmls/src/group/mls_group/test_mls_group.rs b/openmls/src/group/mls_group/test_mls_group.rs index fec6b237d2..91a02c333e 100644 --- a/openmls/src/group/mls_group/test_mls_group.rs +++ b/openmls/src/group/mls_group/test_mls_group.rs @@ -14,7 +14,10 @@ use crate::{ test_utils::*, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_mls_group_persistence( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -65,6 +68,7 @@ async fn test_mls_group_persistence( // This tests if the remover is correctly passed to the callback when one member // issues a RemoveProposal and another members issues the next Commit. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); @@ -218,6 +222,7 @@ async fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn export_secret(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); @@ -257,6 +262,7 @@ async fn export_secret(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPro } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Some basic setup functions for the MlsGroup. let mls_group_config = MlsGroupConfig::test_default(ciphersuite); @@ -359,6 +365,7 @@ async fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMls } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); @@ -547,6 +554,7 @@ async fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl Open // Test that the key package and the corresponding private key are deleted when // creating a new group for a welcome message. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn key_package_deletion(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); @@ -614,6 +622,7 @@ async fn key_package_deletion(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn remove_prosposal_by_ref(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let group_id = GroupId::from_slice(b"Test Group"); diff --git a/openmls/src/group/tests/external_add_proposal.rs b/openmls/src/group/tests/external_add_proposal.rs index 5dcb1e8cc7..b151ed6a64 100644 --- a/openmls/src/group/tests/external_add_proposal.rs +++ b/openmls/src/group/tests/external_add_proposal.rs @@ -17,6 +17,8 @@ use openmls_traits::types::Ciphersuite; use super::utils::*; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + struct ProposalValidationTestSetup { alice_group: (MlsGroup, SignatureKeyPair), bob_group: (MlsGroup, SignatureKeyPair), @@ -110,6 +112,7 @@ async fn validation_test_setup( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn external_add_proposal_should_succeed( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -228,6 +231,7 @@ async fn external_add_proposal_should_succeed( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn external_add_proposal_should_be_signed_by_key_package_it_references( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -276,6 +280,7 @@ async fn external_add_proposal_should_be_signed_by_key_package_it_references( // TODO #1093: move this test to a dedicated external proposal ValSem test module once all external proposals implemented #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn new_member_proposal_sender_should_be_reserved_for_join_proposals( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/group/tests/external_remove_proposal.rs b/openmls/src/group/tests/external_remove_proposal.rs index 808515b049..f0880181a0 100644 --- a/openmls/src/group/tests/external_remove_proposal.rs +++ b/openmls/src/group/tests/external_remove_proposal.rs @@ -12,6 +12,8 @@ use openmls_traits::types::Ciphersuite; use super::utils::*; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + // Creates a standalone group async fn new_test_group( identity: &str, @@ -92,6 +94,7 @@ async fn validation_test_setup( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn external_remove_proposal_should_remove_member( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -191,6 +194,7 @@ async fn external_remove_proposal_should_remove_member( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn external_remove_proposal_should_fail_when_invalid_external_senders_index( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -249,6 +253,7 @@ async fn external_remove_proposal_should_fail_when_invalid_external_senders_inde } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn external_remove_proposal_should_fail_when_invalid_signature( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -308,6 +313,7 @@ async fn external_remove_proposal_should_fail_when_invalid_signature( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn external_remove_proposal_should_fail_when_no_external_senders( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/key_packages/lifetime.rs b/openmls/src/key_packages/lifetime.rs index 9b8c09199e..6f089dd70e 100644 --- a/openmls/src/key_packages/lifetime.rs +++ b/openmls/src/key_packages/lifetime.rs @@ -94,7 +94,10 @@ mod tests { use super::Lifetime; + wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] + #[wasm_bindgen_test::wasm_bindgen_test] fn lifetime() { // A freshly created extensions must be valid. let ext = Lifetime::default(); @@ -102,7 +105,7 @@ mod tests { // An extension without lifetime is invalid (waiting for 1 second). let ext = Lifetime::new(0); - std::thread::sleep(std::time::Duration::from_secs(1)); + sleep(1); assert!(!ext.is_valid()); // Test (de)serializing invalid extension @@ -113,4 +116,19 @@ mod tests { .expect("Error deserializing lifetime"); assert!(!ext_deserialized.is_valid()); } + + fn sleep(secs: u64) { + #[cfg(not(target_family = "wasm"))] + { + std::thread::sleep(core::time::Duration::from_secs(secs)) + } + #[cfg(target_family = "wasm")] + { + let w: web_sys::Window = web_sys::window().unwrap(); + let closure = js_sys::Function::default(); + let secs = secs.try_into().unwrap(); + w.set_timeout_with_callback_and_timeout_and_arguments_0(&closure, secs) + .unwrap(); + } + } } diff --git a/openmls/src/key_packages/test_key_packages.rs b/openmls/src/key_packages/test_key_packages.rs index fcc8e45e78..07f456480e 100644 --- a/openmls/src/key_packages/test_key_packages.rs +++ b/openmls/src/key_packages/test_key_packages.rs @@ -6,6 +6,8 @@ use tls_codec::Deserialize; use crate::{extensions::*, key_packages::*}; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// Helper function to generate key packages pub(crate) async fn key_package( ciphersuite: Ciphersuite, @@ -39,6 +41,7 @@ pub(crate) async fn key_package( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn generate_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (key_package, _credential, _signature_keys) = key_package(ciphersuite, backend).await; @@ -49,6 +52,7 @@ async fn generate_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn serialization(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (key_package, _, _) = key_package(ciphersuite, backend).await; @@ -64,6 +68,7 @@ async fn serialization(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPro } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn application_id_extension(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let credential = Credential::new_basic(b"Sasha".to_vec()); let signature_keys = SignatureKeyPair::new( @@ -113,6 +118,7 @@ async fn application_id_extension(ciphersuite: Ciphersuite, backend: &impl OpenM /// - The protocol version is correct /// - The init key is not equal to the encryption key #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn key_package_validation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let (key_package_orig, _, _) = key_package(ciphersuite, backend).await; diff --git a/openmls/src/messages/tests/test_codec.rs b/openmls/src/messages/tests/test_codec.rs index b2b9edc574..6b4a87408b 100644 --- a/openmls/src/messages/tests/test_codec.rs +++ b/openmls/src/messages/tests/test_codec.rs @@ -9,9 +9,12 @@ use crate::{ test_utils::*, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// Test the encoding for PreSharedKeyProposal, that also covers some of the /// other PSK-related structs #[apply(backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_pre_shared_key_proposal_codec(backend: &impl OpenMlsCryptoProvider) { // External let psk = PreSharedKeyId { @@ -80,6 +83,7 @@ async fn test_pre_shared_key_proposal_codec(backend: &impl OpenMlsCryptoProvider /// Test the encoding for ReInitProposal, that also covers some of the /// other PSK-related structs #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_reinit_proposal_codec( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/messages/tests/test_export_group_info.rs b/openmls/src/messages/tests/test_export_group_info.rs index dc46c15f72..e14c14c5e1 100644 --- a/openmls/src/messages/tests/test_export_group_info.rs +++ b/openmls/src/messages/tests/test_export_group_info.rs @@ -10,8 +10,11 @@ use crate::{ test_utils::*, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// Tests the creation of an [UnverifiedGroupInfo] and verifies it was correctly signed. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn export_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Alice creates a group let (group_alice, _, signer, pk) = setup_alice_group(ciphersuite, backend).await; diff --git a/openmls/src/messages/tests/test_proposals.rs b/openmls/src/messages/tests/test_proposals.rs index 581552316d..3f8ca0e160 100644 --- a/openmls/src/messages/tests/test_proposals.rs +++ b/openmls/src/messages/tests/test_proposals.rs @@ -11,9 +11,12 @@ use crate::{ test_utils::*, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// This test encodes and decodes the `ProposalOrRef` struct and makes sure the /// decoded values are the same as the original #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn proposals_codec(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Proposal diff --git a/openmls/src/messages/tests/test_welcome.rs b/openmls/src/messages/tests/test_welcome.rs index 2180e364c4..2c08b85d9c 100644 --- a/openmls/src/messages/tests/test_welcome.rs +++ b/openmls/src/messages/tests/test_welcome.rs @@ -32,11 +32,14 @@ use crate::{ use openmls_traits::random::OpenMlsRand; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// This test detects if the decryption of the encrypted group secrets fails due to a change in /// the encrypted group info. As the group info is part of the decryption context of the encrypted /// group info, it is not possible to generate a matching encrypted group context with different /// parameters. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_welcome_context_mismatch( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -211,6 +214,7 @@ async fn test_welcome_context_mismatch( } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_welcome_msg(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { test_welcome_message(ciphersuite, backend); } @@ -323,6 +327,7 @@ fn test_welcome_message(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr } #[test] +#[wasm_bindgen_test::wasm_bindgen_test] fn invalid_welcomes() { // An almost good welcome message. let mut bytes = &[ diff --git a/openmls/src/schedule/unit_tests.rs b/openmls/src/schedule/unit_tests.rs index fccc6e457a..6fd2dbb38a 100644 --- a/openmls/src/schedule/unit_tests.rs +++ b/openmls/src/schedule/unit_tests.rs @@ -11,7 +11,10 @@ use crate::{ versions::ProtocolVersion, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Create a new PSK secret from multiple PSKs. let prng = backend.rand(); diff --git a/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs b/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs index 9f3c9f60b5..13d16b9a9c 100644 --- a/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs +++ b/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs @@ -10,8 +10,11 @@ use crate::{ }; use std::collections::HashMap; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + // This tests the boundaries of the generations from a SecretTree #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_boundaries(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let configuration = &SenderRatchetConfiguration::default(); let encryption_secret = EncryptionSecret::random(ciphersuite, backend); @@ -157,6 +160,7 @@ async fn test_boundaries(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP // This tests if the generation gets incremented correctly and that the returned // values are unique. #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn increment_generation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { const SIZE: usize = 100; const MAX_GENERATIONS: usize = 10; @@ -224,6 +228,7 @@ async fn increment_generation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr } #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn secret_tree(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let leaf_index = 0u32; let generation = 0; diff --git a/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs b/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs index a3e5d7a7f3..ec8cc5ebf0 100644 --- a/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs +++ b/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs @@ -5,8 +5,11 @@ use crate::{ tree::sender_ratchet::*, versions::ProtocolVersion, }; +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + // Test the maximum forward ratcheting #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_max_forward_distance(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let configuration = &SenderRatchetConfiguration::default(); let secret = Secret::random(ciphersuite, backend, ProtocolVersion::Mls10) @@ -45,6 +48,7 @@ async fn test_max_forward_distance(ciphersuite: Ciphersuite, backend: &impl Open // Test out-of-order generations #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_out_of_order_generations( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -86,6 +90,7 @@ async fn test_out_of_order_generations( // Test forward secrecy #[apply(ciphersuites_and_backends)] +#[wasm_bindgen_test::wasm_bindgen_test] async fn test_forward_secrecy(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // Encryption Ratchets are forward-secret by default, since they don't store // any keys. Thus, we can only test FS on Decryption Ratchets. From 588fc2c3d7d0204c0c4bc69c59d9c41ed29380b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Augusto=20C=C3=A9sar=20Dias?= Date: Thu, 1 Jun 2023 12:52:08 +0200 Subject: [PATCH 34/54] feat: Building blocks for reinit implementation --- openmls/src/group/core_group/mod.rs | 44 ++++-- .../src/group/core_group/new_from_welcome.rs | 135 +++++++++++++++++- openmls/src/group/core_group/proposals.rs | 17 ++- openmls/src/group/errors.rs | 17 +++ openmls/src/group/mls_group/errors.rs | 31 +++- openmls/src/group/mls_group/mod.rs | 1 + openmls/src/group/mls_group/processing.rs | 3 + openmls/src/group/mls_group/reinit.rs | 106 ++++++++++++++ .../src/group/public_group/staged_commit.rs | 1 + openmls/src/group/public_group/validation.rs | 73 +++++++++- openmls/src/messages/group_info.rs | 4 + openmls/src/schedule/errors.rs | 3 + openmls/src/schedule/psk.rs | 9 ++ openmls/src/treesync/errors.rs | 3 + openmls/src/treesync/node/leaf_node.rs | 8 ++ 15 files changed, 438 insertions(+), 17 deletions(-) create mode 100644 openmls/src/group/mls_group/reinit.rs diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 8471377b1c..4a9ef07c23 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -50,6 +50,7 @@ use super::{ ProposeGroupContextExtensionError, ValidationError, }, group_context::*, + mls_group::errors::ProposeReInitError, public_group::{diff::compute_path::PathComputationResult, PublicGroup}, }; @@ -485,6 +486,33 @@ impl CoreGroup { ) .map_err(|e| e.into()) } + /// Create a `ReInit` proposal + pub(crate) fn create_reinit_proposal( + &self, + framing_parameters: FramingParameters, + extensions: Extensions, + ciphersuite: Ciphersuite, + version: ProtocolVersion, + signer: &impl Signer, + ) -> Result { + self.public_group() + .check_reinit(&extensions, ciphersuite, version)?; + let reinit_proposal = ReInitProposal { + group_id: self.group_id().clone(), + version, + ciphersuite, + extensions, + }; + let proposal = Proposal::ReInit(reinit_proposal); + AuthenticatedContent::member_proposal( + framing_parameters, + self.own_leaf_index(), + proposal, + self.context(), + signer, + ) + .map_err(|e| e.into()) + } // Create application message pub(crate) fn create_application_message( &mut self, @@ -904,6 +932,8 @@ impl CoreGroup { .validate_pre_shared_key_proposals(&proposal_queue)?; self.public_group() .validate_group_context_extensions_proposals(&proposal_queue)?; + self.public_group() + .validate_reinit_proposal(&proposal_queue)?; // Validate update proposals for member commits if let Sender::Member(sender_index) = &sender { // ValSem110 @@ -1042,14 +1072,12 @@ impl CoreGroup { }; // Create to-be-signed group info. - let group_info_tbs = { - GroupInfoTBS::new( - diff.group_context().clone(), - other_extensions, - confirmation_tag, - self.own_leaf_index(), - ) - }; + let group_info_tbs = GroupInfoTBS::new( + diff.group_context().clone(), + other_extensions, + confirmation_tag, + self.own_leaf_index(), + ); // Sign to-be-signed group info. Some(group_info_tbs.sign(signer)?) } else { diff --git a/openmls/src/group/core_group/new_from_welcome.rs b/openmls/src/group/core_group/new_from_welcome.rs index c1c6c0b4fe..7efc63765b 100644 --- a/openmls/src/group/core_group/new_from_welcome.rs +++ b/openmls/src/group/core_group/new_from_welcome.rs @@ -4,7 +4,10 @@ use openmls_traits::key_store::OpenMlsKeyStore; use crate::{ ciphersuite::hash_ref::HashReference, group::{core_group::*, errors::WelcomeError}, - schedule::psk::store::ResumptionPskStore, + schedule::{ + errors::PskError, + psk::{store::ResumptionPskStore, ResumptionPsk, ResumptionPskUsage}, + }, treesync::{ errors::{DerivePathError, PublicTreeError}, node::encryption_keys::EncryptionKeyPair, @@ -65,7 +68,7 @@ impl CoreGroup { )?; // Prepare the PskSecret - let psk_secret = { + let (has_reinit_branch, psk_secret) = { let psks = load_psks( backend.key_store(), &resumption_psk_store, @@ -73,7 +76,13 @@ impl CoreGroup { ) .await?; - PskSecret::new(backend, ciphersuite, psks).await? + ( + check_welcome_psks( + psks.iter() + .filter_map(|(psk_id, _)| psk_id.psk().resumption()), + )?, + PskSecret::new(backend, ciphersuite, psks).await?, + ) }; // Create key schedule @@ -99,6 +108,11 @@ impl CoreGroup { backend, )?; + // if the welcome has reinit or branch psks the group epoch must be 1 + if has_reinit_branch && verifiable_group_info.context().epoch() != GroupEpoch(1) { + return Err(WelcomeError::InvalidEpoch); + } + // Make sure that we can support the required capabilities in the group info. if let Some(required_capabilities) = verifiable_group_info.extensions().required_capabilities() @@ -263,3 +277,118 @@ impl CoreGroup { None } } + +/// Checks if there are multiple ocurrences of resumption psks of type Branch or Reinit +/// It will return true if it contains any of those types +fn check_welcome_psks<'i>( + resumption_psks: impl Iterator, +) -> Result { + let mut has_branch = false; + let mut has_reinit = false; + for resumption in resumption_psks { + match resumption.usage() { + ResumptionPskUsage::Branch => { + if has_branch { + return Err(PskError::TooManyBranchReinitResumptionPsks); + } + has_branch = true; + } + ResumptionPskUsage::Reinit => { + if has_reinit { + return Err(PskError::TooManyBranchReinitResumptionPsks); + } else { + has_reinit = true; + } + } + ResumptionPskUsage::Application => continue, + } + } + Ok(has_reinit || has_branch) +} + +#[cfg(test)] +mod tests { + use crate::{ + group::{GroupEpoch, GroupId}, + schedule::{ + errors::PskError, + psk::{ResumptionPsk, ResumptionPskUsage}, + }, + }; + + use super::check_welcome_psks; + + #[test] + fn psk_ids_should_be_valid() { + let group_id = GroupId::from_slice(b"test"); + let epoch = GroupEpoch(1); + let psks = vec![ + ResumptionPsk::new(ResumptionPskUsage::Reinit, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Branch, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Application, group_id, epoch), + ]; + assert!(check_welcome_psks(psks.iter()).unwrap()); + } + + #[test] + fn psk_ids_should_be_valid_application() { + let group_id = GroupId::from_slice(b"test"); + let epoch = GroupEpoch(1); + let psks = vec![ + ResumptionPsk::new(ResumptionPskUsage::Application, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Application, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Application, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Application, group_id, epoch), + ]; + assert!(!check_welcome_psks(psks.iter()).unwrap()); + } + + #[test] + fn psk_ids_should_be_invalid_reinit() { + let group_id = GroupId::from_slice(b"test"); + let epoch = GroupEpoch(1); + let psks = vec![ + ResumptionPsk::new(ResumptionPskUsage::Reinit, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Branch, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Reinit, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Application, group_id, epoch), + ]; + assert_eq!( + check_welcome_psks(psks.iter()).unwrap_err(), + PskError::TooManyBranchReinitResumptionPsks + ); + } + + #[test] + fn psk_ids_should_be_invalid_branch() { + let group_id = GroupId::from_slice(b"test"); + let epoch = GroupEpoch(1); + let psks = vec![ + ResumptionPsk::new(ResumptionPskUsage::Reinit, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Branch, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Branch, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Application, group_id, epoch), + ]; + assert_eq!( + check_welcome_psks(psks.iter()).unwrap_err(), + PskError::TooManyBranchReinitResumptionPsks + ); + } + + #[test] + fn psk_ids_should_be_invalid() { + let group_id = GroupId::from_slice(b"test"); + let epoch = GroupEpoch(1); + let psks = vec![ + ResumptionPsk::new(ResumptionPskUsage::Reinit, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Branch, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Reinit, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Branch, group_id.clone(), epoch), + ResumptionPsk::new(ResumptionPskUsage::Application, group_id, epoch), + ]; + assert_eq!( + check_welcome_psks(psks.iter()).unwrap_err(), + PskError::TooManyBranchReinitResumptionPsks + ); + } +} diff --git a/openmls/src/group/core_group/proposals.rs b/openmls/src/group/core_group/proposals.rs index 551d0846cc..06195e2909 100644 --- a/openmls/src/group/core_group/proposals.rs +++ b/openmls/src/group/core_group/proposals.rs @@ -13,7 +13,7 @@ use crate::{ group::errors::*, messages::proposals::{ AddProposal, PreSharedKeyProposal, Proposal, ProposalOrRef, ProposalOrRefType, - ProposalType, RemoveProposal, UpdateProposal, + ProposalType, ReInitProposal, RemoveProposal, UpdateProposal, }, utils::vector_converter, }; @@ -187,6 +187,10 @@ impl ProposalQueue { pub fn is_empty(&self) -> bool { self.proposal_references.is_empty() } + /// Returns the amount of proposals in the queue + pub fn count(&self) -> usize { + self.proposal_references.len() + } /// Returns a new `QueuedProposalQueue` from proposals that were committed and /// don't need filtering. /// This functions does the following checks: @@ -365,6 +369,17 @@ impl ProposalQueue { }) } + /// Returns the first Reinit proposal in the queue + pub fn reinit_proposal(&self) -> Option<&ReInitProposal> { + self.queued_proposals().find_map(|p| { + if let Proposal::ReInit(reinit) = p.proposal() { + Some(reinit) + } else { + None + } + }) + } + /// Filters received proposals /// /// 11.2 Commit diff --git a/openmls/src/group/errors.rs b/openmls/src/group/errors.rs index a63d0886bc..ca378c7bab 100644 --- a/openmls/src/group/errors.rs +++ b/openmls/src/group/errors.rs @@ -89,6 +89,9 @@ pub enum WelcomeError { /// This error indicates the leaf node is invalid. See [`LeafNodeValidationError`] for more details. #[error(transparent)] LeafNodeValidation(#[from] LeafNodeValidationError), + /// Group epoch must be 0 if the the welcome message contains reinit or branch psks + #[error("Group epoch must be 0 if the the welcome message contains reinit or branch psks")] + InvalidEpoch, } /// External Commit error @@ -365,6 +368,20 @@ pub enum ProposalValidationError { /// There cannot be more than 1 GroupContextExtensions proposal in a commit #[error("Expected at most 1 GroupContextExtensions proposal, found {0}")] TooManyGroupContextExtensions(usize), + /// See [`ReInitValidationError`] for more details. + #[error(transparent)] + ReInit(#[from] ReInitValidationError), +} + +/// ReInit validation error +#[derive(Error, Debug, PartialEq, Clone)] +pub enum ReInitValidationError { + /// See [`LeafNodeValidationError`] for more details. + #[error(transparent)] + LeafNode(#[from] LeafNodeValidationError), + /// ReInit cannot set new group to an older version of the protocol + #[error("ReInit cannot set new group to an older version of the protocol")] + ReInitOldVersion, } /// External Commit validaton error diff --git a/openmls/src/group/mls_group/errors.rs b/openmls/src/group/mls_group/errors.rs index 556402ba25..77ae6c4242 100644 --- a/openmls/src/group/mls_group/errors.rs +++ b/openmls/src/group/mls_group/errors.rs @@ -13,8 +13,8 @@ use crate::{ error::LibraryError, extensions::errors::{ExtensionError, InvalidExtensionError}, group::errors::{ - CreateAddProposalError, CreateCommitError, MergeCommitError, StageCommitError, - ValidationError, + CreateAddProposalError, CreateCommitError, MergeCommitError, ReInitValidationError, + StageCommitError, ValidationError, }, prelude::KeyPackageExtensionSupportError, schedule::errors::PskError, @@ -289,6 +289,33 @@ pub enum ProposeGroupContextExtensionError { MemberExtensionValidationError(#[from] MemberExtensionValidationError), } +/// ReInit error +#[derive(Error, Debug, PartialEq, Clone)] +pub enum ReInitError { + /// See [`LibraryError`] for more details. + #[error(transparent)] + LibraryError(#[from] LibraryError), + /// See [`CreateCommitError`] for more details. + #[error(transparent)] + CreateCommitError(#[from] CreateCommitError), + /// See [`MlsGroupStateError`] for more details. + #[error(transparent)] + GroupStateError(#[from] MlsGroupStateError), +} + +/// Create ReInit proposal error +#[derive(Error, Debug, PartialEq, Clone)] +pub enum ProposeReInitError { + /// See [`LibraryError`] for more details. + #[error(transparent)] + LibraryError(#[from] LibraryError), + /// See [`MlsGroupStateError`] for more details. + #[error(transparent)] + GroupStateError(#[from] MlsGroupStateError), + /// See [`ReInitValidationError`] for more details. + #[error(transparent)] + ReInitValidationError(#[from] ReInitValidationError), +} /// Commit to pending proposals error #[derive(Error, Debug, PartialEq, Clone)] pub enum CommitToPendingProposalsError { diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 7dd68cf4b9..9d97162391 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -33,6 +33,7 @@ pub(crate) mod extension; pub(crate) mod membership; pub(crate) mod processing; pub(crate) mod proposal; +pub(crate) mod reinit; pub(crate) mod ser; // Tests diff --git a/openmls/src/group/mls_group/processing.rs b/openmls/src/group/mls_group/processing.rs index 54d35e2071..e59d80d337 100644 --- a/openmls/src/group/mls_group/processing.rs +++ b/openmls/src/group/mls_group/processing.rs @@ -162,6 +162,9 @@ impl MlsGroup { /// Merges the pending [`StagedCommit`] if there is one, and /// clears the field by setting it to `None`. + /// + /// If the commit contains a ReInit proposal it will return a welcome message, a new group and + /// set the current as inactive. pub async fn merge_pending_commit( &mut self, backend: &impl OpenMlsCryptoProvider, diff --git a/openmls/src/group/mls_group/reinit.rs b/openmls/src/group/mls_group/reinit.rs new file mode 100644 index 0000000000..d1d9cec12f --- /dev/null +++ b/openmls/src/group/mls_group/reinit.rs @@ -0,0 +1,106 @@ +use openmls_traits::signatures::Signer; + +use crate::group::core_group::create_commit_params::CreateCommitParams; +use crate::{messages::group_info::GroupInfo, versions::ProtocolVersion}; + +use super::*; + +impl MlsGroup { + /// Propose the group to be reinitialized. When commited this will make the current group + /// innactive and a new one should be created from the commit message. The new group will have + /// the new given extensions, ciphersuite and version from the proposal. + /// + /// Returns an error if there is a pending commit, if the new proposed version is older than + /// the current or if any member doesn't support the proposed extensions and/or ciphersuite. + pub fn propose_reinit( + &mut self, + backend: &impl OpenMlsCryptoProvider, + signer: &impl Signer, + extensions: Extensions, + ciphersuite: Ciphersuite, + version: ProtocolVersion, + ) -> Result<(MlsMessageOut, ProposalRef), ProposeReInitError> { + self.is_operational()?; + + let reinit_proposal = self.group.create_reinit_proposal( + self.framing_parameters(), + extensions, + ciphersuite, + version, + signer, + )?; + + let proposal = QueuedProposal::from_authenticated_content( + self.ciphersuite(), + backend, + reinit_proposal.clone(), + ProposalOrRefType::Proposal, + )?; + let reference = proposal.proposal_reference(); + + self.proposal_store.add(proposal); + + let mls_message = self.content_to_mls_message(reinit_proposal, backend)?; + + // Since the state of the group might be changed, arm the state flag + self.flag_state_change(); + + Ok((mls_message, reference)) + } + + /// ReInits the group. If there are any proposals in the `ProposalStore` they are going to be + /// commited, but the ReInit won't be issued. A ReInit must be done exclusively with a + /// empty `ProposalStore`. In that case the ReInit must be reissued. + /// + /// If successful, it returns a triple where the first element + /// contains the commit, the second one the [Welcome] and the third an optional [GroupInfo] that + /// will be [Some] if the group has the `use_ratchet_tree_extension` flag set. + /// + /// Returns an error if there is a pending commit. + pub async fn reinit( + &mut self, + backend: &impl OpenMlsCryptoProvider, + signer: &impl Signer, + extensions: Extensions, + ciphersuite: Ciphersuite, + version: ProtocolVersion, + ) -> Result< + (MlsMessageOut, Option, Option), + ReInitError, + > { + self.is_operational()?; + let proposal = Proposal::ReInit(ReInitProposal { + group_id: self.group_id().clone(), + version, + ciphersuite, + extensions, + }); + let params = CreateCommitParams::builder() + .framing_parameters(self.framing_parameters()) + .proposal_store(&self.proposal_store) + .inline_proposals(vec![proposal]) + .build(); + + let create_commit_result = self.group.create_commit(params, backend, signer).await?; + + // Convert PublicMessage messages to MLSMessage and encrypt them if required by + // the configuration + let mls_messages = self.content_to_mls_message(create_commit_result.commit, backend)?; + + // Set the current group state to [`MlsGroupState::PendingCommit`], + // storing the current [`StagedCommit`] from the commit results + self.group_state = MlsGroupState::PendingCommit(Box::new(PendingCommitState::Member( + create_commit_result.staged_commit, + ))); + + // Since the state of the group might be changed, arm the state flag + self.flag_state_change(); + Ok(( + mls_messages, + create_commit_result + .welcome_option + .map(|w| MlsMessageOut::from_welcome(w, self.group.version())), + create_commit_result.group_info, + )) + } +} diff --git a/openmls/src/group/public_group/staged_commit.rs b/openmls/src/group/public_group/staged_commit.rs index e6ceb837fa..da71b102fc 100644 --- a/openmls/src/group/public_group/staged_commit.rs +++ b/openmls/src/group/public_group/staged_commit.rs @@ -92,6 +92,7 @@ impl PublicGroup { // ValSem403 self.validate_pre_shared_key_proposals(&proposal_queue)?; self.validate_group_context_extensions_proposals(&proposal_queue)?; + self.validate_reinit_proposal(&proposal_queue)?; match sender { Sender::Member(leaf_index) => { diff --git a/openmls/src/group/public_group/validation.rs b/openmls/src/group/public_group/validation.rs index a8369000fe..cd2fe1495a 100644 --- a/openmls/src/group/public_group/validation.rs +++ b/openmls/src/group/public_group/validation.rs @@ -1,19 +1,26 @@ //! This module contains validation functions for incoming messages //! as defined in -use std::collections::{BTreeSet, HashSet}; +use std::{ + collections::{BTreeSet, HashSet}, + iter, +}; -use openmls_traits::types::VerifiableCiphersuite; +use openmls_traits::types::{Ciphersuite, VerifiableCiphersuite}; use super::PublicGroup; use crate::{ binary_tree::array_representation::LeafNodeIndex, + extensions::Extensions, framing::{ mls_auth_content_in::VerifiableAuthenticatedContentIn, ContentType, ProtocolMessage, Sender, WireFormat, }, group::{ - errors::{ExternalCommitValidationError, ProposalValidationError, ValidationError}, + errors::{ + ExternalCommitValidationError, ProposalValidationError, ReInitValidationError, + ValidationError, + }, past_secrets::MessageSecretsStore, Member, ProposalQueue, }, @@ -23,6 +30,7 @@ use crate::{ }, schedule::errors::PskError, treesync::{errors::LeafNodeValidationError, node::leaf_node::LeafNode}, + versions::ProtocolVersion, }; impl PublicGroup { @@ -531,6 +539,50 @@ impl PublicGroup { Ok(()) } + /// Validate ReInit proposals. + /// + /// There must be at most one ReInit proposal and no other proposal in the queue. + pub(crate) fn validate_reinit_proposal( + &self, + proposal_queue: &ProposalQueue, + ) -> Result<(), ProposalValidationError> { + // if there are other proposals in the queue, the reinit should be ignored and reissued + if proposal_queue.count() > 1 { + return Ok(()); + } + + if let Some(reinit_proposal) = proposal_queue.reinit_proposal() { + self.check_reinit( + &reinit_proposal.extensions, + reinit_proposal.ciphersuite, + reinit_proposal.version, + )?; + } + + Ok(()) + } + + pub(crate) fn check_reinit( + &self, + extensions: &Extensions, + ciphersuite: Ciphersuite, + version: ProtocolVersion, + ) -> Result<(), ReInitValidationError> { + // cannot go to older version + if version < self.version() { + return Err(ReInitValidationError::ReInitOldVersion); + } + let proposal_extension_types = extensions + .iter() + .map(|e| e.extension_type()) + .collect::>(); + // the reinit proposal must the only one being processed, so empty can be passed to + // removed + self.check_extension_support(&proposal_extension_types, iter::empty())?; + self.check_ciphersuite_support(ciphersuite)?; + Ok(()) + } + /// Returns a [`LeafNodeValidationError`] if an [`ExtensionType`] /// in `extensions` is not supported by a leaf in this tree. /// A list of leaves proposed to be removed must be provided as they @@ -548,4 +600,19 @@ impl PublicGroup { } Ok(()) } + + pub(crate) fn check_ciphersuite_support( + &self, + ciphersuite: Ciphersuite, + ) -> Result<(), LeafNodeValidationError> { + if self + .treesync() + .full_leaves() + .all(|l| l.supports_ciphersuite(ciphersuite)) + { + Ok(()) + } else { + Err(LeafNodeValidationError::UnsupportedCipherSuite) + } + } } diff --git a/openmls/src/messages/group_info.rs b/openmls/src/messages/group_info.rs index 3318f920e8..ee0f2492dd 100644 --- a/openmls/src/messages/group_info.rs +++ b/openmls/src/messages/group_info.rs @@ -93,6 +93,10 @@ impl VerifiableGroupInfo { pub(crate) fn group_id(&self) -> &GroupId { self.payload.group_context.group_id() } + + pub(crate) fn context(&self) -> &GroupContext { + &self.payload.group_context + } } #[cfg(test)] diff --git a/openmls/src/schedule/errors.rs b/openmls/src/schedule/errors.rs index ca38629c4e..a0e7d04486 100644 --- a/openmls/src/schedule/errors.rs +++ b/openmls/src/schedule/errors.rs @@ -53,6 +53,9 @@ pub enum PskError { /// First detected duplicate. first: PreSharedKeyId, }, + /// Too many Branch or Reinit PSKs in welcome. + #[error("Only one Resumption PSK of usage Reinit and/or Branch is allowed in the welcome")] + TooManyBranchReinitResumptionPsks, } // === Crate === diff --git a/openmls/src/schedule/psk.rs b/openmls/src/schedule/psk.rs index 813f5bae64..87e3abac68 100644 --- a/openmls/src/schedule/psk.rs +++ b/openmls/src/schedule/psk.rs @@ -169,6 +169,15 @@ pub enum Psk { Resumption(ResumptionPsk), } +impl Psk { + pub(crate) fn resumption(&self) -> Option<&ResumptionPsk> { + match self { + Psk::Resumption(ref resumption) => Some(resumption), + _ => None, + } + } +} + /// ```c /// // draft-ietf-mls-protocol-19 /// enum { diff --git a/openmls/src/treesync/errors.rs b/openmls/src/treesync/errors.rs index ec8d659b4e..084857e9a6 100644 --- a/openmls/src/treesync/errors.rs +++ b/openmls/src/treesync/errors.rs @@ -233,6 +233,9 @@ pub enum LeafNodeValidationError { /// Credentials are not acceptable. #[error("Credentials are not acceptable.")] UnsupportedCredentials, + /// Ciphersuite is not acceptable. + #[error("Ciphersuite is not acceptable.")] + UnsupportedCipherSuite, /// The leaf node's credential type is not listed in the leaf node's capabilities." #[error("The leaf node's credential type is not listed in the leaf node's capabilities.")] CredentialNotInCapabilities, diff --git a/openmls/src/treesync/node/leaf_node.rs b/openmls/src/treesync/node/leaf_node.rs index d17bd5c9ce..54124f3745 100644 --- a/openmls/src/treesync/node/leaf_node.rs +++ b/openmls/src/treesync/node/leaf_node.rs @@ -400,6 +400,14 @@ impl LeafNode { } Ok(()) } + + /// Checks if leaf node supports the given ciphersuite. + pub(crate) fn supports_ciphersuite(&self, ciphersuite: Ciphersuite) -> bool { + self.payload + .capabilities + .ciphersuites() + .contains(&ciphersuite.into()) + } } #[cfg(test)] From eaecb4242b32aed9b4ba42eb8a68203c0626e8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Augusto=20C=C3=A9sar=20Dias?= Date: Wed, 7 Jun 2023 15:36:15 +0200 Subject: [PATCH 35/54] fix: return specialized error for very old epochs --- openmls/src/framing/validation.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openmls/src/framing/validation.rs b/openmls/src/framing/validation.rs index e549521496..a339c206e5 100644 --- a/openmls/src/framing/validation.rs +++ b/openmls/src/framing/validation.rs @@ -108,11 +108,11 @@ impl DecryptedMessage { // Revisit when the transition is further along. let (message_secrets, _old_leaves) = group .message_secrets_and_leaves_mut(ciphertext.epoch()) - .map_err(|_| MessageDecryptionError::AeadError)?; + .map_err(MessageDecryptionError::from)?; let sender_data = ciphertext.sender_data(message_secrets, backend, ciphersuite)?; let message_secrets = group .message_secrets_mut(ciphertext.epoch()) - .map_err(|_| MessageDecryptionError::AeadError)?; + .map_err(MessageDecryptionError::from)?; let verifiable_content = ciphertext.to_verifiable_content( ciphersuite, backend, From 04af534da4f6d24d392b284b5d21338079e48023 Mon Sep 17 00:00:00 2001 From: beltram Date: Thu, 8 Jun 2023 14:48:55 +0200 Subject: [PATCH 36/54] fix: x509 cert time validation was not compatible with WASM --- openmls/Cargo.toml | 3 +-- traits/src/types.rs | 1 + x509_credential/Cargo.toml | 10 ++-------- x509_credential/src/lib.rs | 10 +++++----- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index d3d74c7eef..f7b40b7631 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -19,7 +19,7 @@ backtrace = "0.3" hex = "0.4" async-trait = { workspace = true } openmls_basic_credential = { version = "0.1.0", path = "../basic_credential", features = ["clonable", "test-utils"] } -openmls_x509_credential = { version = "0.1.0", path = "../x509_credential", features = ["test-utils"] } +openmls_x509_credential = { version = "0.1.0", path = "../x509_credential" } x509-cert = "0.2" subtle = "2.5" fluvio-wasm-timer = "0.2" @@ -48,7 +48,6 @@ test-utils = [ "dep:rstest", "dep:rstest_reuse", "openmls_basic_credential/test-utils", - "openmls_x509_credential/test-utils", ] crypto-debug = [] # ☣️ Enable logging of sensitive cryptographic information content-debug = [] # ☣️ Enable logging of sensitive message content diff --git a/traits/src/types.rs b/traits/src/types.rs index 3803c2f69b..f74cc7c8dc 100644 --- a/traits/src/types.rs +++ b/traits/src/types.rs @@ -156,6 +156,7 @@ pub enum CryptoError { CertificateEncodingError, IncompleteCertificate(&'static str), InvalidCertificate, + TimeError, } impl std::fmt::Display for CryptoError { diff --git a/x509_credential/Cargo.toml b/x509_credential/Cargo.toml index 0477b49b88..2c9475c5a5 100644 --- a/x509_credential/Cargo.toml +++ b/x509_credential/Cargo.toml @@ -12,15 +12,9 @@ tls_codec = { workspace = true } async-trait = { workspace = true } serde = "1.0" openmls_basic_credential = { version = "0.1.0", path = "../basic_credential" } +fluvio-wasm-timer = "0.2" # Rust Crypto secrecy = { version = "0.8", features = ["serde"] } x509-cert = "0.2" -oid-registry = "0.6" - -[dev-dependencies] -rcgen = "0.10" -serde_json = "1.0" - -[features] -test-utils = [] # Only use for tests! +oid-registry = "0.6" \ No newline at end of file diff --git a/x509_credential/src/lib.rs b/x509_credential/src/lib.rs index 9d0526f40f..68e422a793 100644 --- a/x509_credential/src/lib.rs +++ b/x509_credential/src/lib.rs @@ -116,12 +116,12 @@ impl X509Ext for Certificate { not_before, not_after, } = self.tbs_certificate.validity; - let x509_cert::time::Validity { - not_before: now, .. - } = x509_cert::time::Validity::from_now(core::time::Duration::default()) - .map_err(|_| CryptoError::CryptoLibraryError)?; - let now = now.to_unix_duration(); + let now = fluvio_wasm_timer::SystemTime::now(); + let now = now + .duration_since(fluvio_wasm_timer::UNIX_EPOCH) + .map_err(|_| CryptoError::TimeError)?; + let is_nbf = now > not_before.to_unix_duration(); let is_naf = now < not_after.to_unix_duration(); Ok(is_nbf && is_naf) From 3a03087d760d3b7328c41e660f6759f76f567fa9 Mon Sep 17 00:00:00 2001 From: beltram Date: Thu, 8 Jun 2023 16:08:42 +0200 Subject: [PATCH 37/54] test: fix `test_past_secrets_in_group` failing tests --- openmls/src/group/tests/test_past_secrets.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openmls/src/group/tests/test_past_secrets.rs b/openmls/src/group/tests/test_past_secrets.rs index 7a3a639b65..52afcca41f 100644 --- a/openmls/src/group/tests/test_past_secrets.rs +++ b/openmls/src/group/tests/test_past_secrets.rs @@ -7,6 +7,7 @@ use rstest::*; use rstest_reuse::{self, *}; use super::utils::{generate_credential_with_key, generate_key_package}; +use crate::prelude::SecretTreeError; use crate::{ framing::{MessageDecryptionError, ProcessedMessageContent}, group::{config::CryptoConfig, errors::*, *}, @@ -145,7 +146,7 @@ async fn test_past_secrets_in_group( assert_eq!( err, ProcessMessageError::ValidationError(ValidationError::UnableToDecrypt( - MessageDecryptionError::AeadError + MessageDecryptionError::SecretTreeError(SecretTreeError::TooDistantInThePast) ),) ); } From b48b685ae630c6f9ec048a7b998b2da5ce0c66bf Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Thu, 8 Jun 2023 18:14:33 +0200 Subject: [PATCH 38/54] fix: Stop issuing full commits when not needed --- openmls/src/group/core_group/create_commit_params.rs | 2 +- openmls/src/group/tests/test_group.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/openmls/src/group/core_group/create_commit_params.rs b/openmls/src/group/core_group/create_commit_params.rs index d30a52a8b5..76cdc5f3f0 100644 --- a/openmls/src/group/core_group/create_commit_params.rs +++ b/openmls/src/group/core_group/create_commit_params.rs @@ -55,7 +55,7 @@ impl<'a> TempBuilderCCPM1<'a> { framing_parameters: self.framing_parameters, proposal_store, inline_proposals: vec![], - force_self_update: true, + force_self_update: false, commit_type: CommitType::Member, credential_with_key: None, }, diff --git a/openmls/src/group/tests/test_group.rs b/openmls/src/group/tests/test_group.rs index eabf350d54..c7fddfe64f 100644 --- a/openmls/src/group/tests/test_group.rs +++ b/openmls/src/group/tests/test_group.rs @@ -76,6 +76,7 @@ async fn create_commit_optional_path( let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) .proposal_store(&proposal_store) + .force_self_update(true) .build(); let create_commit_result = match group_alice .create_commit( @@ -115,7 +116,6 @@ async fn create_commit_optional_path( let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) .proposal_store(&proposal_store) - .force_self_update(false) .build(); let create_commit_result = match group_alice .create_commit(params, backend, &alice_credential_with_keys.signer) @@ -202,7 +202,6 @@ async fn create_commit_optional_path( let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) .proposal_store(&proposal_store) - .force_self_update(false) .build(); let create_commit_result = match group_alice .create_commit(params, backend, &alice_credential_with_keys.signer) From 3cdea564ae5c940e7c3a3e58a7f0fbbbd91b9504 Mon Sep 17 00:00:00 2001 From: beltram Date: Fri, 9 Jun 2023 10:16:06 +0200 Subject: [PATCH 39/54] fix: update commit were not updating the keys --- openmls/src/group/core_group/create_commit_params.rs | 5 ++++- openmls/src/group/mls_group/updates.rs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/openmls/src/group/core_group/create_commit_params.rs b/openmls/src/group/core_group/create_commit_params.rs index 76cdc5f3f0..60dcfcef70 100644 --- a/openmls/src/group/core_group/create_commit_params.rs +++ b/openmls/src/group/core_group/create_commit_params.rs @@ -68,19 +68,22 @@ impl<'a> CreateCommitParamsBuilder<'a> { self.ccp.inline_proposals = inline_proposals; self } - #[cfg(test)] + pub(crate) fn force_self_update(mut self, force_self_update: bool) -> Self { self.ccp.force_self_update = force_self_update; self } + pub(crate) fn commit_type(mut self, commit_type: CommitType) -> Self { self.ccp.commit_type = commit_type; self } + pub(crate) fn credential_with_key(mut self, credential_with_key: CredentialWithKey) -> Self { self.ccp.credential_with_key = Some(credential_with_key); self } + pub(crate) fn build(self) -> CreateCommitParams<'a> { self.ccp } diff --git a/openmls/src/group/mls_group/updates.rs b/openmls/src/group/mls_group/updates.rs index 0e78269fbc..4d1a54e4e6 100644 --- a/openmls/src/group/mls_group/updates.rs +++ b/openmls/src/group/mls_group/updates.rs @@ -34,6 +34,7 @@ impl MlsGroup { let params = CreateCommitParams::builder() .framing_parameters(self.framing_parameters()) .proposal_store(&self.proposal_store) + .force_self_update(true) .build(); // Create Commit over all proposals. // TODO #751 From 635dc8335c488e410260084e978857404fe6d9ce Mon Sep 17 00:00:00 2001 From: beltram Date: Fri, 9 Jun 2023 17:02:10 +0200 Subject: [PATCH 40/54] fix: use latest tls_codec commits to fix a bug related to variable length encoding --- Cargo.toml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0c1990c14f..868a74700d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,5 +15,18 @@ members = [ resolver = "2" [workspace.dependencies] -tls_codec = { version = "0.3.0-pre.3", features = ["derive", "serde", "mls"] } async-trait = "0.1" + +[workspace.dependencies.tls_codec] +version = "0.3.0-pre.3" +features = ["derive", "serde", "mls"] +git = "https://github.com/RustCrypto/formats.git" +package = "tls_codec" +ref = "2fcc4599" + +[workspace.dependencies.tls_codec_derive] +version = "0.3.0-pre.3" +features = ["derive", "serde", "mls"] +git = "https://github.com/RustCrypto/formats.git" +package = "tls_codec/derive" +ref = "2fcc4599" From e7665c7f3f6c8c77ad232b28958707903cc49e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Augusto=20C=C3=A9sar=20Dias?= Date: Fri, 9 Jun 2023 11:36:31 +0200 Subject: [PATCH 41/54] feat: Add root certificates group context --- openmls/Cargo.toml | 2 +- openmls/src/credentials/mod.rs | 7 +- openmls/src/extensions/codec.rs | 9 ++- openmls/src/extensions/mod.rs | 15 +++++ .../src/extensions/trust_anchor_extension.rs | 64 +++++++++++++++++++ openmls/src/group/core_group/mod.rs | 10 +++ openmls/src/group/mls_group/config.rs | 17 +++++ openmls/src/group/mls_group/creation.rs | 1 + openmls/src/group/public_group/builder.rs | 29 +++++++-- openmls/src/group/tests/test_past_secrets.rs | 3 +- 10 files changed, 144 insertions(+), 13 deletions(-) create mode 100644 openmls/src/extensions/trust_anchor_extension.rs diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index f7b40b7631..950fddd3ef 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -33,7 +33,7 @@ itertools = { version = "0.10", optional = true } openmls_rust_crypto = { version = "0.1.0", path = "../openmls_rust_crypto", optional = true } rstest = { version = "^0.16", optional = true } rstest_reuse = { version = "0.4", optional = true } -tokio = { version = "1.24", optional = true, features = ["macros", "rt"] } +tokio = { version = "1.24", optional = true, features = ["macros", "rt", "rt-multi-thread"] } [features] default = [] diff --git a/openmls/src/credentials/mod.rs b/openmls/src/credentials/mod.rs index 585e8705e8..d557b30fab 100644 --- a/openmls/src/credentials/mod.rs +++ b/openmls/src/credentials/mod.rs @@ -163,11 +163,16 @@ impl Certificate { /// MlsCredentialType. /// /// This enum contains variants containing the different available credentials. -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[derive( + Debug, PartialEq, Eq, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, +)] +#[repr(u8)] pub enum MlsCredentialType { /// A [`BasicCredential`] + #[tls_codec(discriminant = 1)] Basic(BasicCredential), /// An X.509 [`Certificate`] + #[tls_codec(discriminant = 2)] X509(Certificate), } diff --git a/openmls/src/extensions/codec.rs b/openmls/src/extensions/codec.rs index a254eee176..6cfd75652b 100644 --- a/openmls/src/extensions/codec.rs +++ b/openmls/src/extensions/codec.rs @@ -4,8 +4,8 @@ use tls_codec::{Deserialize, Serialize, Size, VLBytes}; use crate::extensions::{ ApplicationIdExtension, Extension, ExtensionType, ExternalPubExtension, - ExternalSendersExtension, RatchetTreeExtension, RequiredCapabilitiesExtension, - UnknownExtension, + ExternalSendersExtension, PerDomainTrustAnchorsExtension, RatchetTreeExtension, + RequiredCapabilitiesExtension, UnknownExtension, }; fn vlbytes_len_len(length: usize) -> usize { @@ -34,6 +34,7 @@ impl Size for Extension { Extension::RequiredCapabilities(e) => e.tls_serialized_len(), Extension::ExternalPub(e) => e.tls_serialized_len(), Extension::ExternalSenders(e) => e.tls_serialized_len(), + Extension::PerDomainTrustAnchor(e) => e.tls_serialized_len(), Extension::Unknown(_, e) => e.0.len(), }; @@ -65,6 +66,7 @@ impl Serialize for Extension { Extension::RequiredCapabilities(e) => e.tls_serialize(&mut extension_data), Extension::ExternalPub(e) => e.tls_serialize(&mut extension_data), Extension::ExternalSenders(e) => e.tls_serialize(&mut extension_data), + Extension::PerDomainTrustAnchor(e) => e.tls_serialize(&mut extension_data), Extension::Unknown(_, e) => extension_data .write_all(e.0.as_slice()) .map(|_| e.0.len()) @@ -111,6 +113,9 @@ impl Deserialize for Extension { ExtensionType::ExternalSenders => Extension::ExternalSenders( ExternalSendersExtension::tls_deserialize(&mut extension_data)?, ), + ExtensionType::PerDomainTrustAnchor => Extension::PerDomainTrustAnchor( + PerDomainTrustAnchorsExtension::tls_deserialize(&mut extension_data)?, + ), ExtensionType::Unknown(unknown) => { Extension::Unknown(unknown, UnknownExtension(extension_data.to_vec())) } diff --git a/openmls/src/extensions/mod.rs b/openmls/src/extensions/mod.rs index 8fc165c87f..2e3ae622eb 100644 --- a/openmls/src/extensions/mod.rs +++ b/openmls/src/extensions/mod.rs @@ -33,6 +33,7 @@ mod external_pub_extension; mod external_sender_extension; mod ratchet_tree_extension; mod required_capabilities; +mod trust_anchor_extension; use errors::*; // Public @@ -46,6 +47,9 @@ pub use external_sender_extension::{ }; pub use ratchet_tree_extension::RatchetTreeExtension; pub use required_capabilities::RequiredCapabilitiesExtension; +pub use trust_anchor_extension::{ + AnchorCredentialType, PerDomainTrustAnchor, PerDomainTrustAnchorsExtension, +}; #[cfg(test)] mod test_extensions; @@ -87,6 +91,10 @@ pub enum ExtensionType { /// of senders that are permitted to send external proposals to the group. ExternalSenders, + /// Group context extension to restrict the set of trust anchors used for + /// identity validation in MLS groups + PerDomainTrustAnchor, + /// A currently unknown extension type. Unknown(u16), } @@ -125,6 +133,7 @@ impl From for ExtensionType { 3 => ExtensionType::RequiredCapabilities, 4 => ExtensionType::ExternalPub, 5 => ExtensionType::ExternalSenders, + 0x000a => ExtensionType::PerDomainTrustAnchor, unknown => ExtensionType::Unknown(unknown), } } @@ -138,6 +147,7 @@ impl From for u16 { ExtensionType::RequiredCapabilities => 3, ExtensionType::ExternalPub => 4, ExtensionType::ExternalSenders => 5, + ExtensionType::PerDomainTrustAnchor => 0x000a, ExtensionType::Unknown(unknown) => unknown, } } @@ -153,6 +163,7 @@ impl ExtensionType { | ExtensionType::RequiredCapabilities | ExtensionType::ExternalPub | ExtensionType::ExternalSenders + | ExtensionType::PerDomainTrustAnchor ) } } @@ -188,6 +199,9 @@ pub enum Extension { /// A [`ExternalPubExtension`] ExternalSenders(ExternalSendersExtension), + /// A [`PerDomainTrustAnchorsExtension`] + PerDomainTrustAnchor(PerDomainTrustAnchorsExtension), + /// A currently unknown extension. Unknown(u16, UnknownExtension), } @@ -442,6 +456,7 @@ impl Extension { Extension::RequiredCapabilities(_) => ExtensionType::RequiredCapabilities, Extension::ExternalPub(_) => ExtensionType::ExternalPub, Extension::ExternalSenders(_) => ExtensionType::ExternalSenders, + Extension::PerDomainTrustAnchor(_) => ExtensionType::PerDomainTrustAnchor, Extension::Unknown(kind, _) => ExtensionType::Unknown(*kind), } } diff --git a/openmls/src/extensions/trust_anchor_extension.rs b/openmls/src/extensions/trust_anchor_extension.rs new file mode 100644 index 0000000000..08a11cc427 --- /dev/null +++ b/openmls/src/extensions/trust_anchor_extension.rs @@ -0,0 +1,64 @@ +use serde::{Deserialize, Serialize}; +use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize}; + +use crate::credentials::{errors::CredentialError, MlsCredentialType}; + +/// Each `PerDomainTrustAnchor` represents a specific identity domain which is expected +/// and authorized to participate in the MLS group. It contains the domain name and +/// the specific trust anchor used to validate identities for members in that domain. +/// ```c +/// // draft-mahy-mls-group-anchors-00 +/// struct { +/// opaque domain_name; +/// CredentialType credential_type; +/// select (Credential.credential_type) { +/// case x509: +/// Certificate chain; +/// }; +/// } PerDomainTrustAnchor; +/// ``` +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, +)] +pub struct PerDomainTrustAnchor { + domain_name: Vec, + credential_type: AnchorCredentialType, +} + +/// Extension data for the anchors +pub type PerDomainTrustAnchorsExtension = Vec; + +impl PerDomainTrustAnchor { + /// Creates a new instance of a `PerDomainTrustAnchor` + pub fn new(domain_name: Vec, credential_type: AnchorCredentialType) -> Self { + Self { + domain_name, + credential_type, + } + } +} + +/// Defines the type of cretential for the domain trust anchor. +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, +)] +pub struct AnchorCredentialType(MlsCredentialType); + +impl AnchorCredentialType { + /// Creates a new instance of `AnchorCredentialType`. Returns an error if the the credential + /// type is not supported + pub fn new(credential_type: MlsCredentialType) -> Result { + Self::try_from(credential_type) + } +} + +impl TryFrom for AnchorCredentialType { + type Error = CredentialError; + + fn try_from(value: MlsCredentialType) -> Result { + if matches!(value, MlsCredentialType::Basic(_)) { + return Err(CredentialError::UnsupportedCredentialType); + } + Ok(Self(value)) + } +} diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 4a9ef07c23..eeb1d9ea27 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -206,6 +206,16 @@ impl CoreGroupBuilder { .with_leaf_extensions(leaf_extensions); self } + /// Set the [`Extensions`] of the own leaf in [`CoreGroup`]. + pub(crate) fn with_trust_certificates( + mut self, + group_extensions: PerDomainTrustAnchorsExtension, + ) -> Self { + self.public_group_builder = self + .public_group_builder + .with_trust_certificates(group_extensions); + self + } /// Set the [`Capabilities`] of the own leaf in [`CoreGroup`]. pub(crate) fn with_leaf_capabilities(mut self, leaf_capabilities: Capabilities) -> Self { self.public_group_builder = self diff --git a/openmls/src/group/mls_group/config.rs b/openmls/src/group/mls_group/config.rs index 723c271153..499f94ef34 100644 --- a/openmls/src/group/mls_group/config.rs +++ b/openmls/src/group/mls_group/config.rs @@ -64,6 +64,8 @@ pub struct MlsGroupConfig { pub(crate) leaf_extensions: Extensions, /// Capabilities of the own leaf node pub(crate) leaf_capabilities: Option, + /// Extensions to be added to the group's context + pub(crate) trust_certificates: PerDomainTrustAnchorsExtension, } impl MlsGroupConfig { @@ -122,6 +124,11 @@ impl MlsGroupConfig { &self.leaf_extensions } + /// Returns the [`MlsGroupConfig`] group extensions configuration. + pub fn trust_certificates(&self) -> &PerDomainTrustAnchorsExtension { + &self.trust_certificates + } + #[cfg(any(feature = "test-utils", test))] pub fn test_default(ciphersuite: Ciphersuite) -> Self { Self::builder() @@ -222,6 +229,16 @@ impl MlsGroupConfigBuilder { self } + /// Sets the group's context extensions + pub fn trust_certificates( + mut self, + trust_certificates: PerDomainTrustAnchorsExtension, + ) -> Self { + self.config.trust_certificates = trust_certificates; + + self + } + /// Sets the group creator's leaf capabilities pub fn leaf_capabilities(mut self, leaf_capabilities: Capabilities) -> Self { self.config.leaf_capabilities = Some(leaf_capabilities); diff --git a/openmls/src/group/mls_group/creation.rs b/openmls/src/group/mls_group/creation.rs index ce8c06dc0b..292c452735 100644 --- a/openmls/src/group/mls_group/creation.rs +++ b/openmls/src/group/mls_group/creation.rs @@ -61,6 +61,7 @@ impl MlsGroup { .with_max_past_epoch_secrets(mls_group_config.max_past_epochs) .with_lifetime(*mls_group_config.lifetime()) .with_leaf_extensions(mls_group_config.leaf_extensions().clone()) + .with_trust_certificates(mls_group_config.trust_certificates().clone()) .with_leaf_capabilities( mls_group_config .leaf_capabilities diff --git a/openmls/src/group/public_group/builder.rs b/openmls/src/group/public_group/builder.rs index 4607ea17a1..a9071984de 100644 --- a/openmls/src/group/public_group/builder.rs +++ b/openmls/src/group/public_group/builder.rs @@ -6,7 +6,7 @@ use crate::{ error::LibraryError, extensions::{ errors::ExtensionError, Extension, Extensions, ExternalSendersExtension, - RequiredCapabilitiesExtension, + PerDomainTrustAnchorsExtension, RequiredCapabilitiesExtension, }, group::{config::CryptoConfig, GroupContext, GroupId}, key_packages::Lifetime, @@ -27,6 +27,7 @@ pub(crate) struct TempBuilderPG1 { external_senders: Option, leaf_extensions: Option, leaf_capabilities: Option, + trust_certificates: Option, } impl TempBuilderPG1 { @@ -60,6 +61,16 @@ impl TempBuilderPG1 { self } + pub(crate) fn with_trust_certificates( + mut self, + trust_certificates: PerDomainTrustAnchorsExtension, + ) -> Self { + if !trust_certificates.is_empty() { + self.trust_certificates = Some(trust_certificates); + } + self + } + pub(crate) fn with_leaf_capabilities(mut self, leaf_capabilities: Capabilities) -> Self { self.leaf_capabilities = Some(leaf_capabilities); self @@ -90,12 +101,15 @@ impl TempBuilderPG1 { _ => LibraryError::custom("Unexpected ExtensionError").into(), })?; let required_capabilities = Extension::RequiredCapabilities(required_capabilities); - let extensions = - if let Some(ext_senders) = self.external_senders.map(Extension::ExternalSenders) { - vec![required_capabilities, ext_senders] - } else { - vec![required_capabilities] - }; + let mut extensions = vec![required_capabilities]; + if let Some(ext_senders) = self.external_senders.map(Extension::ExternalSenders) { + extensions.push(ext_senders); + } + if let Some(trust_certificates) = + self.trust_certificates.map(Extension::PerDomainTrustAnchor) + { + extensions.push(trust_certificates); + } let group_context = GroupContext::create_initial_group_context( self.crypto_config.ciphersuite, self.group_id, @@ -176,6 +190,7 @@ impl PublicGroup { external_senders: None, leaf_extensions: None, leaf_capabilities: None, + trust_certificates: None, } } } diff --git a/openmls/src/group/tests/test_past_secrets.rs b/openmls/src/group/tests/test_past_secrets.rs index 52afcca41f..b97245fecd 100644 --- a/openmls/src/group/tests/test_past_secrets.rs +++ b/openmls/src/group/tests/test_past_secrets.rs @@ -7,9 +7,8 @@ use rstest::*; use rstest_reuse::{self, *}; use super::utils::{generate_credential_with_key, generate_key_package}; -use crate::prelude::SecretTreeError; use crate::{ - framing::{MessageDecryptionError, ProcessedMessageContent}, + framing::{errors::SecretTreeError, MessageDecryptionError, ProcessedMessageContent}, group::{config::CryptoConfig, errors::*, *}, }; From 9e5ccef866b6bf4008012f7f0c50ab6c3b4235fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Augusto=20C=C3=A9sar=20Dias?= Date: Wed, 14 Jun 2023 14:22:14 +0200 Subject: [PATCH 42/54] fix: switch to CredentialType in the PerDomainTrustActor extension --- openmls/src/extensions/mod.rs | 4 +- .../src/extensions/trust_anchor_extension.rs | 44 ++++++------------- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/openmls/src/extensions/mod.rs b/openmls/src/extensions/mod.rs index 2e3ae622eb..1613e50343 100644 --- a/openmls/src/extensions/mod.rs +++ b/openmls/src/extensions/mod.rs @@ -47,9 +47,7 @@ pub use external_sender_extension::{ }; pub use ratchet_tree_extension::RatchetTreeExtension; pub use required_capabilities::RequiredCapabilitiesExtension; -pub use trust_anchor_extension::{ - AnchorCredentialType, PerDomainTrustAnchor, PerDomainTrustAnchorsExtension, -}; +pub use trust_anchor_extension::{PerDomainTrustAnchor, PerDomainTrustAnchorsExtension}; #[cfg(test)] mod test_extensions; diff --git a/openmls/src/extensions/trust_anchor_extension.rs b/openmls/src/extensions/trust_anchor_extension.rs index 08a11cc427..9cbedd10bc 100644 --- a/openmls/src/extensions/trust_anchor_extension.rs +++ b/openmls/src/extensions/trust_anchor_extension.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize}; -use crate::credentials::{errors::CredentialError, MlsCredentialType}; +use crate::credentials::{errors::CredentialError, CredentialType}; /// Each `PerDomainTrustAnchor` represents a specific identity domain which is expected /// and authorized to participate in the MLS group. It contains the domain name and @@ -22,7 +22,8 @@ use crate::credentials::{errors::CredentialError, MlsCredentialType}; )] pub struct PerDomainTrustAnchor { domain_name: Vec, - credential_type: AnchorCredentialType, + credential_type: CredentialType, + certificate_chain: Vec>, } /// Extension data for the anchors @@ -30,35 +31,18 @@ pub type PerDomainTrustAnchorsExtension = Vec; impl PerDomainTrustAnchor { /// Creates a new instance of a `PerDomainTrustAnchor` - pub fn new(domain_name: Vec, credential_type: AnchorCredentialType) -> Self { - Self { - domain_name, - credential_type, - } - } -} - -/// Defines the type of cretential for the domain trust anchor. -#[derive( - Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, -)] -pub struct AnchorCredentialType(MlsCredentialType); - -impl AnchorCredentialType { - /// Creates a new instance of `AnchorCredentialType`. Returns an error if the the credential - /// type is not supported - pub fn new(credential_type: MlsCredentialType) -> Result { - Self::try_from(credential_type) - } -} - -impl TryFrom for AnchorCredentialType { - type Error = CredentialError; - - fn try_from(value: MlsCredentialType) -> Result { - if matches!(value, MlsCredentialType::Basic(_)) { + pub fn new( + domain_name: Vec, + credential_type: CredentialType, + certificate_chain: Vec>, + ) -> Result { + if credential_type == CredentialType::Basic { return Err(CredentialError::UnsupportedCredentialType); } - Ok(Self(value)) + Ok(Self { + domain_name, + credential_type, + certificate_chain, + }) } } From d4151a255232584bdde5e758644e4664487f24e9 Mon Sep 17 00:00:00 2001 From: beltram Date: Wed, 14 Jun 2023 14:56:37 +0200 Subject: [PATCH 43/54] chore: use pattern matching in PerDomainTrustAnchor constructor --- openmls/src/extensions/trust_anchor_extension.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/openmls/src/extensions/trust_anchor_extension.rs b/openmls/src/extensions/trust_anchor_extension.rs index 9cbedd10bc..9f707d214f 100644 --- a/openmls/src/extensions/trust_anchor_extension.rs +++ b/openmls/src/extensions/trust_anchor_extension.rs @@ -36,13 +36,15 @@ impl PerDomainTrustAnchor { credential_type: CredentialType, certificate_chain: Vec>, ) -> Result { - if credential_type == CredentialType::Basic { - return Err(CredentialError::UnsupportedCredentialType); + match credential_type { + CredentialType::X509 => Ok(Self { + domain_name, + credential_type, + certificate_chain, + }), + CredentialType::Basic | CredentialType::Unknown(_) => { + Err(CredentialError::UnsupportedCredentialType) + } } - Ok(Self { - domain_name, - credential_type, - certificate_chain, - }) } } From 58430d3c67f6a1a3214a6eae004e943f43f62d34 Mon Sep 17 00:00:00 2001 From: beltram Date: Wed, 14 Jun 2023 15:01:59 +0200 Subject: [PATCH 44/54] fix: remove tls discriminants from `MlsCredentialType` --- openmls/src/credentials/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/openmls/src/credentials/mod.rs b/openmls/src/credentials/mod.rs index d557b30fab..de82c53555 100644 --- a/openmls/src/credentials/mod.rs +++ b/openmls/src/credentials/mod.rs @@ -169,10 +169,8 @@ impl Certificate { #[repr(u8)] pub enum MlsCredentialType { /// A [`BasicCredential`] - #[tls_codec(discriminant = 1)] Basic(BasicCredential), /// An X.509 [`Certificate`] - #[tls_codec(discriminant = 2)] X509(Certificate), } From 83499a6fb07a679cad60c11768623d9d99c740f5 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Mon, 19 Jun 2023 11:42:27 +0200 Subject: [PATCH 45/54] feat: Added support for X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519 ciphersuite --- openmls/Cargo.toml | 2 + openmls/src/group/mls_group/test_mls_group.rs | 8 +- openmls/src/group/tests/test_encoding.rs | 2 + openmls/src/group/tests/utils.rs | 2 + openmls/src/test_utils/mod.rs | 4 + .../src/test_utils/test_framework/client.rs | 26 +++-- openmls/src/test_utils/test_framework/mod.rs | 109 ++++++++---------- .../treesync/node/leaf_node/capabilities.rs | 1 + openmls/tests/test_decryption_key_index.rs | 2 +- openmls/tests/test_interop_scenarios.rs | 16 ++- openmls/tests/test_managed_api.rs | 3 +- openmls_rust_crypto/Cargo.toml | 6 +- openmls_rust_crypto/src/provider.rs | 49 +++++++- traits/src/types.rs | 29 ++++- 14 files changed, 169 insertions(+), 90 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 950fddd3ef..d926d3e11f 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -31,6 +31,7 @@ serde_json = { version = "1.0", optional = true } # Crypto backends required for KAT and testing - "test-utils" feature itertools = { version = "0.10", optional = true } openmls_rust_crypto = { version = "0.1.0", path = "../openmls_rust_crypto", optional = true } +async-lock = { version = "2.7", optional = true } rstest = { version = "^0.16", optional = true } rstest_reuse = { version = "0.4", optional = true } tokio = { version = "1.24", optional = true, features = ["macros", "rt", "rt-multi-thread"] } @@ -47,6 +48,7 @@ test-utils = [ "dep:tokio", "dep:rstest", "dep:rstest_reuse", + "dep:async-lock", "openmls_basic_credential/test-utils", ] crypto-debug = [] # ☣️ Enable logging of sensitive cryptographic information diff --git a/openmls/src/group/mls_group/test_mls_group.rs b/openmls/src/group/mls_group/test_mls_group.rs index 91a02c333e..919e1d530d 100644 --- a/openmls/src/group/mls_group/test_mls_group.rs +++ b/openmls/src/group/mls_group/test_mls_group.rs @@ -279,7 +279,7 @@ async fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMls .create_random_group(10, ciphersuite) .await .expect("An unexpected error occurred."); - let mut groups = setup.groups.write().expect("An unexpected error occurred."); + let mut groups = setup.groups.write().await; let group = groups .get_mut(&group_id) .expect("An unexpected error occurred."); @@ -289,12 +289,12 @@ async fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMls .find(|(index, _)| index == &0) .expect("An unexpected error occurred."); - let clients = setup.clients.read().expect("An unexpected error occurred."); + let clients = setup.clients.read().await; let client = clients .get(client_id) .expect("An unexpected error occurred.") .read() - .expect("An unexpected error occurred."); + .await; let (mls_message, _welcome_option, _group_info) = client .self_update(Commit, &group_id, None) @@ -302,7 +302,7 @@ async fn test_invalid_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMls .expect("error creating self update"); // Store the context and membership key so that we can re-compute the membership tag later. - let client_groups = client.groups.read().unwrap(); + let client_groups = client.groups.read().await; let client_group = client_groups.get(&group_id).unwrap(); let membership_key = client_group.group().message_secrets().membership_key(); diff --git a/openmls/src/group/tests/test_encoding.rs b/openmls/src/group/tests/test_encoding.rs index 1b3f1a3c26..9c59a52e58 100644 --- a/openmls/src/group/tests/test_encoding.rs +++ b/openmls/src/group/tests/test_encoding.rs @@ -1,3 +1,5 @@ +#![allow(clippy::await_holding_refcell_ref)] + use openmls_rust_crypto::OpenMlsRustCrypto; use openmls_traits::crypto::OpenMlsCrypto; use tls_codec::{Deserialize, Serialize}; diff --git a/openmls/src/group/tests/utils.rs b/openmls/src/group/tests/utils.rs index c86bda81e9..9152ac6d53 100644 --- a/openmls/src/group/tests/utils.rs +++ b/openmls/src/group/tests/utils.rs @@ -4,6 +4,8 @@ //! Most tests require to set up groups, clients, credentials, and identities. //! This module implements helpers to do that. +#![allow(clippy::await_holding_refcell_ref)] + use std::{cell::RefCell, collections::HashMap}; use config::CryptoConfig; diff --git a/openmls/src/test_utils/mod.rs b/openmls/src/test_utils/mod.rs index b8706b4dd4..d8507a2bd0 100644 --- a/openmls/src/test_utils/mod.rs +++ b/openmls/src/test_utils/mod.rs @@ -231,6 +231,9 @@ pub async fn backends(backend: &impl OpenMlsCryptoProvider) {} ), case::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519( Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 + ), + case::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519( + Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519 ) )] #[allow(non_snake_case)] @@ -245,6 +248,7 @@ pub async fn ciphersuites(ciphersuite: Ciphersuite) {} case::rust_crypto_MLS_128_DHKEMP256_AES128GCM_SHA256_P256(Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, &OpenMlsRustCrypto::default()), case::rust_crypto_MLS_256_DHKEMP384_AES256GCM_SHA384_P384(Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, &OpenMlsRustCrypto::default()), case::rust_crypto_MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519(Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, &OpenMlsRustCrypto::default()), + case::rust_crypto_MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519(Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519, &OpenMlsRustCrypto::default()), ) ] #[allow(non_snake_case)] diff --git a/openmls/src/test_utils/test_framework/client.rs b/openmls/src/test_utils/test_framework/client.rs index 2b615c252d..5dbd9b5926 100644 --- a/openmls/src/test_utils/test_framework/client.rs +++ b/openmls/src/test_utils/test_framework/client.rs @@ -1,7 +1,8 @@ //! This module provides the `Client` datastructure, which contains the state //! associated with a client in the context of MLS, along with functions to have //! that client perform certain MLS operations. -use std::{collections::HashMap, sync::RwLock}; +use async_lock::RwLock; +use std::collections::HashMap; use openmls_basic_credential::SignatureKeyPair; use openmls_rust_crypto::OpenMlsRustCrypto; @@ -110,7 +111,7 @@ impl Client { let group_id = group_state.group_id().clone(); self.groups .write() - .expect("An unexpected error occurred.") + .await .insert(group_state.group_id().clone(), group_state); Ok(group_id) } @@ -130,7 +131,7 @@ impl Client { .await?; self.groups .write() - .expect("An unexpected error occurred.") + .await .insert(new_group.group_id().to_owned(), new_group); Ok(()) } @@ -143,7 +144,7 @@ impl Client { message: &ProtocolMessage, sender_id: &[u8], ) -> Result<(), ClientError> { - let mut group_states = self.groups.write().expect("An unexpected error occurred."); + let mut group_states = self.groups.write().await; let group_id = message.group_id(); let group_state = group_states .get_mut(group_id) @@ -184,8 +185,11 @@ impl Client { /// Get the credential and the index of each group member of the group with /// the given id. Returns an error if no group exists with the given group /// id. - pub fn get_members_of_group(&self, group_id: &GroupId) -> Result, ClientError> { - let groups = self.groups.read().expect("An unexpected error occurred."); + pub async fn get_members_of_group( + &self, + group_id: &GroupId, + ) -> Result, ClientError> { + let groups = self.groups.read().await; let group = groups.get(group_id).ok_or(ClientError::NoMatchingGroup)?; let members = group.members().collect(); Ok(members) @@ -203,7 +207,7 @@ impl Client { group_id: &GroupId, leaf_node: Option, ) -> Result<(MlsMessageOut, Option, Option), ClientError> { - let mut groups = self.groups.write().expect("An unexpected error occurred."); + let mut groups = self.groups.write().await; let group = groups .get_mut(group_id) .ok_or(ClientError::NoMatchingGroup)?; @@ -243,7 +247,7 @@ impl Client { group_id: &GroupId, key_packages: &[KeyPackage], ) -> Result<(Vec, Option, Option), ClientError> { - let mut groups = self.groups.write().expect("An unexpected error occurred."); + let mut groups = self.groups.write().await; let group = groups .get_mut(group_id) .ok_or(ClientError::NoMatchingGroup)?; @@ -294,7 +298,7 @@ impl Client { group_id: &GroupId, targets: &[LeafNodeIndex], ) -> Result<(Vec, Option, Option), ClientError> { - let mut groups = self.groups.write().expect("An unexpected error occurred."); + let mut groups = self.groups.write().await; let group = groups .get_mut(group_id) .ok_or(ClientError::NoMatchingGroup)?; @@ -329,8 +333,8 @@ impl Client { } /// Get the identity of this client in the given group. - pub fn identity(&self, group_id: &GroupId) -> Option> { - let groups = self.groups.read().unwrap(); + pub async fn identity(&self, group_id: &GroupId) -> Option> { + let groups = self.groups.read().await; let group = groups.get(group_id).unwrap(); group.own_identity().map(|s| s.to_vec()) } diff --git a/openmls/src/test_utils/test_framework/mod.rs b/openmls/src/test_utils/test_framework/mod.rs index 2b6b3aed4d..c48f498bef 100644 --- a/openmls/src/test_utils/test_framework/mod.rs +++ b/openmls/src/test_utils/test_framework/mod.rs @@ -32,6 +32,7 @@ use crate::{ treesync::{node::Node, LeafNode, RatchetTree, RatchetTreeIn}, }; use ::rand::{rngs::OsRng, RngCore}; +use async_lock::RwLock; use openmls_basic_credential::SignatureKeyPair; use openmls_rust_crypto::OpenMlsRustCrypto; use openmls_traits::{ @@ -41,7 +42,7 @@ use openmls_traits::{ types::{Ciphersuite, HpkeKeyPair, SignatureScheme}, OpenMlsCryptoProvider, }; -use std::{collections::HashMap, sync::RwLock}; +use std::collections::HashMap; use tls_codec::*; pub mod client; @@ -197,49 +198,46 @@ impl MlsGroupTestSetup { ciphersuite: Ciphersuite, ) -> Result { let key_package = client.get_fresh_key_package(ciphersuite).await?; - self.waiting_for_welcome - .write() - .expect("An unexpected error occurred.") - .insert( - key_package - .hash_ref(client.crypto.crypto())? - .as_slice() - .to_vec(), - client.identity.clone(), - ); + self.waiting_for_welcome.write().await.insert( + key_package + .hash_ref(client.crypto.crypto())? + .as_slice() + .to_vec(), + client.identity.clone(), + ); Ok(key_package) } /// Convert an index in the tree into the corresponding identity. - pub fn identity_by_index(&self, index: usize, group: &Group) -> Option> { + pub async fn identity_by_index(&self, index: usize, group: &Group) -> Option> { let (_, id) = group .members .iter() .find(|(leaf_index, _)| index == *leaf_index) .expect("Couldn't find member at leaf index"); - let clients = self.clients.read().expect("An unexpected error occurred."); + let clients = self.clients.read().await; let client = clients .get(id) .expect("An unexpected error occurred.") .read() - .expect("An unexpected error occurred."); - client.identity(&group.group_id) + .await; + client.identity(&group.group_id).await } /// Convert an identity in the tree into the corresponding key package reference. - pub fn identity_by_id(&self, id: &[u8], group: &Group) -> Option> { + pub async fn identity_by_id(&self, id: &[u8], group: &Group) -> Option> { let (_, id) = group .members .iter() .find(|(_, leaf_id)| id == leaf_id) .expect("Couldn't find member at leaf index"); - let clients = self.clients.read().expect("An unexpected error occurred."); + let clients = self.clients.read().await; let client = clients .get(id) .expect("An unexpected error occurred.") .read() - .expect("An unexpected error occurred."); - client.identity(&group.group_id) + .await; + client.identity(&group.group_id).await } /// Deliver a Welcome message to the intended recipients. It uses the given @@ -261,19 +259,19 @@ impl MlsGroupTestSetup { CodecUse::StructMessages => welcome, }; if self.use_codec == CodecUse::SerializedMessages {} - let clients = self.clients.read().expect("An unexpected error occurred."); + let clients = self.clients.read().await; for egs in welcome.secrets() { let client_id = self .waiting_for_welcome .write() - .expect("An unexpected error occurred.") + .await .remove(egs.new_member().as_slice()) .ok_or(SetupError::NoFreshKeyPackage)?; let client = clients .get(&client_id) .expect("An unexpected error occurred.") .read() - .expect("An unexpected error occurred."); + .await; client .join_group( group.group_config.clone(), @@ -308,7 +306,7 @@ impl MlsGroupTestSetup { } .into_protocol_message() .expect("Unexptected message type."); - let clients = self.clients.read().expect("An unexpected error occurred."); + let clients = self.clients.read().await; // Distribute message to all members, except to the sender in the case of application messages for member_id in group.members().filter_map(|(_index, member_id)| { if message.content_type() == ContentType::Application && member_id == sender_id { @@ -321,7 +319,7 @@ impl MlsGroupTestSetup { .get(&member_id) .expect("An unexpected error occurred.") .read() - .expect("An unexpected error occurred."); + .await; member .receive_messages_for_group(&message, sender_id) .await?; @@ -332,13 +330,14 @@ impl MlsGroupTestSetup { .get(sender_id) .expect("An unexpected error occurred.") .read() - .expect("An unexpected error occurred."); - let sender_groups = sender.groups.read().expect("An unexpected error occurred."); + .await; + let sender_groups = sender.groups.read().await; let sender_group = sender_groups .get(&group.group_id) .expect("An unexpected error occurred."); group.members = sender - .get_members_of_group(&group.group_id)? + .get_members_of_group(&group.group_id) + .await? .iter() .map( |Member { @@ -357,15 +356,15 @@ impl MlsGroupTestSetup { /// these messages to all other members. This function panics if any of the /// above tests fail. pub async fn check_group_states(&self, group: &mut Group) { - let clients = self.clients.read().expect("An unexpected error occurred."); + let clients = self.clients.read().await; let mut messages: Vec<(Vec, MlsMessageOut)> = Vec::new(); for (_, m_id) in group.members() { let m = clients .get(&m_id) .expect("An unexpected error occurred.") .read() - .expect("An unexpected error occurred."); - let mut group_states = m.groups.write().expect("An unexpected error occurred."); + .await; + let mut group_states = m.groups.write().await; // Some group members may not have received their welcome messages yet. if let Some(group_state) = group_states.get_mut(&group.group_id) { assert_eq!(group_state.export_ratchet_tree(), group.public_tree); @@ -401,12 +400,12 @@ impl MlsGroupTestSetup { /// not already members of this group, this function will return an error. /// TODO #310: Make this function ensure that the given members support the /// ciphersuite of the group. - pub fn random_new_members_for_group( + pub async fn random_new_members_for_group( &self, group: &Group, number_of_members: usize, ) -> Result>, SetupError> { - let clients = self.clients.read().expect("An unexpected error occurred."); + let clients = self.clients.read().await; if number_of_members + group.members.len() > clients.len() { return Err(SetupError::NotEnoughClients); } @@ -441,7 +440,7 @@ impl MlsGroupTestSetup { /// of client ids supporting it. pub async fn create_group(&self, ciphersuite: Ciphersuite) -> Result { // Pick a random group creator. - let clients = self.clients.read().expect("An unexpected error occurred."); + let clients = self.clients.read().await; let group_creator_id = ((OsRng.next_u32() as usize) % clients.len()) .to_be_bytes() .to_vec(); @@ -449,15 +448,12 @@ impl MlsGroupTestSetup { .get(&group_creator_id) .expect("An unexpected error occurred.") .read() - .expect("An unexpected error occurred."); - let mut groups = self.groups.write().expect("An unexpected error occurred."); + .await; + let mut groups = self.groups.write().await; let group_id = group_creator .create_group(self.default_mgc.clone(), ciphersuite) .await?; - let creator_groups = group_creator - .groups - .read() - .expect("An unexpected error occurred."); + let creator_groups = group_creator.groups.read().await; let group = creator_groups .get(&group_id) .expect("An unexpected error occurred."); @@ -485,13 +481,15 @@ impl MlsGroupTestSetup { // Create the initial group. let group_id = self.create_group(ciphersuite).await?; - let mut groups = self.groups.write().expect("An unexpected error occurred."); + let mut groups = self.groups.write().await; let group = groups .get_mut(&group_id) .expect("An unexpected error occurred."); // Get new members to add to the group. - let mut new_members = self.random_new_members_for_group(group, target_group_size - 1)?; + let mut new_members = self + .random_new_members_for_group(group, target_group_size - 1) + .await?; // Add new members bit by bit. while !new_members.is_empty() { @@ -516,12 +514,12 @@ impl MlsGroupTestSetup { client_id: &[u8], leaf_node: Option, ) -> Result<(), SetupError> { - let clients = self.clients.read().expect("An unexpected error occurred."); + let clients = self.clients.read().await; let client = clients .get(client_id) .ok_or(SetupError::UnknownClientId)? .read() - .expect("An unexpected error occurred."); + .await; let (messages, welcome_option, _) = client .self_update(action_type, &group.group_id, leaf_node) .await?; @@ -546,12 +544,12 @@ impl MlsGroupTestSetup { adder_id: &[u8], addees: Vec>, ) -> Result<(), SetupError> { - let clients = self.clients.read().expect("An unexpected error occurred."); + let clients = self.clients.read().await; let adder = clients .get(adder_id) .ok_or(SetupError::UnknownClientId)? .read() - .expect("An unexpected error occurred."); + .await; if group .members .iter() @@ -565,7 +563,7 @@ impl MlsGroupTestSetup { .get(addee_id) .ok_or(SetupError::UnknownClientId)? .read() - .expect("An unexpected error occurred."); + .await; let key_package = self .get_fresh_key_package(&addee, group.ciphersuite) .await?; @@ -595,12 +593,12 @@ impl MlsGroupTestSetup { remover_id: &[u8], target_members: &[LeafNodeIndex], ) -> Result<(), SetupError> { - let clients = self.clients.read().expect("An unexpected error occurred."); + let clients = self.clients.read().await; let remover = clients .get(remover_id) .ok_or(SetupError::UnknownClientId)? .read() - .expect("An unexpected error occurred."); + .await; let (messages, welcome_option, _) = remover .remove_members(action_type, &group.group_id, target_members) .await?; @@ -654,7 +652,7 @@ impl MlsGroupTestSetup { let mut target_member_leaf_indices = Vec::new(); let mut target_member_identities = Vec::new(); - let clients = self.clients.read().expect("An unexpected error occurred."); + let clients = self.clients.read().await; // Get the client references, as opposed to just the member indices. println!("Removing members:"); for _ in 0..number_of_removals { @@ -678,9 +676,8 @@ impl MlsGroupTestSetup { .get(&identity) .expect("An unexpected error occurred.") .read() - .expect("An unexpected error occurred."); - let client_group = - client.groups.read().expect("An unexpected error occurred."); + .await; + let client_group = client.groups.read().await; let client_group = client_group .get(&group.group_id) .expect("An unexpected error occurred."); @@ -698,16 +695,12 @@ impl MlsGroupTestSetup { } 2 => { // First, figure out if there are clients left to add. - let clients_left = self - .clients - .read() - .expect("An unexpected error occurred.") - .len() - - group.members.len(); + let clients_left = self.clients.read().await.len() - group.members.len(); if clients_left > 0 { let number_of_adds = (((OsRng.next_u32() as usize) % clients_left) % 5) + 1; let new_member_ids = self .random_new_members_for_group(group, number_of_adds) + .await .expect("An unexpected error occurred."); println!("{action_type:?}: Adding new clients: {new_member_ids:?}"); // Have the adder add them to the group. diff --git a/openmls/src/treesync/node/leaf_node/capabilities.rs b/openmls/src/treesync/node/leaf_node/capabilities.rs index aaf18685a7..8fd6eb545b 100644 --- a/openmls/src/treesync/node/leaf_node/capabilities.rs +++ b/openmls/src/treesync/node/leaf_node/capabilities.rs @@ -203,6 +203,7 @@ pub(super) fn default_ciphersuites() -> Vec { Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, + Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519, ] } diff --git a/openmls/tests/test_decryption_key_index.rs b/openmls/tests/test_decryption_key_index.rs index 66bb084d12..b79b301acb 100644 --- a/openmls/tests/test_decryption_key_index.rs +++ b/openmls/tests/test_decryption_key_index.rs @@ -26,7 +26,7 @@ async fn decryption_key_index_computation(ciphersuite: Ciphersuite) { .create_random_group(10, ciphersuite) .await .expect("An unexpected error occurred."); - let mut groups = setup.groups.write().expect("An unexpected error occurred."); + let mut groups = setup.groups.write().await; let group = groups .get_mut(&group_id) .expect("An unexpected error occurred."); diff --git a/openmls/tests/test_interop_scenarios.rs b/openmls/tests/test_interop_scenarios.rs index 1084929eff..243b9bf5ac 100644 --- a/openmls/tests/test_interop_scenarios.rs +++ b/openmls/tests/test_interop_scenarios.rs @@ -31,7 +31,7 @@ async fn one_to_one_join(ciphersuite: Ciphersuite) { .create_group(ciphersuite) .await .expect("Error while trying to create group."); - let mut groups = setup.groups.write().expect("An unexpected error occurred."); + let mut groups = setup.groups.write().await; let group = groups .get_mut(&group_id) .expect("An unexpected error occurred."); @@ -44,6 +44,7 @@ async fn one_to_one_join(ciphersuite: Ciphersuite) { // A vector including bob's id. let bob_id = setup .random_new_members_for_group(group, 1) + .await .expect("An unexpected error occurred."); setup @@ -80,7 +81,7 @@ async fn three_party_join(ciphersuite: Ciphersuite) { .create_group(ciphersuite) .await .expect("Error while trying to create group."); - let mut groups = setup.groups.write().expect("An unexpected error occurred."); + let mut groups = setup.groups.write().await; let group = groups .get_mut(&group_id) .expect("An unexpected error occurred."); @@ -93,6 +94,7 @@ async fn three_party_join(ciphersuite: Ciphersuite) { // A vector including Bob's id. let bob_id = setup .random_new_members_for_group(group, 1) + .await .expect("An unexpected error occurred."); // Create the add commit and deliver the welcome. @@ -104,6 +106,7 @@ async fn three_party_join(ciphersuite: Ciphersuite) { // A vector including Charly's id. let charly_id = setup .random_new_members_for_group(group, 1) + .await .expect("An unexpected error occurred."); setup @@ -139,7 +142,7 @@ async fn multiple_joins(ciphersuite: Ciphersuite) { .create_group(ciphersuite) .await .expect("Error while trying to create group."); - let mut groups = setup.groups.write().expect("An unexpected error occurred."); + let mut groups = setup.groups.write().await; let group = groups .get_mut(&group_id) .expect("An unexpected error occurred."); @@ -152,6 +155,7 @@ async fn multiple_joins(ciphersuite: Ciphersuite) { // A vector including Bob's and Charly's id. let bob_charly_id = setup .random_new_members_for_group(group, 2) + .await .expect("An unexpected error occurred."); // Create the add commit and deliver the welcome. @@ -189,7 +193,7 @@ async fn update(ciphersuite: Ciphersuite) { .create_random_group(2, ciphersuite) .await .expect("Error while trying to create group."); - let mut groups = setup.groups.write().expect("An unexpected error occurred."); + let mut groups = setup.groups.write().await; let group = groups .get_mut(&group_id) .expect("An unexpected error occurred."); @@ -234,7 +238,7 @@ async fn remove(ciphersuite: Ciphersuite) { .create_random_group(2, ciphersuite) .await .expect("Error while trying to create group."); - let mut groups = setup.groups.write().expect("An unexpected error occurred."); + let mut groups = setup.groups.write().await; let group = groups .get_mut(&group_id) .expect("An unexpected error occurred."); @@ -294,7 +298,7 @@ async fn large_group_lifecycle(ciphersuite: Ciphersuite) { .create_random_group(number_of_clients, ciphersuite) .await .expect("Error while trying to create group."); - let mut groups = setup.groups.write().expect("An unexpected error occurred."); + let mut groups = setup.groups.write().await; let group = groups .get_mut(&group_id) .expect("An unexpected error occurred."); diff --git a/openmls/tests/test_managed_api.rs b/openmls/tests/test_managed_api.rs index 17635a687d..97a0a074c1 100644 --- a/openmls/tests/test_managed_api.rs +++ b/openmls/tests/test_managed_api.rs @@ -21,7 +21,7 @@ async fn test_mls_group_api(ciphersuite: Ciphersuite) { .create_random_group(3, ciphersuite) .await .expect("An unexpected error occurred."); - let mut groups = setup.groups.write().expect("An unexpected error occurred."); + let mut groups = setup.groups.write().await; let group = groups .get_mut(&group_id) .expect("An unexpected error occurred."); @@ -30,6 +30,7 @@ async fn test_mls_group_api(ciphersuite: Ciphersuite) { let (_, adder_id) = group.members().next().unwrap(); let new_members = setup .random_new_members_for_group(group, 2) + .await .expect("An unexpected error occurred."); setup .add_clients(ActionType::Commit, group, &adder_id, new_members) diff --git a/openmls_rust_crypto/Cargo.toml b/openmls_rust_crypto/Cargo.toml index 506eac8760..d569c85b33 100644 --- a/openmls_rust_crypto/Cargo.toml +++ b/openmls_rust_crypto/Cargo.toml @@ -29,6 +29,6 @@ signature = "2.1" thiserror = "1.0" [dependencies.hpke] -git = "https://github.com/rozbb/rust-hpke.git" -rev = "2867b0ae90a36f27e2c312fe741f268ad558abbd" -features = ["x25519", "p256", "p384", "serde_impls"] +git = "https://github.com/otak/rust-hpke.git" +branch = "xyber768d00" +features = ["x25519", "p256", "p384", "xyber768d00", "serde_impls"] diff --git a/openmls_rust_crypto/src/provider.rs b/openmls_rust_crypto/src/provider.rs index f9cf51491a..43b3ee7bcf 100644 --- a/openmls_rust_crypto/src/provider.rs +++ b/openmls_rust_crypto/src/provider.rs @@ -88,7 +88,8 @@ impl OpenMlsCrypto for RustCrypto { Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 | Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 - | Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => Ok(()), + | Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 + | Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519 => Ok(()), _ => Err(CryptoError::UnsupportedCiphersuite), } } @@ -99,6 +100,7 @@ impl OpenMlsCrypto for RustCrypto { Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, + Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519, ] } @@ -419,6 +421,15 @@ impl OpenMlsCrypto for RustCrypto { hpke::kdf::HkdfSha384, hpke::kem::DhP384HkdfSha384, >(pk_r, info, aad, ptxt, &mut *rng), + HpkeConfig( + HpkeKemType::X25519Kyber768Draft00, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_seal::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::X25519Kyber768Draft00, + >(pk_r, info, aad, ptxt, &mut *rng), _ => Err(CryptoError::UnsupportedKem), } } @@ -492,6 +503,21 @@ impl OpenMlsCrypto for RustCrypto { aad, input.ciphertext.as_slice(), )?, + HpkeConfig( + HpkeKemType::X25519Kyber768Draft00, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_open::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::X25519Kyber768Draft00, + >( + sk_r, + input.kem_output.as_slice(), + info, + aad, + input.ciphertext.as_slice(), + )?, _ => return Err(CryptoError::UnsupportedKem), }; @@ -548,6 +574,15 @@ impl OpenMlsCrypto for RustCrypto { hpke::kdf::HkdfSha384, hpke::kem::DhP384HkdfSha384, >(pk_r, info, exporter_context, exporter_length, &mut *rng)?, + HpkeConfig( + HpkeKemType::X25519Kyber768Draft00, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_export_tx::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::X25519Kyber768Draft00, + >(pk_r, info, exporter_context, exporter_length, &mut *rng)?, _ => return Err(CryptoError::UnsupportedKem), }; @@ -602,6 +637,15 @@ impl OpenMlsCrypto for RustCrypto { hpke::kdf::HkdfSha384, hpke::kem::DhP384HkdfSha384, >(enc, sk_r, info, exporter_context, exporter_length)?, + HpkeConfig( + HpkeKemType::X25519Kyber768Draft00, + HpkeKdfType::HkdfSha256, + HpkeAeadType::AesGcm128, + ) => hpke_core::hpke_export_rx::< + hpke::aead::AesGcm128, + hpke::kdf::HkdfSha256, + hpke::kem::X25519Kyber768Draft00, + >(enc, sk_r, info, exporter_context, exporter_length)?, _ => return Err(CryptoError::UnsupportedKem), }; @@ -625,6 +669,9 @@ impl OpenMlsCrypto for RustCrypto { HpkeKemType::DhKem25519 => { hpke_core::hpke_derive_keypair::(ikm) } + HpkeKemType::X25519Kyber768Draft00 => { + hpke_core::hpke_derive_keypair::(ikm) + } _ => Err(CryptoError::UnsupportedKem), } } diff --git a/traits/src/types.rs b/traits/src/types.rs index f74cc7c8dc..39edad497d 100644 --- a/traits/src/types.rs +++ b/traits/src/types.rs @@ -191,6 +191,9 @@ pub enum HpkeKemType { /// DH KEM on x448 DhKem448 = 0x0021, + + /// Hybrid x25519Kyber768Draft00 KEM + X25519Kyber768Draft00 = 0x0030, } /// KDF Types for HPKE @@ -357,6 +360,9 @@ pub enum Ciphersuite { /// DH KEM P384 | AES-GCM 256 | SHA2-384 | EcDSA P384 MLS_256_DHKEMP384_AES256GCM_SHA384_P384 = 0x0007, + + /// x25519Kyber768Draft00 Hybrid KEM | AES-GCM 128 | SHA2-256 | Ed25519 + MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519 = 0xF031, // 0xF000 + 0x0001 + 0x0030 } impl core::fmt::Display for Ciphersuite { @@ -392,6 +398,7 @@ impl TryFrom for Ciphersuite { 0x0005 => Ok(Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521), 0x0006 => Ok(Ciphersuite::MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448), 0x0007 => Ok(Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384), + 0xF031 => Ok(Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519), _ => Err(Self::Error::DecodingError(format!( "{v} is not a valid ciphersuite value" ))), @@ -448,7 +455,8 @@ impl Ciphersuite { match self { Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 | Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 - | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => { + | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 + | Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519 => { HashType::Sha2_256 } Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => HashType::Sha2_384, @@ -463,7 +471,8 @@ impl Ciphersuite { pub const fn signature_algorithm(&self) -> SignatureScheme { match self { Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 - | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => { + | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 + | Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519 => { SignatureScheme::ED25519 } Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 => { @@ -487,7 +496,10 @@ impl Ciphersuite { pub const fn aead_algorithm(&self) -> AeadType { match self { Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 - | Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 => AeadType::Aes128Gcm, + | Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 + | Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519 => { + AeadType::Aes128Gcm + } Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 | Ciphersuite::MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 => { AeadType::ChaCha20Poly1305 @@ -504,7 +516,8 @@ impl Ciphersuite { match self { Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 | Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 - | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => { + | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 + | Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519 => { HpkeKdfType::HkdfSha256 } Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => HpkeKdfType::HkdfSha384, @@ -529,6 +542,9 @@ impl Ciphersuite { | Ciphersuite::MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 => HpkeKemType::DhKem448, Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => HpkeKemType::DhKemP384, Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521 => HpkeKemType::DhKemP521, + Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519 => { + HpkeKemType::X25519Kyber768Draft00 + } } } @@ -537,7 +553,10 @@ impl Ciphersuite { pub const fn hpke_aead_algorithm(&self) -> HpkeAeadType { match self { Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 - | Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 => HpkeAeadType::AesGcm128, + | Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 + | Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519 => { + HpkeAeadType::AesGcm128 + } Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => { HpkeAeadType::ChaCha20Poly1305 } From bc73bf73167e3695e36cf7b68b37bb6a5b1659d7 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Wed, 21 Jun 2023 08:41:15 +0200 Subject: [PATCH 46/54] fix: Docs errors --- .github/workflows/docs.yml | 2 +- openmls/src/ciphersuite/mac.rs | 2 ++ openmls/src/ciphersuite/mod.rs | 2 +- openmls/src/framing/mod.rs | 4 ++-- openmls/src/framing/public_message_in.rs | 2 +- openmls/src/group/core_group/proposals.rs | 2 +- openmls/src/group/errors.rs | 2 +- openmls/src/lib.rs | 2 +- openmls/src/messages/mod.rs | 2 +- openmls/src/treesync/errors.rs | 2 +- openmls/src/treesync/node/leaf_node.rs | 2 +- traits/src/types.rs | 2 +- 12 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4ab323feda..89e53f65a7 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,4 +17,4 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - - run: cargo doc -p openmls --message-format json + - run: cargo doc -p openmls diff --git a/openmls/src/ciphersuite/mac.rs b/openmls/src/ciphersuite/mac.rs index ed61b0ec59..4250b90092 100644 --- a/openmls/src/ciphersuite/mac.rs +++ b/openmls/src/ciphersuite/mac.rs @@ -2,7 +2,9 @@ use super::*; /// 7.1 Content Authentication /// +/// ```ignore /// opaque MAC; +/// ``` #[derive(Debug, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize)] pub struct Mac { pub(crate) mac_value: VLBytes, diff --git a/openmls/src/ciphersuite/mod.rs b/openmls/src/ciphersuite/mod.rs index 38bd47b5c3..b74253b626 100644 --- a/openmls/src/ciphersuite/mod.rs +++ b/openmls/src/ciphersuite/mod.rs @@ -17,7 +17,7 @@ use std::hash::Hash; mod aead; mod codec; -pub(crate) mod hpke; +pub mod hpke; mod kdf_label; mod mac; mod reuse_guard; diff --git a/openmls/src/framing/mod.rs b/openmls/src/framing/mod.rs index 66b7a96b86..801975bc98 100644 --- a/openmls/src/framing/mod.rs +++ b/openmls/src/framing/mod.rs @@ -55,8 +55,8 @@ pub(crate) mod codec; pub(crate) mod message_in; pub(crate) mod message_out; -pub(crate) mod mls_auth_content; -pub(crate) mod mls_auth_content_in; +pub mod mls_auth_content; +pub mod mls_auth_content_in; pub(crate) mod mls_content; pub(crate) mod mls_content_in; pub(crate) mod private_message; diff --git a/openmls/src/framing/public_message_in.rs b/openmls/src/framing/public_message_in.rs index e695bd02fc..886367c905 100644 --- a/openmls/src/framing/public_message_in.rs +++ b/openmls/src/framing/public_message_in.rs @@ -175,7 +175,7 @@ impl PublicMessageIn { &self.content.group_id } - /// Turn this [`PublicMessageIn`] into a [`VerifiableAuthenticatedContent`]. + /// Turn this [`PublicMessageIn`] into a [`VerifiableAuthenticatedContentIn`]. pub fn into_verifiable_content( self, serialized_context: impl Into>>, diff --git a/openmls/src/group/core_group/proposals.rs b/openmls/src/group/core_group/proposals.rs index 06195e2909..c90839fdba 100644 --- a/openmls/src/group/core_group/proposals.rs +++ b/openmls/src/group/core_group/proposals.rs @@ -172,7 +172,7 @@ impl QueuedProposal { /// accessed efficiently. To enable iteration over the queue in order, the /// `ProposalQueue` also contains a vector of `ProposalRef`s. #[derive(Default, Clone, Debug, Serialize, Deserialize)] -pub(crate) struct ProposalQueue { +pub struct ProposalQueue { /// `proposal_references` holds references to the proposals in the queue and /// determines the order of the queue. proposal_references: Vec, diff --git a/openmls/src/group/errors.rs b/openmls/src/group/errors.rs index ca378c7bab..b7cc529add 100644 --- a/openmls/src/group/errors.rs +++ b/openmls/src/group/errors.rs @@ -455,7 +455,7 @@ pub enum ProposalQueueError { SenderError(#[from] SenderError), } -/// Errors that can arise when creating a [`ProposalQueue`] from committed +/// Errors that can arise when creating a [`crate::group::core_group::proposals::ProposalQueue`] from committed /// proposals. #[derive(Error, Debug, PartialEq, Clone)] pub enum FromCommittedProposalsError { diff --git a/openmls/src/lib.rs b/openmls/src/lib.rs index f3968af210..6ede86d116 100644 --- a/openmls/src/lib.rs +++ b/openmls/src/lib.rs @@ -186,7 +186,7 @@ pub mod treesync; pub mod versions; // Private -mod binary_tree; +pub mod binary_tree; mod tree; /// Single place, re-exporting the most used public functions. diff --git a/openmls/src/messages/mod.rs b/openmls/src/messages/mod.rs index a2bff79ece..b5bd58c232 100644 --- a/openmls/src/messages/mod.rs +++ b/openmls/src/messages/mod.rs @@ -367,7 +367,7 @@ impl PathSecret { /// Path secret error #[derive(Error, Debug, PartialEq, Clone)] pub enum PathSecretError { - /// See [`hpke::Error`] for more details. + /// See [`crate::ciphersuite::hpke::Error`] for more details. #[error(transparent)] DecryptionError(#[from] hpke::Error), } diff --git a/openmls/src/treesync/errors.rs b/openmls/src/treesync/errors.rs index 084857e9a6..541aa912ef 100644 --- a/openmls/src/treesync/errors.rs +++ b/openmls/src/treesync/errors.rs @@ -89,7 +89,7 @@ pub enum TreeSyncError { /// A requested leaf is not in the tree. #[error("The leaf does not exist in the tree.")] LeafNotInTree, - /// See [`TreeSyncSetPathError`] for more details. + /// See [`DerivePathError`] for more details. #[error(transparent)] SetPathError(#[from] DerivePathError), /// See [`MlsBinaryTreeError`] for more details. diff --git a/openmls/src/treesync/node/leaf_node.rs b/openmls/src/treesync/node/leaf_node.rs index 54124f3745..0a30e0516f 100644 --- a/openmls/src/treesync/node/leaf_node.rs +++ b/openmls/src/treesync/node/leaf_node.rs @@ -576,7 +576,7 @@ pub struct LeafNodeTbs { } impl LeafNodeTbs { - /// Build a [`LeafNodeTbs`] from a [`LeafNode`] and a [`TreeInfo`] + /// Build a [`LeafNodeTbs`] from a [`LeafNode`] and a [`TreeInfoTbs`] /// to update a leaf node. pub(crate) fn from(leaf_node: LeafNode, tree_info_tbs: TreeInfoTbs) -> Self { Self { diff --git a/traits/src/types.rs b/traits/src/types.rs index 39edad497d..322483a964 100644 --- a/traits/src/types.rs +++ b/traits/src/types.rs @@ -302,7 +302,7 @@ impl From> for ExporterSecret { /// A currently unknown ciphersuite. /// -/// Used to accept unknown values, e.g., in [`Capabilities`]. +/// Used to accept unknown values, e.g., in Capabilities. #[derive( Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, )] From 8b111701950d9416d0512d09a4a25cd8156b97fb Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Wed, 21 Jun 2023 11:57:51 +0200 Subject: [PATCH 47/54] fix(dep): Pin tls_codec versions --- Cargo.toml | 16 ++++++++-------- openmls/Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 868a74700d..d2931df917 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,15 +18,15 @@ resolver = "2" async-trait = "0.1" [workspace.dependencies.tls_codec] -version = "0.3.0-pre.3" +version = "0.3.0-pre.4" features = ["derive", "serde", "mls"] -git = "https://github.com/RustCrypto/formats.git" -package = "tls_codec" -ref = "2fcc4599" +# git = "https://github.com/RustCrypto/formats.git" +# package = "tls_codec" +# ref = "2fcc4599" [workspace.dependencies.tls_codec_derive] -version = "0.3.0-pre.3" +version = "0.3.0-pre.4" features = ["derive", "serde", "mls"] -git = "https://github.com/RustCrypto/formats.git" -package = "tls_codec/derive" -ref = "2fcc4599" +# git = "https://github.com/RustCrypto/formats.git" +# package = "tls_codec/derive" +# ref = "2fcc4599" diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index d926d3e11f..fe326acd9e 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls" -version = "0.20.0" +version = "0.20.2" authors = ["OpenMLS Authors"] edition = "2021" description = "This is a WIP Rust implementation of the Messaging Layer Security (MLS) protocol based on draft 20-ish." From 05c1004995f79439efa66fdc930289931544d163 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Thu, 22 Jun 2023 12:11:37 +0200 Subject: [PATCH 48/54] chore: Use stabilized tls_codec 0.3.0 --- Cargo.toml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d2931df917..6cbf1eadad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,15 +18,9 @@ resolver = "2" async-trait = "0.1" [workspace.dependencies.tls_codec] -version = "0.3.0-pre.4" +version = "0.3.0" features = ["derive", "serde", "mls"] -# git = "https://github.com/RustCrypto/formats.git" -# package = "tls_codec" -# ref = "2fcc4599" [workspace.dependencies.tls_codec_derive] -version = "0.3.0-pre.4" +version = "0.3.0" features = ["derive", "serde", "mls"] -# git = "https://github.com/RustCrypto/formats.git" -# package = "tls_codec/derive" -# ref = "2fcc4599" From 91a2f394153012ad0c92c4b85addefeb0a0ce80c Mon Sep 17 00:00:00 2001 From: beltram Date: Thu, 15 Jun 2023 13:59:40 +0200 Subject: [PATCH 49/54] feat: all methods required for updating with a LeafNode --- basic_credential/Cargo.toml | 3 + basic_credential/src/lib.rs | 67 ++++++ .../binary_tree/array_representation/diff.rs | 2 +- .../binary_tree/array_representation/tree.rs | 4 +- openmls/src/framing/mls_auth_content_in.rs | 8 + openmls/src/framing/validation.rs | 6 +- openmls/src/group/core_group/mod.rs | 18 ++ openmls/src/group/errors.rs | 4 + openmls/src/group/mls_group/creation.rs | 6 +- openmls/src/group/mls_group/proposal.rs | 18 +- openmls/src/group/mls_group/test_mls_group.rs | 2 +- openmls/src/group/mls_group/updates.rs | 204 +++++++++++++----- openmls/src/group/public_group/diff.rs | 2 +- .../group/public_group/diff/compute_path.rs | 8 + openmls/src/group/public_group/mod.rs | 5 +- openmls/src/group/public_group/process.rs | 29 +-- .../src/group/tests/external_add_proposal.rs | 2 +- .../src/group/tests/test_commit_validation.rs | 2 +- .../group/tests/test_proposal_validation.rs | 9 +- openmls/src/key_packages/errors.rs | 11 + openmls/src/key_packages/mod.rs | 9 +- .../src/test_utils/test_framework/client.rs | 20 +- openmls/src/treesync/diff.rs | 2 +- openmls/src/treesync/mod.rs | 7 +- openmls/src/treesync/node/encryption_keys.rs | 2 +- openmls/src/treesync/node/leaf_node.rs | 23 +- openmls/src/treesync/node/parent_node.rs | 5 +- openmls/src/treesync/treesync_node.rs | 2 +- openmls/tests/book_code.rs | 6 +- openmls/tests/test_mls_group.rs | 2 +- traits/src/types.rs | 2 + x509_credential/src/lib.rs | 2 +- 32 files changed, 367 insertions(+), 125 deletions(-) diff --git a/basic_credential/Cargo.toml b/basic_credential/Cargo.toml index 06347cb47d..9f1230758c 100644 --- a/basic_credential/Cargo.toml +++ b/basic_credential/Cargo.toml @@ -23,6 +23,9 @@ secrecy = { version = "0.8", features = ["serde"] } rand_core = "0.6" getrandom = { version = "0.2", features = ["js"] } +[dev-dependencies] +rand = "0.8" + [features] clonable = [] # Make the keys clonable test-utils = [] # Only use for tests! diff --git a/basic_credential/src/lib.rs b/basic_credential/src/lib.rs index dfae218859..9ea795a840 100644 --- a/basic_credential/src/lib.rs +++ b/basic_credential/src/lib.rs @@ -147,6 +147,53 @@ impl SignatureKeyPair { } } + /// Create a new KeyPair but verify that the private key actually matches the public key + pub fn try_from_raw( + signature_scheme: SignatureScheme, + private: Vec, + public: Vec, + ) -> Result { + match signature_scheme { + SignatureScheme::ED25519 => { + let sk = ed25519_dalek::SigningKey::try_from( + &private[..ed25519_dalek::SECRET_KEY_LENGTH], + ) + .map_err(|_| CryptoError::InvalidKey)?; + let pk = ed25519_dalek::VerifyingKey::try_from(&public[..]) + .map_err(|_| CryptoError::InvalidKey)?; + + if sk.verifying_key() != pk { + return Err(CryptoError::MismatchKeypair); + } + } + SignatureScheme::ECDSA_SECP256R1_SHA256 => { + let sk = p256::ecdsa::SigningKey::try_from(&private[..]) + .map_err(|_| CryptoError::InvalidKey)?; + let pk = p256::ecdsa::VerifyingKey::try_from(&public[..]) + .map_err(|_| CryptoError::InvalidKey)?; + if sk.verifying_key() != &pk { + return Err(CryptoError::MismatchKeypair); + } + } + SignatureScheme::ECDSA_SECP384R1_SHA384 => { + let sk = p384::ecdsa::SigningKey::try_from(&private[..]) + .map_err(|_| CryptoError::InvalidKey)?; + let pk = p384::ecdsa::VerifyingKey::try_from(&public[..]) + .map_err(|_| CryptoError::InvalidKey)?; + if sk.verifying_key() != &pk { + return Err(CryptoError::MismatchKeypair); + } + } + _ => {} + }; + + Ok(Self { + private: private.into(), + public, + signature_scheme, + }) + } + /// Store this signature key pair in the key store. pub async fn store(&self, key_store: &T) -> Result<(), ::Error> where @@ -180,3 +227,23 @@ impl SignatureKeyPair { self.private.expose_secret() } } + +#[cfg(test)] +pub mod tests { + use super::*; + + #[test] + fn signature_keypair_try_from_raw_should_work() { + let schemes = [ + SignatureScheme::ED25519, + SignatureScheme::ECDSA_SECP256R1_SHA256, + SignatureScheme::ECDSA_SECP384R1_SHA384, + ]; + for scheme in schemes { + let kp = SignatureKeyPair::new(scheme, &mut rand::thread_rng()).unwrap(); + let sk = kp.private.expose_secret().clone(); + let pk = kp.public.clone(); + SignatureKeyPair::try_from_raw(scheme, sk, pk).unwrap(); + } + } +} diff --git a/openmls/src/binary_tree/array_representation/diff.rs b/openmls/src/binary_tree/array_representation/diff.rs index a4d284fc50..1733819913 100644 --- a/openmls/src/binary_tree/array_representation/diff.rs +++ b/openmls/src/binary_tree/array_representation/diff.rs @@ -43,7 +43,7 @@ use super::{ /// lifetime is not tied to that of the original tree. #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct StagedAbDiff { - leaf_diff: BTreeMap, + pub(crate) leaf_diff: BTreeMap, parent_diff: BTreeMap, size: TreeSize, } diff --git a/openmls/src/binary_tree/array_representation/tree.rs b/openmls/src/binary_tree/array_representation/tree.rs index f8a8dd69df..64a4db0c5b 100644 --- a/openmls/src/binary_tree/array_representation/tree.rs +++ b/openmls/src/binary_tree/array_representation/tree.rs @@ -213,10 +213,8 @@ impl ABinaryTree { ) -> Vec { common_direct_path(leaf_index_1, leaf_index_2, self.tree_size()) } -} -#[cfg(test)] -impl ABinaryTree { + #[cfg(test)] pub(crate) fn parent(&self, parent_index: ParentNodeIndex) -> &P { self.parent_nodes .get(parent_index.usize()) diff --git a/openmls/src/framing/mls_auth_content_in.rs b/openmls/src/framing/mls_auth_content_in.rs index 06b4a0af03..c3c09e3c01 100644 --- a/openmls/src/framing/mls_auth_content_in.rs +++ b/openmls/src/framing/mls_auth_content_in.rs @@ -173,6 +173,14 @@ impl VerifiableAuthenticatedContentIn { } } + /// Returns the [`Credential`] and the [`SignaturePublicKey`] when this message is a commit with an UpdatePath + pub(crate) fn update_path_credential(&self) -> Option { + match (&self.tbs.content.sender, &self.tbs.content.body) { + (Sender::Member(_), FramedContentBodyIn::Commit(c)) => c.unverified_credential(), + _ => None, + } + } + /// Get the wire format. pub(crate) fn wire_format(&self) -> WireFormat { self.tbs.wire_format diff --git a/openmls/src/framing/validation.rs b/openmls/src/framing/validation.rs index a339c206e5..61cba2e8b3 100644 --- a/openmls/src/framing/validation.rs +++ b/openmls/src/framing/validation.rs @@ -163,8 +163,10 @@ impl DecryptedMessage { old_leaves: &[Member], external_senders: Option<&ExternalSendersExtension>, ) -> Result { - let sender = self.sender(); - match sender { + if let Some(credential) = self.verifiable_content.update_path_credential() { + return Ok(credential); + } + match self.sender() { Sender::Member(leaf_index) => { match treesync.leaf(*leaf_index) { Some(sender_leaf) => { diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index eeb1d9ea27..9450daa839 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -969,12 +969,30 @@ impl CoreGroup { || contains_own_updates || params.force_self_update() { + + // a bit chaotic for sure but too much indirection to do this cleanly + // Basically, inline proposals were not introspected for an explicit leaf_node + // this fixes exactly that + let leaf_node = if contains_own_updates { + let local_proposals = params.proposal_store().proposals().map(QueuedProposal::proposal); + let inline_proposals = params.inline_proposals().iter(); + let mut all_proposals = local_proposals.chain(inline_proposals); + + all_proposals.find_map(|p| { + match p { + Proposal::Update(UpdateProposal{leaf_node}) => Some(leaf_node.clone()), + _ => None, + } + }) + } else { None }; + // Process the path. This includes updating the provisional // group context by updating the epoch and computing the new // tree hash. diff.compute_path( backend, self.own_leaf_index(), + leaf_node, apply_proposals_values.exclusion_list(), params.commit_type(), signer, diff --git a/openmls/src/group/errors.rs b/openmls/src/group/errors.rs index b7cc529add..1a8a57d1b8 100644 --- a/openmls/src/group/errors.rs +++ b/openmls/src/group/errors.rs @@ -6,6 +6,7 @@ use thiserror::Error; pub use super::mls_group::errors::*; use super::public_group::errors::{CreationFromExternalError, PublicGroupBuildError}; +use crate::prelude::KeyPackageDeleteError; use crate::{ ciphersuite::signable::SignatureError, error::LibraryError, @@ -92,6 +93,9 @@ pub enum WelcomeError { /// Group epoch must be 0 if the the welcome message contains reinit or branch psks #[error("Group epoch must be 0 if the the welcome message contains reinit or branch psks")] InvalidEpoch, + /// Failed deleting the KeyPackage + #[error(transparent)] + KeyPackageDeleteError(#[from] KeyPackageDeleteError), } /// External Commit error diff --git a/openmls/src/group/mls_group/creation.rs b/openmls/src/group/mls_group/creation.rs index 292c452735..4825be1522 100644 --- a/openmls/src/group/mls_group/creation.rs +++ b/openmls/src/group/mls_group/creation.rs @@ -147,11 +147,7 @@ impl MlsGroup { // Delete the [`KeyPackage`] and the corresponding private key from the // key store - key_package_bundle - .key_package - .delete(backend) - .await - .map_err(WelcomeError::KeyStoreError)?; + key_package_bundle.key_package.delete(backend).await?; let mut group = CoreGroup::new_from_welcome( welcome, diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index 4ed26dabe1..041c525dba 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -140,12 +140,20 @@ impl MlsGroup { }, Propose::Update(leaf_node) => match ref_or_value { - ProposalOrRefType::Proposal => self - .propose_self_update_by_value(backend, signer, leaf_node) - .await - .map_err(|e| e.into()), + ProposalOrRefType::Proposal => { + if let Some(ln) = leaf_node { + // FIXME: this does not work since both signers are the same + self.propose_explicit_self_update(backend, signer, ln, signer) + .await + .map_err(|e| e.into()) + } else { + self.propose_self_update_by_value(backend, signer) + .await + .map_err(|e| e.into()) + } + } ProposalOrRefType::Reference => self - .propose_self_update(backend, signer, leaf_node) + .propose_self_update(backend, signer) .await .map_err(|e| e.into()), }, diff --git a/openmls/src/group/mls_group/test_mls_group.rs b/openmls/src/group/mls_group/test_mls_group.rs index 919e1d530d..ba567163a0 100644 --- a/openmls/src/group/mls_group/test_mls_group.rs +++ b/openmls/src/group/mls_group/test_mls_group.rs @@ -474,7 +474,7 @@ async fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl Open SelfUpdateError::GroupStateError(MlsGroupStateError::PendingCommit) )); let error = alice_group - .propose_self_update(backend, &alice_signer, None) + .propose_self_update(backend, &alice_signer) .await .expect_err("no error creating a proposal while a commit is pending"); assert!(matches!( diff --git a/openmls/src/group/mls_group/updates.rs b/openmls/src/group/mls_group/updates.rs index 4d1a54e4e6..cb2b610a7e 100644 --- a/openmls/src/group/mls_group/updates.rs +++ b/openmls/src/group/mls_group/updates.rs @@ -28,14 +28,61 @@ impl MlsGroup { ) -> Result< (MlsMessageOut, Option, Option), SelfUpdateError, + > { + self.explicit_self_update(backend, signer, None).await + } + + /// Like [Self::self_update] but accepts an explicit node. Mostly to rotate its credential + #[allow(clippy::type_complexity)] + pub async fn explicit_self_update( + &mut self, + backend: &impl OpenMlsCryptoProvider, + signer: &impl Signer, + leaf_node: Option, + ) -> Result< + (MlsMessageOut, Option, Option), + SelfUpdateError, > { self.is_operational()?; - let params = CreateCommitParams::builder() - .framing_parameters(self.framing_parameters()) - .proposal_store(&self.proposal_store) - .force_self_update(true) - .build(); + let parameters = self.framing_parameters(); + let builder = CreateCommitParams::builder() + .framing_parameters(parameters) + .proposal_store(&self.proposal_store); + + let params = if let Some(leaf_node) = leaf_node { + let mut own_leaf = self + .own_leaf() + .ok_or_else(|| LibraryError::custom("The tree is broken. Couldn't find own leaf."))? + .clone(); + + let keypair = own_leaf + .rekey( + self.group_id(), + self.own_leaf_index(), + Some(leaf_node), + self.ciphersuite(), + ProtocolVersion::default(), + backend, + signer, + ) + .unwrap(); + keypair + .write_to_key_store(backend) + .await + .map_err(ProposeSelfUpdateError::KeyStoreError) + .unwrap(); + + let update_proposal = Proposal::Update(UpdateProposal { + leaf_node: own_leaf, + }); + + builder.inline_proposals(vec![update_proposal]) + } else { + builder.force_self_update(true) + } + .build(); + // Create Commit over all proposals. // TODO #751 let create_commit_result = self.group.create_commit(params, backend, signer).await?; @@ -62,6 +109,52 @@ impl MlsGroup { )) } + /// Creates a proposal to update the own leaf node. + pub async fn propose_self_update( + &mut self, + backend: &impl OpenMlsCryptoProvider, + signer: &impl Signer, + ) -> Result<(MlsMessageOut, ProposalRef), ProposeSelfUpdateError> { + let update_proposal = self._propose_self_update(backend, signer).await?; + let proposal = QueuedProposal::from_authenticated_content_by_ref( + self.ciphersuite(), + backend, + update_proposal.clone(), + )?; + let proposal_ref = proposal.proposal_reference(); + self.proposal_store.add(proposal); + + let mls_message = self.content_to_mls_message(update_proposal, backend)?; + + // Since the state of the group might be changed, arm the state flag + self.flag_state_change(); + + Ok((mls_message, proposal_ref)) + } + + /// Creates a proposal to update the own leaf node. + pub async fn propose_self_update_by_value( + &mut self, + backend: &impl OpenMlsCryptoProvider, + signer: &impl Signer, + ) -> Result<(MlsMessageOut, ProposalRef), ProposeSelfUpdateError> { + let update_proposal = self._propose_self_update(backend, signer).await?; + let proposal = QueuedProposal::from_authenticated_content_by_value( + self.ciphersuite(), + backend, + update_proposal.clone(), + )?; + let proposal_ref = proposal.proposal_reference(); + self.proposal_store.add(proposal); + + let mls_message = self.content_to_mls_message(update_proposal, backend)?; + + // Since the state of the group might be changed, arm the state flag + self.flag_state_change(); + + Ok((mls_message, proposal_ref)) + } + /// Creates a proposal to update the own leaf node. Optionally, a /// [`LeafNode`] can be provided to update the leaf node. Note that its /// private key must be manually added to the key store. @@ -69,7 +162,6 @@ impl MlsGroup { &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, - leaf_node: Option, ) -> Result> { self.is_operational()?; @@ -78,34 +170,23 @@ impl MlsGroup { // The new leaf node will be applied later when the proposal is // committed. let mut own_leaf = self - .group - .public_group() - .leaf(self.own_leaf_index()) + .own_leaf() .ok_or_else(|| LibraryError::custom("The tree is broken. Couldn't find own leaf."))? .clone(); - if let Some(leaf) = leaf_node { - own_leaf.update_and_re_sign( - None, - leaf, - self.group_id().clone(), - self.own_leaf_index(), - signer, - )? - } else { - let keypair = own_leaf.rekey( - self.group_id(), - self.own_leaf_index(), - self.ciphersuite(), - ProtocolVersion::default(), // XXX: openmls/openmls#1065 - backend, - signer, - )?; - // TODO #1207: Move to the top of the function. - keypair - .write_to_key_store(backend) - .await - .map_err(ProposeSelfUpdateError::KeyStoreError)?; - }; + let keypair = own_leaf.rekey( + self.group_id(), + self.own_leaf_index(), + None, + self.ciphersuite(), + ProtocolVersion::default(), // XXX: openmls/openmls#1065 + backend, + signer, + )?; + // TODO #1207: Move to the top of the function. + keypair + .write_to_key_store(backend) + .await + .map_err(ProposeSelfUpdateError::KeyStoreError)?; let update_proposal = self.group.create_update_proposal( self.framing_parameters(), @@ -119,16 +200,17 @@ impl MlsGroup { } /// Creates a proposal to update the own leaf node. - pub async fn propose_self_update( + pub async fn propose_explicit_self_update( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, - leaf_node: Option, + leaf_node: LeafNode, + leaf_node_signer: &impl Signer, ) -> Result<(MlsMessageOut, ProposalRef), ProposeSelfUpdateError> { let update_proposal = self - ._propose_self_update(backend, signer, leaf_node) + ._propose_explicit_self_update(backend, signer, leaf_node, leaf_node_signer) .await?; - let proposal = QueuedProposal::from_authenticated_content_by_ref( + let proposal = QueuedProposal::from_authenticated_content_by_value( self.ciphersuite(), backend, update_proposal.clone(), @@ -144,29 +226,49 @@ impl MlsGroup { Ok((mls_message, proposal_ref)) } - /// Creates a proposal to update the own leaf node. - pub async fn propose_self_update_by_value( + /// Creates a proposal to update the own leaf node. Optionally, a + /// [`LeafNode`] can be provided to update the leaf node. Note that its + /// private key must be manually added to the key store. + pub(crate) async fn _propose_explicit_self_update( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, - leaf_node: Option, - ) -> Result<(MlsMessageOut, ProposalRef), ProposeSelfUpdateError> { - let update_proposal = self - ._propose_self_update(backend, signer, leaf_node) - .await?; - let proposal = QueuedProposal::from_authenticated_content_by_value( + leaf_node: LeafNode, + leaf_node_signer: &impl Signer, + ) -> Result> { + self.is_operational()?; + + // Here we clone our own leaf to rekey it such that we don't change the + // tree. + // The new leaf node will be applied later when the proposal is + // committed. + let mut own_leaf = self + .own_leaf() + .ok_or_else(|| LibraryError::custom("The tree is broken. Couldn't find own leaf."))? + .clone(); + + let keypair = own_leaf.rekey( + self.group_id(), + self.own_leaf_index(), + Some(leaf_node), self.ciphersuite(), + ProtocolVersion::default(), // XXX: openmls/openmls#1065 backend, - update_proposal.clone(), + leaf_node_signer, )?; - let proposal_ref = proposal.proposal_reference(); - self.proposal_store.add(proposal); + keypair + .write_to_key_store(backend) + .await + .map_err(ProposeSelfUpdateError::KeyStoreError)?; - let mls_message = self.content_to_mls_message(update_proposal, backend)?; + let update_proposal = self.group.create_update_proposal( + self.framing_parameters(), + own_leaf.clone(), + signer, + )?; - // Since the state of the group might be changed, arm the state flag - self.flag_state_change(); + self.own_leaf_nodes.push(own_leaf); - Ok((mls_message, proposal_ref)) + Ok(update_proposal) } } diff --git a/openmls/src/group/public_group/diff.rs b/openmls/src/group/public_group/diff.rs index 20eaecfc38..34855d8a2a 100644 --- a/openmls/src/group/public_group/diff.rs +++ b/openmls/src/group/public_group/diff.rs @@ -239,7 +239,7 @@ impl<'a> PublicGroupDiff<'a> { /// modified. Its only use is to merge it into the original [`PublicGroup`]. #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct StagedPublicGroupDiff { - pub(super) staged_diff: StagedTreeSyncDiff, + pub(crate) staged_diff: StagedTreeSyncDiff, pub(super) group_context: GroupContext, pub(super) interim_transcript_hash: Vec, pub(super) confirmation_tag: ConfirmationTag, diff --git a/openmls/src/group/public_group/diff/compute_path.rs b/openmls/src/group/public_group/diff/compute_path.rs index 66264fed5f..24140d3da0 100644 --- a/openmls/src/group/public_group/diff/compute_path.rs +++ b/openmls/src/group/public_group/diff/compute_path.rs @@ -41,6 +41,7 @@ impl<'a> PublicGroupDiff<'a> { &mut self, backend: &impl OpenMlsCryptoProvider, leaf_index: LeafNodeIndex, + mut leaf_node: Option, exclusion_list: HashSet<&LeafNodeIndex>, commit_type: CommitType, signer: &impl Signer, @@ -77,14 +78,21 @@ impl<'a> PublicGroupDiff<'a> { .map_err(|_| LibraryError::custom("Tree full: cannot add more members"))?; vec![encryption_keypair] } else { + // When we update with an explicit leaf_node consider it (instead of the old one) when computing the UpdatePath + if let Some(leaf_node) = leaf_node.take() { + self.diff.update_leaf(leaf_node, leaf_index) + } + // If we're already in the tree, we rekey our existing leaf. let own_diff_leaf = self .diff .leaf_mut(leaf_index) .ok_or_else(|| LibraryError::custom("Unable to get own leaf from diff"))?; + let encryption_keypair = own_diff_leaf.rekey( &group_id, leaf_index, + None, ciphersuite, version, backend, diff --git a/openmls/src/group/public_group/mod.rs b/openmls/src/group/public_group/mod.rs index 59eeb9d921..fd1b9baaaf 100644 --- a/openmls/src/group/public_group/mod.rs +++ b/openmls/src/group/public_group/mod.rs @@ -11,9 +11,6 @@ //! To avoid duplication of code and functionality, [`CoreGroup`] internally //! relies on a [`PublicGroup`] as well. -#[cfg(test)] -use std::collections::HashSet; - use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite, OpenMlsCryptoProvider}; use serde::{Deserialize, Serialize}; @@ -345,7 +342,7 @@ impl PublicGroup { ciphersuite: Ciphersuite, path: &[PlainUpdatePathNode], group_context: &[u8], - exclusion_list: &HashSet<&LeafNodeIndex>, + exclusion_list: &std::collections::HashSet<&LeafNodeIndex>, own_leaf_index: LeafNodeIndex, ) -> Result, LibraryError> { self.treesync().empty_diff().encrypt_path( diff --git a/openmls/src/group/public_group/process.rs b/openmls/src/group/public_group/process.rs index 2f867a6a8b..7e60b362f3 100644 --- a/openmls/src/group/public_group/process.rs +++ b/openmls/src/group/public_group/process.rs @@ -51,6 +51,20 @@ impl PublicGroup { message_secrets_store_option, )?; + // For commit messages, we need to check if the sender is a member or a + // new member and set the tree position accordingly. + let sender_context = match decrypted_message.sender() { + Sender::Member(leaf_index) => Some(SenderContext::Member(( + self.group_id().clone(), + *leaf_index, + ))), + Sender::NewMemberCommit => Some(SenderContext::ExternalCommit(( + self.group_id().clone(), + self.treesync().free_leaf_index(), + ))), + Sender::External(_) | Sender::NewMemberProposal => None, + }; + // Extract the credential if the sender is a member or a new member. // Checks the following semantic validation: // - ValSem112 @@ -68,25 +82,12 @@ impl PublicGroup { .unwrap_or_default(), self.group_context().extensions().external_senders(), )?; + let signature_public_key = OpenMlsSignaturePublicKey::from_signature_key( signature_key, self.ciphersuite().signature_algorithm(), ); - // For commit messages, we need to check if the sender is a member or a - // new member and set the tree position accordingly. - let sender_context = match decrypted_message.sender() { - Sender::Member(leaf_index) => Some(SenderContext::Member(( - self.group_id().clone(), - *leaf_index, - ))), - Sender::NewMemberCommit => Some(SenderContext::ExternalCommit(( - self.group_id().clone(), - self.treesync().free_leaf_index(), - ))), - Sender::External(_) | Sender::NewMemberProposal => None, - }; - Ok(UnverifiedMessage::from_decrypted_message( decrypted_message, credential, diff --git a/openmls/src/group/tests/external_add_proposal.rs b/openmls/src/group/tests/external_add_proposal.rs index b151ed6a64..e129ba1b76 100644 --- a/openmls/src/group/tests/external_add_proposal.rs +++ b/openmls/src/group/tests/external_add_proposal.rs @@ -354,7 +354,7 @@ async fn new_member_proposal_sender_should_be_reserved_for_join_proposals( // Update proposal cannot have a 'new_member_proposal' sender let update_proposal = alice_group - .propose_self_update(backend, &alice_signer, None) + .propose_self_update(backend, &alice_signer) .await .map(|(out, _)| MlsMessageIn::from(out)) .unwrap(); diff --git a/openmls/src/group/tests/test_commit_validation.rs b/openmls/src/group/tests/test_commit_validation.rs index a19867b25b..327208e6da 100644 --- a/openmls/src/group/tests/test_commit_validation.rs +++ b/openmls/src/group/tests/test_commit_validation.rs @@ -818,7 +818,7 @@ async fn test_partial_proposal_commit( // Create second proposal in Alice's group let proposal_2 = alice_group - .propose_self_update(backend, &alice_credential.signer, None) + .propose_self_update(backend, &alice_credential.signer) .await .map(|(out, _)| MlsMessageIn::from(out)) .unwrap(); diff --git a/openmls/src/group/tests/test_proposal_validation.rs b/openmls/src/group/tests/test_proposal_validation.rs index 29068bf9fe..950489a0e8 100644 --- a/openmls/src/group/tests/test_proposal_validation.rs +++ b/openmls/src/group/tests/test_proposal_validation.rs @@ -1713,13 +1713,14 @@ async fn test_valsem110(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // We first go the manual route let update_proposal: MlsMessageIn = bob_group - .propose_self_update( + .propose_explicit_self_update( backend, &bob_credential_with_key_and_signer.signer, - Some(update_leaf_node.clone()), + update_leaf_node.clone(), + &bob_credential_with_key_and_signer.signer, ) .await - .map(|(out, _)| MlsMessageIn::from(out)) + .map(|(out, ..)| MlsMessageIn::from(out)) .expect("error while creating remove proposal"); // Have Alice process this proposal. @@ -2005,7 +2006,7 @@ async fn test_valsem112(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // However, we can test the receiving side by crafting such a proposal // manually. let commit = alice_group - .propose_self_update(backend, &alice_credential_with_key_and_signer.signer, None) + .propose_self_update(backend, &alice_credential_with_key_and_signer.signer) .await .expect("Error creating self-update"); diff --git a/openmls/src/key_packages/errors.rs b/openmls/src/key_packages/errors.rs index babe1c1549..db9cda1b40 100644 --- a/openmls/src/key_packages/errors.rs +++ b/openmls/src/key_packages/errors.rs @@ -62,3 +62,14 @@ pub enum KeyPackageNewError { #[error(transparent)] SignatureError(#[from] SignatureError), } + +/// KeyPackage delete error +#[derive(Error, Debug, PartialEq, Clone)] +pub enum KeyPackageDeleteError { + /// See [`LibraryError`] for more details. + #[error(transparent)] + LibraryError(#[from] LibraryError), + /// Accessing the key store failed. + #[error("Accessing the key store failed.")] + KeyStoreError(KeyStoreError), +} diff --git a/openmls/src/key_packages/mod.rs b/openmls/src/key_packages/mod.rs index 41e15e5d6f..baa9c9a277 100644 --- a/openmls/src/key_packages/mod.rs +++ b/openmls/src/key_packages/mod.rs @@ -291,15 +291,18 @@ impl KeyPackage { pub async fn delete( &self, backend: &impl OpenMlsCryptoProvider, - ) -> Result<(), KeyStore::Error> { + ) -> Result<(), KeyPackageDeleteError> { + let kp_ref = self.hash_ref(backend.crypto())?; backend .key_store() - .delete::(self.hash_ref(backend.crypto()).unwrap().as_slice()) - .await?; + .delete::(kp_ref.as_slice()) + .await + .map_err(KeyPackageDeleteError::KeyStoreError)?; backend .key_store() .delete::(self.hpke_init_key().as_slice()) .await + .map_err(KeyPackageDeleteError::KeyStoreError) } /// Get a reference to the extensions of this key package. diff --git a/openmls/src/test_utils/test_framework/client.rs b/openmls/src/test_utils/test_framework/client.rs index 5dbd9b5926..6b6a7ff179 100644 --- a/openmls/src/test_utils/test_framework/client.rs +++ b/openmls/src/test_utils/test_framework/client.rs @@ -219,14 +219,18 @@ impl Client { .unwrap(); let (msg, welcome_option, group_info) = match action_type { ActionType::Commit => group.self_update(&self.crypto, &signer).await?, - ActionType::Proposal => ( - group - .propose_self_update(&self.crypto, &signer, leaf_node) - .await - .map(|(out, _)| out)?, - None, - None, - ), + ActionType::Proposal => { + let proposal = if let Some(ln) = leaf_node { + // FIXME: this does not work since both signers are the same + group + .propose_explicit_self_update(&self.crypto, &signer, ln, &signer) + .await + } else { + group.propose_self_update(&self.crypto, &signer).await + } + .map(|(out, _)| out)?; + (proposal, None, None) + } }; Ok(( msg, diff --git a/openmls/src/treesync/diff.rs b/openmls/src/treesync/diff.rs index b5d26cab7d..021d556cb0 100644 --- a/openmls/src/treesync/diff.rs +++ b/openmls/src/treesync/diff.rs @@ -59,7 +59,7 @@ pub(crate) type UpdatePathResult = ( /// and later merged into a [`TreeSync`] instance. #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct StagedTreeSyncDiff { - diff: StagedMlsBinaryTreeDiff, + pub(crate) diff: StagedMlsBinaryTreeDiff, new_tree_hash: Vec, } diff --git a/openmls/src/treesync/mod.rs b/openmls/src/treesync/mod.rs index 8180135155..eeb7e3fc93 100644 --- a/openmls/src/treesync/mod.rs +++ b/openmls/src/treesync/mod.rs @@ -46,8 +46,6 @@ use self::{ }, treesync_node::{TreeSyncLeafNode, TreeSyncNode, TreeSyncParentNode}, }; -#[cfg(test)] -use crate::binary_tree::array_representation::ParentNodeIndex; use crate::{ binary_tree::{ @@ -703,7 +701,10 @@ impl TreeSync { /// Return a reference to the parent node at the given `ParentNodeIndex` or /// `None` if the node is blank. - pub(crate) fn parent(&self, node_index: ParentNodeIndex) -> Option<&ParentNode> { + pub(crate) fn parent( + &self, + node_index: crate::binary_tree::array_representation::ParentNodeIndex, + ) -> Option<&ParentNode> { let tsn = self.tree.parent(node_index); tsn.node().as_ref() } diff --git a/openmls/src/treesync/node/encryption_keys.rs b/openmls/src/treesync/node/encryption_keys.rs index 7536427eb7..d1ee30ddf1 100644 --- a/openmls/src/treesync/node/encryption_keys.rs +++ b/openmls/src/treesync/node/encryption_keys.rs @@ -32,7 +32,7 @@ impl EncryptionKey { } /// Return the internal [`HpkePublicKey`] as slice. - pub(crate) fn as_slice(&self) -> &[u8] { + pub fn as_slice(&self) -> &[u8] { self.key.as_slice() } diff --git a/openmls/src/treesync/node/leaf_node.rs b/openmls/src/treesync/node/leaf_node.rs index 0a30e0516f..7756fe92da 100644 --- a/openmls/src/treesync/node/leaf_node.rs +++ b/openmls/src/treesync/node/leaf_node.rs @@ -259,6 +259,8 @@ impl LeafNode { // Update credential if let Some(leaf_node) = leaf_node.into() { leaf_node_tbs.payload.credential = leaf_node.credential().clone(); + leaf_node_tbs.payload.signature_key = leaf_node.signature_key().clone(); + leaf_node_tbs.payload.encryption_key = leaf_node.encryption_key().clone(); leaf_node_tbs.payload.leaf_node_source = LeafNodeSource::Update; } else if let Some(new_encryption_key) = new_encryption_key.into() { @@ -284,22 +286,25 @@ impl LeafNode { /// Replace the encryption key in this leaf with a random one. /// /// This signs the new leaf node as well. - pub(crate) fn rekey( + #[allow(clippy::too_many_arguments)] + pub fn rekey( &mut self, group_id: &GroupId, leaf_index: LeafNodeIndex, + leaf_node: Option, ciphersuite: Ciphersuite, protocol_version: ProtocolVersion, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, ) -> Result { - if !self + let ciphersuite_capable = self .payload .capabilities .ciphersuites - .contains(&ciphersuite.into()) - || !self.capabilities().versions.contains(&protocol_version) - { + .contains(&ciphersuite.into()); + let version_capable = self.capabilities().versions.contains(&protocol_version); + + if !ciphersuite_capable || !version_capable { debug_assert!( false, "Ciphersuite or protocol version is not supported by this leaf node.\ @@ -321,7 +326,7 @@ impl LeafNode { self.update_and_re_sign( key_pair.public_key().clone(), - None, + leaf_node, group_id.clone(), leaf_index, signer, @@ -408,6 +413,12 @@ impl LeafNode { .ciphersuites() .contains(&ciphersuite.into()) } + + /// Replace the credential material in the LeafNode. + pub fn set_credential_with_key(&mut self, credential_with_key: CredentialWithKey) { + self.payload.credential = credential_with_key.credential; + self.payload.signature_key = credential_with_key.signature_key; + } } #[cfg(test)] diff --git a/openmls/src/treesync/node/parent_node.rs b/openmls/src/treesync/node/parent_node.rs index 6669cfaddf..0f74af8ed2 100644 --- a/openmls/src/treesync/node/parent_node.rs +++ b/openmls/src/treesync/node/parent_node.rs @@ -7,9 +7,10 @@ use openmls_traits::{ }; use serde::{Deserialize, Serialize}; use thiserror::*; -use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize, VLBytes}; +use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize}; use super::encryption_keys::{EncryptionKey, EncryptionKeyPair}; +use crate::treesync::node::leaf_node::ParentHash; use crate::{ binary_tree::array_representation::{LeafNodeIndex, ParentNodeIndex}, ciphersuite::HpkePublicKey, @@ -27,7 +28,7 @@ use crate::{ )] pub struct ParentNode { pub(super) encryption_key: EncryptionKey, - pub(super) parent_hash: VLBytes, + pub(super) parent_hash: ParentHash, pub(super) unmerged_leaves: UnmergedLeaves, } diff --git a/openmls/src/treesync/treesync_node.rs b/openmls/src/treesync/treesync_node.rs index 241570a8cc..d380bbf893 100644 --- a/openmls/src/treesync/treesync_node.rs +++ b/openmls/src/treesync/treesync_node.rs @@ -61,7 +61,7 @@ impl From for TreeNode { /// hash values. Blank nodes are represented by [`TreeSyncNode`] instances where /// `node = None`. pub(crate) struct TreeSyncLeafNode { - node: Option, + pub(crate) node: Option, } impl TreeSyncLeafNode { diff --git a/openmls/tests/book_code.rs b/openmls/tests/book_code.rs index 64d8e44e18..546dff538e 100644 --- a/openmls/tests/book_code.rs +++ b/openmls/tests/book_code.rs @@ -349,11 +349,7 @@ async fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP // === Alice updates and commits === // ANCHOR: propose_self_update let (mls_message_out, _proposal_ref) = alice_group - .propose_self_update( - backend, - &alice_signature_keys, - None, // We don't provide a leaf node, it will be created on the fly instead - ) + .propose_self_update(backend, &alice_signature_keys) .await .expect("Could not create update proposal."); // ANCHOR_END: propose_self_update diff --git a/openmls/tests/test_mls_group.rs b/openmls/tests/test_mls_group.rs index c382bc998f..10563a0a02 100644 --- a/openmls/tests/test_mls_group.rs +++ b/openmls/tests/test_mls_group.rs @@ -229,7 +229,7 @@ async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr // === Alice updates and commits === let (queued_message, _) = alice_group - .propose_self_update(backend, &alice_signer, None) + .propose_self_update(backend, &alice_signer) .await .unwrap(); diff --git a/traits/src/types.rs b/traits/src/types.rs index 322483a964..4c68509889 100644 --- a/traits/src/types.rs +++ b/traits/src/types.rs @@ -157,6 +157,8 @@ pub enum CryptoError { IncompleteCertificate(&'static str), InvalidCertificate, TimeError, + InvalidKey, + MismatchKeypair, } impl std::fmt::Display for CryptoError { diff --git a/x509_credential/src/lib.rs b/x509_credential/src/lib.rs index 68e422a793..d3578074aa 100644 --- a/x509_credential/src/lib.rs +++ b/x509_credential/src/lib.rs @@ -38,7 +38,7 @@ impl CertificateKeyPair { let signature_scheme = leaf.signature_scheme()?; let pk = leaf.public_key()?; - let kp = SignatureKeyPair::from_raw(signature_scheme, sk, pk.to_vec()); + let kp = SignatureKeyPair::try_from_raw(signature_scheme, sk, pk.to_vec())?; Ok(Self(kp)) } From 3bdb2762212c83220f7a5fbe101a838c5db4cb8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Augusto=20C=C3=A9sar=20Dias?= Date: Wed, 21 Jun 2023 15:55:11 +0200 Subject: [PATCH 50/54] chore: expose group context --- openmls/src/extensions/mod.rs | 9 +++++++++ openmls/src/extensions/trust_anchor_extension.rs | 12 ++++++++++++ openmls/src/framing/mls_auth_content.rs | 5 +++-- openmls/src/framing/test_framing.rs | 1 + openmls/src/group/core_group/kat_welcome.rs | 2 +- openmls/src/group/core_group/test_core_group.rs | 2 +- openmls/src/group/core_group/test_proposals.rs | 3 ++- openmls/src/group/mls_group/mod.rs | 2 +- openmls/src/group/mod.rs | 6 +----- openmls/src/group/public_group/builder.rs | 2 +- openmls/src/group/public_group/diff.rs | 2 +- openmls/src/group/public_group/mod.rs | 2 +- openmls/src/group/tests/test_framing.rs | 2 +- openmls/src/messages/group_info.rs | 2 +- openmls/src/messages/tests/test_welcome.rs | 2 +- openmls/src/schedule/mod.rs | 2 +- .../tests_and_kats/kats/kat_message_protection.rs | 2 +- .../tests_and_kats/kats/kat_tree_operations.rs | 5 ++++- .../src/treesync/tests_and_kats/kats/kat_treekem.rs | 2 +- traits/src/types.rs | 1 + x509_credential/src/lib.rs | 2 +- 21 files changed, 46 insertions(+), 22 deletions(-) diff --git a/openmls/src/extensions/mod.rs b/openmls/src/extensions/mod.rs index 1613e50343..1ae48916e7 100644 --- a/openmls/src/extensions/mod.rs +++ b/openmls/src/extensions/mod.rs @@ -378,6 +378,15 @@ impl Extensions { _ => None, }) } + + /// Get a reference to the [`PerDomainTrustAnchorsExtension`] if there is any. + pub fn per_domain_trust_anchors(&self) -> Option<&PerDomainTrustAnchorsExtension> { + self.find_by_type(ExtensionType::PerDomainTrustAnchor) + .and_then(|e| match e { + Extension::PerDomainTrustAnchor(e) => Some(e), + _ => None, + }) + } } impl Extension { diff --git a/openmls/src/extensions/trust_anchor_extension.rs b/openmls/src/extensions/trust_anchor_extension.rs index 9f707d214f..a71abab52f 100644 --- a/openmls/src/extensions/trust_anchor_extension.rs +++ b/openmls/src/extensions/trust_anchor_extension.rs @@ -47,4 +47,16 @@ impl PerDomainTrustAnchor { } } } + + pub fn domain_name(&self) -> &[u8] { + self.domain_name.as_ref() + } + + pub fn credential_type(&self) -> CredentialType { + self.credential_type + } + + pub fn certificate_chain(&self) -> &[Vec] { + self.certificate_chain.as_ref() + } } diff --git a/openmls/src/framing/mls_auth_content.rs b/openmls/src/framing/mls_auth_content.rs index 670950c2f2..98d50e9e36 100644 --- a/openmls/src/framing/mls_auth_content.rs +++ b/openmls/src/framing/mls_auth_content.rs @@ -12,9 +12,10 @@ use tls_codec::{ }; use super::{ + group_context::GroupContext, mls_content::{FramedContent, FramedContentBody, FramedContentTbs}, - Commit, ConfirmationTag, ContentType, FramingParameters, GroupContext, GroupEpoch, GroupId, - Proposal, Sender, Signature, WireFormat, + Commit, ConfirmationTag, ContentType, FramingParameters, GroupEpoch, GroupId, Proposal, Sender, + Signature, WireFormat, }; use crate::{ binary_tree::LeafNodeIndex, diff --git a/openmls/src/framing/test_framing.rs b/openmls/src/framing/test_framing.rs index 55afdc1dc6..67afe49fde 100644 --- a/openmls/src/framing/test_framing.rs +++ b/openmls/src/framing/test_framing.rs @@ -16,6 +16,7 @@ use crate::{ group::{ core_group::proposals::{ProposalStore, QueuedProposal}, errors::*, + group_context::GroupContext, CreateCommitParams, }, key_packages::{test_key_packages::key_package, KeyPackageBundle}, diff --git a/openmls/src/group/core_group/kat_welcome.rs b/openmls/src/group/core_group/kat_welcome.rs index 9652a99773..e7cd1fa7c9 100644 --- a/openmls/src/group/core_group/kat_welcome.rs +++ b/openmls/src/group/core_group/kat_welcome.rs @@ -28,7 +28,7 @@ use crate::{ binary_tree::{array_representation::TreeSize, LeafNodeIndex}, ciphersuite::signable::Verifiable, framing::{MlsMessageIn, MlsMessageInBody}, - group::*, + group::{group_context::GroupContext, *}, key_packages::*, messages::*, prelude::group_info::{GroupInfo, VerifiableGroupInfo}, diff --git a/openmls/src/group/core_group/test_core_group.rs b/openmls/src/group/core_group/test_core_group.rs index 07bfab1630..471bdb91e9 100644 --- a/openmls/src/group/core_group/test_core_group.rs +++ b/openmls/src/group/core_group/test_core_group.rs @@ -111,7 +111,7 @@ async fn test_failed_groupinfo_decryption( .await; let group_info_tbs = { - let group_context = GroupContext::new( + let group_context = group_context::GroupContext::new( ciphersuite, group_id, epoch, diff --git a/openmls/src/group/core_group/test_proposals.rs b/openmls/src/group/core_group/test_proposals.rs index d6ec7e98a2..b21fc2086c 100644 --- a/openmls/src/group/core_group/test_proposals.rs +++ b/openmls/src/group/core_group/test_proposals.rs @@ -13,10 +13,11 @@ use crate::{ group::{ config::CryptoConfig, errors::*, + group_context::GroupContext, proposals::{ProposalQueue, ProposalStore, QueuedProposal}, public_group::errors::PublicGroupBuildError, test_core_group::{setup_client, setup_client_with_extensions}, - CreateCommitParams, GroupContext, GroupId, + CreateCommitParams, GroupId, }, key_packages::{KeyPackageBundle, KeyPackageIn}, messages::proposals::{AddProposal, Proposal, ProposalOrRef, ProposalType}, diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 9d97162391..0344ad142f 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -398,7 +398,7 @@ impl MlsGroup { MlsGroupState::Operational => Ok(()), } } - pub fn export_group_context(&self) -> &GroupContext { + pub fn export_group_context(&self) -> &group_context::GroupContext { self.group.context() } diff --git a/openmls/src/group/mod.rs b/openmls/src/group/mod.rs index 3f26c201f8..8bce84c933 100644 --- a/openmls/src/group/mod.rs +++ b/openmls/src/group/mod.rs @@ -2,7 +2,7 @@ //! //! This module contains the API to interact with groups. -mod group_context; +pub mod group_context; use std::fmt::Display; @@ -19,8 +19,6 @@ pub(crate) mod core_group; pub(crate) mod public_group; pub(crate) use core_group::*; pub(crate) mod mls_group; -#[cfg(not(any(feature = "test-utils", test)))] -pub(crate) use group_context::*; // Public pub mod config; @@ -39,8 +37,6 @@ pub use public_group::*; pub(crate) use core_group::create_commit_params::*; #[cfg(any(feature = "test-utils", test))] pub(crate) mod tests; -#[cfg(any(feature = "test-utils", test))] -pub use group_context::GroupContext; use openmls_traits::random::OpenMlsRand; #[cfg(any(feature = "test-utils", test))] pub use proposals::*; diff --git a/openmls/src/group/public_group/builder.rs b/openmls/src/group/public_group/builder.rs index a9071984de..c7deec3a67 100644 --- a/openmls/src/group/public_group/builder.rs +++ b/openmls/src/group/public_group/builder.rs @@ -8,7 +8,7 @@ use crate::{ errors::ExtensionError, Extension, Extensions, ExternalSendersExtension, PerDomainTrustAnchorsExtension, RequiredCapabilitiesExtension, }, - group::{config::CryptoConfig, GroupContext, GroupId}, + group::{config::CryptoConfig, group_context::GroupContext, GroupId}, key_packages::Lifetime, messages::ConfirmationTag, schedule::CommitSecret, diff --git a/openmls/src/group/public_group/diff.rs b/openmls/src/group/public_group/diff.rs index 34855d8a2a..aacb070ef3 100644 --- a/openmls/src/group/public_group/diff.rs +++ b/openmls/src/group/public_group/diff.rs @@ -14,7 +14,7 @@ use crate::{ error::LibraryError, extensions::Extensions, framing::{mls_auth_content::AuthenticatedContent, public_message::InterimTranscriptHashInput}, - group::GroupContext, + group::group_context::GroupContext, messages::{proposals::AddProposal, ConfirmationTag, EncryptedGroupSecrets}, schedule::{psk::PreSharedKeyId, CommitSecret, JoinerSecret}, treesync::{ diff --git a/openmls/src/group/public_group/mod.rs b/openmls/src/group/public_group/mod.rs index fd1b9baaaf..532c08180b 100644 --- a/openmls/src/group/public_group/mod.rs +++ b/openmls/src/group/public_group/mod.rs @@ -18,7 +18,7 @@ use self::{ diff::{PublicGroupDiff, StagedPublicGroupDiff}, errors::CreationFromExternalError, }; -use super::{GroupContext, GroupId, Member, ProposalStore, QueuedProposal}; +use super::{group_context::GroupContext, GroupId, Member, ProposalStore, QueuedProposal}; #[cfg(test)] use crate::treesync::{node::parent_node::PlainUpdatePathNode, treekem::UpdatePathNode}; use crate::{ diff --git a/openmls/src/group/tests/test_framing.rs b/openmls/src/group/tests/test_framing.rs index 2e41ba6147..afe248960e 100644 --- a/openmls/src/group/tests/test_framing.rs +++ b/openmls/src/group/tests/test_framing.rs @@ -129,7 +129,7 @@ async fn bad_padding(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi let sender = Sender::build_member(LeafNodeIndex::new(654)); - let group_context = GroupContext::new( + let group_context = group_context::GroupContext::new( ciphersuite, GroupId::random(backend), 1, diff --git a/openmls/src/messages/group_info.rs b/openmls/src/messages/group_info.rs index ee0f2492dd..34458504af 100644 --- a/openmls/src/messages/group_info.rs +++ b/openmls/src/messages/group_info.rs @@ -11,7 +11,7 @@ use crate::{ AeadKey, AeadNonce, Signature, }, extensions::Extensions, - group::{GroupContext, GroupId}, + group::{group_context::GroupContext, GroupId}, messages::ConfirmationTag, }; diff --git a/openmls/src/messages/tests/test_welcome.rs b/openmls/src/messages/tests/test_welcome.rs index 2c08b85d9c..f3b632df10 100644 --- a/openmls/src/messages/tests/test_welcome.rs +++ b/openmls/src/messages/tests/test_welcome.rs @@ -14,7 +14,7 @@ use crate::{ }, extensions::Extensions, group::{ - config::CryptoConfig, errors::WelcomeError, GroupContext, GroupId, MlsGroup, + config::CryptoConfig, errors::WelcomeError, group_context::GroupContext, GroupId, MlsGroup, MlsGroupConfigBuilder, }, messages::{ diff --git a/openmls/src/schedule/mod.rs b/openmls/src/schedule/mod.rs index 8c87fd1966..d1a0026434 100644 --- a/openmls/src/schedule/mod.rs +++ b/openmls/src/schedule/mod.rs @@ -130,7 +130,7 @@ use crate::{ ciphersuite::{AeadKey, AeadNonce, HpkePrivateKey, Mac, Secret}, error::LibraryError, framing::{mls_content::AuthenticatedContentTbm, MembershipTag}, - group::GroupContext, + group::group_context::GroupContext, messages::{ConfirmationTag, PathSecret}, tree::secret_tree::SecretTree, versions::ProtocolVersion, diff --git a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs index 43935a8664..4946b82611 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs @@ -188,7 +188,7 @@ pub async fn run_test_vector( use crate::{ binary_tree::array_representation::TreeSize, extensions::Extensions, - group::config::CryptoConfig, + group::{config::CryptoConfig, group_context::GroupContext}, messages::{proposals_in::ProposalIn, CommitIn, ConfirmationTag}, prelude::KeyPackageBundle, prelude_test::{Mac, Secret}, diff --git a/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs b/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs index f2abfcf473..c3a61ffe02 100644 --- a/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs +++ b/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs @@ -30,7 +30,10 @@ use crate::{ ciphersuite::{Mac, Secret}, extensions::Extensions, framing::Sender, - group::{GroupContext, GroupEpoch, GroupId, ProposalQueue, PublicGroup, QueuedProposal}, + group::{ + group_context::GroupContext, GroupEpoch, GroupId, ProposalQueue, PublicGroup, + QueuedProposal, + }, messages::{proposals::Proposal, proposals_in::ProposalIn, ConfirmationTag}, test_utils::*, treesync::{node::NodeIn, RatchetTree, RatchetTreeIn, TreeSync}, diff --git a/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs b/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs index 4514af9632..381e0518e6 100644 --- a/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs +++ b/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs @@ -10,7 +10,7 @@ use tls_codec::{Deserialize as TlsDeserializeTrait, Serialize as TlsSerializeTra use crate::{ binary_tree::{array_representation::ParentNodeIndex, LeafNodeIndex}, extensions::{Extensions, RatchetTreeExtension}, - group::{GroupContext, GroupEpoch, GroupId}, + group::{group_context::GroupContext, GroupEpoch, GroupId}, messages::PathSecret, prelude_test::Secret, schedule::CommitSecret, diff --git a/traits/src/types.rs b/traits/src/types.rs index 4c68509889..bf2626f8cb 100644 --- a/traits/src/types.rs +++ b/traits/src/types.rs @@ -156,6 +156,7 @@ pub enum CryptoError { CertificateEncodingError, IncompleteCertificate(&'static str), InvalidCertificate, + ExpiredCertificate, TimeError, InvalidKey, MismatchKeypair, diff --git a/x509_credential/src/lib.rs b/x509_credential/src/lib.rs index d3578074aa..c7c4093951 100644 --- a/x509_credential/src/lib.rs +++ b/x509_credential/src/lib.rs @@ -105,7 +105,7 @@ pub trait X509Ext { impl X509Ext for Certificate { fn is_valid(&self) -> Result<(), CryptoError> { if !self.is_time_valid()? { - return Err(CryptoError::InvalidCertificate); + return Err(CryptoError::ExpiredCertificate); } Ok(()) } From 37964fe84e554eefb33326733d9e201c089c00a8 Mon Sep 17 00:00:00 2001 From: beltram Date: Fri, 7 Jul 2023 13:25:34 +0200 Subject: [PATCH 51/54] chore: add helpers for detecting duplicate messages --- openmls/src/framing/message_in.rs | 6 ++++++ openmls/src/framing/public_message_in.rs | 6 ++++++ openmls/src/group/core_group/mod.rs | 10 ++++++---- openmls/src/group/mls_group/mod.rs | 14 ++++++++++++++ openmls/src/prelude.rs | 4 +++- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/openmls/src/framing/message_in.rs b/openmls/src/framing/message_in.rs index c67c1c3cf8..5153e8e1cc 100644 --- a/openmls/src/framing/message_in.rs +++ b/openmls/src/framing/message_in.rs @@ -111,6 +111,12 @@ impl MlsMessageIn { self.body } + /// Extract the content of an [`MlsMessageIn`] after deserialization for use + /// with the [`MlsGroup`] API. + pub fn body_as_ref(&self) -> &MlsMessageInBody { + &self.body + } + #[cfg(any(test, feature = "test-utils"))] pub fn into_keypackage(self) -> Option { match self.body { diff --git a/openmls/src/framing/public_message_in.rs b/openmls/src/framing/public_message_in.rs index 886367c905..412d876325 100644 --- a/openmls/src/framing/public_message_in.rs +++ b/openmls/src/framing/public_message_in.rs @@ -13,6 +13,7 @@ use super::{ *, }; +use crate::prelude::mls_content_in::FramedContentBodyIn; use openmls_traits::OpenMlsCryptoProvider; use std::{ convert::TryFrom, @@ -197,6 +198,11 @@ impl PublicMessageIn { pub fn confirmation_tag(&self) -> Option<&ConfirmationTag> { self.auth.confirmation_tag.as_ref() } + + /// Message body getter + pub fn body(&self) -> &FramedContentBodyIn { + &self.content.body + } } #[cfg(test)] diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 9450daa839..cba8a379cb 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -646,13 +646,15 @@ impl CoreGroup { }; // Create to-be-signed group info. + let confirmation_tag = self + .message_secrets() + .confirmation_key() + .tag(backend, self.context().confirmed_transcript_hash()) + .map_err(LibraryError::unexpected_crypto_error)?; let group_info_tbs = GroupInfoTBS::new( self.context().clone(), extensions, - self.message_secrets() - .confirmation_key() - .tag(backend, self.context().confirmed_transcript_hash()) - .map_err(LibraryError::unexpected_crypto_error)?, + confirmation_tag, self.own_leaf_index(), ); diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 0344ad142f..a9926ae325 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -23,8 +23,10 @@ mod creation; mod exporting; mod updates; +use crate::prelude::ConfirmationTag; use config::*; use errors::*; +use openmls_traits::types::CryptoError; // Crate pub(crate) mod config; @@ -335,6 +337,18 @@ impl MlsGroup { pub fn export_ratchet_tree(&self) -> RatchetTree { self.group.public_group().export_ratchet_tree() } + + /// Calculates the confirmation tag of the current group + pub fn compute_confirmation_tag( + &self, + backend: &impl OpenMlsCryptoProvider, + ) -> Result { + let cth = self.export_group_context().confirmed_transcript_hash(); + self.group + .message_secrets() + .confirmation_key() + .tag(backend, cth) + } } // Private methods of MlsGroup diff --git a/openmls/src/prelude.rs b/openmls/src/prelude.rs index 3437085439..415275f2ab 100644 --- a/openmls/src/prelude.rs +++ b/openmls/src/prelude.rs @@ -24,7 +24,9 @@ pub use crate::versions::*; pub use crate::extensions::{errors::*, *}; // Framing -pub use crate::framing::{message_in::*, message_out::*, sender::*, validation::*, *}; +pub use crate::framing::{ + message_in::*, message_out::*, mls_content_in::FramedContentBodyIn, sender::*, validation::*, *, +}; // Key packages pub use crate::key_packages::{errors::*, *}; From 67e13ef652472dcaebe8dedf793ca1b307411715 Mon Sep 17 00:00:00 2001 From: beltram Date: Tue, 11 Jul 2023 15:49:49 +0200 Subject: [PATCH 52/54] chore: expose `VerifiableGroupInfo::group_id` --- openmls/src/messages/group_info.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmls/src/messages/group_info.rs b/openmls/src/messages/group_info.rs index 34458504af..7bdb50d82b 100644 --- a/openmls/src/messages/group_info.rs +++ b/openmls/src/messages/group_info.rs @@ -90,7 +90,7 @@ impl VerifiableGroupInfo { /// /// Note: This method should only be used when necessary to verify the group /// info signature. - pub(crate) fn group_id(&self) -> &GroupId { + pub fn group_id(&self) -> &GroupId { self.payload.group_context.group_id() } From e84e0ff5b115a4768dc7f44db47a1f14cd862733 Mon Sep 17 00:00:00 2001 From: beltram Date: Tue, 11 Jul 2023 12:03:04 +0200 Subject: [PATCH 53/54] WPB-2823 --- openmls/src/framing/message_out.rs | 4 ++-- openmls/src/framing/public_message_in.rs | 2 +- openmls/src/group/core_group/staged_commit.rs | 8 ++++++++ openmls/src/group/public_group/diff.rs | 2 +- openmls/src/prelude.rs | 3 ++- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/openmls/src/framing/message_out.rs b/openmls/src/framing/message_out.rs index 13bac0d07c..13127b0f10 100644 --- a/openmls/src/framing/message_out.rs +++ b/openmls/src/framing/message_out.rs @@ -21,7 +21,7 @@ use crate::messages::group_info::VerifiableGroupInfo; #[derive(Debug, Clone, PartialEq, TlsSerialize, TlsSize)] pub struct MlsMessageOut { pub(crate) version: ProtocolVersion, - pub(crate) body: MlsMessageOutBody, + pub body: MlsMessageOutBody, } /// MLSMessage (Body) @@ -53,7 +53,7 @@ pub struct MlsMessageOut { /// ``` #[derive(Debug, PartialEq, Clone, TlsSerialize, TlsSize)] #[repr(u16)] -pub(crate) enum MlsMessageOutBody { +pub enum MlsMessageOutBody { /// Plaintext message #[tls_codec(discriminant = 1)] PublicMessage(PublicMessage), diff --git a/openmls/src/framing/public_message_in.rs b/openmls/src/framing/public_message_in.rs index 412d876325..e890d76d80 100644 --- a/openmls/src/framing/public_message_in.rs +++ b/openmls/src/framing/public_message_in.rs @@ -40,7 +40,7 @@ use tls_codec::{ #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct PublicMessageIn { pub(crate) content: FramedContentIn, - pub(crate) auth: FramedContentAuthData, + pub auth: FramedContentAuthData, pub(crate) membership_tag: Option, } diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index f5d91d93be..acc9014881 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -462,6 +462,14 @@ impl StagedCommit { pub(crate) fn into_state(self) -> StagedCommitState { self.state } + + pub fn get_confirmation_tag(&self) -> &ConfirmationTag { + match &self.state { + StagedCommitState::PublicState(diff) => &diff.confirmation_tag, + StagedCommitState::GroupMember(diff) => &diff.staged_diff.confirmation_tag, + StagedCommitState::ExternalMember(diff) => &diff.staged_diff.confirmation_tag, + } + } } /// This struct is used internally by [StagedCommit] to encapsulate all the modified group state. diff --git a/openmls/src/group/public_group/diff.rs b/openmls/src/group/public_group/diff.rs index aacb070ef3..20f7f24e9c 100644 --- a/openmls/src/group/public_group/diff.rs +++ b/openmls/src/group/public_group/diff.rs @@ -242,7 +242,7 @@ pub(crate) struct StagedPublicGroupDiff { pub(crate) staged_diff: StagedTreeSyncDiff, pub(super) group_context: GroupContext, pub(super) interim_transcript_hash: Vec, - pub(super) confirmation_tag: ConfirmationTag, + pub(crate) confirmation_tag: ConfirmationTag, } impl StagedPublicGroupDiff { diff --git a/openmls/src/prelude.rs b/openmls/src/prelude.rs index 415275f2ab..c03f5f5238 100644 --- a/openmls/src/prelude.rs +++ b/openmls/src/prelude.rs @@ -25,7 +25,8 @@ pub use crate::extensions::{errors::*, *}; // Framing pub use crate::framing::{ - message_in::*, message_out::*, mls_content_in::FramedContentBodyIn, sender::*, validation::*, *, + message_in::*, message_out::MlsMessageOutBody, message_out::*, + mls_content_in::FramedContentBodyIn, sender::*, validation::*, *, }; // Key packages From 5c459a97266e439962a48894baa15d314bd86f87 Mon Sep 17 00:00:00 2001 From: beltram Date: Thu, 20 Jul 2023 12:08:29 +0200 Subject: [PATCH 54/54] chore: expose `PrivateMessage::content_type()` --- openmls/src/framing/private_message_in.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmls/src/framing/private_message_in.rs b/openmls/src/framing/private_message_in.rs index d63adf0157..564a71b1dc 100644 --- a/openmls/src/framing/private_message_in.rs +++ b/openmls/src/framing/private_message_in.rs @@ -203,7 +203,7 @@ impl PrivateMessageIn { } /// Get the `content_type` in the `PrivateMessage`. - pub(crate) fn content_type(&self) -> ContentType { + pub fn content_type(&self) -> ContentType { self.content_type }