sframe 1.2.0

pure rust implementation of SFrame (RFC 9605)
Documentation
use crate::{
    CipherSuite,
    crypto::buffer::{DecryptionBufferView, EncryptionBufferView},
    error::{Result, SframeError},
    header::Counter,
};

/// Trait for AEAD encryption implementations as defined in [RFC 9605 Section 4.4.3](https://www.rfc-editor.org/rfc/rfc9605.html#section-4.4.3).
pub trait AeadEncrypt: TryFrom<CipherSuite, Error = SframeError> {
    /// The secret key material this backend consumes, as produced by its
    /// [`KeyDerivation`](super::key_derivation::KeyDerivation) implementation.
    type Secret;

    /// Encrypts the plaintext in the buffer in-place.
    fn encrypt<'a, B>(&self, secret: &Self::Secret, buffer: B, counter: Counter) -> Result<()>
    where
        B: Into<EncryptionBufferView<'a>>;
}

/// Trait for AEAD decryption implementations as defined in [RFC 9605 Section 4.4.4](https://www.rfc-editor.org/rfc/rfc9605.html#section-4.4.4).
pub trait AeadDecrypt: TryFrom<CipherSuite, Error = SframeError> {
    /// The secret key material this backend consumes, as produced by its
    /// [`KeyDerivation`](super::key_derivation::KeyDerivation) implementation.
    type Secret;

    /// Decrypts the ciphertext in the buffer in-place.
    fn decrypt<'a, B>(&self, secret: &Self::Secret, buffer: B, counter: Counter) -> Result<()>
    where
        B: Into<DecryptionBufferView<'a>>;
}

#[cfg(all(test, crypto_backend))]
mod test {
    use crate::{
        crypto::{
            buffer::{DecryptionBufferView, EncryptionBufferView, encryption::EncryptionBuffer},
            cipher_suite::CipherSuite,
        },
        header::{KeyId, SframeHeader},
        key::{DecryptionKey, EncryptionKey},
        test_vectors::get_sframe_test_vector,
        util::test::assert_bytes_eq,
    };

    use rand::{RngExt, rng};
    use test_case::test_case;

    const KEY_MATERIAL: &str = "THIS_IS_RANDOM";

    #[test]
    fn encrypt_random_frame() {
        let mut data = vec![0u8; 1024];
        rng().fill(data.as_mut_slice());
        let header = SframeHeader::new(0, 0);
        let enc_key = EncryptionKey::derive_from(
            CipherSuite::AesGcm256Sha512,
            KeyId::default(),
            KEY_MATERIAL.as_bytes(),
        )
        .unwrap();

        let mut frame_buffer = Vec::new();
        let mut encryption_buffer = EncryptionBuffer::try_allocate(
            &mut frame_buffer,
            enc_key.cipher_suite(),
            &Vec::from(&header),
            &data,
        )
        .unwrap();
        enc_key
            .encrypt(&mut encryption_buffer, header.counter())
            .unwrap();
    }

    #[test_case(CipherSuite::AesGcm128Sha256; "AesGcm128Sha256")]
    #[test_case(CipherSuite::AesGcm256Sha512; "AesGcm256Sha512")]
    #[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_80; "AesCtr128HmacSha256_80"))]
    #[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_64; "AesCtr128HmacSha256_64"))]
    #[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_32; "AesCtr128HmacSha256_32"))]
    fn encrypt_test_vector(cipher_suite: CipherSuite) {
        let test_vec = get_sframe_test_vector(&cipher_suite.to_string());

        let enc_key = EncryptionKey::from_test_vector(cipher_suite, test_vec);

        let header = SframeHeader::new(test_vec.key_id, test_vec.counter);
        let header_buffer = Vec::from(&header);

        let mut aad = [header_buffer.as_slice(), test_vec.metadata.as_slice()].concat();
        assert_bytes_eq(&aad, &test_vec.aad);

        let mut cipher_text = test_vec.plain_text.clone();
        let mut tag = vec![0u8; enc_key.cipher_suite().auth_tag_len()];
        let encryption_buffer = EncryptionBufferView {
            aad: &mut aad,
            data: &mut cipher_text,
            tag: &mut tag,
        };

        enc_key
            .encrypt(encryption_buffer, header.counter())
            .unwrap();

        let full_frame = [header_buffer, cipher_text, tag].concat().to_vec();
        assert_bytes_eq(&full_frame, &test_vec.cipher_text);
    }

    #[test_case(CipherSuite::AesGcm128Sha256; "AesGcm128Sha256")]
    #[test_case(CipherSuite::AesGcm256Sha512; "AesGcm256Sha512")]
    #[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_80; "AesCtr128HmacSha256_80"))]
    #[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_64; "AesCtr128HmacSha256_64"))]
    #[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_32; "AesCtr128HmacSha256_32"))]
    fn decrypt_test_vector(cipher_suite: CipherSuite) {
        let test_vec = get_sframe_test_vector(&cipher_suite.to_string());

        let dec_key = DecryptionKey::from_test_vector(cipher_suite, test_vec);
        let header: SframeHeader = SframeHeader::new(test_vec.key_id, test_vec.counter);
        let header_buffer = Vec::from(&header);

        let mut aad = [header_buffer.as_slice(), test_vec.metadata.as_slice()].concat();
        assert_bytes_eq(&aad, &test_vec.aad);

        let mut data = Vec::from(&test_vec.cipher_text[header.len()..]);

        let decryption_buffer = DecryptionBufferView {
            aad: &mut aad,
            data: &mut data,
        };

        dec_key
            .decrypt(decryption_buffer, header.counter())
            .unwrap();
        data.truncate(data.len() - dec_key.cipher_suite().auth_tag_len());

        assert_bytes_eq(&data, &test_vec.plain_text);
    }
}