From c43549adef56e6c59bdd160574803bcc617a495f Mon Sep 17 00:00:00 2001 From: Sebastian Faller Date: Wed, 13 Aug 2025 16:55:33 +0200 Subject: [PATCH] feat: Implemented the RustCrypto traits for signatures and kems (#137) --- 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 66ae692611..968ade936c 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" @@ -43,4 +46,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 ccd0c803f1..988ba98fbb 100644 --- a/oqs/src/kem.rs +++ b/oqs/src/kem.rs @@ -3,9 +3,13 @@ //! See [`Kem`] for the main functionality. //! [`Algorithm`] lists the available algorithms. use alloc::vec::Vec; +use rand_core::CryptoRngCore; use core::ptr::NonNull; +use ::kem::Decapsulate as RustCryptoDecapsulate; +use ::kem::Encapsulate as RustCryptoEncapsulate; + #[cfg(not(feature = "std"))] use cstr_core::CStr; #[cfg(feature = "std")] @@ -478,3 +482,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 3870b4927e..57fd60851d 100644 --- a/oqs/src/sig.rs +++ b/oqs/src/sig.rs @@ -14,6 +14,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}; @@ -531,3 +533,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()), + } + } +}