relay-crypto 0.1.0

The crypto library for the Relay Ecosystem.
Documentation
use chrono::Utc;
use x25519_dalek::{PublicKey, StaticSecret};

use relay_crypto::{crypto::CryptoError, prelude::e2e, record::KeyRecord, rng::os_rng_hkdf};

#[test]
fn encrypt_decrypt_roundtrip() {
    let mut recipient_rng = os_rng_hkdf(Some(b"recipient nonce"), b"e2e/test").expect("RNG failed");

    let (recipient_secret, recipient_record) = {
        let recipient_secret = StaticSecret::random_from_rng(&mut recipient_rng);
        let recipient_pub = PublicKey::from(&recipient_secret);
        let recipient_pub_bytes = recipient_pub.as_bytes();
        (
            recipient_secret,
            KeyRecord {
                id: "test".to_string(),
                version: 1,
                x25519: *recipient_pub_bytes,
                ed25519: [0u8; 32],
                created_at: Utc::now(),
                expires_at: None,
            },
        )
    };

    let rng = os_rng_hkdf(Some(b"encryption nonce"), b"e2e/test").expect("RNG failed");
    let message = b"Hello, world!";
    let aad = b"canonical-meta-payload";

    let (meta, ciphertext) =
        e2e::encrypt(rng, &recipient_record, message, aad).expect("Encryption failed");

    let payload =
        e2e::decrypt(&recipient_secret, &meta, &ciphertext, aad).expect("Decryption failed");

    assert_eq!(payload, message);
}

#[test]
fn decrypt_rejects_wrong_add() {
    let mut recipient_rng = os_rng_hkdf(Some(b"recipient nonce"), b"e2e/test").expect("RNG failed");

    let (recipient_secret, recipient_record) = {
        let recipient_secret = StaticSecret::random_from_rng(&mut recipient_rng);
        let recipient_pub = PublicKey::from(&recipient_secret);
        let recipient_pub_bytes = recipient_pub.as_bytes();
        (
            recipient_secret,
            KeyRecord {
                id: "test".to_string(),
                version: 1,
                x25519: *recipient_pub_bytes,
                ed25519: [0u8; 32],
                created_at: Utc::now(),
                expires_at: None,
            },
        )
    };

    let rng = os_rng_hkdf(Some(b"encryption nonce"), b"e2e/test").expect("RNG failed");
    let message = b"Hello, world!";
    let aad_encrypt = b"canonical-meta-payload";
    let aad_decrypt = b"modified-meta-payload";

    let (meta, ciphertext) =
        e2e::encrypt(rng, &recipient_record, message, aad_encrypt).expect("Encryption failed");

    let payload_error = e2e::decrypt(&recipient_secret, &meta, &ciphertext, aad_decrypt)
        .expect_err("Decryption should have failed with wrong AAD");

    assert_eq!(
        payload_error,
        CryptoError::Decrypt("aead::Error".to_string())
    );
}

#[test]
fn decrypt_rejects_wrong_alg() {
    let mut recipient_rng = os_rng_hkdf(Some(b"recipient nonce"), b"e2e/test").expect("RNG failed");

    let (recipient_secret, recipient_record) = {
        let recipient_secret = StaticSecret::random_from_rng(&mut recipient_rng);
        let recipient_pub = PublicKey::from(&recipient_secret);
        let recipient_pub_bytes = recipient_pub.as_bytes();
        (
            recipient_secret,
            KeyRecord {
                id: "test".to_string(),
                version: 1,
                x25519: *recipient_pub_bytes,
                ed25519: [0u8; 32],
                created_at: Utc::now(),
                expires_at: None,
            },
        )
    };

    let rng = os_rng_hkdf(Some(b"encryption nonce"), b"e2e/test").expect("RNG failed");
    let message = b"Hello, world!";
    let aad = b"canonical-meta-payload";

    let (mut meta, ciphertext) =
        e2e::encrypt(rng, &recipient_record, message, aad).expect("Encryption failed");

    meta.alg = "wrong-algorithm".to_string();

    let payload_error = e2e::decrypt(&recipient_secret, &meta, &ciphertext, aad)
        .expect_err("Decryption should have failed with wrong AAD");

    assert_eq!(
        payload_error,
        CryptoError::InvalidMeta("Unsupported algorithm: wrong-algorithm".to_string())
    );
}

#[test]
fn decrypt_rejects_corrupt_nonce() {
    let mut recipient_rng = os_rng_hkdf(Some(b"recipient nonce"), b"e2e/test").expect("RNG failed");

    let (recipient_secret, recipient_record) = {
        let recipient_secret = StaticSecret::random_from_rng(&mut recipient_rng);
        let recipient_pub = PublicKey::from(&recipient_secret);
        let recipient_pub_bytes = recipient_pub.as_bytes();
        (
            recipient_secret,
            KeyRecord {
                id: "test".to_string(),
                version: 1,
                x25519: *recipient_pub_bytes,
                ed25519: [0u8; 32],
                created_at: Utc::now(),
                expires_at: None,
            },
        )
    };

    let rng = os_rng_hkdf(Some(b"encryption nonce"), b"e2e/test").expect("RNG failed");
    let message = b"Hello, world!";
    let aad = b"canonical-meta-payload";

    let (mut meta, ciphertext) =
        e2e::encrypt(rng, &recipient_record, message, aad).expect("Encryption failed");

    meta.nonce = [0u8; 24].to_vec();

    let payload_error = e2e::decrypt(&recipient_secret, &meta, &ciphertext, aad)
        .expect_err("Decryption should have failed with wrong AAD");

    assert_eq!(
        payload_error,
        CryptoError::Decrypt("aead::Error".to_string())
    );
}