rmls 0.0.4

Messaging Layer Security in Rust
Documentation
use serde::{Deserialize, Serialize};

#[cfg(feature = "RingCryptoProvider")]
use crate::crypto::provider::RingCryptoProvider;
#[cfg(feature = "RustCryptoProvider")]
use crate::crypto::provider::RustCryptoProvider;
use crate::crypto::{cipher_suite::CipherSuite, provider::CryptoProvider};
use crate::secret_tree::*;
use crate::utilities::error::*;
use crate::utilities::serde::serde_test::load_test_vector;

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
struct SenderData {
    #[serde(with = "hex")]
    sender_data_secret: Vec<u8>,
    #[serde(with = "hex")]
    ciphertext: Vec<u8>,
    #[serde(with = "hex")]
    key: Vec<u8>,
    #[serde(with = "hex")]
    nonce: Vec<u8>,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
struct Leaf {
    generation: u32,
    #[serde(with = "hex")]
    handshake_key: Vec<u8>,
    #[serde(with = "hex")]
    handshake_nonce: Vec<u8>,
    #[serde(with = "hex")]
    application_key: Vec<u8>,
    #[serde(with = "hex")]
    application_nonce: Vec<u8>,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
struct SecretTreeTest {
    cipher_suite: u16,
    sender_data: SenderData,
    #[serde(with = "hex")]
    encryption_secret: Vec<u8>,
    leaves: Vec<Vec<Leaf>>,
}

fn secret_tree_test(
    crypto_provider: &impl CryptoProvider,
    cipher_suite: CipherSuite,
    tc: &SecretTreeTest,
) -> Result<()> {
    let key = expand_sender_data_key(
        crypto_provider,
        cipher_suite,
        &tc.sender_data.sender_data_secret,
        &tc.sender_data.ciphertext,
    )?;
    assert_eq!(&key, &tc.sender_data.key);

    let nonce = expand_sender_data_nonce(
        crypto_provider,
        cipher_suite,
        &tc.sender_data.sender_data_secret,
        &tc.sender_data.ciphertext,
    )?;
    assert_eq!(&nonce, &tc.sender_data.nonce);

    let tree = SecretTree::new(
        crypto_provider,
        cipher_suite,
        NumLeaves(tc.leaves.len() as u32),
        &tc.encryption_secret,
    )?;

    for (i, gens) in tc.leaves.iter().enumerate() {
        let li = LeafIndex(i as u32);
        test_ratchet_secret(
            crypto_provider,
            cipher_suite,
            &tree,
            li,
            RatchetLabel::Handshake,
            gens,
        )?;

        test_ratchet_secret(
            crypto_provider,
            cipher_suite,
            &tree,
            li,
            RatchetLabel::Application,
            gens,
        )?;
    }

    Ok(())
}

fn test_ratchet_secret(
    crypto_provider: &impl CryptoProvider,
    cipher_suite: CipherSuite,
    tree: &SecretTree,
    li: LeafIndex,
    label: RatchetLabel,
    gens: &[Leaf],
) -> Result<()> {
    let mut secret =
        tree.derive_ratchet_root(crypto_provider, cipher_suite, li.node_index(), label)?;

    for gen in gens {
        assert!(!(gen.generation < secret.generation));

        while secret.generation != gen.generation {
            secret = secret.derive_next(crypto_provider, cipher_suite)?;
        }

        let (want_key, want_nonce) = match label {
            RatchetLabel::Handshake => (&gen.handshake_key, &gen.handshake_nonce),
            RatchetLabel::Application => (&gen.application_key, &gen.application_nonce),
        };

        let key = secret.derive_key(crypto_provider, cipher_suite)?;
        assert_eq!(&key, &want_key);

        let nonce = secret.derive_nonce(crypto_provider, cipher_suite)?;
        assert_eq!(&nonce, &want_nonce);
    }

    Ok(())
}

fn test_secret_tree_with_crypto_provider(
    tests: &[SecretTreeTest],
    crypto_provider: &impl CryptoProvider,
) -> Result<()> {
    for tc in tests {
        let cipher_suite: CipherSuite = tc.cipher_suite.try_into()?;
        println!("test_secret_tree {}", cipher_suite);

        secret_tree_test(crypto_provider, cipher_suite, tc)?;
    }

    Ok(())
}

#[test]
fn test_secret_tree() -> Result<()> {
    let tests: Vec<SecretTreeTest> = load_test_vector("test-vectors/secret-tree.json")?;

    #[cfg(feature = "RingCryptoProvider")]
    test_secret_tree_with_crypto_provider(&tests, &RingCryptoProvider::default())?;
    #[cfg(feature = "RustCryptoProvider")]
    test_secret_tree_with_crypto_provider(&tests, &RustCryptoProvider::default())?;

    Ok(())
}