Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Threema/domain/libthreema/lib/src/crypto/     Datei vom 25.3.2026 mit Größe 12 kB image not shown  

Quelle  mod.rs   Sprache: unbekannt

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

//! Higher-level wrappers around crypto libraries used and some commonly used abstractions.
//!
//! Note: Always use this module instead of using the crypto dependencies directly!
pub mod chunked;

/// Minimal interface of a generic fixed-size array (used for some ciphers).
pub(crate) mod generic_array {
    pub(crate) use cipher::generic_array::GenericArray;
}

/// Type aliases for all constants used by Threema protocols.
pub(crate) mod consts {
    pub(crate) use cipher::consts::{U16, U24, U32, U64};
}

/// Minimal abstract interface for general ciphers.
pub(crate) mod cipher {
    pub(crate) use cipher::{KeyInit, KeyIvInit, StreamCipher, StreamCipherSeek, Unsigned};
}

/// Minimal abstract interface for hash digests.
pub(crate) mod digest {
    pub(crate) use digest::{Digest, FixedOutput, Mac, block_buffer::EagerBuffer};

    pub(crate) const MAC_256_LENGTH: usize = 32;
}

/// Minimal utility functions.
pub(crate) mod subtle {
    pub(crate) use subtle::ConstantTimeEq;
}

/// Minimal abstract interface for AEAD ciphers.
pub(crate) mod aead {
    pub(crate) use aead::{AeadInPlace, Buffer, Error};
    use aead::{Nonce, Payload, Result};

    use super::cipher::Unsigned as _;
    use crate::utils::bytes::InsertSlice as _;

    // TODO(LIB-31): Use `ByteWriter`?
    pub(crate) trait AeadRandomNonceAhead: AeadInPlace {
        /// Encrypt the given buffer containing a plaintext message in-place, place a random nonce ahead and
        /// return the used nonce.
        ///
        /// The buffer must have sufficient capacity to store the random nonce ahead of the ciphertext
        /// message, which will always be larger than the original plaintext. The exact size needed is
        /// cipher-dependent, but generally includes the size of an authentication tag.
        ///
        /// Returns an error if the buffer has insufficient capacity to store the resulting ciphertext
        /// message.
        fn encrypt_in_place_random_nonce_ahead(
            &self,
            associated_data: &[u8],
            buffer: &mut Vec<u8>,
        ) -> Result<Nonce<Self>> {
            let nonce = Self::generate_nonce(rand::thread_rng());
            self.encrypt_in_place(&nonce, associated_data, buffer)?;
            buffer.insert_at(0, &nonce);
            Ok(nonce)
        }

        /// Decrypt the message with the random nonce ahead in-place, returning the nonce or an error in the
        /// event the provided authentication tag does not match the given ciphertext.
        ///
        /// The buffer will be truncated to the original plaintext message upon success.
        fn decrypt_in_place_random_nonce_ahead(
            &self,
            associated_data: &[u8],
            buffer: &mut Vec<u8>,
        ) -> Result<Nonce<Self>> {
            if buffer.len() < Self::NonceSize::to_usize() {
                return Err(Error);
            }
            let nonce = Nonce::<Self>::from(buffer.drain(..Self::NonceSize::to_usize()).collect());
            self.decrypt_in_place(&nonce, associated_data, buffer)?;
            Ok(nonce)
        }

        /// Encrypt the given plaintext payload and a random nonce ahead, and return the used nonce along the
        /// resulting ciphertext as a vector of bytes.
        ///
        /// The [`Payload`] type can be used to provide Additional Associated Data (AAD) along with the
        /// message: this is an optional bytestring which is not encrypted, but *is* authenticated along with
        /// the message. Failure to pass the same AAD that was used during encryption will cause decryption to
        /// fail, which is useful if you would like to "bind" the ciphertext to some other identifier, like a
        /// digital signature key or other identifier.
        ///
        /// If you don't care about AAD and just want to encrypt a plaintext message, `&[u8]` will
        /// automatically be coerced into a `Payload`:
        ///
        /// ```nobuild
        /// let plaintext = b"Top secret message, handle with care";
        /// let ciphertext = cipher.encrypt(nonce, plaintext);
        /// ```
        ///
        /// The default implementation assumes a postfix tag (e.g AES-GCM, AES-GCM-SIV, ChaCha20Poly1305).
        /// [`Aead`] implementations which do not use a postfix tag (e.g. Salsa20Poly1305) will need to
        /// override this to correctly assemble the ciphertext message.
        fn encrypt_random_nonce_ahead<'message, 'aad, TPlaintext: Into<Payload<'message, 'aad>>>(
            &self,
            plaintext: TPlaintext,
        ) -> Result<(Nonce<Self>, Vec<u8>)> {
            let payload: Payload<'message, 'aad> = plaintext.into();
            let mut buffer = payload.msg.to_vec();
            let nonce = self.encrypt_in_place_random_nonce_ahead(payload.aad, &mut buffer)?;
            Ok((nonce, buffer))
        }

        /// Decrypt the given ciphertext slice with the random nonce ahead, and return the resulting nonce
        /// along the plaintext as a vector of bytes.
        ///
        /// See notes on [`Aead::encrypt()`] about allowable message payloads and Associated Additional Data
        /// (AAD).
        ///
        /// If you have no AAD, you can call this as follows:
        ///
        /// ```nobuild
        /// let ciphertext = b"...";
        /// let plaintext = cipher.decrypt(nonce, ciphertext)?;
        /// ```
        ///
        /// The default implementation assumes a postfix tag (e.g AES-GCM, AES-GCM-SIV, ChaCha20Poly1305).
        /// [`Aead`] implementations which do not use a postfix tag (e.g. Salsa20Poly1305) will need to
        /// override this to correctly parse the ciphertext message.
        #[expect(dead_code, reason = "May use later")]
        fn decrypt_random_nonce_ahead<'message, 'aad, TCiphertext: Into<Payload<'message, 'aad>>>(
            &self,
            ciphertext: TCiphertext,
        ) -> Result<(Nonce<Self>, Vec<u8>)> {
            let payload: Payload<'message, 'aad> = ciphertext.into();
            let mut buffer = payload.msg.to_vec();
            let nonce = self.decrypt_in_place_random_nonce_ahead(payload.aad, &mut buffer)?;
            Ok((nonce, buffer))
        }
    }

    impl<TAlgorithm: AeadInPlace> AeadRandomNonceAhead for TAlgorithm {}
}

/// Argon2id for password-based key derivations as used by Threema protocols.
pub(crate) mod argon2 {
    pub(crate) use argon2::{Algorithm::Argon2id, Argon2, Params, Version};
}

/// BLAKE2b for hashing and key derivations as used by Threema protocols.
pub(crate) mod blake2b {
    use blake2::Blake2bMac;

    /// A BLAKE2b MAC, commonly used for key derivations and authentication challenges in Threema
    /// protocols.
    pub(crate) type Blake2bMac256 = Blake2bMac<super::consts::U32>;
    pub(crate) type Blake2bMac512 = Blake2bMac<super::consts::U64>;

    pub(crate) const MAC_256_LENGTH: usize = 32;
}

/// SHA2 and HMAC-SHA-2 for hashing as used by various internal mechanisms in Threema and some
/// legacy Threema protocols.
pub(crate) mod sha2 {
    use hmac::Hmac;
    pub(crate) use sha2::Sha256;

    pub(crate) type HmacSha256 = Hmac<Sha256>;
}

/// ChaCha20Poly1305 and XChaCha20Poly1305 ciphers as used by modern Threema protocols.
pub(crate) mod chacha20 {
    pub(crate) use chacha20::XChaCha20;
    pub(crate) use chacha20poly1305::{ChaCha20Poly1305, Key, XChaCha20Poly1305};

    pub(crate) const KEY_LENGTH: usize = 32;
    pub(crate) const NONCE_LENGTH: usize = 24;
    pub(crate) const TAG_LENGTH: usize = 16;
}

/// XSalsa20Poly1305 cipher as used by legacy Threema protocols.
pub(crate) mod salsa20 {
    pub(crate) use crypto_secretbox::{Key, XSalsa20Poly1305};
    pub(crate) use salsa20::XSalsa20;

    use crate::utils::bytes;

    pub(crate) const KEY_LENGTH: usize = 32;
    pub(crate) const NONCE_LENGTH: usize = 24;
    pub(crate) const TAG_LENGTH: usize = 16;

    pub(crate) type EncryptedDataRange = bytes::EncryptedDataRange<{ TAG_LENGTH }>;
}

/// Poly1305 for hashing in combination with XSalsa20/XChaCha20. Should only be used for chunked crypto.
pub(crate) mod poly1305 {
    pub(crate) use poly1305::{Key, Tag};
    use poly1305::{Poly1305, universal_hash::UniversalHash as _};

    use super::cipher::KeyInit as _;

    /// Poly1305 as a MAC construction with an internal buffer handling incomplete blocks.
    pub(crate) struct ChunkedPoly1305 {
        hash: Poly1305,
        buffer: super::digest::EagerBuffer<super::consts::U16>,
    }
    impl ChunkedPoly1305 {
        #[inline]
        pub(crate) fn new(key: &Key) -> Self {
            Self {
                hash: Poly1305::new(key),
                buffer: super::digest::EagerBuffer::default(),
            }
        }

        /// Update with any full blocks and store any leftover bytes for later processing.
        #[inline]
        pub(crate) fn update(&mut self, chunk: &[u8]) {
            // Update the hash from fully constructed blocks and store any leftover bytes in the internal
            // buffer
            self.buffer.digest_blocks(chunk, |blocks| {
                self.hash.update(blocks);
            });
        }
    }

    pub(crate) trait ChunkedPoly1305XChaCha20 {
        /// Zero-pad a _pending_ block.
        ///
        /// If no bytes are in the internal buffer, the block is not considered _pending_. In other words,
        /// this prevents a full block of just zero-padding.
        fn zeropad_pending_block(&mut self);

        /// Hand out the resulting tag.
        ///
        /// WARNING: This disregards any pending blocks. The caller must ensure that all pending blocks have
        /// been written.
        fn finalize_complete_block(self) -> Tag;
    }

    impl ChunkedPoly1305XChaCha20 for ChunkedPoly1305 {
        #[inline]
        fn zeropad_pending_block(&mut self) {
            if self.buffer.get_pos() == 0 {
                return;
            }
            self.hash.update(&[*self.buffer.pad_with_zeros()]);
        }

        #[inline]
        fn finalize_complete_block(self) -> Tag {
            self.hash.finalize()
        }
    }

    pub(crate) trait ChunkedPoly1305XSalsa20 {
        /// Hand out the hash unpadded (yet still hashing the remaining bytes).
        fn finalize_unpadded(self) -> Tag;
    }

    impl ChunkedPoly1305XSalsa20 for ChunkedPoly1305 {
        #[inline]
        fn finalize_unpadded(self) -> Tag {
            self.hash.compute_unpadded(self.buffer.get_data())
        }
    }
}

/// Deprecated stuff that only exists for the sake of backwards compatibility.
pub(crate) mod deprecated {
    pub(crate) mod pbkdf2 {
        pub(crate) use pbkdf2::pbkdf2_hmac_array;
    }

    pub(crate) mod salsa20 {
        pub(crate) use salsa20::XSalsa20;
    }

    pub(crate) mod scrypt {
        pub(crate) use scrypt::{Params, scrypt};
    }
}

// Ed25519-related keys and signatures.
pub(crate) mod ed25519 {
    pub(crate) use ed25519_dalek::{PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH, Signature, VerifyingKey};
}

/// X25519-related derivation and keys, including the intermediate X25519HSalsa20 hash on top of a
/// montgomery point as used by many Threema protocols to derive further keys from.
pub(crate) mod x25519 {
    use aead::consts::U10;
    use salsa20::hsalsa;
    pub(crate) use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret, StaticSecret};
    use zeroize::ZeroizeOnDrop;

    use super::generic_array::GenericArray;

    pub(crate) const KEY_LENGTH: usize = 32;

    /// A uniformly distributed [`SharedSecret`], compatible with classic NaCl shared secret
    /// derivation.
    #[derive(ZeroizeOnDrop)]
    pub(crate) struct SharedSecretHSalsa20([u8; Self::LENGTH]);

    impl SharedSecretHSalsa20 {
        /// The byte length
        pub(crate) const LENGTH: usize = KEY_LENGTH;

        /// Convert this shared secret key to a byte array.
        #[inline]
        #[must_use]
        pub(crate) fn to_bytes(&self) -> [u8; Self::LENGTH] {
            self.0
        }

        /// View this shared secret key as a byte array.
        #[inline]
        #[must_use]
        pub(crate) fn as_bytes(&self) -> &[u8; Self::LENGTH] {
            &self.0
        }
    }

    impl From<SharedSecret> for SharedSecretHSalsa20 {
        fn from(secret: SharedSecret) -> Self {
            // Use HSalsa20 to create a uniformly random key from the shared secret
            Self(
                hsalsa::<U10>(
                    GenericArray::from_slice(secret.as_bytes()),
                    &GenericArray::default(),
                )
                .into(),
            )
        }
    }
}

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