Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  chunked.rs   Sprache: unbekannt

 
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

//! Implement chunked encryption of XChaCha20Poly1305 and XSalsa20Poly1305.
//!
//! The provided API should be mainly used for large chunks of data, e.g. blobs or backups.
use libthreema_macros::concat_fixed_bytes;
use zeroize::Zeroizing;

use crate::crypto::{
    chacha20,
    cipher::{KeyIvInit as _, StreamCipher as _, StreamCipherSeek as _},
    poly1305, salsa20,
    subtle::ConstantTimeEq as _,
};

/// Invalid Tag (aka Message Authentication Code or MAC)
#[derive(thiserror::Error, Debug)]
#[error("Invalid tag")]
pub struct InvalidTag;

pub(super) struct ChunkedXChaCha20Poly1305Cipher {
    cipher: chacha20::XChaCha20,
    mac: poly1305::ChunkedPoly1305,
    associated_data_length: u64,
    ciphertext_length: u64,
}
impl ChunkedXChaCha20Poly1305Cipher {
    #[inline]
    fn new(
        key: &[u8; chacha20::KEY_LENGTH],
        nonce: &[u8; chacha20::NONCE_LENGTH],
        associated_data: &[u8],
    ) -> Self {
        use crate::crypto::poly1305::ChunkedPoly1305XChaCha20 as _;

        let mut cipher = chacha20::XChaCha20::new(key.into(), nonce.into());

        // Derive Poly1305 key from the first 32 bytes of the keystream.
        //
        // See: https://datatracker.ietf.org/doc/html/rfc8439#section-2.6
        let mut mac_key = Zeroizing::new(poly1305::Key::default());
        cipher.apply_keystream(&mut mac_key);

        // Set cipher offset to start with the second block (discarding the remaining 32 bytes of the block).
        // This sets the cipher _counter_ to `1`.
        //
        // See: https://datatracker.ietf.org/doc/html/rfc8439#section-2.8
        cipher.seek(64_usize);

        // Add associated data and `padding1` to the MAC early since we need this at the beginning of the
        // hash.
        //
        // See: https://datatracker.ietf.org/doc/html/rfc8439#section-2.8
        let mut mac = poly1305::ChunkedPoly1305::new(&mac_key);
        mac.update(associated_data);
        mac.zeropad_pending_block();

        Self {
            cipher,
            mac,
            associated_data_length: associated_data.len() as u64,
            ciphertext_length: 0,
        }
    }

    #[inline]
    fn finalize(mut self) -> poly1305::Tag {
        use crate::crypto::poly1305::ChunkedPoly1305XChaCha20 as _;

        // Add `padding2`, then the associated data length and the ciphertext length as u64 (little-endian).
        //
        // See: https://datatracker.ietf.org/doc/html/rfc8439#section-2.8
        self.mac.zeropad_pending_block();
        let length_fields: [u8; 16] = concat_fixed_bytes!(
            self.associated_data_length.to_le_bytes(),
            self.ciphertext_length.to_le_bytes()
        );
        self.mac.update(&length_fields);

        self.mac.finalize_complete_block()
    }
}

/// Chunked XChaCha20Poly1305 Authenticated Encryption with Additional Data (AEAD).
///
/// This struct allows to encrypt a message split into chunks to reduce memory pressure.
pub struct ChunkedXChaCha20Poly1305Encryptor(ChunkedXChaCha20Poly1305Cipher);
impl ChunkedXChaCha20Poly1305Encryptor {
    /// Create a new chunked XChaCha20 encryptor for the given `key`, `nonce` and `associated_data`.
    #[inline]
    #[must_use]
    pub fn new(
        key: &[u8; chacha20::KEY_LENGTH],
        nonce: &[u8; chacha20::NONCE_LENGTH],
        associated_data: &[u8],
    ) -> Self {
        Self(ChunkedXChaCha20Poly1305Cipher::new(key, nonce, associated_data))
    }

    /// Encrypt a chunk.
    ///
    /// Note: To ensure good performance, the chunk should always be a multiple of 16 bytes. To balance
    /// function call overhead with memory pressure, 1 MiB chunks are recommended.
    #[expect(clippy::missing_panics_doc, reason = "Panic will never happen")]
    #[inline]
    pub fn encrypt(&mut self, chunk: &mut [u8]) {
        // Encrypt and add ciphertext to the MAC.
        //
        // See: https://datatracker.ietf.org/doc/html/rfc8439#section-2.8
        self.0.cipher.apply_keystream(chunk);
        self.0.mac.update(chunk);
        self.0.ciphertext_length = self
            .0
            .ciphertext_length
            .checked_add(chunk.len() as u64)
            .expect("Total ciphertext length should not exceed u64");
    }

    /// Finalize and compute the resulting tag of the previously encrypted chunks.
    #[inline]
    #[must_use]
    pub fn finalize(self) -> [u8; chacha20::TAG_LENGTH] {
        self.0.finalize().into()
    }
}

/// Chunked XChaCha20Poly1305 Decryption.
///
/// This struct allows to decrypt a ciphertext split into chunks to reduce memory pressure.
///
/// IMPORTANT: Do not use this API unless you're absolutely sure you need it. Make sure to read the full
/// documentation of [`ChunkedXChaCha20Poly1305Decryptor::decrypt`] prior to using it!
pub struct ChunkedXChaCha20Poly1305Decryptor(ChunkedXChaCha20Poly1305Cipher);
impl ChunkedXChaCha20Poly1305Decryptor {
    /// Create a new chunked XChaCha20 decryptor for the given `key`, `nonce` and `associated_data`.
    #[inline]
    #[must_use]
    pub fn new(
        key: &[u8; chacha20::KEY_LENGTH],
        nonce: &[u8; chacha20::NONCE_LENGTH],
        associated_data: &[u8],
    ) -> Self {
        Self(ChunkedXChaCha20Poly1305Cipher::new(key, nonce, associated_data))
    }

    /// Decrypt a chunk.
    ///
    /// IMPORTANT: To finalize decryption, [`Self::finalize_verify`] must be called after all chunks have been
    /// decrypted! Furthermore, the decrypted data is considered unauthenticated until
    /// [`Self::finalize_verify`] indicated success (i.e. a valid MAC). Decrypted data that was not yet
    /// authenticated or failed the authentication check must not be used!
    ///
    /// Note: To ensure good performance, the chunk should always be a multiple of 16 bytes. To balance
    /// function call overhead with memory pressure, 1 MiB chunks are recommended.
    #[expect(clippy::missing_panics_doc, reason = "Panic will never happen")]
    #[inline]
    pub fn decrypt(&mut self, chunk: &mut [u8]) {
        // Add ciphertext to the MAC and decrypt.
        //
        // See: https://datatracker.ietf.org/doc/html/rfc8439#section-2.8
        self.0.mac.update(chunk);
        self.0.cipher.apply_keystream(chunk);
        self.0.ciphertext_length = self
            .0
            .ciphertext_length
            .checked_add(chunk.len() as u64)
            .expect("Total ciphertext length should not exceed u64");
    }

    /// Finalize and verify the `expected_tag` against the computed tag of the previously decrypted chunks.
    ///
    /// # Errors
    ///
    /// Returns an error in case the tag does not match.
    #[inline]
    pub fn finalize_verify(self, expected_tag: &[u8; chacha20::TAG_LENGTH]) -> Result<(), InvalidTag> {
        let actual_tag: [u8; chacha20::TAG_LENGTH] = self.0.finalize().into();
        if actual_tag.ct_eq(expected_tag).into() {
            Ok(())
        } else {
            Err(InvalidTag)
        }
    }
}

struct ChunkedXSalsa20Poly1305Cipher {
    cipher: salsa20::XSalsa20,
    mac: poly1305::ChunkedPoly1305,
}
impl ChunkedXSalsa20Poly1305Cipher {
    #[inline]
    fn new(key: &[u8; salsa20::KEY_LENGTH], nonce: &[u8; salsa20::NONCE_LENGTH]) -> Self {
        let mut cipher = salsa20::XSalsa20::new(key.into(), nonce.into());

        // Derive Poly1305 key from the first 32 bytes of the keystream.
        let mut mac_key = Zeroizing::new(poly1305::Key::default());
        cipher.apply_keystream(&mut mac_key);

        Self {
            cipher,
            mac: poly1305::ChunkedPoly1305::new(&mac_key),
        }
    }

    /// Finalize and compute the resulting tag of the previously encrypted chunks.
    #[inline]
    fn finalize(self) -> poly1305::Tag {
        use crate::crypto::poly1305::ChunkedPoly1305XSalsa20 as _;

        self.mac.finalize_unpadded()
    }
}

/// Chunked XSalsa20Poly1305 Authenticated Encryption.
///
/// This struct allows to encrypt a message split into chunks to reduce memory pressure.
pub struct ChunkedXSalsa20Poly1305Encryptor(ChunkedXSalsa20Poly1305Cipher);
impl ChunkedXSalsa20Poly1305Encryptor {
    /// Create a new chunked XSalsa20 encryptor for the given `key` and `nonce`.
    #[inline]
    #[must_use]
    pub fn new(key: &[u8; salsa20::KEY_LENGTH], nonce: &[u8; salsa20::NONCE_LENGTH]) -> Self {
        Self(ChunkedXSalsa20Poly1305Cipher::new(key, nonce))
    }

    /// Encrypt a chunk.
    ///
    /// Ensure to call [`Self::finalize`] to obatain the message authentication code (MAC aka tag) that
    /// provides integrity of the ciphertext.
    ///
    /// Note: To ensure good performance, the chunk should always be a multiple of 16 bytes. To balance
    /// function call overhead with memory pressure, 1 MiB chunks are recommended.
    #[inline]
    pub fn encrypt(&mut self, chunk: &mut [u8]) {
        // Encrypt and add ciphertext to the MAC
        self.0.cipher.apply_keystream(chunk);
        self.0.mac.update(chunk);
    }

    /// Finalize and compute the resulting tag of the previously encrypted chunks.
    #[inline]
    #[must_use]
    pub fn finalize(self) -> [u8; salsa20::TAG_LENGTH] {
        self.0.finalize().into()
    }
}

/// Chunked XSalsa20Poly1305 Decryption.
///
/// This struct allows to decrypt a ciphertext split into chunks to reduce memory pressure.
///
/// IMPORTANT: Do not use this API unless you're absolutely sure you need it. Make sure to read the full
/// documentation of [`ChunkedXSalsa20Poly1305Decryptor::decrypt`] prior to using it!
pub struct ChunkedXSalsa20Poly1305Decryptor(ChunkedXSalsa20Poly1305Cipher);
impl ChunkedXSalsa20Poly1305Decryptor {
    /// Create a new chunked XSalsa20 decryptor for the given `key` and `nonce`.
    #[inline]
    #[must_use]
    pub fn new(key: &[u8; salsa20::KEY_LENGTH], nonce: &[u8; salsa20::NONCE_LENGTH]) -> Self {
        Self(ChunkedXSalsa20Poly1305Cipher::new(key, nonce))
    }

    /// Decrypt a chunk.
    ///
    /// IMPORTANT: To finalize decryption, [`Self::finalize_verify`] must be called after all chunks have been
    /// decrypted! Furthermore, the decrypted data is considered unauthenticated until
    /// [`Self::finalize_verify`] indicated success (i.e. a valid MAC). Decrypted data that was not yet
    /// authenticated or failed the authentication check must not be used!
    ///
    /// Note: To ensure good performance, the chunk should always be a multiple of 16 bytes. To balance
    /// function call overhead with memory pressure, 1 MiB chunks are recommended.
    #[inline]
    pub fn decrypt(&mut self, chunk: &mut [u8]) {
        // Add ciphertext to the MAC and decrypt
        self.0.mac.update(chunk);
        self.0.cipher.apply_keystream(chunk);
    }

    /// Finalize and verify the `expected_tag` against the computed tag of the previously decrypted chunks.
    ///
    /// # Errors
    ///
    /// Returns an error in case the tag does not match.
    #[inline]
    pub fn finalize_verify(self, expected_tag: &[u8; salsa20::TAG_LENGTH]) -> Result<(), InvalidTag> {
        let actual_tag = self.0.finalize();
        if actual_tag.ct_eq(expected_tag).into() {
            Ok(())
        } else {
            Err(InvalidTag)
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    mod xchacha20poly1305 {
        use data_encoding::HEXLOWER;
        use rstest::rstest;
        use rstest_reuse::{apply, template};

        use super::{ChunkedXChaCha20Poly1305Decryptor, ChunkedXChaCha20Poly1305Encryptor};
        use crate::crypto::{aead::AeadInPlace as _, chacha20, cipher::KeyInit as _};

        struct TestCase {
            plaintext: Vec<u8>,
            associated_data: Vec<u8>,
            key: [u8; chacha20::KEY_LENGTH],
            nonce: [u8; chacha20::NONCE_LENGTH],
            reference_ciphertext: Vec<u8>,
            reference_tag: [u8; chacha20::TAG_LENGTH],
        }

        impl TestCase {
            fn new(associated_data_length: usize, plaintext_length: usize) -> Self {
                let plaintext = vec![0; plaintext_length];
                let associated_data = vec![0; associated_data_length];
                let key = [0xee_u8; chacha20::KEY_LENGTH];
                let nonce = [0xaa_u8; chacha20::NONCE_LENGTH];

                let (reference_ciphertext, reference_tag): (_, [u8; chacha20::TAG_LENGTH]) = {
                    let mut buffer = plaintext.clone();
                    let tag = chacha20::XChaCha20Poly1305::new((&key).into())
                        .encrypt_in_place_detached((&nonce).into(), &associated_data, &mut buffer)
                        .expect("Reference XChaCha20Poly1305 encryption should not fail");
                    (buffer, tag.into())
                };
                Self {
                    plaintext,
                    associated_data,
                    key,
                    nonce,
                    reference_ciphertext,
                    reference_tag,
                }
            }

            fn test_encryption(self, chunk_size: usize, interleave_zero_byte_chunks: bool) {
                let (actual_ciphertext, actual_tag) = {
                    let mut buffer = self.plaintext.clone();
                    let mut cipher =
                        ChunkedXChaCha20Poly1305Encryptor::new(&self.key, &self.nonce, &self.associated_data);
                    for chunk in buffer.chunks_mut(chunk_size) {
                        cipher.encrypt(chunk);
                        if interleave_zero_byte_chunks {
                            cipher.encrypt(&mut []);
                        }
                    }
                    (buffer, cipher.finalize())
                };

                // Compare results
                assert_eq!(
                    actual_ciphertext, self.reference_ciphertext,
                    "ciphertexts do not match"
                );
                assert_eq!(actual_tag, self.reference_tag, "tags do not match");
            }

            fn test_decryption(mut self, chunk_size: usize, interleave_zero_byte_chunks: bool) {
                let recovered_plaintext = {
                    let mut cipher =
                        ChunkedXChaCha20Poly1305Decryptor::new(&self.key, &self.nonce, &self.associated_data);
                    for chunk in self.reference_ciphertext.chunks_mut(chunk_size) {
                        cipher.decrypt(chunk);
                        if interleave_zero_byte_chunks {
                            cipher.decrypt(&mut []);
                        }
                    }
                    cipher
                        .finalize_verify(&self.reference_tag)
                        .expect("Authentication should pass");
                    self.reference_ciphertext
                };

                // Compare results
                assert_eq!(
                    self.plaintext, recovered_plaintext,
                    "Recovered plaintext does not match expected one"
                );
            }
        }

        #[template]
        #[rstest]
        fn xchacha20poly1305_template(
            #[values(0, 1, 15, 16, 17, 30, 31, 32)] associated_data_length: usize,
            #[values(0, 1, 15, 16, 17, 30, 31, 32, 63, 64, 65, 123, 666, 999)] plaintext_length: usize,
            #[values(1, 15, 16, 1024, 1039, 104857, 104858)] chunk_size: usize,
            #[values(false, true)] interleave_zero_byte_chunks: bool,
        ) {
        }

        #[apply(xchacha20poly1305_template)]
        fn xchacha20poly1305_encryption_lengths(
            associated_data_length: usize,
            plaintext_length: usize,
            chunk_size: usize,
            interleave_zero_byte_chunks: bool,
        ) {
            TestCase::new(associated_data_length, plaintext_length)
                .test_encryption(chunk_size, interleave_zero_byte_chunks);
        }

        /// Implements test vector A.3 of draft-irtf-cfrg-xchacha-03
        /// See <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.3>
        #[test]
        fn xchacha20poly1305_encryption_rfc() {
            TestCase {
                plaintext: HEXLOWER
                    .decode(
                        b"4c616469657320616e642047656e746c656d656e206f662074686520636c6173\
                          73206f66202739393a204966204920636f756c64206f6666657220796f75206f\
                          6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73\
                          637265656e20776f756c642062652069742e",
                    )
                    .expect("plaintext should be hex encoded"),
                associated_data: HEXLOWER
                    .decode(b"50515253c0c1c2c3c4c5c6c7")
                    .expect("associated data should be hex encoded"),
                nonce: HEXLOWER
                    .decode(b"404142434445464748494a4b4c4d4e4f5051525354555657")
                    .expect("nonce should be hex encoded")
                    .try_into()
                    .expect("nonce should have valid length"),
                key: HEXLOWER
                    .decode(b"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f")
                    .expect("key should be hex encoded")
                    .try_into()
                    .expect("key should have valid length"),
                reference_ciphertext: HEXLOWER
                    .decode(
                        b"bd6d179d3e83d43b9576579493c0e939572a1700252bfaccbed2902c21396cbb\
                          731c7f1b0b4aa6440bf3a82f4eda7e39ae64c6708c54c216cb96b72e1213b452\
                          2f8c9ba40db5d945b11b69b982c1bb9e3f3fac2bc369488f76b2383565d3fff9\
                          21f9664c97637da9768812f615c68b13b52e",
                    )
                    .expect("reference ciphertext should be hex encoded"),

                reference_tag: HEXLOWER
                    .decode(b"c0875924c1c7987947deafd8780acf49")
                    .expect("tag should be hex encoded")
                    .try_into()
                    .expect("tag should have valid length"),
            }
            .test_encryption(100, false);
        }

        #[apply(xchacha20poly1305_template)]
        fn xchacha20poly1305_decryption(
            associated_data_length: usize,
            plaintext_length: usize,
            chunk_size: usize,
            interleave_zero_byte_chunks: bool,
        ) {
            TestCase::new(associated_data_length, plaintext_length)
                .test_decryption(chunk_size, interleave_zero_byte_chunks);
        }

        #[test]
        #[should_panic(expected = "Authentication should pass: InvalidTag")]
        fn xchacha20poly1305_decryption_wrong_tag() {
            let mut test_case = TestCase::new(32, 100);

            // Change tag to make verification fail
            test_case.reference_tag = [0; chacha20::TAG_LENGTH];

            test_case.test_decryption(16, false);
        }

        #[test]
        #[should_panic(expected = "Authentication should pass: InvalidTag")]
        fn xchacha20poly1305_decryption_aad() {
            let mut test_case = TestCase::new(32, 100);

            // Remove the associated data to make verification fail
            test_case.associated_data = vec![];

            test_case.test_decryption(16, false);
        }

        #[test]
        #[should_panic(expected = "Authentication should pass: InvalidTag")]
        fn xchacha20poly1305_swap_aad_ciphertext() {
            let mut test_case = TestCase::new(32, 100);

            (test_case.associated_data, test_case.plaintext) =
                (test_case.plaintext, test_case.associated_data);

            test_case.test_decryption(16, false);
        }
    }

    mod xsalsa20poly1305 {
        use data_encoding::HEXLOWER;
        use rstest::rstest;
        use rstest_reuse::{apply, template};

        use super::{ChunkedXSalsa20Poly1305Decryptor, ChunkedXSalsa20Poly1305Encryptor};
        use crate::crypto::{aead::AeadInPlace as _, cipher::KeyInit as _, salsa20};
        struct TestCase {
            plaintext: Vec<u8>,
            key: [u8; salsa20::KEY_LENGTH],
            nonce: [u8; salsa20::NONCE_LENGTH],
            reference_ciphertext: Vec<u8>,
            reference_tag: [u8; salsa20::TAG_LENGTH],
        }

        impl TestCase {
            fn new(plaintext_length: usize) -> Self {
                let plaintext = vec![0; plaintext_length];
                let key = [0xee_u8; salsa20::KEY_LENGTH];
                let nonce = [0xaa_u8; salsa20::NONCE_LENGTH];

                let (reference_ciphertext, reference_tag): (_, [u8; salsa20::TAG_LENGTH]) = {
                    let mut buffer = plaintext.clone();
                    let tag = salsa20::XSalsa20Poly1305::new((&key).into())
                        .encrypt_in_place_detached((&nonce).into(), &[], &mut buffer)
                        .expect("Reference XSalsa20Poly1305 encryption should not fail");
                    (buffer, tag.into())
                };
                Self {
                    plaintext,
                    key,
                    nonce,
                    reference_ciphertext,
                    reference_tag,
                }
            }

            fn test_encryption(self, chunk_size: usize, interleave_zero_byte_chunks: bool) {
                let (actual_ciphertext, actual_tag) = {
                    let mut buffer = self.plaintext.clone();
                    let mut cipher = ChunkedXSalsa20Poly1305Encryptor::new(&self.key, &self.nonce);
                    for chunk in buffer.chunks_mut(chunk_size) {
                        cipher.encrypt(chunk);
                        if interleave_zero_byte_chunks {
                            cipher.encrypt(&mut []);
                        }
                    }
                    (buffer, cipher.finalize())
                };

                // Compare results
                assert_eq!(
                    actual_ciphertext, self.reference_ciphertext,
                    "ciphertexts do not match"
                );
                assert_eq!(actual_tag, self.reference_tag, "tags do not match");
            }

            fn test_decryption(mut self, chunk_size: usize, interleave_zero_byte_chunks: bool) {
                let recovered_plaintext = {
                    let mut cipher = ChunkedXSalsa20Poly1305Decryptor::new(&self.key, &self.nonce);
                    for chunk in self.reference_ciphertext.chunks_mut(chunk_size) {
                        cipher.decrypt(chunk);
                        if interleave_zero_byte_chunks {
                            cipher.decrypt(&mut []);
                        }
                    }
                    cipher
                        .finalize_verify(&self.reference_tag)
                        .expect("Authentication should pass");
                    self.reference_ciphertext
                };

                // Compare results
                assert_eq!(
                    self.plaintext, recovered_plaintext,
                    "Recovered plaintext does not match expected one"
                );
            }
        }

        #[template]
        #[rstest]
        fn xsalsa20poly1305_template(
            #[values(0, 1, 63, 64, 65, 123, 666, 999)] plaintext_length: usize,
            #[values(1, 15, 16, 1024, 1039, 104857, 104858)] chunk_size: usize,
            #[values(false, true)] interleave_zero_byte_chunks: bool,
        ) {
        }

        #[apply(xsalsa20poly1305_template)]
        fn xsalsa20poly1305_encryption_lengths(
            plaintext_length: usize,
            chunk_size: usize,
            interleave_zero_byte_chunks: bool,
        ) {
            TestCase::new(plaintext_length).test_encryption(chunk_size, interleave_zero_byte_chunks);
        }

        #[rustfmt::skip]
        /// Implements Rooterberg's test vector number 3, see
        /// <https://github.com/bleichenbacher-daniel/Rooterberg/blob/0d4bc48105dd817de4af746c602621f2be086b0a/test_vectors/auth_enc/nacl_xsalsa20_poly1305.json#L62-L72>
        #[test]
        fn xsalsa20poly1305_encryption_rooterberg() {
            TestCase {
                plaintext: HEXLOWER
                    .decode(b"2021222324252627")
                    .expect("plaintext should be hex encoded"),
                key: [0; salsa20::KEY_LENGTH],
                nonce: [0; salsa20::NONCE_LENGTH],
                reference_ciphertext: HEXLOWER
                    .decode(b"e61f99dcdaa0e80b")
                    .expect("ciphertext should be hex encoded"),
                reference_tag: HEXLOWER
                    .decode(b"f9ad226979fb26db0379ec522f3e0903")
                    .expect("reference tag should be hex encoded")
                    .try_into()
                    .expect("reference tag should have valid length"),
            }
            .test_decryption(10, false);
        }

        #[apply(xsalsa20poly1305_template)]
        fn xsalsa20poly1305_decryption(
            plaintext_length: usize,
            chunk_size: usize,
            interleave_zero_byte_chunks: bool,
        ) {
            TestCase::new(plaintext_length).test_decryption(chunk_size, interleave_zero_byte_chunks);
        }

        #[test]
        #[should_panic(expected = "Authentication should pass: InvalidTag")]
        fn xsalsapoly1305_decryption_wrong_tag() {
            let mut test_case = TestCase::new(32);

            test_case.reference_tag = [0; salsa20::TAG_LENGTH];

            test_case.test_decryption(16, false);
        }
    }
}

[Dauer der Verarbeitung: 0.4 Sekunden, vorverarbeitet 2026-04-27]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge