diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index c2b7197..7b69137 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -144,5 +144,5 @@ jobs: cargo fuzz run fuzz_aead_enc_into -- -max_total_time=5 cargo fuzz run fuzz_blake2b -- -max_total_time=5 cargo fuzz run fuzz_handle_msg -- -max_total_time=5 - cargo fuzz run fuzz_kyber_encaps -- -max_total_time=5 + ulimit -s 8192000 && RUST_MIN_STACK=33554432000 && cargo fuzz run fuzz_kyber_encaps -- -max_total_time=5 cargo fuzz run fuzz_mceliece_encaps -- -max_total_time=5 diff --git a/Cargo.lock b/Cargo.lock index fd64bb2..4b46a70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1110,9 +1110,9 @@ dependencies = [ "log", "memoffset", "mio", - "oqs-sys", "paste", "rand", + "rosenpass-cipher-traits", "rosenpass-ciphers", "rosenpass-constant-time", "rosenpass-secret-memory", @@ -1127,12 +1127,17 @@ dependencies = [ "toml", ] +[[package]] +name = "rosenpass-cipher-traits" +version = "0.1.0" + [[package]] name = "rosenpass-ciphers" version = "0.1.0" dependencies = [ "anyhow", "rosenpass-constant-time", + "rosenpass-oqs", "rosenpass-secret-memory", "rosenpass-sodium", "rosenpass-to", @@ -1154,6 +1159,7 @@ dependencies = [ "arbitrary", "libfuzzer-sys", "rosenpass", + "rosenpass-cipher-traits", "rosenpass-ciphers", "rosenpass-secret-memory", "rosenpass-sodium", @@ -1161,6 +1167,16 @@ dependencies = [ "stacker", ] +[[package]] +name = "rosenpass-oqs" +version = "0.1.0" +dependencies = [ + "oqs-sys", + "paste", + "rosenpass-cipher-traits", + "rosenpass-util", +] + [[package]] name = "rosenpass-secret-memory" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d6800d1..b903cf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,12 @@ resolver = "2" members = [ "rosenpass", + "cipher-traits", "ciphers", "util", "constant-time", "sodium", + "oqs", "to", "fuzz", "secret-memory", @@ -25,9 +27,11 @@ rosenpass = { path = "rosenpass" } rosenpass-util = { path = "util" } rosenpass-constant-time = { path = "constant-time" } rosenpass-sodium = { path = "sodium" } +rosenpass-cipher-traits = { path = "cipher-traits" } rosenpass-ciphers = { path = "ciphers" } rosenpass-to = { path = "to" } rosenpass-secret-memory = { path = "secret-memory" } +rosenpass-oqs = { path = "oqs" } criterion = "0.4.0" test_bin = "0.4.0" libfuzzer-sys = "0.4" @@ -48,7 +52,7 @@ log = { version = "0.4.20" } clap = { version = "4.4.10", features = ["derive"] } serde = { version = "1.0.193", features = ["derive"] } arbitrary = { version = "1.3.2", features = ["derive"] } -anyhow = { version = "1.0.75", features = ["backtrace"] } +anyhow = { version = "1.0.75", features = ["backtrace", "std"] } mio = { version = "0.8.9", features = ["net", "os-poll"] } libsodium-sys-stable= { version = "1.20.4", features = ["use-pkg-config"] } oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] } diff --git a/cipher-traits/Cargo.toml b/cipher-traits/Cargo.toml new file mode 100644 index 0000000..b5c08cc --- /dev/null +++ b/cipher-traits/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rosenpass-cipher-traits" +authors = ["Karolin Varner ", "wucke13 "] +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Rosenpass internal traits for cryptographic primitives" +homepage = "https://rosenpass.eu/" +repository = "https://github.com/rosenpass/rosenpass" +readme = "readme.md" + +[dependencies] diff --git a/cipher-traits/readme.md b/cipher-traits/readme.md new file mode 100644 index 0000000..97ca5ac --- /dev/null +++ b/cipher-traits/readme.md @@ -0,0 +1,5 @@ +# Rosenpass internal libsodium bindings + +Rosenpass internal library providing traits for cryptographic primitives. + +This is an internal library; not guarantee is made about its API at this point in time. diff --git a/cipher-traits/src/kem.rs b/cipher-traits/src/kem.rs new file mode 100644 index 0000000..f03f5f7 --- /dev/null +++ b/cipher-traits/src/kem.rs @@ -0,0 +1,47 @@ +//! Traits and implementations for Key Encapsulation Mechanisms (KEMs) +//! +//! KEMs are the interface provided by almost all post-quantum +//! secure key exchange mechanisms. +//! +//! Conceptually KEMs are akin to public-key encryption, but instead of encrypting +//! arbitrary data, KEMs are limited to the transmission of keys, randomly chosen during +//! +//! encapsulation. +//! The [KEM] Trait describes the basic API offered by a Key Encapsulation +//! Mechanism. Two implementations for it are provided, [StaticKEM] and [EphemeralKEM]. + +use std::result::Result; + +/// Key Encapsulation Mechanism +/// +/// The KEM interface defines three operations: Key generation, key encapsulation and key +/// decapsulation. +pub trait Kem { + type Error; + + /// Secrete Key length + const SK_LEN: usize; + /// Public Key length + const PK_LEN: usize; + /// Ciphertext length + const CT_LEN: usize; + /// Shared Secret length + const SHK_LEN: usize; + + /// Generate a keypair consisting of secret key (`sk`) and public key (`pk`) + /// + /// `keygen() -> sk, pk` + fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), Self::Error>; + + /// From a public key (`pk`), generate a shared key (`shk`, for local use) + /// and a cipher text (`ct`, to be sent to the owner of the `pk`). + /// + /// `encaps(pk) -> shk, ct` + fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), Self::Error>; + + /// From a secret key (`sk`) and a cipher text (`ct`) derive a shared key + /// (`shk`) + /// + /// `decaps(sk, ct) -> shk` + fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), Self::Error>; +} diff --git a/cipher-traits/src/lib.rs b/cipher-traits/src/lib.rs new file mode 100644 index 0000000..99369d9 --- /dev/null +++ b/cipher-traits/src/lib.rs @@ -0,0 +1,2 @@ +mod kem; +pub use kem::Kem; diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index 4d739d4..a226239 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -15,5 +15,6 @@ rosenpass-sodium = { workspace = true } rosenpass-to = { workspace = true } rosenpass-constant-time = { workspace = true } rosenpass-secret-memory = { workspace = true } +rosenpass-oqs = { workspace = true } static_assertions = { workspace = true } zeroize = { workspace = true } diff --git a/ciphers/src/lib.rs b/ciphers/src/lib.rs index f0040ea..994431b 100644 --- a/ciphers/src/lib.rs +++ b/ciphers/src/lib.rs @@ -22,3 +22,8 @@ pub mod xaead { } pub mod hash_domain; + +pub mod kem { + pub use rosenpass_oqs::ClassicMceliece460896 as StaticKem; + pub use rosenpass_oqs::Kyber512 as EphemeralKem; +} diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 345f553..47c1344 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -14,6 +14,7 @@ stacker = { workspace = true } rosenpass-secret-memory = { workspace = true } rosenpass-sodium = { workspace = true } rosenpass-ciphers = { workspace = true } +rosenpass-cipher-traits = { workspace = true } rosenpass-to = { workspace = true } rosenpass = { workspace = true } diff --git a/fuzz/fuzz_targets/kyber_encaps.rs b/fuzz/fuzz_targets/kyber_encaps.rs index dc61f06..9bdfe64 100644 --- a/fuzz/fuzz_targets/kyber_encaps.rs +++ b/fuzz/fuzz_targets/kyber_encaps.rs @@ -4,7 +4,8 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; -use rosenpass::pqkem::{EphemeralKEM, KEM}; +use rosenpass_cipher_traits::Kem; +use rosenpass_ciphers::kem::EphemeralKem; #[derive(arbitrary::Arbitrary, Debug)] pub struct Input { @@ -15,5 +16,5 @@ fuzz_target!(|input: Input| { let mut ciphertext = [0u8; 768]; let mut shared_secret = [0u8; 32]; - EphemeralKEM::encaps(&mut shared_secret, &mut ciphertext, &input.pk).unwrap(); + EphemeralKem::encaps(&mut shared_secret, &mut ciphertext, &input.pk).unwrap(); }); diff --git a/fuzz/fuzz_targets/mceliece_encaps.rs b/fuzz/fuzz_targets/mceliece_encaps.rs index 00aa05e..25e36ae 100644 --- a/fuzz/fuzz_targets/mceliece_encaps.rs +++ b/fuzz/fuzz_targets/mceliece_encaps.rs @@ -3,12 +3,13 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; -use rosenpass::pqkem::{StaticKEM, KEM}; +use rosenpass_cipher_traits::Kem; +use rosenpass_ciphers::kem::StaticKem; -fuzz_target!(|input: &[u8]| { +fuzz_target!(|input: [u8; StaticKem::PK_LEN]| { let mut ciphertext = [0u8; 188]; let mut shared_secret = [0u8; 32]; // We expect errors while fuzzing therefore we do not check the result. - let _ = StaticKEM::encaps(&mut shared_secret, &mut ciphertext, input); + let _ = StaticKem::encaps(&mut shared_secret, &mut ciphertext, &input); }); diff --git a/oqs/Cargo.toml b/oqs/Cargo.toml new file mode 100644 index 0000000..ed93c74 --- /dev/null +++ b/oqs/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rosenpass-oqs" +authors = ["Karolin Varner ", "wucke13 "] +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Rosenpass internal bindings to liboqs" +homepage = "https://rosenpass.eu/" +repository = "https://github.com/rosenpass/rosenpass" +readme = "readme.md" + +[dependencies] +rosenpass-cipher-traits = { workspace = true } +rosenpass-util = { workspace = true } +oqs-sys = { workspace = true } +paste = { workspace = true } diff --git a/oqs/readme.md b/oqs/readme.md new file mode 100644 index 0000000..f6602ad --- /dev/null +++ b/oqs/readme.md @@ -0,0 +1,5 @@ +# Rosenpass internal liboqs bindings + +Rosenpass internal library providing bindings to liboqs. + +This is an internal library; not guarantee is made about its API at this point in time. diff --git a/oqs/src/kem_macro.rs b/oqs/src/kem_macro.rs new file mode 100644 index 0000000..d26a53b --- /dev/null +++ b/oqs/src/kem_macro.rs @@ -0,0 +1,80 @@ +macro_rules! oqs_kem { + ($name:ident) => { ::paste::paste!{ + mod [< $name:snake >] { + use rosenpass_cipher_traits::Kem; + use rosenpass_util::result::Guaranteed; + + pub enum [< $name:camel >] {} + + /// # Panic & Safety + /// + /// This Trait impl calls unsafe [oqs_sys] functions, that write to byte + /// slices only identified using raw pointers. It must be ensured that the raw + /// pointers point into byte slices of sufficient length, to avoid UB through + /// overwriting of arbitrary data. This is ensured through assertions in the + /// implementation. + /// + /// __Note__: This requirement is stricter than necessary, it would suffice + /// to only check that the buffers are big enough, allowing them to be even + /// bigger. However, from a correctness point of view it does not make sense to + /// allow bigger buffers. + impl Kem for [< $name:camel >] { + type Error = ::std::convert::Infallible; + + const SK_LEN: usize = ::oqs_sys::kem::[] as usize; + const PK_LEN: usize = ::oqs_sys::kem::[] as usize; + const CT_LEN: usize = ::oqs_sys::kem::[] as usize; + const SHK_LEN: usize = ::oqs_sys::kem::[] as usize; + + fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Guaranteed<()> { + assert_eq!(sk.len(), Self::SK_LEN); + assert_eq!(pk.len(), Self::PK_LEN); + unsafe { + oqs_call!( + ::oqs_sys::kem::[< OQS_KEM _ $name:snake _ keypair >], + pk.as_mut_ptr(), + sk.as_mut_ptr() + ); + } + + Ok(()) + } + + fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Guaranteed<()> { + assert_eq!(shk.len(), Self::SHK_LEN); + assert_eq!(ct.len(), Self::CT_LEN); + assert_eq!(pk.len(), Self::PK_LEN); + unsafe { + oqs_call!( + ::oqs_sys::kem::[< OQS_KEM _ $name:snake _ encaps >], + ct.as_mut_ptr(), + shk.as_mut_ptr(), + pk.as_ptr() + ); + } + + Ok(()) + } + + fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Guaranteed<()> { + assert_eq!(shk.len(), Self::SHK_LEN); + assert_eq!(sk.len(), Self::SK_LEN); + assert_eq!(ct.len(), Self::CT_LEN); + unsafe { + oqs_call!( + ::oqs_sys::kem::[< OQS_KEM _ $name:snake _ decaps >], + shk.as_mut_ptr(), + ct.as_ptr(), + sk.as_ptr() + ); + } + + Ok(()) + } + } + + } + + pub use [< $name:snake >] :: [< $name:camel >]; + }} +} diff --git a/oqs/src/lib.rs b/oqs/src/lib.rs new file mode 100644 index 0000000..f8f4506 --- /dev/null +++ b/oqs/src/lib.rs @@ -0,0 +1,21 @@ +macro_rules! oqs_call { + ($name:path, $($args:expr),*) => {{ + use oqs_sys::common::OQS_STATUS::*; + + match $name($($args),*) { + OQS_SUCCESS => {}, // nop + OQS_EXTERNAL_LIB_ERROR_OPENSSL => { + panic!("OpenSSL error in liboqs' {}.", stringify!($name)); + }, + OQS_ERROR => { + panic!("Unknown error in liboqs' {}.", stringify!($name)); + } + } + }}; + ($name:ident) => { oqs_call!($name, ) }; +} + +#[macro_use] +mod kem_macro; +oqs_kem!(kyber_512); +oqs_kem!(classic_mceliece_460896); diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 65f43c5..f8feb6f 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -18,13 +18,13 @@ rosenpass-util = { workspace = true } rosenpass-constant-time = { workspace = true } rosenpass-sodium = { workspace = true } rosenpass-ciphers = { workspace = true } +rosenpass-cipher-traits = { workspace = true } rosenpass-to = { workspace = true } rosenpass-secret-memory = { workspace = true } anyhow = { workspace = true } static_assertions = { workspace = true } memoffset = { workspace = true } libsodium-sys-stable = { workspace = true } -oqs-sys = { workspace = true } thiserror = { workspace = true } paste = { workspace = true } log = { workspace = true } diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index ddb9ad3..dc6b596 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -1,15 +1,14 @@ use anyhow::{bail, ensure}; use clap::Parser; +use rosenpass_cipher_traits::Kem; +use rosenpass_ciphers::kem::StaticKem; use rosenpass_secret_memory::file::StoreSecret; use rosenpass_util::file::{LoadValue, LoadValueB64}; use std::path::PathBuf; use crate::app_server; use crate::app_server::AppServer; -use crate::{ - pqkem::{StaticKEM, KEM}, - protocol::{SPk, SSk, SymKey}, -}; +use crate::protocol::{SPk, SSk, SymKey}; use super::config; @@ -163,7 +162,7 @@ impl Cli { // generate the keys and store them in files let mut ssk = crate::protocol::SSk::random(); let mut spk = crate::protocol::SPk::random(); - StaticKEM::keygen(ssk.secret_mut(), spk.secret_mut())?; + StaticKem::keygen(ssk.secret_mut(), spk.secret_mut())?; ssk.store_secret(skf)?; spk.store_secret(pkf)?; diff --git a/rosenpass/src/lib.rs b/rosenpass/src/lib.rs index a6cf82c..1f37c25 100644 --- a/rosenpass/src/lib.rs +++ b/rosenpass/src/lib.rs @@ -3,15 +3,10 @@ pub mod cli; pub mod config; pub mod hash_domains; pub mod msgs; -pub mod pqkem; pub mod protocol; #[derive(thiserror::Error, Debug)] pub enum RosenpassError { - #[error("error in OQS")] - Oqs, - #[error("error from external library while calling OQS")] - OqsExternalLib, #[error("buffer size mismatch, required {required_size} but found {actual_size}")] BufferSizeMismatch { required_size: usize, @@ -20,34 +15,3 @@ pub enum RosenpassError { #[error("invalid message type")] InvalidMessageType(u8), } - -impl RosenpassError { - /// Helper function to check a buffer size - fn check_buffer_size(required_size: usize, actual_size: usize) -> Result<(), Self> { - if required_size != actual_size { - Err(Self::BufferSizeMismatch { - required_size, - actual_size, - }) - } else { - Ok(()) - } - } -} - -/// Extension trait to attach function calls to foreign types. -trait RosenpassMaybeError { - /// Checks whether something is an error or not - fn to_rg_error(&self) -> Result<(), RosenpassError>; -} - -impl RosenpassMaybeError for oqs_sys::common::OQS_STATUS { - fn to_rg_error(&self) -> Result<(), RosenpassError> { - use oqs_sys::common::OQS_STATUS; - match self { - OQS_STATUS::OQS_SUCCESS => Ok(()), - OQS_STATUS::OQS_ERROR => Err(RosenpassError::Oqs), - OQS_STATUS::OQS_EXTERNAL_LIB_ERROR_OPENSSL => Err(RosenpassError::OqsExternalLib), - } - } -} diff --git a/rosenpass/src/msgs.rs b/rosenpass/src/msgs.rs index eac0369..1aefba5 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -44,7 +44,8 @@ //! ``` use super::RosenpassError; -use crate::pqkem::*; +use rosenpass_cipher_traits::Kem; +use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; // Macro magic //////////////////////////////////////////////////////////////// @@ -108,7 +109,7 @@ macro_rules! data_lense( (maybe_docstring_link $x:expr) => (stringify!([$x])); // struct name < optional generics > := optional doc string field name : field length, ... - ($type:ident $( < $( $generic:ident ),+ > )? := $( $( #[ $attr:meta ] )* $field:ident : $len:expr ),+) => (::paste::paste!{ +($type:ident $( < $( $generic:ident ),+ > )? := $( $( #[ $attr:meta ] )* $field:ident : $len:expr ),+) => (::paste::paste!{ #[allow(rustdoc::broken_intra_doc_links)] /// A data lense to manipulate byte slices. @@ -274,9 +275,9 @@ data_lense! { InitHello := /// Randomly generated connection id sidi: 4, /// Kyber 512 Ephemeral Public Key - epki: EphemeralKEM::PK_LEN, + epki: EphemeralKem::PK_LEN, /// Classic McEliece Ciphertext - sctr: StaticKEM::CT_LEN, + sctr: StaticKem::CT_LEN, /// Encryped: 16 byte hash of McEliece initiator static key pidic: aead::TAG_LEN + 32, /// Encrypted TAI64N Time Stamp (against replay attacks) @@ -289,9 +290,9 @@ data_lense! { RespHello := /// Copied from InitHello sidi: 4, /// Kyber 512 Ephemeral Ciphertext - ecti: EphemeralKEM::CT_LEN, + ecti: EphemeralKem::CT_LEN, /// Classic McEliece Ciphertext - scti: StaticKEM::CT_LEN, + scti: StaticKem::CT_LEN, /// Empty encrypted message (just an auth tag) auth: aead::TAG_LEN, /// Responders handshake state in encrypted form diff --git a/rosenpass/src/pqkem.rs b/rosenpass/src/pqkem.rs deleted file mode 100644 index 36084da..0000000 --- a/rosenpass/src/pqkem.rs +++ /dev/null @@ -1,168 +0,0 @@ -//! Traits and implementations for Key Encapsulation Mechanisms (KEMs) -//! -//! KEMs are the interface provided by almost all post-quantum -//! secure key exchange mechanisms. -//! -//! Conceptually KEMs are akin to public-key encryption, but instead of encrypting -//! arbitrary data, KEMs are limited to the transmission of keys, randomly chosen during -//! -//! encapsulation. -//! The [KEM] Trait describes the basic API offered by a Key Encapsulation -//! Mechanism. Two implementations for it are provided, [StaticKEM] and [EphemeralKEM]. - -use crate::{RosenpassError, RosenpassMaybeError}; - -/// Key Encapsulation Mechanism -/// -/// The KEM interface defines three operations: Key generation, key encapsulation and key -/// decapsulation. -pub trait KEM { - /// Secrete Key length - const SK_LEN: usize; - /// Public Key length - const PK_LEN: usize; - /// Ciphertext length - const CT_LEN: usize; - /// Shared Secret length - const SHK_LEN: usize; - - /// Generate a keypair consisting of secret key (`sk`) and public key (`pk`) - /// - /// `keygen() -> sk, pk` - fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError>; - - /// From a public key (`pk`), generate a shared key (`shk`, for local use) - /// and a cipher text (`ct`, to be sent to the owner of the `pk`). - /// - /// `encaps(pk) -> shk, ct` - fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError>; - - /// From a secret key (`sk`) and a cipher text (`ct`) derive a shared key - /// (`shk`) - /// - /// `decaps(sk, ct) -> shk` - fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError>; -} - -/// A KEM that is secure against Chosen Ciphertext Attacks (CCA). -/// In the context of rosenpass this is used for static keys. -/// Uses [Classic McEliece](https://classic.mceliece.org/) 460896 from liboqs. -/// -/// Classic McEliece is chosen because of its high security margin and its small -/// ciphertexts. The public keys are humongous, but (being static keys) the are never transmitted over -/// the wire so this is not a big problem. -pub struct StaticKEM; - -/// # Safety -/// -/// This Trait impl calls unsafe [oqs_sys] functions, that write to byte -/// slices only identified using raw pointers. It must be ensured that the raw -/// pointers point into byte slices of sufficient length, to avoid UB through -/// overwriting of arbitrary data. This is checked in the following code before -/// the unsafe calls, and an early return with an Err occurs if the byte slice -/// size does not match the required size. -/// -/// __Note__: This requirement is stricter than necessary, it would suffice -/// to only check that the buffers are big enough, allowing them to be even -/// bigger. However, from a correctness point of view it does not make sense to -/// allow bigger buffers. -impl KEM for StaticKEM { - const SK_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_secret_key as usize; - const PK_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_public_key as usize; - const CT_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_ciphertext as usize; - const SHK_LEN: usize = - oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_shared_secret as usize; - - fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?; - RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_classic_mceliece_460896_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) - .to_rg_error() - } - } - - fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?; - RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?; - RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_classic_mceliece_460896_encaps( - ct.as_mut_ptr(), - shk.as_mut_ptr(), - pk.as_ptr(), - ) - .to_rg_error() - } - } - - fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?; - RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?; - RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_classic_mceliece_460896_decaps( - shk.as_mut_ptr(), - ct.as_ptr(), - sk.as_ptr(), - ) - .to_rg_error() - } - } -} - -/// Implements a KEM that is secure against Chosen Plaintext Attacks (CPA). -/// In the context of rosenpass this is used for ephemeral keys. -/// Currently the implementation uses -/// [Kyber 512](https://openquantumsafe.org/liboqs/algorithms/kem/kyber) from liboqs. -/// -/// This is being used for ephemeral keys; since these are use-once the first post quantum -/// wireguard paper claimed that CPA security would be sufficient. Nonetheless we choose kyber -/// which provides CCA security since there are no publicly vetted KEMs out there which provide -/// only CPA security. -pub struct EphemeralKEM; - -/// # Safety -/// -/// This Trait impl calls unsafe [oqs_sys] functions, that write to byte -/// slices only identified using raw pointers. It must be ensured that the raw -/// pointers point into byte slices of sufficient length, to avoid UB through -/// overwriting of arbitrary data. This is checked in the following code before -/// the unsafe calls, and an early return with an Err occurs if the byte slice -/// size does not match the required size. -/// -/// __Note__: This requirement is stricter than necessary, it would suffice -/// to only check that the buffers are big enough, allowing them to be even -/// bigger. However, from a correctness point of view it does not make sense to -/// allow bigger buffers. -impl KEM for EphemeralKEM { - const SK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_secret_key as usize; - const PK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_public_key as usize; - const CT_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_ciphertext as usize; - const SHK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_shared_secret as usize; - fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?; - RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_kyber_512_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()).to_rg_error() - } - } - fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?; - RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?; - RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_kyber_512_encaps(ct.as_mut_ptr(), shk.as_mut_ptr(), pk.as_ptr()) - .to_rg_error() - } - } - fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?; - RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?; - RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_kyber_512_decaps(shk.as_mut_ptr(), ct.as_ptr(), sk.as_ptr()) - .to_rg_error() - } - } -} diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index ce86b1c..c719609 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -19,8 +19,9 @@ //! [CryptoServer]. //! //! ``` +//! use rosenpass_cipher_traits::Kem; +//! use rosenpass_ciphers::kem::StaticKem; //! use rosenpass::{ -//! pqkem::{StaticKEM, KEM}, //! protocol::{SSk, SPk, MsgBuf, PeerPtr, CryptoServer, SymKey}, //! }; //! # fn main() -> anyhow::Result<()> { @@ -30,11 +31,11 @@ //! //! // initialize secret and public key for peer a ... //! let (mut peer_a_sk, mut peer_a_pk) = (SSk::zero(), SPk::zero()); -//! StaticKEM::keygen(peer_a_sk.secret_mut(), peer_a_pk.secret_mut())?; +//! StaticKem::keygen(peer_a_sk.secret_mut(), peer_a_pk.secret_mut())?; //! //! // ... and for peer b //! let (mut peer_b_sk, mut peer_b_pk) = (SSk::zero(), SPk::zero()); -//! StaticKEM::keygen(peer_b_sk.secret_mut(), peer_b_pk.secret_mut())?; +//! StaticKem::keygen(peer_b_sk.secret_mut(), peer_b_pk.secret_mut())?; //! //! // initialize server and a pre-shared key //! let psk = SymKey::random(); @@ -67,9 +68,11 @@ //! # } //! ``` -use crate::{hash_domains, msgs::*, pqkem::*}; +use crate::{hash_domains, msgs::*}; use anyhow::{bail, ensure, Context, Result}; +use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; +use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; use rosenpass_secret_memory::{Public, Secret}; use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; @@ -77,6 +80,7 @@ use std::collections::hash_map::{ Entry::{Occupied, Vacant}, HashMap, }; +use std::convert::Infallible; // CONSTANTS & SETTINGS ////////////////////////// @@ -135,10 +139,10 @@ pub fn has_happened(ev: Timing, now: Timing) -> bool { // DATA STRUCTURES & BASIC TRAITS & ACCESSORS //// -pub type SPk = Secret<{ StaticKEM::PK_LEN }>; // Just Secret<> instead of Public<> so it gets allocated on the heap -pub type SSk = Secret<{ StaticKEM::SK_LEN }>; -pub type EPk = Public<{ EphemeralKEM::PK_LEN }>; -pub type ESk = Secret<{ EphemeralKEM::SK_LEN }>; +pub type SPk = Secret<{ StaticKem::PK_LEN }>; // Just Secret<> instead of Public<> so it gets allocated on the heap +pub type SSk = Secret<{ StaticKem::SK_LEN }>; +pub type EPk = Public<{ EphemeralKem::PK_LEN }>; +pub type ESk = Secret<{ EphemeralKem::SK_LEN }>; pub type SymKey = Secret; pub type SymHash = Public; @@ -1248,7 +1252,7 @@ impl HandshakeState { } // I loathe "error: constant expression depends on a generic parameter" - pub fn encaps_and_mix( + pub fn encaps_and_mix, const SHK_LEN: usize>( &mut self, ct: &mut [u8], pk: &[u8], @@ -1258,7 +1262,7 @@ impl HandshakeState { self.mix(pk)?.mix(shk.secret())?.mix(ct) } - pub fn decaps_and_mix( + pub fn decaps_and_mix, const SHK_LEN: usize>( &mut self, sk: &[u8], pk: &[u8], @@ -1422,7 +1426,7 @@ impl CryptoServer { ih.sidi_mut().copy_from_slice(&hs.core.sidi.value); // IHI3 - EphemeralKEM::keygen(hs.eski.secret_mut(), &mut *hs.epki)?; + EphemeralKem::keygen(hs.eski.secret_mut(), &mut *hs.epki)?; ih.epki_mut().copy_from_slice(&hs.epki.value); // IHI4 @@ -1430,7 +1434,7 @@ impl CryptoServer { // IHI5 hs.core - .encaps_and_mix::( + .encaps_and_mix::( ih.sctr_mut(), peer.get(self).spkt.secret(), )?; @@ -1469,7 +1473,7 @@ impl CryptoServer { core.mix(ih.sidi())?.mix(ih.epki())?; // IHR5 - core.decaps_and_mix::( + core.decaps_and_mix::( self.sskm.secret(), self.spkm.secret(), ih.sctr(), @@ -1499,10 +1503,10 @@ impl CryptoServer { core.mix(rh.sidr())?.mix(rh.sidi())?; // RHR4 - core.encaps_and_mix::(rh.ecti_mut(), ih.epki())?; + core.encaps_and_mix::(rh.ecti_mut(), ih.epki())?; // RHR5 - core.encaps_and_mix::( + core.encaps_and_mix::( rh.scti_mut(), peer.get(self).spkt.secret(), )?; @@ -1567,14 +1571,14 @@ impl CryptoServer { core.mix(rh.sidr())?.mix(rh.sidi())?; // RHI4 - core.decaps_and_mix::( + core.decaps_and_mix::( hs!().eski.secret(), &*hs!().epki, rh.ecti(), )?; // RHI5 - core.decaps_and_mix::( + core.decaps_and_mix::( self.sskm.secret(), self.spkm.secret(), rh.scti(), @@ -1810,7 +1814,7 @@ mod test { fn keygen() -> Result<(SSk, SPk)> { // TODO: Copied from the benchmark; deduplicate let (mut sk, mut pk) = (SSk::zero(), SPk::zero()); - StaticKEM::keygen(sk.secret_mut(), pk.secret_mut())?; + StaticKem::keygen(sk.secret_mut(), pk.secret_mut())?; Ok((sk, pk)) }