diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 index 2c39f9c..2c828cb --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ Cargo.lock **/*.rs.bk .vscode/ -evercrypt_provider/target +libcrux_provider/target no-std-support-check/target rust_crypto_provider/target traits/target/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 7859e33..c64d5d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.2.1] - Unreleased +- [#72](https://github.com/cryspen/hpke-rs/pull/72): + - add support for X-Wing KEM + - upgrade rand dependency from 0.8 -> 0.9 + - replace Evercrypt provider with Libcrux provider - [#66](https://github.com/franziskuskiefer/hpke-rs/pull/66): add support for secp256k1 curve. This adds `DhKemK256 = 0x0016` to the `KemAlgorithms` ## [0.2.0] - 2023-12-01 diff --git a/Cargo.toml b/Cargo.toml old mode 100755 new mode 100644 index e88d682..277c1e8 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "hpke-rs" -version = "0.2.1-pre.1" +version = "0.2.1-alpha.1" authors = ["Franziskus Kiefer "] edition = "2021" license = "MPL-2.0" documentation = "https://docs.rs/hpke-rs" description = "HPKE Implementation" readme = "Readme.md" -repository = "https://github.com/franziskuskiefer/hpke-rs" +repository = "https://github.com/cryspen/hpke-rs" exclude = ["/tests"] [dependencies] @@ -15,28 +15,34 @@ log = "0.4" serde = { version = "1.0", features = ["derive"], optional = true } tls_codec = { version = "0.4.1-pre.1", features = ["derive"], optional = true } zeroize = { version = "1.5", features = ["zeroize_derive"] } -hpke-rs-crypto = { version = "0.2.0", path = "./traits" } +hpke-rs-crypto = { version = "0.3.0-alpha.1", path = "./traits", default-features = false } +rand_core = { version = "0.9", default-features = false} +libcrux-sha3 = { version = "0.0.2" } [features] default = [] -std = [] +std = ["rand_core/std", "hpke-rs-crypto/std"] serialization = ["serde", "tls_codec", "tls_codec/serde", "std"] hazmat = [] hpke-test = ["std"] -hpke-test-prng = [] # ⚠️ Enable testing PRNG - DO NOT USE +hpke-test-prng = [] # ⚠️ Enable testing PRNG - DO NOT USE [dev-dependencies] -hpke-rs-crypto = { version = "0.2.0", path = "./traits", features = ["std"] } +hpke-rs-crypto = { version = "0.3.0-alpha.1", path = "./traits", features = [ + "std", +] } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } lazy_static = "1.4" rayon = "1.5" hpke-rs = { path = ".", features = ["hpke-test", "hazmat"] } -hpke-rs-rust-crypto = { version = "0.2.0", path = "./rust_crypto_provider", features = [ +hpke-rs-rust-crypto = { version = "0.3.0-alpha.1", path = "./rust_crypto_provider", features = [ + "deterministic-prng", +] } +hpke-rs-libcrux = { version = "0.2.0-alpha.1", path = "./libcrux_provider", features = [ "deterministic-prng", ] } -# hpke-rs-evercrypt = { version = "0.1.3-pre.1", path = "./evercrypt_provider", features = ["deterministic-prng"] } -rand = { version = "0.8" } +rand = { version = "0.9" } pretty_env_logger = "0.5" criterion = { version = "0.5", features = ["html_reports"] } diff --git a/Readme.md b/Readme.md index 02c322f..d5e833c 100644 --- a/Readme.md +++ b/Readme.md @@ -50,7 +50,7 @@ Instead it expects an implementation of the [HpkeCrypto] trait. [crate-link]: https://crates.io/crates/hpke-rs [docs-badge]: https://img.shields.io/badge/docs-rs-blue.svg?style=for-the-badge [docs-link]: https://docs.rs/hpke-rs -[evercrypt]: https://github.com/franziskuskiefer/evercrypt-rust +[libcrux]: https://github.com/cryspen/libcrux [hpke (RFC 9180)]: https://www.rfc-editor.org/rfc/rfc9180.html [hpkecrypto]: https://docs.rs/hpke-rs-crypto [rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg?style=for-the-badge diff --git a/benches/bench.rs b/benches/bench.rs index c82d1b4..804869d 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -4,9 +4,8 @@ use hpke_rs_crypto::{ types::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}, HpkeCrypto, RngCore, }; -// use hpke_rs_evercrypt::*; +use hpke_rs_libcrux::HpkeLibcrux; use hpke_rs_rust_crypto::*; -use rand::rngs::OsRng; const MODES: [Mode; 4] = [ HpkeMode::Base, @@ -81,13 +80,13 @@ fn benchmark(c: &mut Criterion) { (None, None) }; - let mut group = c.benchmark_group(format!("{}", label)); + let mut group = c.benchmark_group(label.to_string()); group.bench_function("Setup Sender", |b| { b.iter(|| { let mut hpke = Hpke::::new(hpke_mode, kem_mode, kdf_mode, aead_mode); hpke.setup_sender( - &pk_rm, + pk_rm, &info, psk.as_ref().map(Vec::as_ref), psk_id.as_ref().map(Vec::as_ref), @@ -102,7 +101,7 @@ fn benchmark(c: &mut Criterion) { Hpke::::new(hpke_mode, kem_mode, kdf_mode, aead_mode); hpke.setup_receiver( enc, - &sk_rm, + sk_rm, &info, psk.as_ref().map(Vec::as_ref), psk_id.as_ref().map(Vec::as_ref), @@ -112,14 +111,14 @@ fn benchmark(c: &mut Criterion) { }) }); - group.bench_function(&format!("Seal {}({})", AEAD_PAYLOAD, AEAD_AAD), |b| { + group.bench_function(format!("Seal {}({})", AEAD_PAYLOAD, AEAD_AAD), |b| { b.iter_batched( || { let mut hpke = Hpke::::new(hpke_mode, kem_mode, kdf_mode, aead_mode); let (_enc, context) = hpke .setup_sender( - &pk_rm, + pk_rm, &info, psk.as_ref().map(Vec::as_ref), psk_id.as_ref().map(Vec::as_ref), @@ -127,9 +126,9 @@ fn benchmark(c: &mut Criterion) { ) .unwrap(); let mut aad = vec![0u8; AEAD_AAD]; - OsRng.fill_bytes(&mut aad); + rand::rng().fill_bytes(&mut aad); let mut ptxt = vec![0u8; AEAD_PAYLOAD]; - OsRng.fill_bytes(&mut ptxt); + rand::rng().fill_bytes(&mut ptxt); (context, aad, ptxt) }, |(mut context, aad, ptxt)| { @@ -138,14 +137,14 @@ fn benchmark(c: &mut Criterion) { BatchSize::SmallInput, ) }); - group.bench_function(&format!("Open {}({})", AEAD_PAYLOAD, AEAD_AAD), |b| { + group.bench_function(format!("Open {}({})", AEAD_PAYLOAD, AEAD_AAD), |b| { b.iter_batched( || { let mut hpke = Hpke::::new(hpke_mode, kem_mode, kdf_mode, aead_mode); let (enc, mut sender_context) = hpke .setup_sender( - &pk_rm, + pk_rm, &info, psk.as_ref().map(Vec::as_ref), psk_id.as_ref().map(Vec::as_ref), @@ -153,15 +152,15 @@ fn benchmark(c: &mut Criterion) { ) .unwrap(); let mut aad = vec![0u8; AEAD_AAD]; - OsRng.fill_bytes(&mut aad); + rand::rng().fill_bytes(&mut aad); let mut ptxt = vec![0u8; AEAD_PAYLOAD]; - OsRng.fill_bytes(&mut ptxt); + rand::rng().fill_bytes(&mut ptxt); let ctxt = sender_context.seal(&aad, &ptxt).unwrap(); let context = hpke .setup_receiver( &enc, - &sk_rm, + sk_rm, &info, psk.as_ref().map(Vec::as_ref), psk_id.as_ref().map(Vec::as_ref), @@ -178,7 +177,7 @@ fn benchmark(c: &mut Criterion) { }); group.bench_function( - &format!("Single-Shot Seal {}({})", AEAD_PAYLOAD, AEAD_AAD), + format!("Single-Shot Seal {}({})", AEAD_PAYLOAD, AEAD_AAD), |b| { b.iter_batched( || { @@ -186,15 +185,15 @@ fn benchmark(c: &mut Criterion) { hpke_mode, kem_mode, kdf_mode, aead_mode, ); let mut aad = vec![0u8; AEAD_AAD]; - OsRng.fill_bytes(&mut aad); + rand::rng().fill_bytes(&mut aad); let mut ptxt = vec![0u8; AEAD_PAYLOAD]; - OsRng.fill_bytes(&mut ptxt); + rand::rng().fill_bytes(&mut ptxt); (hpke, aad, ptxt) }, |(mut hpke, aad, ptxt)| { let _ctxt = hpke .seal( - &pk_rm, + pk_rm, &info, &aad, &ptxt, @@ -209,7 +208,7 @@ fn benchmark(c: &mut Criterion) { }, ); group.bench_function( - &format!("Single-Shot Open {}({})", AEAD_PAYLOAD, AEAD_AAD), + format!("Single-Shot Open {}({})", AEAD_PAYLOAD, AEAD_AAD), |b| { b.iter_batched( || { @@ -218,7 +217,7 @@ fn benchmark(c: &mut Criterion) { ); let (enc, mut sender_context) = hpke .setup_sender( - &pk_rm, + pk_rm, &info, psk.as_ref().map(Vec::as_ref), psk_id.as_ref().map(Vec::as_ref), @@ -226,9 +225,9 @@ fn benchmark(c: &mut Criterion) { ) .unwrap(); let mut aad = vec![0u8; AEAD_AAD]; - OsRng.fill_bytes(&mut aad); + rand::rng().fill_bytes(&mut aad); let mut ptxt = vec![0u8; AEAD_PAYLOAD]; - OsRng.fill_bytes(&mut ptxt); + rand::rng().fill_bytes(&mut ptxt); let ctxt = sender_context.seal(&aad, &ptxt).unwrap(); (hpke, aad, ctxt, enc) @@ -237,7 +236,7 @@ fn benchmark(c: &mut Criterion) { let _ctxt_out = hpke .open( &enc, - &sk_rm, + sk_rm, &info, &aad, &ctxt, @@ -259,7 +258,7 @@ fn benchmark(c: &mut Criterion) { criterion_group!( benches, - // benchmark::, + benchmark::, benchmark::, ); criterion_main!(benches); diff --git a/benches/manual_benches.rs b/benches/manual_benches.rs index 4210cf8..005d486 100644 --- a/benches/manual_benches.rs +++ b/benches/manual_benches.rs @@ -5,9 +5,8 @@ use hpke_rs_crypto::{ types::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}, HpkeCrypto, RngCore, }; -// use hpke_rs_evercrypt::*; +use hpke_rs_libcrux::HpkeLibcrux; use hpke_rs_rust_crypto::*; -use rand::rngs::OsRng; fn duration(d: Duration) -> f64 { ((d.as_secs() as f64) + (d.subsec_nanos() as f64 * 1e-9)) * 1000000f64 @@ -103,7 +102,7 @@ fn benchmark() { Hpke::::new(hpke_mode, kem_mode, kdf_mode, aead_mode); let _sender = hpke .setup_sender( - &pk_rm, + pk_rm, &info, psk.as_ref().map(Vec::as_ref), psk_id.as_ref().map(Vec::as_ref), @@ -121,7 +120,7 @@ fn benchmark() { let _receiver = hpke .setup_receiver( enc, - &sk_rm, + sk_rm, &info, psk.as_ref().map(Vec::as_ref), psk_id.as_ref().map(Vec::as_ref), @@ -135,7 +134,7 @@ fn benchmark() { let (enc, mut context) = hpke .setup_sender( - &pk_rm, + pk_rm, &info, psk.as_ref().map(Vec::as_ref), psk_id.as_ref().map(Vec::as_ref), @@ -143,9 +142,9 @@ fn benchmark() { ) .unwrap(); let mut aad = vec![0u8; AEAD_AAD]; - OsRng.fill_bytes(&mut aad); + rand::rng().fill_bytes(&mut aad); let mut ptxt = vec![0u8; AEAD_PAYLOAD]; - OsRng.fill_bytes(&mut ptxt); + rand::rng().fill_bytes(&mut ptxt); let mut ctxts = Vec::with_capacity((AEAD_PAYLOAD + 16) * ITERATIONS); let start = Instant::now(); @@ -165,7 +164,7 @@ fn benchmark() { let mut context = hpke .setup_receiver( &enc, - &sk_rm, + sk_rm, &info, psk.as_ref().map(Vec::as_ref), psk_id.as_ref().map(Vec::as_ref), @@ -190,9 +189,9 @@ fn benchmark() { assert_eq!(ptxts[0], ptxt); let mut aad = vec![0u8; AEAD_AAD]; - OsRng.fill_bytes(&mut aad); + rand::rng().fill_bytes(&mut aad); let mut ptxt = vec![0u8; AEAD_PAYLOAD]; - OsRng.fill_bytes(&mut ptxt); + rand::rng().fill_bytes(&mut ptxt); let mut enc = Vec::::new(); let mut ctxt = Vec::::new(); @@ -200,7 +199,7 @@ fn benchmark() { for _ in 0..ITERATIONS { let (new_enc, new_ctxt) = hpke .seal( - &pk_rm, + pk_rm, &info, &aad, &ptxt, @@ -227,7 +226,7 @@ fn benchmark() { ptxt_out = hpke .open( &enc, - &sk_rm, + sk_rm, &info, &aad, &ctxt, @@ -253,6 +252,6 @@ fn benchmark() { } fn main() { - // benchmark::(); + benchmark::(); benchmark::(); } diff --git a/evercrypt_provider/Cargo.toml b/evercrypt_provider/Cargo.toml deleted file mode 100644 index f40a2e8..0000000 --- a/evercrypt_provider/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "hpke-rs-evercrypt" -version = "0.1.3-pre.1" -authors = ["Franziskus Kiefer "] -edition = "2021" -license = "MPL-2.0" -documentation = "https://docs.rs/hpke-rs-evercrypt" -description = "Crypto backend for HPKE using formally verified code from Evercrypt." -readme = "Readme.md" -repository = "https://github.com/franziskuskiefer/hpke-rs" - -[dependencies] -hpke-rs-crypto = { version = "0.1.2-pre.1", path = "../traits" } -# Evercrypt -evercrypt = { version = "0.0.11", features = ["serialization"] } -# Randomness -rand = { version = "0.8" } -rand_chacha = { version = "0.3" } - -[dev-dependencies] -criterion = { version = "0.5", features = ["html_reports"] } - -[features] -deterministic-prng = [] # ⚠️ FOR TESTING ONLY. - -[[bench]] -name = "bench_hkdf" -harness = false - -[[bench]] -name = "bench_p256" -harness = false - -[[bench]] -name = "bench_x25519" -harness = false diff --git a/evercrypt_provider/Readme.md b/evercrypt_provider/Readme.md deleted file mode 100644 index 0010c4e..0000000 --- a/evercrypt_provider/Readme.md +++ /dev/null @@ -1,18 +0,0 @@ -# HPKE Crypto provider using Evercrypt - -[![crates.io][crate-badge]][crate-link] -[![Docs][docs-badge]][docs-link] -![Rust Version][rustc-image] - -This crate provides an implementation of the [HpkeCrypto] trait using [Evercrypt]. - -Please see [hpke-rs] for more details. - -[evercrypt]: https://crates.io/crates/evercrypt -[hpkecrypto]: https://github.com/franziskuskiefer/hpke-rs/tree/main/traits -[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg?style=for-the-badge -[docs-badge]: https://img.shields.io/badge/docs-rs-blue.svg?style=for-the-badge -[docs-link]: https://docs.rs/hpke-rs-evercrypt -[crate-badge]: https://img.shields.io/crates/v/hpke-rs-evercrypt.svg?style=for-the-badge -[crate-link]: https://crates.io/crates/hpke-rs-evercrypt -[hpke-rs]: https://github.com/franziskuskiefer/hpke-rs diff --git a/evercrypt_provider/src/lib.rs b/evercrypt_provider/src/lib.rs deleted file mode 100644 index 61c7c64..0000000 --- a/evercrypt_provider/src/lib.rs +++ /dev/null @@ -1,268 +0,0 @@ -#![doc = include_str!("../Readme.md")] - -use std::{fmt::Display, sync::RwLock}; - -use evercrypt::prelude::*; -use hpke_rs_crypto::{ - error::Error, - types::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}, - HpkeCrypto, HpkeTestRng, -}; -use rand::{CryptoRng, RngCore, SeedableRng}; - -/// The Evercrypt HPKE Provider -#[derive(Debug)] -pub struct HpkeEvercrypt {} - -/// The PRNG for the Evercrypt Provider. -pub struct HpkeEvercryptPrng { - #[cfg(feature = "deterministic-prng")] - fake_rng: Vec, - rng: RwLock, -} - -impl HpkeCrypto for HpkeEvercrypt { - fn name() -> String { - "Evercrypt".into() - } - - fn kdf_extract(alg: KdfAlgorithm, salt: &[u8], ikm: &[u8]) -> Vec { - match alg { - KdfAlgorithm::HkdfSha256 => hkdf_extract(HmacMode::Sha256, salt, ikm), - KdfAlgorithm::HkdfSha384 => hkdf_extract(HmacMode::Sha384, salt, ikm), - KdfAlgorithm::HkdfSha512 => hkdf_extract(HmacMode::Sha512, salt, ikm), - } - } - - fn kdf_expand( - alg: KdfAlgorithm, - prk: &[u8], - info: &[u8], - output_size: usize, - ) -> Result, Error> { - Ok(match alg { - KdfAlgorithm::HkdfSha256 => hkdf_expand(HmacMode::Sha256, prk, info, output_size), - KdfAlgorithm::HkdfSha384 => hkdf_expand(HmacMode::Sha384, prk, info, output_size), - KdfAlgorithm::HkdfSha512 => hkdf_expand(HmacMode::Sha512, prk, info, output_size), - }) - } - - fn kem_derive(alg: KemAlgorithm, pk: &[u8], sk: &[u8]) -> Result, Error> { - let evercrypt_mode = kem_key_type_to_mode(alg)?; - ecdh_derive(evercrypt_mode, pk, sk) - .map_err(|e| Error::CryptoLibraryError(format!("ECDH derive error: {:?}", e))) - .map(|mut p| { - if evercrypt_mode == EcdhMode::P256 { - // We only want the x-coordinate here but evercrypt gives us the entire point - p.truncate(32); - p - } else { - p - } - }) - } - - fn kem_derive_base(alg: KemAlgorithm, sk: &[u8]) -> Result, Error> { - let evercrypt_mode = kem_key_type_to_mode(alg)?; - ecdh_derive_base(evercrypt_mode, sk) - .map_err(|e| Error::CryptoLibraryError(format!("ECDH derive base error: {:?}", e))) - .map(|p| { - if evercrypt_mode == EcdhMode::P256 { - nist_format_uncompressed(p) - } else { - p - } - }) - } - - fn kem_key_gen(alg: KemAlgorithm, _: &mut Self::HpkePrng) -> Result, Error> { - // XXX: Evercypt doesn't support bring your own randomness yet. - // https://github.com/franziskuskiefer/evercrypt-rust/issues/35 - let evercrypt_mode = kem_key_type_to_mode(alg)?; - ecdh::key_gen(evercrypt_mode) - .map_err(|e| Error::CryptoLibraryError(format!("ECDH key gen error: {:?}", e))) - } - - fn kem_validate_sk(alg: KemAlgorithm, sk: &[u8]) -> Result, Error> { - match alg { - KemAlgorithm::DhKemP256 => p256_validate_sk(&sk) - .map_err(|e| Error::CryptoLibraryError(format!("ECDH invalid sk error: {:?}", e))) - .map(|sk| sk.to_vec()), - _ => Err(Error::UnknownKemAlgorithm), - } - } - - fn aead_seal( - alg: AeadAlgorithm, - key: &[u8], - nonce: &[u8], - aad: &[u8], - msg: &[u8], - ) -> Result, Error> { - let mode = aead_type_to_mode(alg)?; - if nonce.len() != 12 { - return Err(Error::AeadInvalidNonce); - } - - let cipher = match Aead::new(mode, key) { - Ok(c) => c, - Err(_) => return Err(Error::CryptoLibraryError(format!("Invalid configuration"))), - }; - - cipher - .encrypt_combined(&msg, &nonce, &aad) - .map_err(|e| Error::CryptoLibraryError(format!("AEAD encrypt error: {:?}", e))) - } - - fn aead_open( - alg: AeadAlgorithm, - key: &[u8], - nonce: &[u8], - aad: &[u8], - cipher_txt: &[u8], - ) -> Result, Error> { - let mode = aead_type_to_mode(alg)?; - let cipher = match Aead::new(mode, key) { - Ok(c) => c, - Err(_) => { - return Err(Error::CryptoLibraryError(format!( - "Invalid configuration or unsupported algorithm {:?}", - mode - ))) - } - }; - - cipher - .decrypt_combined(&cipher_txt, &nonce, &aad) - .map_err(|e| Error::CryptoLibraryError(format!("AEAD decryption error: {:?}", e))) - } - - type HpkePrng = HpkeEvercryptPrng; - - fn prng() -> Self::HpkePrng { - #[cfg(feature = "deterministic-prng")] - { - let mut fake_rng = vec![0u8; 256]; - rand_chacha::ChaCha20Rng::from_entropy().fill_bytes(&mut fake_rng); - HpkeEvercryptPrng { - fake_rng, - rng: RwLock::new(rand_chacha::ChaCha20Rng::from_entropy()), - } - } - #[cfg(not(feature = "deterministic-prng"))] - HpkeEvercryptPrng { - rng: RwLock::new(rand_chacha::ChaCha20Rng::from_entropy()), - } - } - - /// Returns an error if the KDF algorithm is not supported by this crypto provider. - fn supports_kdf(_: KdfAlgorithm) -> Result<(), Error> { - Ok(()) - } - - /// Returns an error if the KEM algorithm is not supported by this crypto provider. - fn supports_kem(alg: KemAlgorithm) -> Result<(), Error> { - match alg { - KemAlgorithm::DhKem25519 | KemAlgorithm::DhKemP256 => Ok(()), - _ => Err(Error::UnknownKemAlgorithm), - } - } - - /// Returns an error if the AEAD algorithm is not supported by this crypto provider. - fn supports_aead(alg: AeadAlgorithm) -> Result<(), Error> { - match alg { - AeadAlgorithm::Aes128Gcm | AeadAlgorithm::Aes256Gcm => aes_support(), - AeadAlgorithm::ChaCha20Poly1305 | AeadAlgorithm::HpkeExport => Ok(()), - } - } -} - -#[cfg(all(target_arch = "x86_64", not(target_os = "macos")))] -fn aes_support() -> Result<(), Error> { - Ok(()) -} - -#[cfg(any(not(target_arch = "x86_64"), target_os = "macos"))] -fn aes_support() -> Result<(), Error> { - Err(Error::UnknownAeadAlgorithm) -} - -/// Prepend 0x04 for uncompressed NIST curve points. -#[inline(always)] -fn nist_format_uncompressed(mut pk: Vec) -> Vec { - let mut tmp = Vec::with_capacity(pk.len() + 1); - tmp.push(0x04); - tmp.append(&mut pk); - tmp -} - -#[inline(always)] -fn kem_key_type_to_mode(alg: KemAlgorithm) -> Result { - match alg { - KemAlgorithm::DhKem25519 => Ok(EcdhMode::X25519), - KemAlgorithm::DhKemP256 => Ok(EcdhMode::P256), - _ => Err(Error::UnknownKemAlgorithm), - } -} - -#[inline(always)] -fn aead_type_to_mode(alg: AeadAlgorithm) -> Result { - match alg { - AeadAlgorithm::Aes128Gcm => Ok(AeadMode::Aes128Gcm), - AeadAlgorithm::Aes256Gcm => Ok(AeadMode::Aes256Gcm), - AeadAlgorithm::ChaCha20Poly1305 => Ok(AeadMode::Chacha20Poly1305), - _ => Err(Error::UnknownAeadAlgorithm), - } -} - -impl RngCore for HpkeEvercryptPrng { - fn next_u32(&mut self) -> u32 { - let mut rng = self.rng.write().unwrap(); - rng.next_u32() - } - - fn next_u64(&mut self) -> u64 { - let mut rng = self.rng.write().unwrap(); - rng.next_u64() - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - let mut rng = self.rng.write().unwrap(); - rng.fill_bytes(dest) - } - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { - let mut rng = self.rng.write().unwrap(); - rng.try_fill_bytes(dest) - } -} -impl CryptoRng for HpkeEvercryptPrng {} - -impl HpkeTestRng for HpkeEvercryptPrng { - #[cfg(feature = "deterministic-prng")] - fn try_fill_test_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { - // Here we fake our randomness for testing. - if dest.len() > self.fake_rng.len() { - return Err(rand::Error::new(Error::InsufficientRandomness)); - } - dest.clone_from_slice(&self.fake_rng.split_off(self.fake_rng.len() - dest.len())); - Ok(()) - } - #[cfg(not(feature = "deterministic-prng"))] - fn try_fill_test_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { - self.rng.write().unwrap().try_fill_bytes(dest) - } - - #[cfg(feature = "deterministic-prng")] - fn seed(&mut self, seed: &[u8]) { - self.fake_rng = seed.to_vec(); - } - #[cfg(not(feature = "deterministic-prng"))] - fn seed(&mut self, _: &[u8]) {} -} - -impl Display for HpkeEvercrypt { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", Self::name()) - } -} diff --git a/libcrux_provider/.gitignore b/libcrux_provider/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/libcrux_provider/.gitignore @@ -0,0 +1 @@ +/target diff --git a/evercrypt_provider/CHANGELOG.md b/libcrux_provider/CHANGELOG.md similarity index 65% rename from evercrypt_provider/CHANGELOG.md rename to libcrux_provider/CHANGELOG.md index 449b591..eb77875 100644 --- a/evercrypt_provider/CHANGELOG.md +++ b/libcrux_provider/CHANGELOG.md @@ -4,12 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.1.3] - 2023-03-04 - -### Changed -- [#37](https://github.com/franziskuskiefer/hpke-rs/pull/37): correct error type for aead_type_to_mode - -## 0.1.2 (2022-02-24) +## 0.1.0 - Unreleased * initial release diff --git a/libcrux_provider/Cargo.toml b/libcrux_provider/Cargo.toml new file mode 100644 index 0000000..173cdc4 --- /dev/null +++ b/libcrux_provider/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "hpke-rs-libcrux" +version = "0.2.0-alpha.1" +authors = ["Franziskus Kiefer "] +edition = "2021" +license = "MPL-2.0" +documentation = "https://docs.rs/hpke-rs-libcrux" +description = "Crypto backend for HPKE using formally verified code from libcrux." +readme = "Readme.md" +repository = "https://github.com/cryspen/hpke-rs" + +[dependencies] +hpke-rs-crypto = { version = "0.3.0-alpha.1", path = "../traits" } +libcrux-ecdh = { version = "0.0.3-alpha.1", git = "https://github.com/cryspen/libcrux", branch = "main", default-features = false } +libcrux-hkdf = { version = "0.0.3-alpha.1", git = "https://github.com/cryspen/libcrux", branch = "main" } +libcrux-kem = { version = "0.0.3-alpha.1", git = "https://github.com/cryspen/libcrux", branch = "main", default-features = false } +libcrux-chacha20poly1305 = { version = "0.0.2", git = "https://github.com/cryspen/libcrux", branch = "main" } +# Randomness +rand = { version = "0.9", default-features = false } +rand_core = { version = "0.9", features = ["os_rng"] } +rand_chacha = { version = "0.9", default-features = false } + +[dev-dependencies] +criterion = { version = "0.5", features = ["html_reports"] } + +[features] +deterministic-prng = [] # ⚠️ FOR TESTING ONLY. +std = ["rand/std", "rand_chacha/std", "libcrux-ecdh/std", "libcrux-kem/std", "hpke-rs-crypto/std"] + +[[bench]] +name = "bench_hkdf" +harness = false + +[[bench]] +name = "bench_p256" +harness = false + +[[bench]] +name = "bench_x25519" +harness = false diff --git a/libcrux_provider/Readme.md b/libcrux_provider/Readme.md new file mode 100644 index 0000000..4eefce2 --- /dev/null +++ b/libcrux_provider/Readme.md @@ -0,0 +1,18 @@ +# HPKE Crypto provider using Libcrux + +[![crates.io][crate-badge]][crate-link] +[![Docs][docs-badge]][docs-link] +![Rust Version][rustc-image] + +This crate provides an implementation of the [HpkeCrypto] trait using [Libcrux]. + +Please see [hpke-rs] for more details. + +[libcrux]: https://crates.io/crates/libcrux +[hpkecrypto]: https://github.com/cryspen/hpke-rs/tree/main/traits +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg?style=for-the-badge +[docs-badge]: https://img.shields.io/badge/docs-rs-blue.svg?style=for-the-badge +[docs-link]: https://docs.rs/hpke-rs-libcrux +[crate-badge]: https://img.shields.io/crates/v/hpke-rs-libcrux.svg?style=for-the-badge +[crate-link]: https://crates.io/crates/hpke-rs-libcrux +[hpke-rs]: https://github.com/cryspen/hpke-rs diff --git a/evercrypt_provider/benches/bench_hkdf.rs b/libcrux_provider/benches/bench_hkdf.rs similarity index 69% rename from evercrypt_provider/benches/bench_hkdf.rs rename to libcrux_provider/benches/bench_hkdf.rs index 0f28b75..007d680 100644 --- a/evercrypt_provider/benches/bench_hkdf.rs +++ b/libcrux_provider/benches/bench_hkdf.rs @@ -1,7 +1,6 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use hpke_rs_crypto::{types::*, HpkeCrypto, RngCore}; -use hpke_rs_evercrypt::*; -use rand::rngs::OsRng; +use hpke_rs_libcrux::*; fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("HKDF SHA256 Extract"), |b| { @@ -9,12 +8,12 @@ fn criterion_benchmark(c: &mut Criterion) { || { let mut salt = vec![0u8; 77]; let mut ikm = vec![0u8; 32]; - OsRng.fill_bytes(&mut salt); - OsRng.fill_bytes(&mut ikm); + rand::rng().fill_bytes(&mut salt); + rand::rng().fill_bytes(&mut ikm); (salt.clone(), ikm.clone()) }, |(salt, ikm)| { - let _ = HpkeEvercrypt::kdf_extract(KdfAlgorithm::HkdfSha256, &salt, &ikm); + let _ = HpkeLibcrux::kdf_extract(KdfAlgorithm::HkdfSha256, &salt, &ikm); }, BatchSize::SmallInput, ) @@ -24,12 +23,12 @@ fn criterion_benchmark(c: &mut Criterion) { || { let mut info = vec![0u8; 77]; let mut prk = vec![0u8; 32]; - OsRng.fill_bytes(&mut info); - OsRng.fill_bytes(&mut prk); + rand::rng().fill_bytes(&mut info); + rand::rng().fill_bytes(&mut prk); (prk.clone(), info.clone()) }, |(prk, info)| { - let _ = HpkeEvercrypt::kdf_expand(KdfAlgorithm::HkdfSha256, &prk, &info, 32); + let _ = HpkeLibcrux::kdf_expand(KdfAlgorithm::HkdfSha256, &prk, &info, 32); }, BatchSize::SmallInput, ) diff --git a/evercrypt_provider/benches/bench_p256.rs b/libcrux_provider/benches/bench_p256.rs similarity index 53% rename from evercrypt_provider/benches/bench_p256.rs rename to libcrux_provider/benches/bench_p256.rs index 0a85136..e67961e 100644 --- a/evercrypt_provider/benches/bench_p256.rs +++ b/libcrux_provider/benches/bench_p256.rs @@ -1,21 +1,18 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use hpke_rs_crypto::{types::KemAlgorithm, HpkeCrypto}; -use hpke_rs_evercrypt::*; +use hpke_rs_libcrux::*; fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("P256 Derive"), |b| { b.iter_batched( || { - let sk = HpkeEvercrypt::kem_key_gen( - KemAlgorithm::DhKemP256, - &mut HpkeEvercrypt::prng(), - ) - .unwrap(); - let pk = HpkeEvercrypt::kem_derive_base(KemAlgorithm::DhKemP256, &sk).unwrap(); + let (pk, sk) = + HpkeLibcrux::kem_key_gen(KemAlgorithm::DhKemP256, &mut HpkeLibcrux::prng()) + .unwrap(); (sk.clone(), pk.clone()) }, |(sk, pk)| { - let _ = HpkeEvercrypt::kem_derive(KemAlgorithm::DhKemP256, &pk, &sk); + let _ = HpkeLibcrux::dh(KemAlgorithm::DhKemP256, &pk, &sk); }, BatchSize::SmallInput, ) @@ -23,15 +20,13 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("P256 Derive Base"), |b| { b.iter_batched( || { - let sk = HpkeEvercrypt::kem_key_gen( - KemAlgorithm::DhKemP256, - &mut HpkeEvercrypt::prng(), - ) - .unwrap(); + let (_pk, sk) = + HpkeLibcrux::kem_key_gen(KemAlgorithm::DhKemP256, &mut HpkeLibcrux::prng()) + .unwrap(); sk.clone() }, |sk| { - let _pk = HpkeEvercrypt::kem_derive_base(KemAlgorithm::DhKemP256, &sk).unwrap(); + let _pk = HpkeLibcrux::secret_to_public(KemAlgorithm::DhKemP256, &sk).unwrap(); }, BatchSize::SmallInput, ) diff --git a/evercrypt_provider/benches/bench_x25519.rs b/libcrux_provider/benches/bench_x25519.rs similarity index 53% rename from evercrypt_provider/benches/bench_x25519.rs rename to libcrux_provider/benches/bench_x25519.rs index ad4eb5d..e94e6f6 100644 --- a/evercrypt_provider/benches/bench_x25519.rs +++ b/libcrux_provider/benches/bench_x25519.rs @@ -1,21 +1,18 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use hpke_rs_crypto::{types::*, HpkeCrypto}; -use hpke_rs_evercrypt::*; +use hpke_rs_libcrux::*; fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("x25519 Derive"), |b| { b.iter_batched( || { - let sk = HpkeEvercrypt::kem_key_gen( - KemAlgorithm::DhKem25519, - &mut HpkeEvercrypt::prng(), - ) - .unwrap(); - let pk = HpkeEvercrypt::kem_derive_base(KemAlgorithm::DhKem25519, &sk).unwrap(); + let (pk, sk) = + HpkeLibcrux::kem_key_gen(KemAlgorithm::DhKem25519, &mut HpkeLibcrux::prng()) + .unwrap(); (sk.clone(), pk.clone()) }, |(sk, pk)| { - let _ = HpkeEvercrypt::kem_derive(KemAlgorithm::DhKem25519, &pk, &sk); + let _ = HpkeLibcrux::dh(KemAlgorithm::DhKem25519, &pk, &sk); }, BatchSize::SmallInput, ) @@ -23,15 +20,13 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("x25519 Derive Base"), |b| { b.iter_batched( || { - let sk = HpkeEvercrypt::kem_key_gen( - KemAlgorithm::DhKem25519, - &mut HpkeEvercrypt::prng(), - ) - .unwrap(); + let (_pk, sk) = + HpkeLibcrux::kem_key_gen(KemAlgorithm::DhKem25519, &mut HpkeLibcrux::prng()) + .unwrap(); sk.clone() }, |sk| { - let _pk = HpkeEvercrypt::kem_derive_base(KemAlgorithm::DhKem25519, &sk).unwrap(); + let _pk = HpkeLibcrux::secret_to_public(KemAlgorithm::DhKem25519, &sk).unwrap(); }, BatchSize::SmallInput, ) diff --git a/libcrux_provider/src/lib.rs b/libcrux_provider/src/lib.rs new file mode 100644 index 0000000..f745bd7 --- /dev/null +++ b/libcrux_provider/src/lib.rs @@ -0,0 +1,340 @@ +#![doc = include_str!("../Readme.md")] +#![cfg_attr(not(test), no_std)] +extern crate alloc; + +use alloc::{string::String, vec::Vec, format}; +use core::fmt::Display; + +use hpke_rs_crypto::{ + error::Error, + types::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}, + CryptoRng, HpkeCrypto, HpkeTestRng, +}; + +use rand_core::SeedableRng; + +/// The Libcrux HPKE Provider +#[derive(Debug)] +pub struct HpkeLibcrux {} + +/// The PRNG for the Libcrux Provider. +pub struct HpkeLibcruxPrng { + #[cfg(feature = "deterministic-prng")] + fake_rng: Vec, + rng: rand_chacha::ChaCha20Rng, +} + +impl HpkeCrypto for HpkeLibcrux { + fn name() -> String { + "Libcrux".into() + } + + fn kdf_extract(alg: KdfAlgorithm, salt: &[u8], ikm: &[u8]) -> Result, Error> { + let alg = kdf_algorithm_to_libcrux_hkdf_algorithm(alg); + libcrux_hkdf::extract(alg, salt, ikm) + .map_err(|e| Error::CryptoLibraryError(format!("KDF extract error: {:?}", e))) + } + + fn kdf_expand( + alg: KdfAlgorithm, + prk: &[u8], + info: &[u8], + output_size: usize, + ) -> Result, Error> { + let alg = kdf_algorithm_to_libcrux_hkdf_algorithm(alg); + libcrux_hkdf::expand(alg, prk, info, output_size) + .map_err(|e| Error::CryptoLibraryError(format!("KDF expand error: {:?}", e))) + } + + fn dh(alg: KemAlgorithm, pk: &[u8], sk: &[u8]) -> Result, Error> { + let alg = kem_key_type_to_ecdh_alg(alg)?; + + libcrux_ecdh::derive(alg, pk, sk) + .map_err(|e| Error::CryptoLibraryError(format!("ECDH derive error: {:?}", e))) + .map(|mut p| { + if alg == libcrux_ecdh::Algorithm::P256 { + p.truncate(32); + p + } else { + p + } + }) + } + + fn secret_to_public(alg: KemAlgorithm, sk: &[u8]) -> Result, Error> { + let alg = kem_key_type_to_ecdh_alg(alg)?; + + kem_ecdh_secret_to_public(alg, sk) + } + + fn kem_key_gen( + alg: KemAlgorithm, + prng: &mut Self::HpkePrng, + ) -> Result<(Vec, Vec), Error> { + match alg { + KemAlgorithm::XWingDraft06 => { + libcrux_kem::key_gen(libcrux_kem::Algorithm::XWingKemDraft06, prng) + .map(|(sk, pk)| (pk.encode(), sk.encode())) + .map_err(|e| Error::CryptoLibraryError(format!("KEM key gen error: {:?}", e))) + } + other_alg => { + // ECDH only + let ecdh_alg = kem_key_type_to_ecdh_alg(other_alg)?; + let sk = libcrux_ecdh::generate_secret(ecdh_alg, prng).map_err(|e| { + Error::CryptoLibraryError(format!("KEM key gen error: {:?}", e)) + })?; + + let pk = kem_ecdh_secret_to_public(ecdh_alg, &sk)?; + + Ok((pk, sk)) + } + } + } + + fn kem_key_gen_derand(alg: KemAlgorithm, seed: &[u8]) -> Result<(Vec, Vec), Error> { + let alg = kem_key_type_to_libcrux_alg(alg)?; + + libcrux_kem::key_gen_derand(alg, seed) + .map_err(|e| Error::CryptoLibraryError(format!("KEM key gen error: {:?}", e))) + .map(|(sk, pk)| (pk.encode(), sk.encode())) + } + + fn kem_encaps( + alg: KemAlgorithm, + pk_r: &[u8], + prng: &mut Self::HpkePrng, + ) -> Result<(Vec, Vec), Error> { + let alg = kem_key_type_to_libcrux_alg(alg)?; + + let pk = + libcrux_kem::PublicKey::decode(alg, pk_r).map_err(|_| Error::KemInvalidPublicKey)?; + pk.encapsulate(prng) + .map_err(|e| Error::CryptoLibraryError(format!("Encaps error {:?}", e))) + .map(|(ss, ct)| (ss.encode(), ct.encode())) + } + + fn kem_decaps(alg: KemAlgorithm, ct: &[u8], sk_r: &[u8]) -> Result, Error> { + let alg = kem_key_type_to_libcrux_alg(alg)?; + + let ct = libcrux_kem::Ct::decode(alg, ct).map_err(|_| Error::AeadInvalidCiphertext)?; + let sk = + libcrux_kem::PrivateKey::decode(alg, sk_r).map_err(|_| Error::KemInvalidSecretKey)?; + ct.decapsulate(&sk) + .map_err(|e| Error::CryptoLibraryError(format!("Decaps error {:?}", e))) + .map(|ss| ss.encode()) + } + + fn dh_validate_sk(alg: KemAlgorithm, sk: &[u8]) -> Result, Error> { + match alg { + KemAlgorithm::DhKemP256 => libcrux_ecdh::p256::validate_scalar_slice(&sk) + .map_err(|e| Error::CryptoLibraryError(format!("ECDH invalid sk error: {:?}", e))) + .map(|sk| sk.0.to_vec()), + _ => Err(Error::UnknownKemAlgorithm), + } + } + + fn aead_seal( + alg: AeadAlgorithm, + key: &[u8], + nonce: &[u8], + aad: &[u8], + msg: &[u8], + ) -> Result, Error> { + // only chacha20poly1305 is supported + if !matches!(alg, AeadAlgorithm::ChaCha20Poly1305) { + return Err(Error::UnknownAeadAlgorithm); + } + + let iv = <&[u8; 12]>::try_from(nonce).map_err(|_| Error::AeadInvalidNonce)?; + + // TODO: instead, use key conversion from the libcrux-chacha20poly1305 crate, when available, + let key = <&[u8; 32]>::try_from(key) + .map_err(|_| Error::CryptoLibraryError("AEAD invalid key length".into()))?; + + let mut msg_ctx: Vec = alloc::vec![0; msg.len() + 16]; + libcrux_chacha20poly1305::encrypt(key, msg, &mut msg_ctx, aad, iv) + .map_err(|_| Error::CryptoLibraryError("Invalid configuration".into()))?; + + Ok(msg_ctx) + } + + fn aead_open( + alg: AeadAlgorithm, + key: &[u8], + nonce: &[u8], + aad: &[u8], + cipher_txt: &[u8], + ) -> Result, Error> { + // only chacha20poly1305 is supported + if !matches!(alg, AeadAlgorithm::ChaCha20Poly1305) { + return Err(Error::UnknownAeadAlgorithm); + } + if cipher_txt.len() < 16 { + return Err(Error::AeadInvalidCiphertext); + } + + let boundary = cipher_txt.len() - 16; + + let mut ptext = alloc::vec![0; boundary]; + + let iv = <&[u8; 12]>::try_from(nonce).map_err(|_| Error::AeadInvalidNonce)?; + + // TODO: instead, use key conversion from the libcrux-chacha20poly1305 crate, when available, + let key = <&[u8; 32]>::try_from(key) + .map_err(|_| Error::CryptoLibraryError("AEAD invalid key length".into()))?; + libcrux_chacha20poly1305::decrypt(key, &mut ptext, cipher_txt, aad, iv).map_err( + |e| match e { + libcrux_chacha20poly1305::AeadError::InvalidCiphertext => { + Error::CryptoLibraryError(format!("AEAD decryption error: {:?}", e)) + } + _ => Error::CryptoLibraryError("Invalid configuration".into()), + }, + )?; + + Ok(ptext) + } + + type HpkePrng = HpkeLibcruxPrng; + + fn prng() -> Self::HpkePrng { + #[cfg(feature = "deterministic-prng")] + { + use rand::TryRngCore; + let mut fake_rng = alloc::vec![0u8; 256]; + rand_chacha::ChaCha20Rng::from_os_rng() + .try_fill_bytes(&mut fake_rng) + .unwrap(); + HpkeLibcruxPrng { + fake_rng, + rng: rand_chacha::ChaCha20Rng::from_os_rng(), + } + } + #[cfg(not(feature = "deterministic-prng"))] + HpkeLibcruxPrng { + rng: rand_chacha::ChaCha20Rng::from_os_rng(), + } + } + + /// Returns an error if the KDF algorithm is not supported by this crypto provider. + fn supports_kdf(_: KdfAlgorithm) -> Result<(), Error> { + Ok(()) + } + + /// Returns an error if the KEM algorithm is not supported by this crypto provider. + fn supports_kem(alg: KemAlgorithm) -> Result<(), Error> { + match alg { + KemAlgorithm::DhKem25519 | KemAlgorithm::DhKemP256 | KemAlgorithm::XWingDraft06 => { + Ok(()) + } + _ => Err(Error::UnknownKemAlgorithm), + } + } + + /// Returns an error if the AEAD algorithm is not supported by this crypto provider. + fn supports_aead(alg: AeadAlgorithm) -> Result<(), Error> { + match alg { + // Don't support Aes + AeadAlgorithm::Aes128Gcm | AeadAlgorithm::Aes256Gcm => Err(Error::UnknownAeadAlgorithm), + AeadAlgorithm::ChaCha20Poly1305 => Ok(()), + AeadAlgorithm::HpkeExport => Ok(()), + } + } +} + +#[inline(always)] +fn kem_ecdh_secret_to_public(alg: libcrux_ecdh::Algorithm, sk: &[u8]) -> Result, Error> { + libcrux_ecdh::secret_to_public(alg, sk) + .map_err(|e| Error::CryptoLibraryError(format!("ECDH derive base error: {:?}", e))) + .map(|p| { + if alg == libcrux_ecdh::Algorithm::P256 { + nist_format_uncompressed(p) + } else { + p + } + }) +} + +/// Prepend 0x04 for uncompressed NIST curve points. +#[inline(always)] +fn nist_format_uncompressed(mut pk: Vec) -> Vec { + let mut tmp = Vec::with_capacity(pk.len() + 1); + tmp.push(0x04); + tmp.append(&mut pk); + tmp +} + +#[inline(always)] +fn kdf_algorithm_to_libcrux_hkdf_algorithm(alg: KdfAlgorithm) -> libcrux_hkdf::Algorithm { + match alg { + KdfAlgorithm::HkdfSha256 => libcrux_hkdf::Algorithm::Sha256, + KdfAlgorithm::HkdfSha384 => libcrux_hkdf::Algorithm::Sha384, + KdfAlgorithm::HkdfSha512 => libcrux_hkdf::Algorithm::Sha512, + } +} + +#[inline(always)] +fn kem_key_type_to_libcrux_alg(alg: KemAlgorithm) -> Result { + match alg { + KemAlgorithm::DhKem25519 => Ok(libcrux_kem::Algorithm::X25519), + KemAlgorithm::DhKemP256 => Ok(libcrux_kem::Algorithm::Secp256r1), + KemAlgorithm::XWingDraft06 => Ok(libcrux_kem::Algorithm::XWingKemDraft06), + _ => Err(Error::UnknownKemAlgorithm), + } +} + +#[inline(always)] +fn kem_key_type_to_ecdh_alg(alg: KemAlgorithm) -> Result { + match alg { + KemAlgorithm::DhKem25519 => Ok(libcrux_ecdh::Algorithm::X25519), + KemAlgorithm::DhKemP256 => Ok(libcrux_ecdh::Algorithm::P256), + _ => Err(Error::UnknownKemAlgorithm), + } +} + +impl hpke_rs_crypto::RngCore for HpkeLibcruxPrng { + fn next_u32(&mut self) -> u32 { + self.rng.next_u32() + } + + fn next_u64(&mut self) -> u64 { + self.rng.next_u64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.rng.fill_bytes(dest) + } +} +impl CryptoRng for HpkeLibcruxPrng {} + +impl HpkeTestRng for HpkeLibcruxPrng { + type Error = Error; + + #[cfg(feature = "deterministic-prng")] + fn try_fill_test_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + // Here we fake our randomness for testing. + if dest.len() > self.fake_rng.len() { + return Err(Error::InsufficientRandomness); + } + dest.clone_from_slice(&self.fake_rng.split_off(self.fake_rng.len() - dest.len())); + Ok(()) + } + #[cfg(not(feature = "deterministic-prng"))] + fn try_fill_test_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + use rand_core::TryRngCore; + self.try_fill_bytes(dest) + .map_err(|_| Error::InsufficientRandomness) + } + + #[cfg(feature = "deterministic-prng")] + fn seed(&mut self, seed: &[u8]) { + self.fake_rng = seed.to_vec(); + } + #[cfg(not(feature = "deterministic-prng"))] + fn seed(&mut self, _: &[u8]) {} +} + +impl Display for HpkeLibcrux { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", Self::name()) + } +} diff --git a/no-std-support-check/.cargo/config.toml b/no-std-support-check/.cargo/config.toml new file mode 100644 index 0000000..3dbe7b8 --- /dev/null +++ b/no-std-support-check/.cargo/config.toml @@ -0,0 +1,3 @@ +# enable custom backend for getrandom +[target.thumbv7em-none-eabihf] +rustflags = ['--cfg', 'getrandom_backend="custom"'] diff --git a/no-std-support-check/Cargo.toml b/no-std-support-check/Cargo.toml index 9884d5c..cad38b9 100644 --- a/no-std-support-check/Cargo.toml +++ b/no-std-support-check/Cargo.toml @@ -8,8 +8,13 @@ version = "0.0.0" hpke-rs = { path = ".." } hpke-rs-crypto = { path = "../traits" } hpke-rs-rust-crypto = { path = "../rust_crypto_provider" } +hpke-rs-libcrux = { path = "../libcrux_provider" } # the no-std-support-check CI job uses the `thumbv7em-none-eabihf` target -# `getrandom` does not support that target out of box so this feature needs to be enabled to avoid a compilation error +# this version of `getrandom`, required by `hpke-rs-rust-crypto`, does not support that target out of box so this feature needs to be enabled to avoid a compilation error # (normally this feature should NOT be enabled in a library but this crate is just used for a CI check) -getrandom = { version = "0.2.11", features = ["custom"] } +getrandom_old = { package = "getrandom", version = "0.2.11", features=["custom"] } + +# for the newer version of `getrandom`, which is required by `hpke-rs-libcrux`, the `.cargo/config.toml` needs to contain configuration for a custom backend. +# see https://docs.rs/getrandom/latest/getrandom/#custom-backend +getrandom = { version = "0.3.2" } diff --git a/publish.sh b/publish.sh index f517c50..f0ab133 100755 --- a/publish.sh +++ b/publish.sh @@ -6,8 +6,8 @@ set -e # "hpke-rs-crypto cd traits && cargo publish $@ && cd - -# hpke-rs-evercrypt -# cd evercrypt_provider && cargo publish $@ && cd - +# hpke-rs-libcrux +cd libcrux_provider && cargo publish $@ && cd - # hpke-rs-rust-crypto cd rust_crypto_provider && cargo publish $@ && cd - diff --git a/rust_crypto_provider/CHANGELOG.md b/rust_crypto_provider/CHANGELOG.md index 50d53b2..1db13b6 100644 --- a/rust_crypto_provider/CHANGELOG.md +++ b/rust_crypto_provider/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.0] - Unreleased + +- [#72](https://github.com/cryspen/hpke-rs/pull/72): + - use new hpke-rs-crypto trait API + ## [0.2.0] - 2023-12-01 - [#59](https://github.com/franziskuskiefer/hpke-rs/pull/59): hpke-rs-rust-crypto: make deterministic-prng enable the std feature diff --git a/rust_crypto_provider/Cargo.toml b/rust_crypto_provider/Cargo.toml index 1a0b293..98c36c0 100644 --- a/rust_crypto_provider/Cargo.toml +++ b/rust_crypto_provider/Cargo.toml @@ -1,16 +1,16 @@ [package] name = "hpke-rs-rust-crypto" -version = "0.2.0" +version = "0.3.0-alpha.1" authors = ["Franziskus Kiefer "] edition = "2021" license = "MPL-2.0" documentation = "https://docs.rs/hpke-rs-rust-crypto" description = "Crypto backend for HPKE using native Rust crypto." readme = "Readme.md" -repository = "https://github.com/franziskuskiefer/hpke-rs" +repository = "https://github.com/cryspen/hpke-rs" [dependencies] -hpke-rs-crypto = { version = "0.2.0", path = "../traits" } +hpke-rs-crypto = { version = "0.3.0-alpha.1", path = "../traits" } # Rust crypto hkdf = { version = "0.12" } sha2 = { version = "0.10", default-features = false } @@ -30,13 +30,15 @@ chacha20poly1305 = { version = "0.10", default-features = false, features = [ aes-gcm = { version = "0.10", default-features = false, features = ["aes"] } # Randomness rand_core = { version = "0.6", features = ["getrandom"] } +rand_old = { version = "0.8", package = "rand", default-features = false } rand_chacha = { version = "0.3", default-features = false } [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } -rand = { version = "0.8" } +rand = { version = "0.9" } [features] +std = ["rand_core/std", "rand_old/std"] deterministic-prng = [ "hpke-rs-crypto/std", "rand_core/std", diff --git a/rust_crypto_provider/benches/bench_hkdf.rs b/rust_crypto_provider/benches/bench_hkdf.rs index c865191..08b9b4d 100644 --- a/rust_crypto_provider/benches/bench_hkdf.rs +++ b/rust_crypto_provider/benches/bench_hkdf.rs @@ -1,7 +1,6 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use hpke_rs_crypto::{types::*, HpkeCrypto, RngCore}; use hpke_rs_rust_crypto::*; -use rand::rngs::OsRng; fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("HKDF SHA256 Extract"), |b| { @@ -9,8 +8,8 @@ fn criterion_benchmark(c: &mut Criterion) { || { let mut salt = vec![0u8; 77]; let mut ikm = vec![0u8; 32]; - OsRng.fill_bytes(&mut salt); - OsRng.fill_bytes(&mut ikm); + rand::rng().fill_bytes(&mut salt); + rand::rng().fill_bytes(&mut ikm); (salt.clone(), ikm.clone()) }, |(salt, ikm)| { @@ -24,8 +23,8 @@ fn criterion_benchmark(c: &mut Criterion) { || { let mut info = vec![0u8; 77]; let mut prk = vec![0u8; 32]; - OsRng.fill_bytes(&mut info); - OsRng.fill_bytes(&mut prk); + rand::rng().fill_bytes(&mut info); + rand::rng().fill_bytes(&mut prk); (prk.clone(), info.clone()) }, |(prk, info)| { diff --git a/rust_crypto_provider/benches/bench_k256.rs b/rust_crypto_provider/benches/bench_k256.rs index aaf5b3a..a50917a 100644 --- a/rust_crypto_provider/benches/bench_k256.rs +++ b/rust_crypto_provider/benches/bench_k256.rs @@ -6,16 +6,15 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("K256 Derive"), |b| { b.iter_batched( || { - let sk = HpkeRustCrypto::kem_key_gen( + let (pk, sk) = HpkeRustCrypto::kem_key_gen( KemAlgorithm::DhKemK256, &mut HpkeRustCrypto::prng(), ) .unwrap(); - let pk = HpkeRustCrypto::kem_derive_base(KemAlgorithm::DhKemK256, &sk).unwrap(); (sk.clone(), pk.clone()) }, |(sk, pk)| { - let _ = HpkeRustCrypto::kem_derive(KemAlgorithm::DhKemK256, &pk, &sk); + let _ = HpkeRustCrypto::dh(KemAlgorithm::DhKemK256, &pk, &sk); }, BatchSize::SmallInput, ) @@ -23,7 +22,7 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("K256 Derive Base"), |b| { b.iter_batched( || { - let sk = HpkeRustCrypto::kem_key_gen( + let (_pk, sk) = HpkeRustCrypto::kem_key_gen( KemAlgorithm::DhKemK256, &mut HpkeRustCrypto::prng(), ) @@ -31,7 +30,7 @@ fn criterion_benchmark(c: &mut Criterion) { sk.clone() }, |sk| { - let _pk = HpkeRustCrypto::kem_derive_base(KemAlgorithm::DhKemK256, &sk).unwrap(); + let _pk = HpkeRustCrypto::secret_to_public(KemAlgorithm::DhKemK256, &sk).unwrap(); }, BatchSize::SmallInput, ) diff --git a/rust_crypto_provider/benches/bench_p256.rs b/rust_crypto_provider/benches/bench_p256.rs index 672cfce..7c8c6f4 100644 --- a/rust_crypto_provider/benches/bench_p256.rs +++ b/rust_crypto_provider/benches/bench_p256.rs @@ -6,16 +6,15 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("P256 Derive"), |b| { b.iter_batched( || { - let sk = HpkeRustCrypto::kem_key_gen( + let (pk, sk) = HpkeRustCrypto::kem_key_gen( KemAlgorithm::DhKemP256, &mut HpkeRustCrypto::prng(), ) .unwrap(); - let pk = HpkeRustCrypto::kem_derive_base(KemAlgorithm::DhKemP256, &sk).unwrap(); (sk.clone(), pk.clone()) }, |(sk, pk)| { - let _ = HpkeRustCrypto::kem_derive(KemAlgorithm::DhKemP256, &pk, &sk); + let _ = HpkeRustCrypto::dh(KemAlgorithm::DhKemP256, &pk, &sk); }, BatchSize::SmallInput, ) @@ -23,7 +22,7 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("P256 Derive Base"), |b| { b.iter_batched( || { - let sk = HpkeRustCrypto::kem_key_gen( + let (_pk, sk) = HpkeRustCrypto::kem_key_gen( KemAlgorithm::DhKemP256, &mut HpkeRustCrypto::prng(), ) @@ -31,7 +30,7 @@ fn criterion_benchmark(c: &mut Criterion) { sk.clone() }, |sk| { - let _pk = HpkeRustCrypto::kem_derive_base(KemAlgorithm::DhKemP256, &sk).unwrap(); + let _pk = HpkeRustCrypto::secret_to_public(KemAlgorithm::DhKemP256, &sk).unwrap(); }, BatchSize::SmallInput, ) diff --git a/rust_crypto_provider/benches/bench_x25519.rs b/rust_crypto_provider/benches/bench_x25519.rs index 7e93e3e..3e7b98b 100644 --- a/rust_crypto_provider/benches/bench_x25519.rs +++ b/rust_crypto_provider/benches/bench_x25519.rs @@ -6,16 +6,15 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("x25519 Derive"), |b| { b.iter_batched( || { - let sk = HpkeRustCrypto::kem_key_gen( + let (pk, sk) = HpkeRustCrypto::kem_key_gen( KemAlgorithm::DhKem25519, &mut HpkeRustCrypto::prng(), ) .unwrap(); - let pk = HpkeRustCrypto::kem_derive_base(KemAlgorithm::DhKem25519, &sk).unwrap(); (sk.clone(), pk.clone()) }, |(sk, pk)| { - let _ = HpkeRustCrypto::kem_derive(KemAlgorithm::DhKem25519, &pk, &sk); + let _ = HpkeRustCrypto::dh(KemAlgorithm::DhKem25519, &pk, &sk); }, BatchSize::SmallInput, ) @@ -23,7 +22,7 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("x25519 Derive Base"), |b| { b.iter_batched( || { - let sk = HpkeRustCrypto::kem_key_gen( + let (_pk, sk) = HpkeRustCrypto::kem_key_gen( KemAlgorithm::DhKem25519, &mut HpkeRustCrypto::prng(), ) @@ -31,7 +30,7 @@ fn criterion_benchmark(c: &mut Criterion) { sk.clone() }, |sk| { - let _pk = HpkeRustCrypto::kem_derive_base(KemAlgorithm::DhKem25519, &sk).unwrap(); + let _pk = HpkeRustCrypto::secret_to_public(KemAlgorithm::DhKem25519, &sk).unwrap(); }, BatchSize::SmallInput, ) diff --git a/rust_crypto_provider/src/lib.rs b/rust_crypto_provider/src/lib.rs index 6559708..587651c 100644 --- a/rust_crypto_provider/src/lib.rs +++ b/rust_crypto_provider/src/lib.rs @@ -45,12 +45,12 @@ impl HpkeCrypto for HpkeRustCrypto { "RustCrypto".into() } - fn kdf_extract(alg: KdfAlgorithm, salt: &[u8], ikm: &[u8]) -> Vec { - match alg { + fn kdf_extract(alg: KdfAlgorithm, salt: &[u8], ikm: &[u8]) -> Result, Error> { + Ok(match alg { KdfAlgorithm::HkdfSha256 => sha256_extract(salt, ikm), KdfAlgorithm::HkdfSha384 => sha384_extract(salt, ikm), KdfAlgorithm::HkdfSha512 => sha512_extract(salt, ikm), - } + }) } fn kdf_expand( @@ -66,7 +66,7 @@ impl HpkeCrypto for HpkeRustCrypto { } } - fn kem_derive(alg: KemAlgorithm, pk: &[u8], sk: &[u8]) -> Result, Error> { + fn dh(alg: KemAlgorithm, pk: &[u8], sk: &[u8]) -> Result, Error> { match alg { KemAlgorithm::DhKem25519 => { if sk.len() != 32 { @@ -107,7 +107,26 @@ impl HpkeCrypto for HpkeRustCrypto { } } - fn kem_derive_base(alg: KemAlgorithm, sk: &[u8]) -> Result, Error> { + fn kem_key_gen_derand(_alg: KemAlgorithm, _seed: &[u8]) -> Result<(Vec, Vec), Error> { + // No ciphersuite uses this. + return Err(Error::UnsupportedKemOperation); + } + + fn kem_encaps( + _alg: KemAlgorithm, + _pk_r: &[u8], + _prng: &mut Self::HpkePrng, + ) -> Result<(Vec, Vec), Error> { + // No ciphersuite uses this. + return Err(Error::UnsupportedKemOperation); + } + + fn kem_decaps(_alg: KemAlgorithm, _ct: &[u8], _sk_r: &[u8]) -> Result, Error> { + // No ciphersuite uses this. + return Err(Error::UnsupportedKemOperation); + } + + fn secret_to_public(alg: KemAlgorithm, sk: &[u8]) -> Result, Error> { match alg { KemAlgorithm::DhKem25519 => { if sk.len() != 32 { @@ -126,29 +145,39 @@ impl HpkeCrypto for HpkeRustCrypto { let sk = k256SecretKey::from_slice(sk).map_err(|_| Error::KemInvalidSecretKey)?; Ok(sk.public_key().to_encoded_point(false).as_bytes().into()) } - _ => Err(Error::UnknownKemAlgorithm), + _ => Err(Error::UnsupportedKemOperation), } } - fn kem_key_gen(alg: KemAlgorithm, prng: &mut Self::HpkePrng) -> Result, Error> { + fn kem_key_gen( + alg: KemAlgorithm, + prng: &mut Self::HpkePrng, + ) -> Result<(Vec, Vec), Error> { let rng = &mut prng.rng; match alg { - KemAlgorithm::DhKem25519 => Ok(X25519StaticSecret::random_from_rng(&mut *rng) - .to_bytes() - .to_vec()), - KemAlgorithm::DhKemP256 => Ok(p256SecretKey::random(&mut *rng) - .to_bytes() - .as_slice() - .into()), - KemAlgorithm::DhKemK256 => Ok(k256SecretKey::random(&mut *rng) - .to_bytes() - .as_slice() - .into()), + KemAlgorithm::DhKem25519 => { + let sk = X25519StaticSecret::random_from_rng(&mut *rng); + let pk = X25519PublicKey::from(&sk).as_bytes().to_vec(); + let sk = sk.to_bytes().to_vec(); + Ok((pk, sk)) + } + KemAlgorithm::DhKemP256 => { + let sk = p256SecretKey::random(&mut *rng); + let pk = sk.public_key().to_encoded_point(false).as_bytes().into(); + let sk = sk.to_bytes().as_slice().into(); + Ok((pk, sk)) + } + KemAlgorithm::DhKemK256 => { + let sk = k256SecretKey::random(&mut *rng); + let pk = sk.public_key().to_encoded_point(false).as_bytes().into(); + let sk = sk.to_bytes().as_slice().into(); + Ok((pk, sk)) + } _ => Err(Error::UnknownKemAlgorithm), } } - fn kem_validate_sk(alg: KemAlgorithm, sk: &[u8]) -> Result, Error> { + fn dh_validate_sk(alg: KemAlgorithm, sk: &[u8]) -> Result, Error> { match alg { KemAlgorithm::DhKemP256 => p256SecretKey::from_slice(sk) .map_err(|_| Error::KemInvalidSecretKey) @@ -232,7 +261,10 @@ impl HpkeCrypto for HpkeRustCrypto { } } -impl RngCore for HpkeRustCryptoPrng { +// We need to implement the old and new traits here because the crytpo uses the +// old one. + +impl rand_old::RngCore for HpkeRustCryptoPrng { fn next_u32(&mut self) -> u32 { self.rng.next_u32() } @@ -250,11 +282,29 @@ impl RngCore for HpkeRustCryptoPrng { } } +impl rand_old::CryptoRng for HpkeRustCryptoPrng {} + +use rand_old::RngCore as _; + +impl RngCore for HpkeRustCryptoPrng { + fn next_u32(&mut self) -> u32 { + self.rng.next_u32() + } + + fn next_u64(&mut self) -> u64 { + self.rng.next_u64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.rng.fill_bytes(dest); + } +} + impl CryptoRng for HpkeRustCryptoPrng {} impl HpkeTestRng for HpkeRustCryptoPrng { #[cfg(feature = "deterministic-prng")] - fn try_fill_test_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + fn try_fill_test_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_old::Error> { // Here we fake our randomness for testing. if dest.len() > self.fake_rng.len() { return Err(rand_core::Error::new(Error::InsufficientRandomness)); @@ -268,12 +318,14 @@ impl HpkeTestRng for HpkeRustCryptoPrng { self.fake_rng = seed.to_vec(); } #[cfg(not(feature = "deterministic-prng"))] - fn try_fill_test_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + fn try_fill_test_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_old::Error> { self.rng.try_fill_bytes(dest) } #[cfg(not(feature = "deterministic-prng"))] fn seed(&mut self, _: &[u8]) {} + + type Error = rand_old::Error; } impl Display for HpkeRustCrypto { diff --git a/src/dh_kem.rs b/src/dh_kem.rs old mode 100755 new mode 100644 index 9586f92..02be098 --- a/src/dh_kem.rs +++ b/src/dh_kem.rs @@ -16,7 +16,7 @@ fn extract_and_expand( kem_context: &[u8], suite_id: &[u8], ) -> Result, Error> { - let prk = labeled_extract::(alg.into(), &[], suite_id, "eae_prk", &pk); + let prk = labeled_extract::(alg.into(), &[], suite_id, "eae_prk", &pk)?; labeled_expand::( alg.into(), &prk, @@ -41,12 +41,12 @@ pub(super) fn deserialize(enc: &[u8]) -> Vec { enc.to_vec() } +/// Return (private, public) pub(super) fn key_gen( alg: KemAlgorithm, prng: &mut Crypto::HpkePrng, ) -> Result<(Vec, Vec), Error> { - let sk = Crypto::kem_key_gen(alg, prng)?; - let pk = Crypto::kem_derive_base(alg, &sk)?; + let (pk, sk) = Crypto::kem_key_gen(alg, prng)?; Ok((sk, pk)) } @@ -55,7 +55,7 @@ pub(super) fn derive_key_pair( suite_id: &[u8], ikm: &[u8], ) -> Result<(PublicKey, PrivateKey), Error> { - let dkp_prk = labeled_extract::(alg.into(), &[], suite_id, "dkp_prk", ikm); + let dkp_prk = labeled_extract::(alg.into(), &[], suite_id, "dkp_prk", ikm)?; let sk = match alg { KemAlgorithm::DhKem25519 => labeled_expand::( @@ -81,7 +81,7 @@ pub(super) fn derive_key_pair( alg.private_key_len(), ); if let Ok(sk) = &candidate { - if let Ok(sk) = Crypto::kem_validate_sk(alg, sk) { + if let Ok(sk) = Crypto::dh_validate_sk(alg, sk) { break sk; } } @@ -98,7 +98,7 @@ pub(super) fn derive_key_pair( panic!("This should be unreachable. Only x25519, P256, and K256 KEMs are implemented") } }; - Ok((Crypto::kem_derive_base(alg, &sk)?, sk)) + Ok((Crypto::secret_to_public(alg, &sk)?, sk)) } pub(super) fn encaps( @@ -109,7 +109,7 @@ pub(super) fn encaps( ) -> Result<(Vec, Vec), Error> { debug_assert_eq!(randomness.len(), alg.private_key_len()); let (pk_e, sk_e) = derive_key_pair::(alg, suite_id, randomness)?; - let dh_pk = Crypto::kem_derive(alg, pk_r, &sk_e)?; + let dh_pk = Crypto::dh(alg, pk_r, &sk_e)?; let enc = serialize(&pk_e); let pk_rm = serialize(pk_r); @@ -126,9 +126,9 @@ pub(super) fn decaps( suite_id: &[u8], ) -> Result, Error> { let pk_e = deserialize(enc); - let dh_pk = Crypto::kem_derive(alg, &pk_e, sk_r)?; + let dh_pk = Crypto::dh(alg, &pk_e, sk_r)?; - let pk_rm = serialize(&Crypto::kem_derive_base(alg, sk_r)?); + let pk_rm = serialize(&Crypto::secret_to_public(alg, sk_r)?); let kem_context = concat(&[enc, &pk_rm]); extract_and_expand::(alg, dh_pk, &kem_context, suite_id) @@ -144,13 +144,13 @@ pub(super) fn auth_encaps( debug_assert_eq!(randomness.len(), alg.private_key_len()); let (pk_e, sk_e) = derive_key_pair::(alg, suite_id, randomness)?; let dh_pk = concat(&[ - &Crypto::kem_derive(alg, pk_r, &sk_e)?, - &Crypto::kem_derive(alg, pk_r, sk_s)?, + &Crypto::dh(alg, pk_r, &sk_e)?, + &Crypto::dh(alg, pk_r, sk_s)?, ]); let enc = serialize(&pk_e); let pk_rm = serialize(pk_r); - let pk_sm = serialize(&Crypto::kem_derive_base(alg, sk_s)?); + let pk_sm = serialize(&Crypto::secret_to_public(alg, sk_s)?); let kem_context = concat(&[&enc, &pk_rm, &pk_sm]); @@ -167,11 +167,11 @@ pub(super) fn auth_decaps( ) -> Result, Error> { let pk_e = deserialize(enc); let dh_pk = concat(&[ - &Crypto::kem_derive(alg, &pk_e, sk_r)?, - &Crypto::kem_derive(alg, pk_s, sk_r)?, + &Crypto::dh(alg, &pk_e, sk_r)?, + &Crypto::dh(alg, pk_s, sk_r)?, ]); - let pk_rm = serialize(&Crypto::kem_derive_base(alg, sk_r)?); + let pk_rm = serialize(&Crypto::secret_to_public(alg, sk_r)?); let pk_sm = serialize(pk_s); let kem_context = concat(&[enc, &pk_rm, &pk_sm]); diff --git a/src/kdf.rs b/src/kdf.rs old mode 100755 new mode 100644 index d273070..716862a --- a/src/kdf.rs +++ b/src/kdf.rs @@ -12,7 +12,7 @@ pub(crate) fn labeled_extract( suite_id: &[u8], label: &str, ikm: &[u8], -) -> Vec { +) -> Result, Error> { let labeled_ikm = concat(&[HPKE_VERSION, suite_id, label.as_bytes(), ikm]); Crypto::kdf_extract(alg, salt, &labeled_ikm) } diff --git a/src/kem.rs b/src/kem.rs old mode 100755 new mode 100644 index 83db5a1..f3d7ca3 --- a/src/kem.rs +++ b/src/kem.rs @@ -1,9 +1,8 @@ -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; -use hpke_rs_crypto::{error::Error, types::KemAlgorithm, HpkeCrypto}; +use hpke_rs_crypto::{error::Error, types::KemAlgorithm, HpkeCrypto, RngCore}; -use crate::dh_kem; -use crate::util; +use crate::{dh_kem, util, Hpke}; pub(crate) type PrivateKey = Vec; pub(crate) type PublicKey = Vec; @@ -14,10 +13,10 @@ fn ciphersuite(alg: KemAlgorithm) -> Vec { } pub(crate) fn encaps( - alg: KemAlgorithm, + hpke: &mut Hpke, pk_r: &[u8], - randomness: &[u8], ) -> Result<(Vec, Vec), Error> { + let alg = hpke.kem_id; match alg { KemAlgorithm::DhKemP256 | KemAlgorithm::DhKemK256 @@ -25,8 +24,12 @@ pub(crate) fn encaps( | KemAlgorithm::DhKemP521 | KemAlgorithm::DhKem25519 | KemAlgorithm::DhKem448 => { - dh_kem::encaps::(alg, pk_r, &ciphersuite(alg), randomness) + let randomness = hpke + .random(alg.private_key_len()) + .map_err(|_| Error::InsufficientRandomness)?; + dh_kem::encaps::(alg, pk_r, &ciphersuite(alg), &randomness) } + KemAlgorithm::XWingDraft06 => Crypto::kem_encaps(alg, pk_r, hpke.rng()), } } @@ -42,15 +45,16 @@ pub(crate) fn decaps( | KemAlgorithm::DhKemP521 | KemAlgorithm::DhKem25519 | KemAlgorithm::DhKem448 => dh_kem::decaps::(alg, enc, sk_r, &ciphersuite(alg)), + KemAlgorithm::XWingDraft06 => Crypto::kem_decaps(alg, enc, sk_r), } } pub(crate) fn auth_encaps( - alg: KemAlgorithm, + hpke: &mut Hpke, pk_r: &[u8], sk_s: &[u8], - randomness: &[u8], ) -> Result<(Vec, Vec), Error> { + let alg = hpke.kem_id; match alg { KemAlgorithm::DhKemP256 | KemAlgorithm::DhKemK256 @@ -58,8 +62,12 @@ pub(crate) fn auth_encaps( | KemAlgorithm::DhKemP521 | KemAlgorithm::DhKem25519 | KemAlgorithm::DhKem448 => { - dh_kem::auth_encaps::(alg, pk_r, sk_s, &ciphersuite(alg), randomness) + let randomness = hpke + .random(alg.private_key_len()) + .map_err(|_| Error::InsufficientRandomness)?; + dh_kem::auth_encaps::(alg, pk_r, sk_s, &ciphersuite(alg), &randomness) } + KemAlgorithm::XWingDraft06 => Err(Error::UnsupportedKemOperation), } } @@ -78,20 +86,30 @@ pub(crate) fn auth_decaps( | KemAlgorithm::DhKem448 => { dh_kem::auth_decaps::(alg, enc, sk_r, pk_s, &ciphersuite(alg)) } + KemAlgorithm::XWingDraft06 => Err(Error::UnsupportedKemOperation), } } +/// Returns (private, public) pub(crate) fn key_gen( alg: KemAlgorithm, prng: &mut Crypto::HpkePrng, ) -> Result<(Vec, Vec), Error> { match alg { + // For ECDH based keys, we generate a completely fresh key. KemAlgorithm::DhKemP256 | KemAlgorithm::DhKemK256 | KemAlgorithm::DhKemP384 | KemAlgorithm::DhKemP521 | KemAlgorithm::DhKem25519 | KemAlgorithm::DhKem448 => dh_kem::key_gen::(alg, prng), + KemAlgorithm::XWingDraft06 => { + // For XWing we use the derive key pair function. + let mut seed = vec![0u8; alg.private_key_len()]; + prng.fill_bytes(&mut seed); + let (pk, sk) = derive_key_pair::(alg, &seed)?; + Ok((sk, pk)) + } } } @@ -102,5 +120,17 @@ pub(crate) fn derive_key_pair( alg: KemAlgorithm, ikm: &[u8], ) -> Result<(PublicKey, PrivateKey), Error> { - dh_kem::derive_key_pair::(alg, &ciphersuite(alg), ikm) + match alg { + KemAlgorithm::DhKemP256 + | KemAlgorithm::DhKemK256 + | KemAlgorithm::DhKemP384 + | KemAlgorithm::DhKemP521 + | KemAlgorithm::DhKem25519 + | KemAlgorithm::DhKem448 => dh_kem::derive_key_pair::(alg, &ciphersuite(alg), ikm), + KemAlgorithm::XWingDraft06 => { + let seed = libcrux_sha3::shake256::<32>(ikm); + let kp = Crypto::kem_key_gen_derand(alg, &seed)?; + Ok(kp) + } + } } diff --git a/src/lib.rs b/src/lib.rs old mode 100755 new mode 100644 index 6517298..3895739 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,13 +23,15 @@ use alloc::{ #[cfg(feature = "hpke-test-prng")] use hpke_rs_crypto::HpkeTestRng; -#[cfg(not(feature = "hpke-test-prng"))] -use hpke_rs_crypto::RngCore; use hpke_rs_crypto::{ types::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}, HpkeCrypto, }; use prelude::kdf::{labeled_expand, labeled_extract}; + +#[cfg(not(feature = "hpke-test-prng"))] +use rand_core::TryRngCore; + #[cfg(feature = "serialization")] pub(crate) use serde::{Deserialize, Serialize}; use zeroize::Zeroize; @@ -406,17 +408,14 @@ impl Hpke { psk_id: Option<&[u8]>, sk_s: Option<&HpkePrivateKey>, ) -> Result<(EncapsulatedSecret, Context), HpkeError> { - let randomness = self.random(self.kem_id.private_key_len())?; let (zz, enc) = match self.mode { - Mode::Base | Mode::Psk => { - kem::encaps::(self.kem_id, pk_r.value.as_slice(), &randomness)? - } + Mode::Base | Mode::Psk => kem::encaps::(self, pk_r.value.as_slice())?, Mode::Auth | Mode::AuthPsk => { let sk_s = match sk_s { Some(s) => &s.value, None => return Err(HpkeError::InvalidInput), }; - kem::auth_encaps::(self.kem_id, pk_r.value.as_slice(), sk_s, &randomness)? + kem::auth_encaps::(self, pk_r.value.as_slice(), sk_s)? } }; Ok(( @@ -618,11 +617,20 @@ impl Hpke { } #[inline] - fn key_schedule_context(&self, info: &[u8], psk_id: &[u8], suite_id: &[u8]) -> Vec { + fn key_schedule_context( + &self, + info: &[u8], + psk_id: &[u8], + suite_id: &[u8], + ) -> Result, HpkeError> { let psk_id_hash = - labeled_extract::(self.kdf_id, &[0], suite_id, "psk_id_hash", psk_id); - let info_hash = labeled_extract::(self.kdf_id, &[0], suite_id, "info_hash", info); - util::concat(&[&[self.mode as u8], &psk_id_hash, &info_hash]) + labeled_extract::(self.kdf_id, &[0], suite_id, "psk_id_hash", psk_id)?; + let info_hash = labeled_extract::(self.kdf_id, &[0], suite_id, "info_hash", info)?; + Ok(util::concat(&[ + &[self.mode as u8], + &psk_id_hash, + &info_hash, + ])) } /// Creating the Encryption Context @@ -636,9 +644,10 @@ impl Hpke { ) -> Result, HpkeError> { self.verify_psk_inputs(psk, psk_id)?; let suite_id = self.ciphersuite(); - let key_schedule_context = self.key_schedule_context(info, psk_id, &suite_id); + let key_schedule_context = self.key_schedule_context(info, psk_id, &suite_id)?; let secret = - labeled_extract::(self.kdf_id, shared_secret, &suite_id, "secret", psk); + labeled_extract::(self.kdf_id, shared_secret, &suite_id, "secret", psk) + .map_err(|e| HpkeError::CryptoError(format!("Crypto error: {}", e)))?; let key = labeled_expand::( self.kdf_id, @@ -710,6 +719,11 @@ impl Hpke { Ok(out) } + + /// Get the rng. + pub(crate) fn rng(&mut self) -> &mut Crypto::HpkePrng { + &mut self.prng + } } impl HpkeKeyPair { @@ -999,6 +1013,8 @@ impl From for HpkeError { hpke_rs_crypto::error::Error::InsufficientRandomness => { HpkeError::InsufficientRandomness } + hpke_rs_crypto::error::Error::UnsupportedKemOperation => HpkeError::InvalidConfig, + hpke_rs_crypto::error::Error::KemInvalidCiphertext => HpkeError::InvalidInput, } } } diff --git a/src/test_aead.rs b/src/test_aead.rs index 33b4c4d..62868b4 100644 --- a/src/test_aead.rs +++ b/src/test_aead.rs @@ -1,4 +1,5 @@ use hpke_rs_crypto::{types::AeadAlgorithm, HpkeCrypto}; +use hpke_rs_libcrux::HpkeLibcrux; use hpke_rs_rust_crypto::HpkeRustCrypto; #[test] @@ -12,9 +13,46 @@ fn test_aes_gcm_128_self() { ]; let aad = [0x03, 0x04, 0x05]; let msg = b"test message"; + + // test rust crypto provider let ctxt = HpkeRustCrypto::aead_seal(AeadAlgorithm::Aes128Gcm, &key, &nonce, &aad, msg).unwrap(); let ptxt = HpkeRustCrypto::aead_open(AeadAlgorithm::Aes128Gcm, &key, &nonce, &aad, &ctxt).unwrap(); assert_eq!(&ptxt, msg); + + // assert error on Libcrux provider + HpkeLibcrux::aead_seal(AeadAlgorithm::Aes128Gcm, &key, &nonce, &aad, msg) + .expect_err("Unsupported algorithm"); + HpkeLibcrux::aead_open(AeadAlgorithm::Aes128Gcm, &key, &nonce, &aad, &ctxt) + .expect_err("Unsupported algorithm"); +} + +#[test] +fn test_chacha20poly1305_self() { + let key = [ + 0x5b, 0x96, 0x04, 0xfe, 0x14, 0xea, 0xdb, 0xa9, 0x31, 0xb0, 0xcc, 0xf3, 0x48, 0x43, 0xda, + 0xb9, 0x5b, 0x96, 0x04, 0xfe, 0x14, 0xea, 0xdb, 0xa9, 0x31, 0xb0, 0xcc, 0xf3, 0x48, 0x43, + 0xda, 0xb9, + ]; + let nonce = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + ]; + let aad = [0x03, 0x04, 0x05]; + let msg = b"test message"; + + // test rust crypto provider + let ctxt = HpkeRustCrypto::aead_seal(AeadAlgorithm::ChaCha20Poly1305, &key, &nonce, &aad, msg) + .unwrap(); + let ptxt = + HpkeRustCrypto::aead_open(AeadAlgorithm::ChaCha20Poly1305, &key, &nonce, &aad, &ctxt) + .unwrap(); + assert_eq!(&ptxt, msg); + + // test libcrux provider + let ctxt = + HpkeLibcrux::aead_seal(AeadAlgorithm::ChaCha20Poly1305, &key, &nonce, &aad, msg).unwrap(); + let ptxt = + HpkeLibcrux::aead_open(AeadAlgorithm::ChaCha20Poly1305, &key, &nonce, &aad, &ctxt).unwrap(); + assert_eq!(&ptxt, msg); } diff --git a/src/test_kdf.rs b/src/test_kdf.rs old mode 100755 new mode 100644 index 635f70d..8ae969d --- a/src/test_kdf.rs +++ b/src/test_kdf.rs @@ -1,5 +1,5 @@ use hpke_rs_crypto::{types::KdfAlgorithm, HpkeCrypto}; -use hpke_rs_rust_crypto::HpkeRustCrypto; +use hpke_rs_libcrux::HpkeLibcrux; use crate::test_util::hex_to_bytes; @@ -16,8 +16,9 @@ fn test_hkdf_sha256() { "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865", ); - let prk = HpkeRustCrypto::kdf_extract(KdfAlgorithm::HkdfSha256, &salt, &ikm); - let okm = HpkeRustCrypto::kdf_expand(KdfAlgorithm::HkdfSha256, &prk, &info, len) + let prk = HpkeLibcrux::kdf_extract(KdfAlgorithm::HkdfSha256, &salt, &ikm) + .expect("Error extracting with HKDF"); + let okm = HpkeLibcrux::kdf_expand(KdfAlgorithm::HkdfSha256, &prk, &info, len) .expect("Error expanding with HKDF"); assert_eq!(&expected_prk, &prk); diff --git a/src/util.rs b/src/util.rs old mode 100755 new mode 100644 index 5302272..919c330 --- a/src/util.rs +++ b/src/util.rs @@ -18,6 +18,6 @@ fn test_concat() { let expected = "blablaRFCXXXX "; assert_eq!( expected.as_bytes()[..], - concat(&[&a.as_bytes(), &b.as_bytes()])[..] + concat(&[a.as_bytes(), b.as_bytes()])[..] ) } diff --git a/tests/test_hpke.rs b/tests/test_hpke.rs index db95b94..431f853 100644 --- a/tests/test_hpke.rs +++ b/tests/test_hpke.rs @@ -7,7 +7,7 @@ use hpke_rs_crypto::{ types::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}, HpkeCrypto, RngCore, }; -// use hpke_rs_evercrypt::HpkeEvercrypt; +use hpke_rs_libcrux::HpkeLibcrux; use hpke_rs_rust_crypto::HpkeRustCrypto; use lazy_static::lazy_static; @@ -43,6 +43,7 @@ macro_rules! generate_test_case { ($name:ident, $hpke_mode:expr, $kem_mode:expr, $kdf_mode:expr, $aead_mode:expr, $provider:ident) => { #[test] fn $name() { + let _ = pretty_env_logger::try_init(); let mut hpke = Hpke::<$provider>::new($hpke_mode, $kem_mode, $kdf_mode, $aead_mode); println!("Self test {}", hpke); @@ -273,14 +274,14 @@ generate_test_case!( AeadAlgorithm::ChaCha20Poly1305, HpkeRustCrypto ); -// generate_test_case!( -// base_dhkemp256_hkdfsha256_chacha20poly1305_evercrypt, -// HpkeMode::Base, -// KemAlgorithm::DhKemP256, -// KdfAlgorithm::HkdfSha256, -// AeadAlgorithm::ChaCha20Poly1305, -// HpkeEvercrypt -// ); +generate_test_case!( + base_dhkemp256_hkdfsha256_chacha20poly1305_libcrux, + HpkeMode::Base, + KemAlgorithm::DhKemP256, + KdfAlgorithm::HkdfSha256, + AeadAlgorithm::ChaCha20Poly1305, + HpkeLibcrux +); generate_test_case!( base_dhkem25519_hkdfsha256_chacha20poly1305, HpkeMode::Base, @@ -289,14 +290,22 @@ generate_test_case!( AeadAlgorithm::ChaCha20Poly1305, HpkeRustCrypto ); -// generate_test_case!( -// base_dhkem25519_hkdfsha256_chacha20poly1305_evercrypt, -// HpkeMode::Base, -// KemAlgorithm::DhKem25519, -// KdfAlgorithm::HkdfSha256, -// AeadAlgorithm::ChaCha20Poly1305, -// HpkeEvercrypt -// ); +generate_test_case!( + base_dhkem25519_hkdfsha256_chacha20poly1305_libcrux, + HpkeMode::Base, + KemAlgorithm::DhKem25519, + KdfAlgorithm::HkdfSha256, + AeadAlgorithm::ChaCha20Poly1305, + HpkeLibcrux +); +generate_test_case!( + base_xwingdraft06_hkdfsha256_chacha20poly1305_libcrux, + HpkeMode::Base, + KemAlgorithm::XWingDraft06, + KdfAlgorithm::HkdfSha256, + AeadAlgorithm::ChaCha20Poly1305, + HpkeLibcrux +); generate_test_case!( base_dhkemp256_hkdfsha384_chacha20poly1305, HpkeMode::Base, diff --git a/tests/test_hpke_kat.rs b/tests/test_hpke_kat.rs old mode 100755 new mode 100644 index 3a62ced..f959c0e --- a/tests/test_hpke_kat.rs +++ b/tests/test_hpke_kat.rs @@ -1,6 +1,5 @@ extern crate hpke_rs as hpke; -// use hpke_rs_evercrypt::HpkeEvercrypt; use hpke_rs_rust_crypto::HpkeRustCrypto; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use serde::{self, Deserialize, Serialize}; @@ -12,6 +11,7 @@ use std::time::Instant; use hpke::prelude::*; use hpke::test_util::{hex_to_bytes, hex_to_bytes_option, vec_to_option_slice}; use hpke_rs_crypto::{types::*, HpkeCrypto}; +use hpke_rs_libcrux::HpkeLibcrux; #[derive(Serialize, Deserialize, Debug, Clone)] #[allow(non_snake_case)] @@ -294,10 +294,10 @@ fn test_kat() { let time = now.elapsed(); log::info!("Test vectors with Rust Crypto took: {}s", time.as_secs()); - // let now = Instant::now(); - // kat::(tests); - // let time = now.elapsed(); - // log::info!("Test vectors with Evercrypt took: {}s", time.as_secs()); + let now = Instant::now(); + kat::(tests); + let time = now.elapsed(); + log::info!("Test vectors with Libcrux took: {}s", time.as_secs()); } } diff --git a/traits/CHANGELOG.md b/traits/CHANGELOG.md index 51c0a4c..c978599 100644 --- a/traits/CHANGELOG.md +++ b/traits/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.0] - Unreleased + +- [#72](https://github.com/cryspen/hpke-rs/pull/72): + - redesign `HpkeCrypto` trait to support X-Wing KEM + - upgrade rand dependency from 0.8 -> 0.9 + ## [0.2.0] - 2023-12-01 - [#53](https://github.com/franziskuskiefer/hpke-rs/pull/53): rm getrandom dep diff --git a/traits/Cargo.toml b/traits/Cargo.toml index 424c19c..3be26e2 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -1,18 +1,18 @@ [package] name = "hpke-rs-crypto" -version = "0.2.0" +version = "0.3.0-alpha.1" authors = ["Franziskus Kiefer "] edition = "2021" license = "MPL-2.0" documentation = "https://docs.rs/hpke-rs-crypto" description = "Traits and types for HPKE crypto backends" readme = "Readme.md" -repository = "https://github.com/franziskuskiefer/hpke-rs" +repository = "https://github.com/cryspen/hpke-rs" [dependencies] serde = { version = "1.0", features = ["derive"], optional = true } -rand_core = { version = "0.6.4" } +rand_core = { version = "0.9", default-features = false } [features] serde = ["dep:serde"] -std = [] +std = ["rand_core/std"] diff --git a/traits/src/error.rs b/traits/src/error.rs index 14edd80..0a71317 100644 --- a/traits/src/error.rs +++ b/traits/src/error.rs @@ -20,9 +20,15 @@ pub enum Error { /// Invalid public key for the KEM. KemInvalidPublicKey, + /// Invalid ciphertext for the KEM. + KemInvalidCiphertext, + /// Unknown or unsupported KEM algorithm, UnknownKemAlgorithm, + /// Unsupported operation for KEM algorithm. + UnsupportedKemOperation, + /// Unknown or unsupported AEAD algorithm. UnknownAeadAlgorithm, diff --git a/traits/src/lib.rs b/traits/src/lib.rs index 90d684b..be668c6 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -50,7 +50,7 @@ pub trait HpkeCrypto: core::fmt::Debug + Send + Sync { } /// KDF Extract - fn kdf_extract(alg: types::KdfAlgorithm, salt: &[u8], ikm: &[u8]) -> Vec; + fn kdf_extract(alg: types::KdfAlgorithm, salt: &[u8], ikm: &[u8]) -> Result, Error>; /// KDF Expand fn kdf_expand( @@ -60,17 +60,35 @@ pub trait HpkeCrypto: core::fmt::Debug + Send + Sync { output_size: usize, ) -> Result, Error>; - /// KEM Derive - fn kem_derive(alg: KemAlgorithm, pk: &[u8], sk: &[u8]) -> Result, Error>; + /// Diffie-Hellman + fn dh(alg: KemAlgorithm, pk: &[u8], sk: &[u8]) -> Result, Error>; - /// KEM Derive with base - fn kem_derive_base(alg: KemAlgorithm, sk: &[u8]) -> Result, Error>; + /// Diffie-Hellman with the base (generate public key for secret key `sk`). + fn secret_to_public(alg: KemAlgorithm, sk: &[u8]) -> Result, Error>; - /// KEM Key generation - fn kem_key_gen(alg: KemAlgorithm, prng: &mut Self::HpkePrng) -> Result, Error>; + /// KEM key pair generation (encapsulation key, decapsulation key). + fn kem_key_gen( + alg: KemAlgorithm, + prng: &mut Self::HpkePrng, + ) -> Result<(Vec, Vec), Error>; + + /// KEM key pair generation (encapsulation key, decapsulation key) based + /// on the `seed`. + fn kem_key_gen_derand(alg: KemAlgorithm, seed: &[u8]) -> Result<(Vec, Vec), Error>; + + /// KEM encapsulation to `pk_r` (shared secret, ciphertext). + fn kem_encaps( + alg: KemAlgorithm, + pk_r: &[u8], + prng: &mut Self::HpkePrng, + ) -> Result<(Vec, Vec), Error>; + + /// KEM decapsulation with `sk_r`. + /// Returns the shared secret. + fn kem_decaps(alg: KemAlgorithm, ct: &[u8], sk_r: &[u8]) -> Result, Error>; /// Validate a secret key for its correctness. - fn kem_validate_sk(alg: KemAlgorithm, sk: &[u8]) -> Result, Error>; + fn dh_validate_sk(alg: KemAlgorithm, sk: &[u8]) -> Result, Error>; /// AEAD encrypt. fn aead_seal( @@ -129,8 +147,10 @@ pub trait HpkeCrypto: core::fmt::Debug + Send + Sync { /// PRNG extension for testing that is supposed to return pre-configured bytes. pub trait HpkeTestRng { - /// Like [`RngCore::try_fill_bytes`] but the result is expected to be known. - fn try_fill_test_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error>; + // Error type to replace rand::Error (which is no longer available as of version 0.9) + type Error: core::fmt::Debug + core::fmt::Display; + /// Like [`TryRngCore::try_fill_bytes`] but the result is expected to be known. + fn try_fill_test_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error>; /// Set the randomness state of this test PRNG. fn seed(&mut self, seed: &[u8]); diff --git a/traits/src/types.rs b/traits/src/types.rs index 1ec6175..e960636 100644 --- a/traits/src/types.rs +++ b/traits/src/types.rs @@ -29,6 +29,9 @@ pub enum KemAlgorithm { /// DH KEM on x448 DhKem448 = 0x0021, + + /// X-WING + XWingDraft06 = 0x004D, } impl core::fmt::Display for KemAlgorithm { @@ -47,6 +50,7 @@ impl core::convert::TryFrom for KemAlgorithm { 0x0016 => Ok(KemAlgorithm::DhKemK256), 0x0020 => Ok(KemAlgorithm::DhKem25519), 0x0021 => Ok(KemAlgorithm::DhKem448), + 0x004D => Ok(KemAlgorithm::XWingDraft06), _ => Err(Self::Error::UnknownKemAlgorithm), } } @@ -54,7 +58,7 @@ impl core::convert::TryFrom for KemAlgorithm { impl KemAlgorithm { /// Get the length of the private key for the KEM in bytes. - pub fn private_key_len(&self) -> usize { + pub const fn private_key_len(&self) -> usize { match self { KemAlgorithm::DhKemP256 => 32, KemAlgorithm::DhKemP384 => 48, @@ -62,11 +66,12 @@ impl KemAlgorithm { KemAlgorithm::DhKemK256 => 32, KemAlgorithm::DhKem25519 => 32, KemAlgorithm::DhKem448 => 56, + KemAlgorithm::XWingDraft06 => 32, } } /// Get the length of the shared secret for the KEM in bytes. - pub fn shared_secret_len(&self) -> usize { + pub const fn shared_secret_len(&self) -> usize { match self { KemAlgorithm::DhKemP256 => 32, KemAlgorithm::DhKemP384 => 48, @@ -74,6 +79,7 @@ impl KemAlgorithm { KemAlgorithm::DhKemK256 => 32, KemAlgorithm::DhKem25519 => 32, KemAlgorithm::DhKem448 => 64, + KemAlgorithm::XWingDraft06 => 32, } } } @@ -204,6 +210,7 @@ impl From for KdfAlgorithm { KemAlgorithm::DhKemK256 => KdfAlgorithm::HkdfSha256, KemAlgorithm::DhKem25519 => KdfAlgorithm::HkdfSha256, KemAlgorithm::DhKem448 => KdfAlgorithm::HkdfSha512, + KemAlgorithm::XWingDraft06 => KdfAlgorithm::HkdfSha512, } } }