From 4a7126024368b803ab628a194ab1415c64d883da Mon Sep 17 00:00:00 2001 From: Sebastian Faller Date: Wed, 13 Aug 2025 16:55:33 +0200 Subject: [PATCH 1/3] feat: Implemented the RustCrypto traits for signatures and kems (#137) Signed-off-by: 0rlych1kk4 --- oqs/Cargo.toml | 5 ++- oqs/src/kem.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ oqs/src/sig.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 1 deletion(-) diff --git a/oqs/Cargo.toml b/oqs/Cargo.toml index 346d9b8683..34315aebba 100644 --- a/oqs/Cargo.toml +++ b/oqs/Cargo.toml @@ -13,6 +13,9 @@ license = "MIT OR Apache-2.0" libc = "0.2" cstr_core = { version = "0.2", default-features = false, features = ["alloc"] } serde = { version = "1.0", optional = true, default-features = false, features = ["derive", "alloc"] } +kem = "=0.3.0-pre.0" +signature = "2.2.0" +rand_core = { version = "0.6", features = ["getrandom"] } [dependencies.oqs-sys] path = "../oqs-sys" @@ -42,4 +45,4 @@ falcon = ["oqs-sys/falcon"] mayo = ["oqs-sys/mayo"] ml_dsa = ["oqs-sys/ml_dsa"] sphincs = ["oqs-sys/sphincs"] -uov = ["oqs-sys/uov"] +uov = ["oqs-sys/uov"] \ No newline at end of file diff --git a/oqs/src/kem.rs b/oqs/src/kem.rs index 43616691e8..7a0c400181 100644 --- a/oqs/src/kem.rs +++ b/oqs/src/kem.rs @@ -3,10 +3,14 @@ //! See [`Kem`] for the main functionality. //! [`Algorithm`] lists the available algorithms. use alloc::vec::Vec; +use rand_core::CryptoRngCore; use core::ptr::NonNull; use core::str::FromStr; +use ::kem::Decapsulate as RustCryptoDecapsulate; +use ::kem::Encapsulate as RustCryptoEncapsulate; + #[cfg(not(feature = "std"))] use cstr_core::CStr; #[cfg(feature = "std")] @@ -500,3 +504,91 @@ impl Kem { Ok(ss) } } + +/// Encapsulator uses the public key of a KEM to encapsulate keys. This struct implements RustCrypto's Encapsulate trait. +/// +/// # Example +/// ```rust +/// # if !cfg!(feature = "ml_kem") { return; } +/// use oqs; +/// use kem::{Encapsulate,Decapsulate}; +/// use rand_core::OsRng; +/// oqs::init(); +/// let kem = oqs::kem::Kem::new(oqs::kem::Algorithm::MlKem512).unwrap(); +/// let (pk, sk) = kem.keypair().unwrap(); +/// let encap = oqs::kem::Encapsulator::new(&kem, pk); +/// let decap = oqs::kem::Decapsulator::new(&kem, sk); +/// // Note that encapsulate ignores the provided rng +/// let (ct, ss) = encap.encapsulate(&mut OsRng).unwrap(); +/// let ss2 = decap.decapsulate(&ct).unwrap(); +/// assert_eq!(ss, ss2); +/// ``` +pub struct Encapsulator<'a> { + scheme: &'a Kem, + pk: PublicKey, +} + +impl<'a> Encapsulator<'a> { + /// Creates a new [`Encapsulator`]. + pub fn new(scheme: &'a Kem, pk: PublicKey) -> Self { + Encapsulator { + scheme: scheme, + pk: pk, + } + } +} + +/// Decapsulator struct containing a kem scheme and a secret key. +/// +/// # Example +/// ```rust +/// # if !cfg!(feature = "ml_kem") { return; } +/// use oqs; +/// use kem::{Encapsulate,Decapsulate}; +/// use rand_core::OsRng; +/// oqs::init(); +/// let kem = oqs::kem::Kem::new(oqs::kem::Algorithm::MlKem512).unwrap(); +/// let (pk, sk) = kem.keypair().unwrap(); +/// let encap = oqs::kem::Encapsulator::new(&kem, pk); +/// let decap = oqs::kem::Decapsulator::new(&kem, sk); +/// // Note that encapsulate ignores the provided rng +/// let (ct, ss) = encap.encapsulate(&mut OsRng).unwrap(); +/// let ss2 = decap.decapsulate(&ct).unwrap(); +/// assert_eq!(ss, ss2); +/// ``` +pub struct Decapsulator<'a> { + scheme: &'a Kem, + sk: SecretKey, +} + +impl<'a> Decapsulator<'a> { + /// Creates a new [`Decapsulator`]. + pub fn new(scheme: &'a Kem, sk: SecretKey) -> Self { + Decapsulator { + scheme: scheme, + sk: sk, + } + } +} + +impl<'a> RustCryptoEncapsulate for Encapsulator<'a> { + type Error = crate::Error; + + fn encapsulate( + &self, + _csprng: &mut impl CryptoRngCore, + ) -> core::result::Result<(Ciphertext, SharedSecret), Self::Error> { + self.scheme.encapsulate(&self.pk) + } +} + +impl<'a> RustCryptoDecapsulate for Decapsulator<'a> { + type Error = crate::Error; + + fn decapsulate( + &self, + encapsulated_key: &Ciphertext, + ) -> core::result::Result { + self.scheme.decapsulate(&self.sk, encapsulated_key) + } +} \ No newline at end of file diff --git a/oqs/src/sig.rs b/oqs/src/sig.rs index 8e8527876c..7b0991cc5b 100644 --- a/oqs/src/sig.rs +++ b/oqs/src/sig.rs @@ -15,6 +15,8 @@ use std::ffi::CStr; use crate::ffi::sig as ffi; use crate::newtype_buffer; use crate::*; +use ::signature::Signer as RustCryptoSigner; +use ::signature::Verifier as RustCryptoVerifier; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -549,3 +551,89 @@ impl Sig { status_to_result(status) } } + +/// Signer struct containing a signature scheme and a signing key. +/// +/// # Example +/// ```rust +/// # if !cfg!(feature = "ml_dsa") { return; } +/// use oqs; +/// use signature::{Signer,Verifier}; +/// oqs::init(); +/// let scheme = oqs::sig::Sig::new(oqs::sig::Algorithm::MlDsa44).unwrap(); +/// let message = [0u8; 100]; +/// let (pk, sk) = scheme.keypair().unwrap(); +/// let signer = oqs::sig::Signer::new(&scheme,sk); +/// let verifier = oqs::sig::Verifier::new(&scheme,pk); +/// let signature = signer.try_sign(&message).unwrap(); +/// assert!(verifier.verify(&message, &signature).is_ok()); +/// ``` +pub struct Signer<'a> { + scheme: &'a Sig, + sk: SecretKey, +} + +impl<'a> Signer<'a> { + /// Creates a new [`Signer`] with the given signature scheme and secret key. + pub fn new(scheme: &'a Sig, sk: SecretKey) -> Self { + Signer { + scheme: scheme, + sk: sk, + } + } +} + +impl<'a> RustCryptoSigner for Signer<'a> { + fn try_sign(&self, msg: &[u8]) -> core::result::Result { + match self.scheme.sign(msg, &self.sk) { + Ok(s) => Ok(s), + Err(_) => Err(signature::Error::new()), + } + } +} + +/// Verifier struct containing a signature scheme and a public verification key. +/// +/// # Example +/// ```rust +/// # if !cfg!(feature = "ml_dsa") { return; } +/// use oqs; +/// use signature::{Signer,Verifier}; +/// oqs::init(); +/// let scheme = oqs::sig::Sig::new(oqs::sig::Algorithm::MlDsa44).unwrap(); +/// let message = [0u8; 100]; +/// let (pk, sk) = scheme.keypair().unwrap(); +/// let signer = oqs::sig::Signer::new(&scheme,sk); +/// let verifier = oqs::sig::Verifier::new(&scheme,pk); +/// let signature = signer.try_sign(&message).unwrap(); +/// assert!(verifier.verify(&message, &signature).is_ok()); +/// ``` +/// +/// Used to verify signatures for a given message. +pub struct Verifier<'a> { + scheme: &'a Sig, + pk: PublicKey, +} + +impl<'a> Verifier<'a>{ + /// Creates a new [`Verifier`] with the given signature scheme and public key. + pub fn new(scheme: &'a Sig, pk: PublicKey) -> Self{ + Verifier{ + scheme: scheme, + pk: pk, + } + } +} + +impl<'a> RustCryptoVerifier for Verifier<'a> { + fn verify( + &self, + msg: &[u8], + signature: &sig::Signature, + ) -> core::result::Result<(), signature::Error> { + match self.scheme.verify(msg, signature, &self.pk) { + Ok(_) => Ok(()), + Err(_) => Err(signature::Error::new()), + } + } +} From 631d306bc0afe13d4ac2813836609d41a147db12 Mon Sep 17 00:00:00 2001 From: 0rlych1kk4 Date: Mon, 13 Jul 2026 22:08:31 +0800 Subject: [PATCH 2/3] style: format RustCrypto trait implementation Signed-off-by: 0rlych1kk4 --- oqs/src/kem.rs | 2 +- oqs/src/sig.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/oqs/src/kem.rs b/oqs/src/kem.rs index 7a0c400181..7c684d886b 100644 --- a/oqs/src/kem.rs +++ b/oqs/src/kem.rs @@ -591,4 +591,4 @@ impl<'a> RustCryptoDecapsulate for Decapsulator<'a> { ) -> core::result::Result { self.scheme.decapsulate(&self.sk, encapsulated_key) } -} \ No newline at end of file +} diff --git a/oqs/src/sig.rs b/oqs/src/sig.rs index 7b0991cc5b..d3a9de5289 100644 --- a/oqs/src/sig.rs +++ b/oqs/src/sig.rs @@ -615,10 +615,10 @@ pub struct Verifier<'a> { pk: PublicKey, } -impl<'a> Verifier<'a>{ +impl<'a> Verifier<'a> { /// Creates a new [`Verifier`] with the given signature scheme and public key. - pub fn new(scheme: &'a Sig, pk: PublicKey) -> Self{ - Verifier{ + pub fn new(scheme: &'a Sig, pk: PublicKey) -> Self { + Verifier { scheme: scheme, pk: pk, } From a1d6cb414fcd32f819e3712d810df42dddd55bf1 Mon Sep 17 00:00:00 2001 From: 0rlych1kk4 Date: Mon, 13 Jul 2026 22:21:39 +0800 Subject: [PATCH 3/3] fix: address Clippy warnings in RustCrypto adapters Signed-off-by: 0rlych1kk4 --- oqs/src/kem.rs | 10 ++-------- oqs/src/sig.rs | 10 ++-------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/oqs/src/kem.rs b/oqs/src/kem.rs index 7c684d886b..8c3f5ab82e 100644 --- a/oqs/src/kem.rs +++ b/oqs/src/kem.rs @@ -531,10 +531,7 @@ pub struct Encapsulator<'a> { impl<'a> Encapsulator<'a> { /// Creates a new [`Encapsulator`]. pub fn new(scheme: &'a Kem, pk: PublicKey) -> Self { - Encapsulator { - scheme: scheme, - pk: pk, - } + Encapsulator { scheme, pk } } } @@ -564,10 +561,7 @@ pub struct Decapsulator<'a> { impl<'a> Decapsulator<'a> { /// Creates a new [`Decapsulator`]. pub fn new(scheme: &'a Kem, sk: SecretKey) -> Self { - Decapsulator { - scheme: scheme, - sk: sk, - } + Decapsulator { scheme, sk } } } diff --git a/oqs/src/sig.rs b/oqs/src/sig.rs index d3a9de5289..84c1f1f7a9 100644 --- a/oqs/src/sig.rs +++ b/oqs/src/sig.rs @@ -576,10 +576,7 @@ pub struct Signer<'a> { impl<'a> Signer<'a> { /// Creates a new [`Signer`] with the given signature scheme and secret key. pub fn new(scheme: &'a Sig, sk: SecretKey) -> Self { - Signer { - scheme: scheme, - sk: sk, - } + Signer { scheme, sk } } } @@ -618,10 +615,7 @@ pub struct Verifier<'a> { impl<'a> Verifier<'a> { /// Creates a new [`Verifier`] with the given signature scheme and public key. pub fn new(scheme: &'a Sig, pk: PublicKey) -> Self { - Verifier { - scheme: scheme, - pk: pk, - } + Verifier { scheme, pk } } }