crypto_box 0.9.1

Pure Rust implementation of NaCl's crypto_box public-key authenticated encryption primitive which combines the X25519 Elliptic Curve Diffie-Hellman function and the XSalsa20Poly1305 authenticated encryption cipher
Documentation
//! `crypto_box` test vectors
//!
//! Adapted from PHP Sodium Compat's test vectors:
//! <https://www.phpclasses.org/browse/file/122796.html>

#![cfg(all(
    any(feature = "chacha20", feature = "salsa20"),
    feature = "getrandom",
    feature = "std"
))]

use crypto_box::{
    aead::{generic_array::GenericArray, Aead, AeadInPlace, OsRng},
    PublicKey, SecretKey,
};
use curve25519_dalek::EdwardsPoint;
use hex_literal::hex;

// Alice's keypair
const ALICE_SECRET_KEY: [u8; 32] =
    hex!("68f208412d8dd5db9d0c6d18512e86f0ec75665ab841372d57b042b27ef89d4c");
const ALICE_PUBLIC_KEY: [u8; 32] =
    hex!("ac3a70ba35df3c3fae427a7c72021d68f2c1e044040b75f17313c0c8b5d4241d");

// Bob's keypair
const BOB_SECRET_KEY: [u8; 32] =
    hex!("b581fb5ae182a16f603f39270d4e3b95bc008310b727a11dd4e784a0044d461b");
const BOB_PUBLIC_KEY: [u8; 32] =
    hex!("e8980c86e032f1eb2975052e8d65bddd15c3b59641174ec9678a53789d92c754");

const NONCE: &[u8; 24] = &hex!("69696ee955b62b73cd62bda875fc73d68219e0036b7a0b37");

const PLAINTEXT: &[u8] = &[
    0xbe, 0x07, 0x5f, 0xc5, 0x3c, 0x81, 0xf2, 0xd5, 0xcf, 0x14, 0x13, 0x16, 0xeb, 0xeb, 0x0c, 0x7b,
    0x52, 0x28, 0xc5, 0x2a, 0x4c, 0x62, 0xcb, 0xd4, 0x4b, 0x66, 0x84, 0x9b, 0x64, 0x24, 0x4f, 0xfc,
    0xe5, 0xec, 0xba, 0xaf, 0x33, 0xbd, 0x75, 0x1a, 0x1a, 0xc7, 0x28, 0xd4, 0x5e, 0x6c, 0x61, 0x29,
    0x6c, 0xdc, 0x3c, 0x01, 0x23, 0x35, 0x61, 0xf4, 0x1d, 0xb6, 0x6c, 0xce, 0x31, 0x4a, 0xdb, 0x31,
    0x0e, 0x3b, 0xe8, 0x25, 0x0c, 0x46, 0xf0, 0x6d, 0xce, 0xea, 0x3a, 0x7f, 0xa1, 0x34, 0x80, 0x57,
    0xe2, 0xf6, 0x55, 0x6a, 0xd6, 0xb1, 0x31, 0x8a, 0x02, 0x4a, 0x83, 0x8f, 0x21, 0xaf, 0x1f, 0xde,
    0x04, 0x89, 0x77, 0xeb, 0x48, 0xf5, 0x9f, 0xfd, 0x49, 0x24, 0xca, 0x1c, 0x60, 0x90, 0x2e, 0x52,
    0xf0, 0xa0, 0x89, 0xbc, 0x76, 0x89, 0x70, 0x40, 0xe0, 0x82, 0xf9, 0x37, 0x76, 0x38, 0x48, 0x64,
    0x5e, 0x07, 0x05,
];

#[test]
fn generate_secret_key() {
    SecretKey::generate(&mut OsRng);
}

#[test]
fn secret_and_public_keys() {
    let secret_key = SecretKey::from(ALICE_SECRET_KEY);
    assert_eq!(secret_key.to_bytes(), ALICE_SECRET_KEY);

    // Ensure `Debug` impl on `SecretKey` is covered in tests
    dbg!(&secret_key);

    assert_eq!(secret_key.public_key().as_bytes(), &ALICE_PUBLIC_KEY);
}

#[test]
fn edwards_to_montgomery() {
    let secret_key = SecretKey::from(ALICE_SECRET_KEY);
    let scalar = secret_key.to_scalar();
    let point = EdwardsPoint::mul_base(&scalar);
    let public_key = PublicKey::from(point.to_montgomery());
    assert_eq!(secret_key.public_key(), public_key);
    assert_eq!(secret_key, SecretKey::from(scalar));
}

macro_rules! impl_tests {
    ($box:ty, $plaintext:expr, $ciphertext:expr) => {
        #[test]
        fn encrypt() {
            let secret_key = SecretKey::from(ALICE_SECRET_KEY);
            let public_key = PublicKey::from(BOB_PUBLIC_KEY);
            let nonce = GenericArray::from_slice(NONCE);

            let ciphertext = <$box>::new(&public_key, &secret_key)
                .encrypt(nonce, $plaintext)
                .unwrap();

            assert_eq!($ciphertext, &ciphertext[..]);
        }

        #[test]
        fn encrypt_in_place_detached() {
            let secret_key = SecretKey::from(ALICE_SECRET_KEY);
            let public_key = PublicKey::from(BOB_PUBLIC_KEY);
            let nonce = GenericArray::from_slice(NONCE);
            let mut buffer = $plaintext.to_vec();

            let tag = <$box>::new(&public_key, &secret_key)
                .encrypt_in_place_detached(nonce, b"", &mut buffer)
                .unwrap();

            let (expected_tag, expected_ciphertext) = $ciphertext.split_at(16);
            assert_eq!(expected_tag, &tag[..]);
            assert_eq!(expected_ciphertext, &buffer[..]);
        }

        #[test]
        fn decrypt() {
            let secret_key = SecretKey::from(BOB_SECRET_KEY);
            let public_key = PublicKey::from(ALICE_PUBLIC_KEY);
            let nonce = GenericArray::from_slice(NONCE);

            let plaintext = <$box>::new(&public_key, &secret_key)
                .decrypt(nonce, $ciphertext)
                .unwrap();

            assert_eq!($plaintext, &plaintext[..]);
        }

        #[test]
        fn decrypt_in_place_detached() {
            let secret_key = SecretKey::from(BOB_SECRET_KEY);
            let public_key = PublicKey::from(ALICE_PUBLIC_KEY);
            let nonce = GenericArray::from_slice(NONCE);
            let tag = GenericArray::clone_from_slice(&$ciphertext[..16]);
            let mut buffer = $ciphertext[16..].to_vec();

            <$box>::new(&public_key, &secret_key)
                .decrypt_in_place_detached(nonce, b"", &mut buffer, &tag)
                .unwrap();

            assert_eq!($plaintext, &buffer[..]);
        }
    };
}

#[cfg(feature = "salsa20")]
mod xsalsa20poly1305 {
    use super::*;
    use crypto_box::SalsaBox;
    const CIPHERTEXT: &[u8] = &[
        0xc0, 0x3f, 0x27, 0xd1, 0x88, 0xef, 0x65, 0xc, 0xd1, 0x29, 0x36, 0x91, 0x31, 0x37, 0xbb,
        0x17, 0xed, 0x4c, 0x98, 0xc2, 0x64, 0x89, 0x39, 0xe2, 0xe1, 0xd2, 0xe8, 0x55, 0x47, 0xa,
        0x7b, 0x8c, 0x63, 0x2c, 0xab, 0xfd, 0x5a, 0xb3, 0xb3, 0xc2, 0xd3, 0x13, 0xdc, 0x8c, 0x9e,
        0xcf, 0x5d, 0xa1, 0x73, 0xe1, 0xf9, 0xc3, 0x18, 0xcd, 0xef, 0x1d, 0xce, 0xd6, 0xd2, 0x51,
        0x9e, 0x69, 0x50, 0x85, 0xe6, 0xb5, 0xc4, 0x1, 0xa2, 0xbd, 0x53, 0x31, 0x44, 0x29, 0x86,
        0xc7, 0x7, 0x6d, 0x41, 0x26, 0x25, 0x49, 0x7c, 0x4c, 0xb2, 0xfd, 0x94, 0xc6, 0xf1, 0x3,
        0x96, 0x10, 0x33, 0xb2, 0xc9, 0x30, 0xd7, 0xe8, 0x2e, 0x3, 0x41, 0xf2, 0x9d, 0x38, 0x79,
        0xbd, 0x6a, 0xb9, 0xd8, 0x81, 0xea, 0x3a, 0x1f, 0x36, 0x5d, 0x63, 0x4e, 0x65, 0x3c, 0x6e,
        0x17, 0x1a, 0xac, 0x7f, 0xc1, 0xe7, 0x69, 0x34, 0xd2, 0x3b, 0xe6, 0xf0, 0x4a, 0x54, 0x1,
        0x8, 0x8, 0xdb, 0xf0, 0xf9, 0xbd, 0x30, 0xf6, 0x3b, 0x68, 0xd0, 0x26,
    ];

    impl_tests!(SalsaBox, PLAINTEXT, CIPHERTEXT);
}

#[cfg(feature = "chacha20")]
mod xchacha20poly1305 {
    use super::*;
    use crypto_box::ChaChaBox;
    const CIPHERTEXT: &[u8] = &hex!(
        "0cd5ed093de698c8e410d0d451df2f5283057376b947b9b7392b956e5d675f309218acce8cf85f6c"
        "f6a9e2e09ef8c5b0f97c661ee21b1b3418be566692634056a92b4034d5d0cf14c52420a488b7f0da"
        "0c5740dfc6b85397d3a8f679e84303e8d3f8b048abdb2dd79183b0a62683a1bc2a527fc9b82c5ffa"
        "c4a684bcfeadfdcd28930b2dbe597f4716a658ccfca5b44049e06c"
    );

    impl_tests!(ChaChaBox, PLAINTEXT, CIPHERTEXT);
}

#[cfg(feature = "seal")]
#[test]
fn seal() {
    const SEAL_SECRET_KEY: [u8; 32] = [
        0x15, 0xb3, 0x6c, 0xb0, 0x02, 0x13, 0x37, 0x3f, 0xb3, 0xfb, 0x03, 0x95, 0x8f, 0xb0, 0xcc,
        0x00, 0x12, 0xec, 0xac, 0xa1, 0x12, 0xfd, 0x24, 0x9d, 0x3c, 0xf0, 0x96, 0x1e, 0x31, 0x1c,
        0xaa, 0xc9,
    ];

    const SEAL_PUBLIC_KEY: [u8; 32] = [
        0xfb, 0x4c, 0xb3, 0x4f, 0x74, 0xa9, 0x28, 0xb7, 0x91, 0x23, 0x33, 0x3c, 0x1e, 0x63, 0xd9,
        0x91, 0x06, 0x02, 0x44, 0xcd, 0xa9, 0x8a, 0xff, 0xee, 0x14, 0xc3, 0x39, 0x8c, 0x6d, 0x31,
        0x55, 0x74,
    ];

    const SEAL_PLAINTEXT: &[u8] =
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit.".as_bytes();

    const SEAL_CIPHERTEXT: &[u8] = &[
        0x95, 0xeb, 0x5b, 0xf0, 0x5a, 0xda, 0x25, 0xee, 0x51, 0xf4, 0x15, 0x82, 0x01, 0xc2, 0x61,
        0xa0, 0x0b, 0xfb, 0x19, 0x55, 0xa9, 0x17, 0x6c, 0x8c, 0x7f, 0x1a, 0x62, 0xf2, 0x99, 0xa3,
        0x2e, 0x54, 0xf6, 0xeb, 0xcc, 0xc8, 0xab, 0x9d, 0x2c, 0xe1, 0xb1, 0xd3, 0x71, 0x0b, 0xa3,
        0x7d, 0x8d, 0xb1, 0x7a, 0xee, 0xec, 0x0b, 0x78, 0xfc, 0x3d, 0x32, 0xb3, 0x9b, 0x79, 0xed,
        0x96, 0xf1, 0x89, 0x48, 0xc5, 0xa5, 0x74, 0xb8, 0xe3, 0xf8, 0xec, 0xcc, 0x2f, 0x13, 0x24,
        0x08, 0xc2, 0x16, 0x46, 0xf3, 0xae, 0xda, 0xe4, 0xa6, 0x7f, 0xde, 0x4f, 0x77, 0x15, 0x3b,
        0x54, 0x58, 0xb8, 0xa6, 0xbd, 0x71, 0x2d, 0xd8, 0x36, 0x55, 0x34, 0xc5, 0x67, 0xec,
    ];

    let pk = PublicKey::from(SEAL_PUBLIC_KEY);
    let encrypted = pk.seal(&mut OsRng, SEAL_PLAINTEXT).unwrap();

    let sk = SecretKey::from(SEAL_SECRET_KEY);
    assert_eq!(SEAL_PLAINTEXT, sk.unseal(&encrypted).unwrap());
    assert_eq!(SEAL_PLAINTEXT, sk.unseal(SEAL_CIPHERTEXT).unwrap());
}