Address Feedback

This commit is contained in:
Jan Winkelmann (keks)
2025-02-28 18:26:36 +01:00
parent 30c3de3f87
commit 2dba9205e7
20 changed files with 521 additions and 335 deletions

2
Cargo.lock generated
View File

@@ -2024,7 +2024,7 @@ dependencies = [
"rosenpass-oqs",
"rosenpass-secret-memory",
"rosenpass-to",
"thiserror 2.0.11",
"thiserror 1.0.69",
]
[[package]]

View File

@@ -10,7 +10,7 @@ repository = "https://github.com/rosenpass/rosenpass"
readme = "readme.md"
[dependencies]
thiserror = "2"
thiserror = { workspace = true }
rosenpass-to = { workspace = true }
[dev-dependencies]

View File

@@ -1,77 +1,128 @@
//! This module contains the traits for all the cryptographic algorithms used throughout Rosenpass.
//! These traits are marker traits that signal intent. They can also be used for trait objects.
/// Constants and trait for the Incorrect HMAC over Blake2b, with 256 key and hash length.
pub mod keyed_hash_incorrect_hmac_blake2b {
use crate::primitives::keyed_hash::*;
// These constants describe how they are used here, not what the algorithm defines.
pub const KEY_LEN: usize = 32;
pub const OUT_LEN: usize = 32;
pub trait KeyedHashIncorrectHmacBlake2b: KeyedHash<KEY_LEN, OUT_LEN> {}
/// The key length used in [`KeyedHashIncorrectHmacBlake2b`].
pub const KEY_LEN: usize = 32;
/// The hash length used in [`KeyedHashIncorrectHmacBlake2b`].
pub const HASH_LEN: usize = 32;
/// A [`KeyedHash`] that is an incorrect HMAC over Blake2 (a custom Rosenpass construction)
pub trait KeyedHashIncorrectHmacBlake2b: KeyedHash<KEY_LEN, HASH_LEN> {}
}
/// Constants and trait for Blake2b, with 256 key and hash length.
pub mod keyed_hash_blake2b {
use crate::primitives::keyed_hash::*;
// These constants describe how they are used here, not what the algorithm defines.
pub const KEY_LEN: usize = 32;
pub const OUT_LEN: usize = 32;
pub trait KeyedHashBlake2b: KeyedHash<KEY_LEN, OUT_LEN> {}
/// The key length used in [`KeyedHashBlake2b`].
pub const KEY_LEN: usize = 32;
/// The hash length used in [`KeyedHashBlake2b`].
pub const HASH_LEN: usize = 32;
/// A [`KeyedHash`] that is Blake2b
pub trait KeyedHashBlake2b: KeyedHash<KEY_LEN, HASH_LEN> {}
}
/// Constants and trait for SHAKE256, with 256 key and hash length.
pub mod keyed_hash_shake256 {
use crate::primitives::keyed_hash::*;
// These constants describe how they are used here, not what the algorithm defines.
/// The key length used in [`KeyedHashShake256`].
pub const KEY_LEN: usize = 32;
/// The hash length used in [`KeyedHashShake256`].
pub const OUT_LEN: usize = 32;
/// A [`KeyedHash`] that is SHAKE256.
pub trait KeyedHashShake256: KeyedHash<KEY_LEN, OUT_LEN> {}
}
/// Constants and trait for the ChaCha20Poly1305 AEAD
pub mod aead_chacha20poly1305 {
use crate::primitives::aead::*;
// See https://datatracker.ietf.org/doc/html/rfc7539#section-2.8
/// The key length used in [`AeadChaCha20Poly1305`].
pub const KEY_LEN: usize = 32;
/// The nonce length used in [`AeadChaCha20Poly1305`].
pub const NONCE_LEN: usize = 12;
/// The tag length used in [`AeadChaCha20Poly1305`].
pub const TAG_LEN: usize = 16;
/// An [`Aead`] that is ChaCha20Poly1305.
pub trait AeadChaCha20Poly1305: Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {}
}
/// Constants and trait for the XChaCha20Poly1305 AEAD (i.e. ChaCha20Poly1305 with extended nonce
/// lengths)
pub mod aead_xchacha20poly1305 {
use crate::primitives::aead::*;
// See https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03
/// The key length used in [`AeadXChaCha20Poly1305`].
pub const KEY_LEN: usize = 32;
/// The nonce length used in [`AeadXChaCha20Poly1305`].
pub const NONCE_LEN: usize = 24;
/// The tag length used in [`AeadXChaCha20Poly1305`].
pub const TAG_LEN: usize = 16;
/// An [`Aead`] that is XChaCha20Poly1305.
pub trait AeadXChaCha20Poly1305: Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {}
}
/// Constants and trait for the Kyber512 KEM
pub mod kem_kyber512 {
use crate::primitives::kem::*;
// page 39 of https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf
// (which is ml-kem instead of kyber, but it's the same)
/// The secret key length used in [`KemKyber512`].
pub const SK_LEN: usize = 1632;
/// The public key length used in [`KemKyber512`].
pub const PK_LEN: usize = 800;
/// The ciphertext length used in [`KemKyber512`].
pub const CT_LEN: usize = 768;
/// The shared key length used in [`KemKyber512`].
pub const SHK_LEN: usize = 32;
/// A [`Kem`] that is Kyber512.
pub trait KemKyber512: Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> {}
}
/// Constants and trait for the Classic McEliece 460896 KEM
pub mod kem_classic_mceliece460896 {
use crate::primitives::kem::*;
// page 6 of https://classic.mceliece.org/mceliece-impl-20221023.pdf
/// The secret key length used in [`KemClassicMceliece460896`].
pub const SK_LEN: usize = 13608;
/// The public key length used in [`KemClassicMceliece460896`].
pub const PK_LEN: usize = 524160;
/// The ciphertext length used in [`KemClassicMceliece460896`].
pub const CT_LEN: usize = 156;
/// The shared key length used in [`KemClassicMceliece460896`].
pub const SHK_LEN: usize = 32;
/// A [`Kem`] that is ClassicMceliece460896.
pub trait KemClassicMceliece460896: Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> {}
}

View File

@@ -1,2 +1,5 @@
//! This trait contains traits, constants and wrappers that provid= the interface between Rosenpass
//! as a consumer of cryptographic libraries and the implementations of cryptographic algorithms.
pub mod algorithms;
pub mod primitives;

View File

@@ -1,3 +1,6 @@
//! Traits for cryptographic primitives used in Rosenpass, specifically KEM, AEAD and keyed
//! hashing.
pub(crate) mod aead;
pub(crate) mod kem;
pub(crate) mod keyed_hash;

View File

@@ -1,11 +1,64 @@
use rosenpass_to::{ops::copy_slice, To as _};
use thiserror::Error;
/// Models authenticated encryption with assiciated data (AEAD) functionality.
///
/// The methods of this trait take a `&self` argument as a receiver. This has two reasons:
/// 1. It makes type inference a lot smoother
/// 2. It allows to use the functionality through a trait object or having an enum that has
/// variants for multiple options (like e.g. the `KeyedHash` enum in `rosenpass-ciphers`).
///
/// Since the caller needs an instance of the type to use the functionality, implementors are
/// adviced to implement the [`Default`] trait where possible.
///
/// Example for encrypting a message with a specific [`Aead`] instance:
/// ```
/// use rosenpass_cipher_traits::primitives::Aead;
///
/// const KEY_LEN: usize = 32;
/// const NONCE_LEN: usize = 12;
/// const TAG_LEN: usize = 16;
///
/// fn encrypt_message_given_an_aead<AeadImpl>(
/// aead: &AeadImpl,
/// msg: &str,
/// nonce: &[u8; NONCE_LEN],
/// encrypted: &mut [u8]
/// ) where AeadImpl: Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {
/// let key = [0u8; KEY_LEN]; // This is not a secure key!
/// let ad = b""; // we don't need associated data here
/// aead.encrypt(encrypted, &key, nonce, ad, msg.as_bytes()).unwrap();
/// }
/// ```
///
/// If only the type (but no instance) is available, then we can still encrypt, as long as the type
/// also is [`Default`]:
/// ```
/// use rosenpass_cipher_traits::primitives::Aead;
///
/// const KEY_LEN: usize = 32;
/// const NONCE_LEN: usize = 12;
/// const TAG_LEN: usize = 16;
///
/// fn encrypt_message_without_aead<AeadImpl>(
/// msg: &str,
/// nonce: &[u8; NONCE_LEN],
/// encrypted: &mut [u8]
/// ) where AeadImpl: Default + Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {
/// let key = [0u8; KEY_LEN]; // This is not a secure key!
/// let ad = b""; // we don't need associated data here
/// AeadImpl::default().encrypt(encrypted, &key, nonce, ad, msg.as_bytes()).unwrap();
/// }
/// ```
pub trait Aead<const KEY_LEN: usize, const NONCE_LEN: usize, const TAG_LEN: usize> {
const KEY_LEN: usize = KEY_LEN;
const NONCE_LEN: usize = NONCE_LEN;
const TAG_LEN: usize = TAG_LEN;
/// Encrypts `plaintext` using the given `key` and `nonce`, taking into account the additional
/// data `ad` and writes the result into `ciphertext`.
///
/// `ciphertext` must be exactly `TAG_LEN` longer than `plaintext`.
fn encrypt(
&self,
ciphertext: &mut [u8],
@@ -15,6 +68,10 @@ pub trait Aead<const KEY_LEN: usize, const NONCE_LEN: usize, const TAG_LEN: usiz
plaintext: &[u8],
) -> Result<(), Error>;
/// Decrypts `ciphertexttext` using the given `key` and `nonce`, taking into account the additional
/// data `ad` and writes the result into `plaintext`.
///
/// `ciphertext` must be exactly `TAG_LEN` longer than `plaintext`.
fn decrypt(
&self,
plaintext: &mut [u8],
@@ -25,15 +82,21 @@ pub trait Aead<const KEY_LEN: usize, const NONCE_LEN: usize, const TAG_LEN: usiz
) -> Result<(), Error>;
}
/// The API of XChaCha was a bit weird and moved the nonce into the ciphertext. Instead of changing
/// the protocol code, we recreate that API on top of the more normal API, but move it into a
/// separate crate.
/// Provides an AEAD API where the nonce is part of the ciphertext.
///
/// The old xaead API had the ciphertext begin with the `nonce`. In order to not having to change
/// the calling code too much, we add a wrapper trait that provides this API and implement it for
/// all AEAD.
pub trait AeadWithNonceInCiphertext<
const KEY_LEN: usize,
const NONCE_LEN: usize,
const TAG_LEN: usize,
>: Aead<KEY_LEN, NONCE_LEN, TAG_LEN>
{
/// Encrypts `plaintext` using the given `key` and `nonce`, taking into account the additional
/// data `ad` and writes the result into `ciphertext`.
///
/// `ciphertext` must be exactly `TAG_LEN` + `NONCE_LEN` longer than `plaintext`.
fn encrypt_with_nonce_in_ctxt(
&self,
ciphertext: &mut [u8],
@@ -56,6 +119,10 @@ pub trait AeadWithNonceInCiphertext<
self.encrypt(rest, key, nonce, ad, plaintext)
}
/// Decrypts `ciphertexttext` using the given `key` and `nonce`, taking into account the additional
/// data `ad` and writes the result into `plaintext`.
///
/// `ciphertext` must be exactly `TAG_LEN` + `NONCE_LEN` longer than `plaintext`.
fn decrypt_with_nonce_in_ctxt(
&self,
plaintext: &mut [u8],
@@ -89,12 +156,20 @@ impl<
{
}
/// The error returned by AEAD operations
#[derive(Debug, Error)]
pub enum Error {
/// An internal error occurred. This should never be happen and indicates an error in the
/// AEAD implementation.
#[error("internal error")]
InternalError,
/// Could not decrypt a message because the message is not a valid ciphertext for the given
/// key.
#[error("decryption error")]
DecryptError,
/// The provided buffers have the wrong lengths.
#[error("buffers have invalid length")]
InvalidLengths,
}

View File

@@ -134,6 +134,54 @@ use thiserror::Error;
///
/// The KEM interface defines three operations: Key generation, key encapsulation and key
/// decapsulation. The parameters are made available as associated constants for convenience.
///
/// The methods of this trait take a `&self` argument as a receiver. This has two reasons:
/// 1. It makes type inference a lot smoother
/// 2. It allows to use the functionality through a trait object or having an enum that has
/// variants for multiple options (like e.g. the `KeyedHash` enum in `rosenpass-ciphers`).
///
/// Since the caller needs an instance of the type to use the functionality, implementors are
/// adviced to implement the [`Default`] trait where possible.
///
/// Example for encrypting a message with a specific [`Kem`] instance:
/// ```
/// use rosenpass_cipher_traits::primitives::Kem;
///
/// const SK_LEN: usize = 1632;
/// const PK_LEN: usize = 800;
/// const CT_LEN: usize = 768;
/// const SHK_LEN: usize = 32;
///
/// fn encaps_given_a_kem<KemImpl>(
/// kem: &KemImpl,
/// pk: &[u8l PK_LEN],
/// ct: &mut [u8; CT_LEN]
/// ) where KemImpl: Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> -> [u8; SHK_LEN]{
/// let mut shk = [0u8; SHK_LEN];
/// kem.encaps(&mut shk, ct, pk).unwrap();
/// shk
/// }
/// ```
///
/// If only the type (but no instance) is available, then we can still use the trait, as long as
/// the type also is [`Default`]:
/// ```
/// use rosenpass_cipher_traits::primitives::Kem;
///
/// const SK_LEN: usize = 1632;
/// const PK_LEN: usize = 800;
/// const CT_LEN: usize = 768;
/// const SHK_LEN: usize = 32;
///
/// fn encaps_without_kem<KemImpl>(
/// pk: &[u8l PK_LEN],
/// ct: &mut [u8; CT_LEN]
/// ) where KemImpl: Default + Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> -> [u8; SHK_LEN]{
/// let mut shk = [0u8; SHK_LEN];
/// KemImpl::default().encaps(&mut shk, ct, pk).unwrap();
/// shk
/// }
/// ```
pub trait Kem<const SK_LEN: usize, const PK_LEN: usize, const CT_LEN: usize, const SHK_LEN: usize> {
/// The length of the secret (decapsulation) key.
const SK_LEN: usize = SK_LEN;

View File

@@ -1,6 +1,11 @@
use std::marker::PhantomData;
/// Models a keyed hash function using an associated function (i.e. without `&self` receiver).
pub trait KeyedHash<const KEY_LEN: usize, const HASH_LEN: usize> {
/// The error type used to signal what went wrong.
type Error;
/// Performs a keyed hash using `key` and `data` and writes the output to `out`
fn keyed_hash(
key: &[u8; KEY_LEN],
data: &[u8],
@@ -8,9 +13,15 @@ pub trait KeyedHash<const KEY_LEN: usize, const HASH_LEN: usize> {
) -> Result<(), Self::Error>;
}
/// Models a keyed hash function using using a method (i.e. with a `&self` receiver).
///
/// This makes type inference easier, but also requires having a [`KeyedHashInstance`] value,
/// instead of just the [`KeyedHash`] type.
pub trait KeyedHashInstance<const KEY_LEN: usize, const HASH_LEN: usize> {
/// The error type used to signal what went wrong.
type Error;
/// Performs a keyed hash using `key` and `data` and writes the output to `out`
fn keyed_hash(
&self,
key: &[u8; KEY_LEN],
@@ -19,8 +30,6 @@ pub trait KeyedHashInstance<const KEY_LEN: usize, const HASH_LEN: usize> {
) -> Result<(), Self::Error>;
}
use std::marker::PhantomData;
/// This is a helper to allow for type parameter inference when calling functions
/// that need a [KeyedHash].
///

View File

@@ -13,10 +13,15 @@ readme = "readme.md"
experiment_libcrux_all = [
"experiment_libcrux_blake2",
"experiment_libcrux_chachapoly",
"experiment_libcrux_chachapoly_test",
"experiment_libcrux_kyber",
]
experiment_libcrux_blake2 = ["dep:libcrux-blake2", "dep:thiserror"]
experiment_libcrux_chachapoly = ["dep:libcrux-chacha20poly1305", "dep:libcrux"]
experiment_libcrux_chachapoly = ["dep:libcrux-chacha20poly1305"]
experiment_libcrux_chachapoly_test = [
"experiment_libcrux_chachapoly",
"dep:libcrux",
]
experiment_libcrux_kyber = ["dep:libcrux-ml-kem", "dep:rand"]
[dependencies]
@@ -31,13 +36,16 @@ static_assertions = { workspace = true }
zeroize = { workspace = true }
chacha20poly1305 = { workspace = true }
blake2 = { workspace = true }
libcrux = { workspace = true, optional = true }
libcrux-chacha20poly1305 = { workspace = true, optional = true }
libcrux-blake2 = { workspace = true, optional = true }
libcrux-ml-kem = { workspace = true, optional = true, features = ["kyber"] }
sha3 = { workspace = true }
rand = { workspace = true, optional = true }
thiserror = { workspace = true, optional = true }
libcrux-chacha20poly1305 = { workspace = true, optional = true }
libcrux-blake2 = { workspace = true, optional = true }
libcrux-ml-kem = { workspace = true, optional = true, features = ["kyber"] }
# this one is only used in testing, so it requires the `experiment_libcrux_chachapoly_test` feature.
libcrux = { workspace = true, optional = true }
[dev-dependencies]
rand = { workspace = true }

View File

@@ -1,16 +1,31 @@
//! This module provides types that enabling choosing the keyed hash building block to be used at
//! runtime (using enums) instead of at compile time (using generics).
use anyhow::Result;
use rosenpass_cipher_traits::primitives::KeyedHashInstance;
pub const KEY_LEN: usize = 32;
pub const HASH_LEN: usize = 32;
use crate::subtle::{
custom::incorrect_hmac_blake2b::IncorrectHmacBlake2b, rust_crypto::keyed_shake256::SHAKE256_32,
};
/// Length of symmetric key throughout Rosenpass.
pub const KEY_LEN: usize = 32;
/// The hash is used as a symmetric key and should have the same length.
pub const HASH_LEN: usize = KEY_LEN;
/// Provides a way to pick which keyed hash to use at runtime.
/// Implements [`KeyedHashInstance`] to allow hashing using the respective algorithm.
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum KeyedHash {
KeyedShake256(super::rust_crypto::keyed_shake256::SHAKE256<KEY_LEN, HASH_LEN>),
IncorrectHmacBlake2b(super::custom::incorrect_hmac_blake2b::IncorrectHmacBlake2b),
/// A hasher backed by [`SHAKE256_32`].
KeyedShake256(SHAKE256_32),
/// A hasher backed by [`IncorrectHmacBlake2b`].
IncorrectHmacBlake2b(IncorrectHmacBlake2b),
}
impl KeyedHash {
/// Creates an [`KeyedHash`] backed by SHAKE256.
pub fn keyed_shake256() -> Self {
Self::KeyedShake256(Default::default())
}

View File

@@ -1,21 +1,29 @@
use rosenpass_cipher_traits::algorithms::KeyedHashBlake2b;
use rosenpass_cipher_traits::primitives::KeyedHash;
//! Implementation of the [`KeyedHashBlake2b`] trait based on the [`libcrux_blake2`] crate.
use libcrux_blake2::Blake2bBuilder;
use rosenpass_cipher_traits::algorithms::KeyedHashBlake2b;
use rosenpass_cipher_traits::primitives::KeyedHash;
pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::HASH_LEN;
pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::KEY_LEN;
/// Describles which error occurred
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// An unexpected internal error occurred. Should never be returned and points to a bug in the
/// implementation.
#[error("internal error")]
InternalError,
/// Indicates that the provided data was too long.
#[error("data is too long")]
DataTooLong,
}
/// Hasher for the given `data` with the Blake2b hash function.
pub struct Blake2b;
pub const KEY_LEN: usize = 32;
pub const HASH_LEN: usize = 32;
impl KeyedHash<KEY_LEN, HASH_LEN> for Blake2b {
type Error = Error;

View File

@@ -1,14 +1,11 @@
//! Implementation of the [`AeadChaCha20Poly1305`] trait based on the [`libcrux_chacha20poly1305`] crate.
use rosenpass_cipher_traits::algorithms::AeadChaCha20Poly1305;
use rosenpass_cipher_traits::primitives::{Aead, AeadError};
/// The key length is 32 bytes or 256 bits.
pub const KEY_LEN: usize = 32; // Grrrr! Libcrux, please provide me these constants.
/// The MAC tag length is 16 bytes or 128 bits.
pub const TAG_LEN: usize = 16;
/// The nonce length is 12 bytes or 96 bits.
pub const NONCE_LEN: usize = 12;
pub use rosenpass_cipher_traits::algorithms::aead_chacha20poly1305::{KEY_LEN, NONCE_LEN, TAG_LEN};
/// An implementation of the ChaCha20Poly1305 AEAD from libcrux
/// An implementation of the ChaCha20Poly1305 AEAD based on libcrux
pub struct ChaCha20Poly1305;
impl Aead<KEY_LEN, NONCE_LEN, TAG_LEN> for ChaCha20Poly1305 {
@@ -64,53 +61,7 @@ mod equivalence_tests {
use rand::RngCore;
#[test]
fn fuzz_equivalence_libcrux_old_new() {
let ptxts: [&[u8]; 3] = [
b"".as_slice(),
b"test".as_slice(),
b"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
];
let mut key = [0; KEY_LEN];
let mut rng = rand::thread_rng();
let mut ctxt_left = [0; 64 + TAG_LEN];
let mut ctxt_right = [0; 64 + TAG_LEN];
let mut ptxt_left = [0; 64];
let mut ptxt_right = [0; 64];
let nonce = [0; NONCE_LEN];
let ad = b"";
for ptxt in ptxts {
for _ in 0..1000 {
rng.fill_bytes(&mut key);
let ctxt_left = &mut ctxt_left[..ptxt.len() + TAG_LEN];
let ctxt_right = &mut ctxt_right[..ptxt.len() + TAG_LEN];
let ptxt_left = &mut ptxt_left[..ptxt.len()];
let ptxt_right = &mut ptxt_right[..ptxt.len()];
encrypt(ctxt_left, &key, &nonce, ad, ptxt).unwrap();
ChaCha20Poly1305
.encrypt(ctxt_right, &key, &nonce, ad, ptxt)
.unwrap();
assert_eq!(ctxt_left, ctxt_right);
decrypt(ptxt_left, &key, &nonce, ad, ctxt_left).unwrap();
ChaCha20Poly1305
.decrypt(ptxt_right, &key, &nonce, ad, ctxt_right)
.unwrap();
assert_eq!(ptxt_left, ptxt);
assert_eq!(ptxt_right, ptxt);
}
}
}
#[test]
fn fuzz_equivalence_libcrux_rustcrypto() {
fn proptest_equivalence_libcrux_rustcrypto() {
use crate::subtle::rust_crypto::chacha20poly1305_ietf::ChaCha20Poly1305 as RustCryptoChaCha20Poly1305;
let ptxts: [&[u8]; 3] = [
b"".as_slice(),
@@ -160,115 +111,164 @@ mod equivalence_tests {
}
}
// The functions below are from the old libcrux backend. I am keeping them around so we can
// check if they behave the same.
use rosenpass_to::ops::copy_slice;
use rosenpass_to::To;
use zeroize::Zeroize;
#[test]
#[cfg(feature = "experiment_libcrux_chachapoly_test")]
fn proptest_equivalence_libcrux_old_new() {
let ptxts: [&[u8]; 3] = [
b"".as_slice(),
b"test".as_slice(),
b"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
];
let mut key = [0; KEY_LEN];
let mut rng = rand::thread_rng();
/// Encrypts using ChaCha20Poly1305 as implemented in [libcrux](https://github.com/cryspen/libcrux).
/// Key and nonce MUST be chosen (pseudo-)randomly. The `key` slice MUST have a length of
/// [KEY_LEN]. The `nonce` slice MUST have a length of [NONCE_LEN]. The last [TAG_LEN] bytes
/// written in `ciphertext` are the tag guaranteeing integrity. `ciphertext` MUST have a capacity of
/// `plaintext.len()` + [TAG_LEN].
///
/// # Examples
///```rust
/// # use rosenpass_ciphers::subtle::chacha20poly1305_ietf_libcrux::{encrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
///
/// const PLAINTEXT_LEN: usize = 43;
/// let plaintext = "post-quantum cryptography is very important".as_bytes();
/// assert_eq!(PLAINTEXT_LEN, plaintext.len());
/// let key: &[u8] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
/// let nonce: &[u8] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
/// let mut ciphertext_buffer = [0u8; PLAINTEXT_LEN + TAG_LEN];
///
/// let res: anyhow::Result<()> = encrypt(&mut ciphertext_buffer, key, nonce, additional_data, plaintext);
/// assert!(res.is_ok());
/// # let expected_ciphertext: &[u8] = &[239, 104, 148, 202, 120, 32, 77, 27, 246, 206, 226, 17,
/// # 83, 78, 122, 116, 187, 123, 70, 199, 58, 130, 21, 1, 107, 230, 58, 77, 18, 152, 31, 159, 80,
/// # 151, 72, 27, 236, 137, 60, 55, 180, 31, 71, 97, 199, 12, 60, 155, 70, 221, 225, 110, 132, 191,
/// # 8, 114, 85, 4, 25];
/// # assert_eq!(expected_ciphertext, &ciphertext_buffer);
///```
///
#[inline]
pub fn encrypt(
ciphertext: &mut [u8],
key: &[u8],
nonce: &[u8],
ad: &[u8],
plaintext: &[u8],
) -> anyhow::Result<()> {
let (ciphertext, mac) = ciphertext.split_at_mut(ciphertext.len() - TAG_LEN);
let mut ctxt_left = [0; 64 + TAG_LEN];
let mut ctxt_right = [0; 64 + TAG_LEN];
use libcrux::aead as C;
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
let crux_iv = C::Iv(nonce.try_into().unwrap());
let mut ptxt_left = [0; 64];
let mut ptxt_right = [0; 64];
copy_slice(plaintext).to(ciphertext);
let crux_tag = libcrux::aead::encrypt(&crux_key, ciphertext, crux_iv, ad).unwrap();
copy_slice(crux_tag.as_ref()).to(mac);
let nonce = [0; NONCE_LEN];
let ad = b"";
match crux_key {
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
_ => panic!(),
for ptxt in ptxts {
for _ in 0..1000 {
rng.fill_bytes(&mut key);
let ctxt_left = &mut ctxt_left[..ptxt.len() + TAG_LEN];
let ctxt_right = &mut ctxt_right[..ptxt.len() + TAG_LEN];
let ptxt_left = &mut ptxt_left[..ptxt.len()];
let ptxt_right = &mut ptxt_right[..ptxt.len()];
encrypt(ctxt_left, &key, &nonce, ad, ptxt).unwrap();
ChaCha20Poly1305
.encrypt(ctxt_right, &key, &nonce, ad, ptxt)
.unwrap();
assert_eq!(ctxt_left, ctxt_right);
decrypt(ptxt_left, &key, &nonce, ad, ctxt_left).unwrap();
ChaCha20Poly1305
.decrypt(ptxt_right, &key, &nonce, ad, ctxt_right)
.unwrap();
assert_eq!(ptxt_left, ptxt);
assert_eq!(ptxt_right, ptxt);
}
}
Ok(())
}
// The old libcrux functions:
/// Decrypts a `ciphertext` and verifies the integrity of the `ciphertext` and the additional data
/// `ad`. using ChaCha20Poly1305 as implemented in [libcrux](https://github.com/cryspen/libcrux).
///
/// The `key` slice MUST have a length of [KEY_LEN]. The `nonce` slice MUST have a length of
/// [NONCE_LEN]. The plaintext buffer must have a capacity of `ciphertext.len()` - [TAG_LEN].
///
/// # Examples
///```rust
/// # use rosenpass_ciphers::subtle::chacha20poly1305_ietf_libcrux::{decrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
/// let ciphertext: &[u8] = &[239, 104, 148, 202, 120, 32, 77, 27, 246, 206, 226, 17,
/// 83, 78, 122, 116, 187, 123, 70, 199, 58, 130, 21, 1, 107, 230, 58, 77, 18, 152, 31, 159, 80,
/// 151, 72, 27, 236, 137, 60, 55, 180, 31, 71, 97, 199, 12, 60, 155, 70, 221, 225, 110, 132, 191,
/// 8, 114, 85, 4, 25]; // this is the ciphertext generated by the example for the encryption
/// const PLAINTEXT_LEN: usize = 43;
/// assert_eq!(PLAINTEXT_LEN + TAG_LEN, ciphertext.len());
///
/// let key: &[u8] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
/// let nonce: &[u8] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
/// let mut plaintext_buffer = [0u8; PLAINTEXT_LEN];
///
/// let res: anyhow::Result<()> = decrypt(&mut plaintext_buffer, key, nonce, additional_data, ciphertext);
/// assert!(res.is_ok());
/// let expected_plaintext = "post-quantum cryptography is very important".as_bytes();
/// assert_eq!(expected_plaintext, plaintext_buffer);
///
///```
#[inline]
pub fn decrypt(
plaintext: &mut [u8],
key: &[u8],
nonce: &[u8],
ad: &[u8],
ciphertext: &[u8],
) -> anyhow::Result<()> {
let (ciphertext, mac) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
// The functions below are from the old libcrux backend. I am keeping them around so we can
// check if they behave the same.
use rosenpass_to::ops::copy_slice;
use rosenpass_to::To;
use zeroize::Zeroize;
use libcrux::aead as C;
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
let crux_iv = C::Iv(nonce.try_into().unwrap());
let crux_tag = C::Tag::from_slice(mac).unwrap();
/// Encrypts using ChaCha20Poly1305 as implemented in [libcrux](https://github.com/cryspen/libcrux).
/// Key and nonce MUST be chosen (pseudo-)randomly. The `key` slice MUST have a length of
/// [KEY_LEN]. The `nonce` slice MUST have a length of [NONCE_LEN]. The last [TAG_LEN] bytes
/// written in `ciphertext` are the tag guaranteeing integrity. `ciphertext` MUST have a capacity of
/// `plaintext.len()` + [TAG_LEN].
///
/// # Examples
///```rust
/// # use rosenpass_ciphers::subtle::chacha20poly1305_ietf_libcrux::{encrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
///
/// const PLAINTEXT_LEN: usize = 43;
/// let plaintext = "post-quantum cryptography is very important".as_bytes();
/// assert_eq!(PLAINTEXT_LEN, plaintext.len());
/// let key: &[u8] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
/// let nonce: &[u8] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
/// let mut ciphertext_buffer = [0u8; PLAINTEXT_LEN + TAG_LEN];
///
/// let res: anyhow::Result<()> = encrypt(&mut ciphertext_buffer, key, nonce, additional_data, plaintext);
/// assert!(res.is_ok());
/// # let expected_ciphertext: &[u8] = &[239, 104, 148, 202, 120, 32, 77, 27, 246, 206, 226, 17,
/// # 83, 78, 122, 116, 187, 123, 70, 199, 58, 130, 21, 1, 107, 230, 58, 77, 18, 152, 31, 159, 80,
/// # 151, 72, 27, 236, 137, 60, 55, 180, 31, 71, 97, 199, 12, 60, 155, 70, 221, 225, 110, 132, 191,
/// # 8, 114, 85, 4, 25];
/// # assert_eq!(expected_ciphertext, &ciphertext_buffer);
///```
///
#[inline]
pub fn encrypt(
ciphertext: &mut [u8],
key: &[u8],
nonce: &[u8],
ad: &[u8],
plaintext: &[u8],
) -> anyhow::Result<()> {
let (ciphertext, mac) = ciphertext.split_at_mut(ciphertext.len() - TAG_LEN);
copy_slice(ciphertext).to(plaintext);
libcrux::aead::decrypt(&crux_key, plaintext, crux_iv, ad, &crux_tag).unwrap();
use libcrux::aead as C;
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
let crux_iv = C::Iv(nonce.try_into().unwrap());
match crux_key {
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
_ => panic!(),
copy_slice(plaintext).to(ciphertext);
let crux_tag = libcrux::aead::encrypt(&crux_key, ciphertext, crux_iv, ad).unwrap();
copy_slice(crux_tag.as_ref()).to(mac);
match crux_key {
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
_ => panic!(),
}
Ok(())
}
Ok(())
/// Decrypts a `ciphertext` and verifies the integrity of the `ciphertext` and the additional data
/// `ad`. using ChaCha20Poly1305 as implemented in [libcrux](https://github.com/cryspen/libcrux).
///
/// The `key` slice MUST have a length of [KEY_LEN]. The `nonce` slice MUST have a length of
/// [NONCE_LEN]. The plaintext buffer must have a capacity of `ciphertext.len()` - [TAG_LEN].
///
/// # Examples
///```rust
/// # use rosenpass_ciphers::subtle::chacha20poly1305_ietf_libcrux::{decrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
/// let ciphertext: &[u8] = &[239, 104, 148, 202, 120, 32, 77, 27, 246, 206, 226, 17,
/// 83, 78, 122, 116, 187, 123, 70, 199, 58, 130, 21, 1, 107, 230, 58, 77, 18, 152, 31, 159, 80,
/// 151, 72, 27, 236, 137, 60, 55, 180, 31, 71, 97, 199, 12, 60, 155, 70, 221, 225, 110, 132, 191,
/// 8, 114, 85, 4, 25]; // this is the ciphertext generated by the example for the encryption
/// const PLAINTEXT_LEN: usize = 43;
/// assert_eq!(PLAINTEXT_LEN + TAG_LEN, ciphertext.len());
///
/// let key: &[u8] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
/// let nonce: &[u8] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
/// let mut plaintext_buffer = [0u8; PLAINTEXT_LEN];
///
/// let res: anyhow::Result<()> = decrypt(&mut plaintext_buffer, key, nonce, additional_data, ciphertext);
/// assert!(res.is_ok());
/// let expected_plaintext = "post-quantum cryptography is very important".as_bytes();
/// assert_eq!(expected_plaintext, plaintext_buffer);
///
///```
#[inline]
pub fn decrypt(
plaintext: &mut [u8],
key: &[u8],
nonce: &[u8],
ad: &[u8],
ciphertext: &[u8],
) -> anyhow::Result<()> {
let (ciphertext, mac) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
use libcrux::aead as C;
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
let crux_iv = C::Iv(nonce.try_into().unwrap());
let crux_tag = C::Tag::from_slice(mac).unwrap();
copy_slice(ciphertext).to(plaintext);
libcrux::aead::decrypt(&crux_key, plaintext, crux_iv, ad, &crux_tag).unwrap();
match crux_key {
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
_ => panic!(),
}
Ok(())
}
}
}

View File

@@ -1,10 +1,14 @@
use libcrux_ml_kem::kyber512;
//! Implementation of the [`KemKyber512`] trait based on the [`libcrux_ml_kem`] crate.
use libcrux_ml_kem::kyber512;
use rand::RngCore;
use rosenpass_cipher_traits::algorithms::kem_kyber512::*;
use rosenpass_cipher_traits::algorithms::KemKyber512;
use rosenpass_cipher_traits::primitives::{Kem, KemError};
pub use rosenpass_cipher_traits::algorithms::kem_kyber512::{CT_LEN, PK_LEN, SHK_LEN, SK_LEN};
/// An implementation of the Kyber512 KEM based on libcrux
pub struct Kyber512;
impl Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> for Kyber512 {
@@ -60,3 +64,70 @@ impl Default for Kyber512 {
}
impl KemKyber512 for Kyber512 {}
#[cfg(test)]
mod equivalence_tests {
use super::*;
// Test that libcrux and OQS produce the same results
#[test]
fn proptest_equivalence_libcrux_oqs() {
use rosenpass_oqs::Kyber512 as OqsKyber512;
let (mut sk1, mut pk1) = ([0; SK_LEN], [0; PK_LEN]);
let (mut sk2, mut pk2) = ([0; SK_LEN], [0; PK_LEN]);
let mut ct_left = [0; CT_LEN];
let mut ct_right = [0; CT_LEN];
let mut shk_enc_left = [0; SHK_LEN];
let mut shk_enc_right = [0; SHK_LEN];
// naming schema: shk_dec_{encapsing lib}_{decapsing lib}
// should be the same if the encapsing lib was the same.
let mut shk_dec_left_left = [0; SHK_LEN];
let mut shk_dec_left_right = [0; SHK_LEN];
let mut shk_dec_right_left = [0; SHK_LEN];
let mut shk_dec_right_right = [0; SHK_LEN];
for _ in 0..1000 {
let sk1 = &mut sk1;
let pk1 = &mut pk1;
let sk2 = &mut sk2;
let pk2 = &mut pk2;
let ct_left = &mut ct_left;
let ct_right = &mut ct_right;
let shk_enc_left = &mut shk_enc_left;
let shk_enc_right = &mut shk_enc_right;
let shk_dec_left_left = &mut shk_dec_left_left;
let shk_dec_left_right = &mut shk_dec_left_right;
let shk_dec_right_left = &mut shk_dec_right_left;
let shk_dec_right_right = &mut shk_dec_right_right;
Kyber512.keygen(sk1, pk1).unwrap();
Kyber512.keygen(sk2, pk2).unwrap();
Kyber512.encaps(shk_enc_left, ct_left, pk2).unwrap();
OqsKyber512.encaps(shk_enc_right, ct_right, pk2).unwrap();
Kyber512.decaps(shk_dec_left_left, sk2, ct_left).unwrap();
Kyber512.decaps(shk_dec_right_left, sk2, ct_right).unwrap();
OqsKyber512
.decaps(shk_dec_left_right, sk2, ct_left)
.unwrap();
OqsKyber512
.decaps(shk_dec_right_right, sk2, ct_right)
.unwrap();
assert_eq!(shk_enc_left, shk_dec_left_left);
assert_eq!(shk_enc_left, shk_dec_left_right);
assert_eq!(shk_enc_right, shk_dec_right_left);
assert_eq!(shk_enc_right, shk_dec_right_right);
}
}
}

View File

@@ -1,4 +1,8 @@
//! Implementations backed by libcrux, a verified crypto library
//! Implementations backed by libcrux, a verified crypto library.
//!
//! [Website](https://cryspen.com/libcrux/)
//!
//! [Github](https://github.com/cryspen/libcrux)
#[cfg(feature = "experiment_libcrux_blake2")]
pub mod blake2b;

View File

@@ -1,3 +1,5 @@
//! Contains the implementations of the crypto algorithms used throughout Rosenpass.
pub mod keyed_hash;
pub use custom::incorrect_hmac_blake2b;

View File

@@ -8,26 +8,24 @@ use blake2::Blake2bMac;
use rosenpass_cipher_traits::primitives::KeyedHash;
use rosenpass_to::{ops::copy_slice, To};
pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::HASH_LEN;
pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::KEY_LEN;
/// Specify that the used implementation of BLAKE2b is the MAC version of BLAKE2b
/// with output and key length of 32 bytes (see [Blake2bMac]).
type Impl = Blake2bMac<U32>;
/// The key length for BLAKE2b supported by this API. Currently 32 Bytes.
const KEY_LEN: usize = 32;
/// The output length for BLAKE2b supported by this API. Currently 32 Bytes.
const OUT_LEN: usize = 32;
/// Hashes the given `data` with the [Blake2bMac] hash function under the given `key`.
/// The both the length of the output the length of the key 32 bytes (or 256 bits).
pub struct Blake2b;
impl KeyedHash<KEY_LEN, OUT_LEN> for Blake2b {
impl KeyedHash<KEY_LEN, HASH_LEN> for Blake2b {
type Error = anyhow::Error;
fn keyed_hash(
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; OUT_LEN],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error> {
let mut h = Impl::new_from_slice(key)?;
h.update(data);
@@ -36,7 +34,7 @@ impl KeyedHash<KEY_LEN, OUT_LEN> for Blake2b {
// but it introduces a ton of complexity. This cost me half an hour just to figure
// out the right way to use the imports while allowing for zeroization.
// An API based on slices might actually be simpler.
let mut tmp = Zeroizing::new([0u8; OUT_LEN]);
let mut tmp = Zeroizing::new([0u8; HASH_LEN]);
let tmp = GenericArray::from_mut_slice(tmp.as_mut());
h.finalize_into(tmp);
copy_slice(tmp.as_ref()).to(out);

View File

@@ -1,19 +1,17 @@
use rosenpass_cipher_traits::primitives::{Aead, AeadError};
use rosenpass_to::ops::copy_slice;
use rosenpass_to::To;
use rosenpass_util::typenum2const;
use rosenpass_cipher_traits::algorithms::AeadChaCha20Poly1305;
use rosenpass_cipher_traits::primitives::{Aead, AeadError};
use chacha20poly1305::aead::generic_array::GenericArray;
use chacha20poly1305::ChaCha20Poly1305 as AeadImpl;
use chacha20poly1305::{AeadCore, AeadInPlace, KeyInit, KeySizeUser};
use chacha20poly1305::{AeadInPlace, KeyInit};
/// The key length is 32 bytes or 256 bits.
pub const KEY_LEN: usize = typenum2const! { <AeadImpl as KeySizeUser>::KeySize };
/// The MAC tag length is 16 bytes or 128 bits.
pub const TAG_LEN: usize = typenum2const! { <AeadImpl as AeadCore>::TagSize };
/// The nonce length is 12 bytes or 96 bits.
pub const NONCE_LEN: usize = typenum2const! { <AeadImpl as AeadCore>::NonceSize };
pub use rosenpass_cipher_traits::algorithms::aead_chacha20poly1305::{KEY_LEN, NONCE_LEN, TAG_LEN};
/// Implements the [`Aead`] and [`AeadChaCha20Poly1305`] traits backed by the RustCrypto
/// implementation.
pub struct ChaCha20Poly1305;
impl Aead<KEY_LEN, NONCE_LEN, TAG_LEN> for ChaCha20Poly1305 {
@@ -78,81 +76,4 @@ impl Aead<KEY_LEN, NONCE_LEN, TAG_LEN> for ChaCha20Poly1305 {
}
}
/// Encrypts using ChaCha20Poly1305 as implemented in [RustCrypto](https://github.com/RustCrypto/AEADs/tree/master/chacha20poly1305).
/// `key` MUST be chosen (pseudo-)randomly and `nonce` MOST NOT be reused. The `key` slice MUST have
/// a length of [KEY_LEN]. The `nonce` slice MUST have a length of [NONCE_LEN]. The last [TAG_LEN] bytes
/// written in `ciphertext` are the tag guaranteeing integrity. `ciphertext` MUST have a capacity of
/// `plaintext.len()` + [TAG_LEN].
///
/// # Examples
///```rust
/// # use rosenpass_ciphers::subtle::rust_crypto::chacha20poly1305_ietf::{encrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
///
/// const PLAINTEXT_LEN: usize = 43;
/// let plaintext = "post-quantum cryptography is very important".as_bytes();
/// assert_eq!(PLAINTEXT_LEN, plaintext.len());
/// let key: &[u8; KEY_LEN] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
/// let nonce: &[u8; NONCE_LEN] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
/// let mut ciphertext_buffer = [0u8;PLAINTEXT_LEN + TAG_LEN];
///
/// let res: anyhow::Result<()> = encrypt(&mut ciphertext_buffer, key, nonce, additional_data, plaintext);
/// assert!(res.is_ok());
/// # let expected_ciphertext: &[u8] = &[239, 104, 148, 202, 120, 32, 77, 27, 246, 206, 226, 17,
/// # 83, 78, 122, 116, 187, 123, 70, 199, 58, 130, 21, 1, 107, 230, 58, 77, 18, 152, 31, 159, 80,
/// # 151, 72, 27, 236, 137, 60, 55, 180, 31, 71, 97, 199, 12, 60, 155, 70, 221, 225, 110, 132, 191,
/// # 8, 114, 85, 4, 25];
/// # assert_eq!(expected_ciphertext, &ciphertext_buffer);
///```
#[inline]
pub fn encrypt(
ciphertext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
plaintext: &[u8],
) -> anyhow::Result<()> {
ChaCha20Poly1305
.encrypt(ciphertext, key, nonce, ad, plaintext)
.map_err(anyhow::Error::from)
}
/// Decrypts a `ciphertext` and verifies the integrity of the `ciphertext` and the additional data
/// `ad`. using ChaCha20Poly1305 as implemented in [RustCrypto](https://github.com/RustCrypto/AEADs/tree/master/chacha20poly1305).
///
/// The `key` slice MUST have a length of [KEY_LEN]. The `nonce` slice MUST have a length of
/// [NONCE_LEN]. The plaintext buffer must have a capacity of `ciphertext.len()` - [TAG_LEN].
///
/// # Examples
///```rust
/// # use rosenpass_ciphers::subtle::rust_crypto::chacha20poly1305_ietf::{decrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
/// let ciphertext: &[u8] = &[239, 104, 148, 202, 120, 32, 77, 27, 246, 206, 226, 17,
/// 83, 78, 122, 116, 187, 123, 70, 199, 58, 130, 21, 1, 107, 230, 58, 77, 18, 152, 31, 159, 80,
/// 151, 72, 27, 236, 137, 60, 55, 180, 31, 71, 97, 199, 12, 60, 155, 70, 221, 225, 110, 132, 191,
/// 8, 114, 85, 4, 25]; // this is the ciphertext generated by the example for the encryption
/// const PLAINTEXT_LEN: usize = 43;
/// assert_eq!(PLAINTEXT_LEN + TAG_LEN, ciphertext.len());
///
/// let key: &[u8; KEY_LEN] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
/// let nonce: &[u8; NONCE_LEN] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
/// let mut plaintext_buffer = [0u8; PLAINTEXT_LEN];
///
/// let res: anyhow::Result<()> = decrypt(&mut plaintext_buffer, key, nonce, additional_data, ciphertext);
/// assert!(res.is_ok());
/// let expected_plaintext = "post-quantum cryptography is very important".as_bytes();
/// assert_eq!(expected_plaintext, plaintext_buffer);
///
///```
#[inline]
pub fn decrypt(
plaintext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
ciphertext: &[u8],
) -> anyhow::Result<()> {
ChaCha20Poly1305
.decrypt(plaintext, key, nonce, ad, ciphertext)
.map_err(anyhow::Error::from)
}
impl AeadChaCha20Poly1305 for ChaCha20Poly1305 {}

View File

@@ -3,6 +3,7 @@ use rosenpass_cipher_traits::primitives::{InferKeyedHash, KeyedHash};
use sha3::digest::{ExtendableOutput, Update, XofReader};
use sha3::Shake256;
/// An implementation of the [`KeyedHash`] trait backed by the RustCrypto implementation of SHAKE256.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SHAKE256Core<const KEY_LEN: usize, const HASH_LEN: usize>;

View File

@@ -1,19 +1,17 @@
use rosenpass_cipher_traits::primitives::{Aead, AeadError, AeadWithNonceInCiphertext};
use rosenpass_to::ops::copy_slice;
use rosenpass_to::To;
use rosenpass_util::typenum2const;
use rosenpass_cipher_traits::algorithms::aead_xchacha20poly1305::{
AeadXChaCha20Poly1305, KEY_LEN, NONCE_LEN, TAG_LEN,
};
use rosenpass_cipher_traits::primitives::{Aead, AeadError, AeadWithNonceInCiphertext};
use chacha20poly1305::aead::generic_array::GenericArray;
use chacha20poly1305::XChaCha20Poly1305 as AeadImpl;
use chacha20poly1305::{AeadCore, AeadInPlace, KeyInit, KeySizeUser};
/// The key length is 32 bytes or 256 bits.
pub const KEY_LEN: usize = typenum2const! { <AeadImpl as KeySizeUser>::KeySize };
/// The MAC tag length is 16 bytes or 128 bits.
pub const TAG_LEN: usize = typenum2const! { <AeadImpl as AeadCore>::TagSize };
/// The nonce length is 24 bytes or 192 bits.
pub const NONCE_LEN: usize = typenum2const! { <AeadImpl as AeadCore>::NonceSize };
use chacha20poly1305::{AeadInPlace, KeyInit};
/// Implements the [`Aead`] and [`AeadXChaCha20Poly1305`] traits backed by the RustCrypto
/// implementation.
pub struct XChaCha20Poly1305;
impl Aead<KEY_LEN, NONCE_LEN, TAG_LEN> for XChaCha20Poly1305 {
@@ -77,6 +75,8 @@ impl Aead<KEY_LEN, NONCE_LEN, TAG_LEN> for XChaCha20Poly1305 {
}
}
impl AeadXChaCha20Poly1305 for XChaCha20Poly1305 {}
/// Encrypts using XChaCha20Poly1305 as implemented in [RustCrypto](https://github.com/RustCrypto/AEADs/tree/master/chacha20poly1305).
/// `key` and `nonce` MUST be chosen (pseudo-)randomly. The `key` slice MUST have a length of
/// [KEY_LEN]. The `nonce` slice MUST have a length of [NONCE_LEN].

View File

@@ -3337,35 +3337,18 @@ impl HandshakeState {
const KEM_PK_LEN: usize,
const KEM_CT_LEN: usize,
const KEM_SHK_LEN: usize,
T: Default + Kem<KEM_SK_LEN, KEM_PK_LEN, KEM_CT_LEN, KEM_SHK_LEN>,
KemImpl: Kem<KEM_SK_LEN, KEM_PK_LEN, KEM_CT_LEN, KEM_SHK_LEN>,
>(
&mut self,
kem: &KemImpl,
ct: &mut [u8; KEM_CT_LEN],
pk: &[u8; KEM_PK_LEN],
) -> Result<&mut Self> {
let mut shk = Secret::<KEM_SHK_LEN>::zero();
T::default().encaps(shk.secret_mut(), ct, pk)?;
kem.encaps(shk.secret_mut(), ct, pk)?;
self.mix(pk)?.mix(shk.secret())?.mix(ct)
}
/// Calls [`Self::encaps_and_mix`] with the generic parameters that match [`StaticKem`].
pub fn encaps_and_mix_static(
&mut self,
ct: &mut [u8; StaticKem::CT_LEN],
pk: &[u8; StaticKem::PK_LEN],
) -> Result<&mut Self> {
self.encaps_and_mix::<{StaticKem::SK_LEN},{ StaticKem::PK_LEN}, {StaticKem::CT_LEN}, {StaticKem::SHK_LEN}, StaticKem>(ct, pk)
}
/// Calls [`Self::encaps_and_mix`] with the generic parameters that match [`EphemeralKem`].
pub fn encaps_and_mix_ephemeral(
&mut self,
ct: &mut [u8; EphemeralKem::CT_LEN],
pk: &[u8; EphemeralKem::PK_LEN],
) -> Result<&mut Self> {
self.encaps_and_mix::<{EphemeralKem::SK_LEN},{ EphemeralKem::PK_LEN}, {EphemeralKem::CT_LEN}, {EphemeralKem::SHK_LEN}, EphemeralKem>(ct, pk)
}
/// Decapsulation (decryption) counterpart to [Self::encaps_and_mix].
///
/// Makes sure that the same values are mixed into the chaining that where mixed in on the
@@ -3375,38 +3358,19 @@ impl HandshakeState {
const KEM_PK_LEN: usize,
const KEM_CT_LEN: usize,
const KEM_SHK_LEN: usize,
T: Default + Kem<KEM_SK_LEN, KEM_PK_LEN, KEM_CT_LEN, KEM_SHK_LEN>,
KemImpl: Kem<KEM_SK_LEN, KEM_PK_LEN, KEM_CT_LEN, KEM_SHK_LEN>,
>(
&mut self,
kem: &KemImpl,
sk: &[u8; KEM_SK_LEN],
pk: &[u8; KEM_PK_LEN],
ct: &[u8; KEM_CT_LEN],
) -> Result<&mut Self> {
let mut shk = Secret::<KEM_SHK_LEN>::zero();
T::default().decaps(shk.secret_mut(), sk, ct)?;
kem.decaps(shk.secret_mut(), sk, ct)?;
self.mix(pk)?.mix(shk.secret())?.mix(ct)
}
/// Calls [`Self::decaps_and_mix`] with the generic parameters that match [`StaticKem`].
pub fn decaps_and_mix_static(
&mut self,
sk: &[u8; StaticKem::SK_LEN],
pk: &[u8; StaticKem::PK_LEN],
ct: &[u8; StaticKem::CT_LEN],
) -> Result<&mut Self> {
self.decaps_and_mix::<{StaticKem::SK_LEN},{ StaticKem::PK_LEN}, {StaticKem::CT_LEN}, {StaticKem::SHK_LEN}, StaticKem>(sk, pk, ct)
}
/// Calls [`Self::decaps_and_mix`] with the generic parameters that match [`EphemeralKem`].
pub fn decaps_and_mix_ephemeral(
&mut self,
sk: &[u8; EphemeralKem::SK_LEN],
pk: &[u8; EphemeralKem::PK_LEN],
ct: &[u8; EphemeralKem::CT_LEN],
) -> Result<&mut Self> {
self.decaps_and_mix::<{EphemeralKem::SK_LEN},{ EphemeralKem::PK_LEN}, {EphemeralKem::CT_LEN}, {EphemeralKem::SHK_LEN}, EphemeralKem>(sk, pk, ct)
}
/// Store the chaining key inside a cookie value called a "biscuit".
///
/// This biscuit can be transmitted to the other party and must be returned
@@ -3595,7 +3559,7 @@ impl CryptoServer {
// IHI5
hs.core
.encaps_and_mix_static(&mut ih.sctr, peer.get(self).spkt.deref())?;
.encaps_and_mix(&StaticKem, &mut ih.sctr, peer.get(self).spkt.deref())?;
// IHI6
hs.core.encrypt_and_mix(
@@ -3638,7 +3602,7 @@ impl CryptoServer {
core.mix(&ih.sidi)?.mix(&ih.epki)?;
// IHR5
core.decaps_and_mix_static(self.sskm.secret(), self.spkm.deref(), &ih.sctr)?;
core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &ih.sctr)?;
// IHR6
let peer = {
@@ -3664,10 +3628,10 @@ impl CryptoServer {
core.mix(&rh.sidr)?.mix(&rh.sidi)?;
// RHR4
core.encaps_and_mix_ephemeral(&mut rh.ecti, &ih.epki)?;
core.encaps_and_mix(&EphemeralKem, &mut rh.ecti, &ih.epki)?;
// RHR5
core.encaps_and_mix_static(&mut rh.scti, peer.get(self).spkt.deref())?;
core.encaps_and_mix(&StaticKem, &mut rh.scti, peer.get(self).spkt.deref())?;
// RHR6
core.store_biscuit(self, peer, &mut rh.biscuit)?;
@@ -3727,10 +3691,15 @@ impl CryptoServer {
core.mix(&rh.sidr)?.mix(&rh.sidi)?;
// RHI4
core.decaps_and_mix_ephemeral(hs!().eski.secret(), hs!().epki.deref(), &rh.ecti)?;
core.decaps_and_mix(
&EphemeralKem,
hs!().eski.secret(),
hs!().epki.deref(),
&rh.ecti,
)?;
// RHI5
core.decaps_and_mix_static(self.sskm.secret(), self.spkm.deref(), &rh.scti)?;
core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &rh.scti)?;
// RHI6
core.mix(&rh.biscuit)?;