#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::{
string::ToString,
vec::Vec,
};
#[cfg(feature = "alloc")]
use lib_q_core::api::{
Algorithm,
CryptoProvider,
KemOperations,
};
#[cfg(feature = "alloc")]
use lib_q_core::error::{
Error,
Result,
};
#[cfg(feature = "alloc")]
use lib_q_core::security::SecurityValidator;
#[cfg(feature = "alloc")]
use lib_q_core::traits::{
KemKeypair,
KemPublicKey,
KemSecretKey,
};
#[cfg(feature = "alloc")]
use crate::{
CRYPTO_BYTES,
CRYPTO_CIPHERTEXTBYTES,
CRYPTO_PUBLICKEYBYTES,
CRYPTO_SECRETKEYBYTES,
Ciphertext,
PublicKey,
SecretKey,
decapsulate,
encapsulate,
keypair,
};
#[cfg(feature = "alloc")]
#[derive(Clone)]
pub struct LibQCbKemProvider {
security_validator: SecurityValidator,
}
#[cfg(feature = "alloc")]
impl core::fmt::Debug for LibQCbKemProvider {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LibQCbKemProvider")
.field("security_validator", &"<SecurityValidator>")
.finish()
}
}
#[cfg(feature = "alloc")]
impl LibQCbKemProvider {
pub fn new() -> Result<Self> {
Ok(Self {
security_validator: SecurityValidator::new()?,
})
}
pub fn security_validator(&self) -> &SecurityValidator {
&self.security_validator
}
pub fn security_validator_mut(&mut self) -> &mut SecurityValidator {
&mut self.security_validator
}
}
#[cfg(feature = "alloc")]
impl KemOperations for LibQCbKemProvider {
fn generate_keypair(
&self,
algorithm: Algorithm,
randomness: Option<&[u8]>,
) -> Result<KemKeypair> {
self.security_validator
.validate_algorithm_category(algorithm, lib_q_core::api::AlgorithmCategory::Kem)?;
if let Some(rng) = randomness {
self.security_validator.validate_randomness(rng)?;
}
match algorithm {
Algorithm::CbKem348864 => self.generate_cb_kem_keypair(
CRYPTO_PUBLICKEYBYTES,
CRYPTO_SECRETKEYBYTES,
randomness,
),
Algorithm::CbKem460896 => self.generate_cb_kem_keypair(
CRYPTO_PUBLICKEYBYTES,
CRYPTO_SECRETKEYBYTES,
randomness,
),
Algorithm::CbKem6688128 => self.generate_cb_kem_keypair(
CRYPTO_PUBLICKEYBYTES,
CRYPTO_SECRETKEYBYTES,
randomness,
),
Algorithm::CbKem6960119 => self.generate_cb_kem_keypair(
CRYPTO_PUBLICKEYBYTES,
CRYPTO_SECRETKEYBYTES,
randomness,
),
Algorithm::CbKem8192128 => self.generate_cb_kem_keypair(
CRYPTO_PUBLICKEYBYTES,
CRYPTO_SECRETKEYBYTES,
randomness,
),
_ => Err(Error::InvalidAlgorithm {
algorithm: "Algorithm not supported for Classical McEliece KEM operations",
}),
}
}
fn encapsulate(
&self,
algorithm: Algorithm,
public_key: &KemPublicKey,
randomness: Option<&[u8]>,
) -> Result<(Vec<u8>, Vec<u8>)> {
self.security_validator
.validate_algorithm_category(algorithm, lib_q_core::api::AlgorithmCategory::Kem)?;
self.security_validator
.validate_key_size(algorithm, public_key.as_bytes(), false)?;
if let Some(rng) = randomness {
self.security_validator.validate_randomness(rng)?;
}
match algorithm {
Algorithm::CbKem348864 |
Algorithm::CbKem460896 |
Algorithm::CbKem6688128 |
Algorithm::CbKem6960119 |
Algorithm::CbKem8192128 => self.encapsulate_cb_kem(public_key, randomness),
_ => Err(Error::InvalidAlgorithm {
algorithm: "Algorithm not supported for Classical McEliece KEM operations",
}),
}
}
fn decapsulate(
&self,
algorithm: Algorithm,
secret_key: &KemSecretKey,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
self.security_validator
.validate_algorithm_category(algorithm, lib_q_core::api::AlgorithmCategory::Kem)?;
self.security_validator
.validate_key_size(algorithm, secret_key.as_bytes(), true)?;
self.security_validator
.validate_ciphertext(algorithm, ciphertext)?;
match algorithm {
Algorithm::CbKem348864 |
Algorithm::CbKem460896 |
Algorithm::CbKem6688128 |
Algorithm::CbKem6960119 |
Algorithm::CbKem8192128 => self.decapsulate_cb_kem(secret_key, ciphertext),
_ => Err(Error::InvalidAlgorithm {
algorithm: "Algorithm not supported for Classical McEliece KEM operations",
}),
}
}
fn derive_public_key(
&self,
_algorithm: Algorithm,
_secret_key: &KemSecretKey,
) -> Result<KemPublicKey> {
Err(Error::UnsupportedOperation {
operation: "Classical McEliece does not support public key derivation from secret key"
.to_string(),
})
}
}
#[cfg(feature = "alloc")]
impl LibQCbKemProvider {
fn generate_cb_kem_keypair(
&self,
public_key_size: usize,
secret_key_size: usize,
randomness: Option<&[u8]>,
) -> Result<KemKeypair> {
if public_key_size != CRYPTO_PUBLICKEYBYTES {
return Err(Error::InvalidKeySize {
expected: CRYPTO_PUBLICKEYBYTES,
actual: public_key_size,
});
}
if secret_key_size != CRYPTO_SECRETKEYBYTES {
return Err(Error::InvalidKeySize {
expected: CRYPTO_SECRETKEYBYTES,
actual: secret_key_size,
});
}
let mut public_key_buf = [0u8; CRYPTO_PUBLICKEYBYTES];
let mut secret_key_buf = [0u8; CRYPTO_SECRETKEYBYTES];
let (public_key, secret_key) = if let Some(rng_bytes) = randomness {
{
let mut rng = crate::LibQRng::new_deterministic_from_bytes(rng_bytes);
keypair(&mut public_key_buf, &mut secret_key_buf, &mut rng)
}
} else {
{
let mut rng = crate::LibQRng::new();
keypair(&mut public_key_buf, &mut secret_key_buf, &mut rng)
}
};
Ok(KemKeypair::new(
Vec::from(public_key.as_array().as_slice()),
Vec::from(secret_key.as_array().as_slice()),
))
}
fn encapsulate_cb_kem(
&self,
public_key: &KemPublicKey,
randomness: Option<&[u8]>,
) -> Result<(Vec<u8>, Vec<u8>)> {
if public_key.as_bytes().len() != CRYPTO_PUBLICKEYBYTES {
return Err(Error::InvalidKeySize {
expected: CRYPTO_PUBLICKEYBYTES,
actual: public_key.as_bytes().len(),
});
}
let mut public_key_buf = [0u8; CRYPTO_PUBLICKEYBYTES];
public_key_buf.copy_from_slice(public_key.as_bytes());
let public_key = PublicKey::from(&mut public_key_buf);
let mut shared_secret_buf = [0u8; CRYPTO_BYTES];
let (ciphertext, shared_secret) = if let Some(rng_bytes) = randomness {
{
let mut rng = crate::LibQRng::new_deterministic_from_bytes(rng_bytes);
encapsulate(&public_key, &mut shared_secret_buf, &mut rng)
}
} else {
{
let mut rng = crate::LibQRng::new();
encapsulate(&public_key, &mut shared_secret_buf, &mut rng)
}
};
Ok((
Vec::from(ciphertext.as_array().as_slice()),
Vec::from(shared_secret.as_array().as_slice()),
))
}
fn decapsulate_cb_kem(&self, secret_key: &KemSecretKey, ciphertext: &[u8]) -> Result<Vec<u8>> {
if secret_key.as_bytes().len() != CRYPTO_SECRETKEYBYTES {
return Err(Error::InvalidKeySize {
expected: CRYPTO_SECRETKEYBYTES,
actual: secret_key.as_bytes().len(),
});
}
if ciphertext.len() != CRYPTO_CIPHERTEXTBYTES {
return Err(Error::InvalidCiphertextSize {
expected: CRYPTO_CIPHERTEXTBYTES,
actual: ciphertext.len(),
});
}
let mut secret_key_buf = [0u8; CRYPTO_SECRETKEYBYTES];
secret_key_buf.copy_from_slice(secret_key.as_bytes());
let secret_key = SecretKey::from(&mut secret_key_buf);
let mut ciphertext_buf = [0u8; CRYPTO_CIPHERTEXTBYTES];
ciphertext_buf.copy_from_slice(ciphertext);
let ciphertext = Ciphertext::from(ciphertext_buf);
let mut shared_secret_buf = [0u8; CRYPTO_BYTES];
let shared_secret = decapsulate(&ciphertext, &secret_key, &mut shared_secret_buf);
Ok(Vec::from(shared_secret.as_array().as_slice()))
}
}
#[cfg(feature = "alloc")]
impl CryptoProvider for LibQCbKemProvider {
fn kem(&self) -> Option<&dyn KemOperations> {
Some(self)
}
fn signature(&self) -> Option<&dyn lib_q_core::api::SignatureOperations> {
None
}
fn hash(&self) -> Option<&dyn lib_q_core::api::HashOperations> {
None
}
fn aead(&self) -> Option<&dyn lib_q_core::api::AeadOperations> {
None
}
}
#[cfg(all(test, feature = "alloc"))]
mod tests {
use super::*;
fn compiled_cb_kem_algorithm() -> Algorithm {
#[cfg(any(feature = "cbkem348864", feature = "cbkem348864f"))]
{
Algorithm::CbKem348864
}
#[cfg(all(
not(any(feature = "cbkem348864", feature = "cbkem348864f")),
any(feature = "cbkem460896", feature = "cbkem460896f"),
))]
{
Algorithm::CbKem460896
}
#[cfg(all(
not(any(
feature = "cbkem348864",
feature = "cbkem348864f",
feature = "cbkem460896",
feature = "cbkem460896f",
)),
any(feature = "cbkem6688128", feature = "cbkem6688128f"),
))]
{
Algorithm::CbKem6688128
}
#[cfg(all(
not(any(
feature = "cbkem348864",
feature = "cbkem348864f",
feature = "cbkem460896",
feature = "cbkem460896f",
feature = "cbkem6688128",
feature = "cbkem6688128f",
)),
any(feature = "cbkem6960119", feature = "cbkem6960119f"),
))]
{
Algorithm::CbKem6960119
}
#[cfg(all(
not(any(
feature = "cbkem348864",
feature = "cbkem348864f",
feature = "cbkem460896",
feature = "cbkem460896f",
feature = "cbkem6688128",
feature = "cbkem6688128f",
feature = "cbkem6960119",
feature = "cbkem6960119f",
)),
any(feature = "cbkem8192128", feature = "cbkem8192128f"),
))]
{
Algorithm::CbKem8192128
}
}
#[test]
fn test_provider_creation() {
let provider = LibQCbKemProvider::new();
assert!(provider.is_ok(), "Provider should be created successfully");
}
#[test]
fn test_provider_security_validator() {
let provider = LibQCbKemProvider::new().unwrap();
let _validator = provider.security_validator();
}
#[test]
fn test_provider_security_validator_mut_accessor() {
let mut provider = LibQCbKemProvider::new().unwrap();
let _validator_mut = provider.security_validator_mut();
}
#[test]
fn test_provider_unsupported_algorithm() {
let provider = LibQCbKemProvider::new().unwrap();
let result = provider.generate_keypair(Algorithm::Sha3_256, None);
assert!(
result.is_err(),
"Should return error for unsupported algorithm"
);
if let Err(Error::InvalidAlgorithm { .. }) = result {
} else {
panic!("Expected InvalidAlgorithm error");
}
}
#[test]
fn test_provider_algorithm_routing() {
let provider = LibQCbKemProvider::new().unwrap();
let alg = compiled_cb_kem_algorithm();
provider
.security_validator()
.validate_algorithm_category(alg, lib_q_core::api::AlgorithmCategory::Kem)
.expect("compiled CB-KEM algorithm should be a KEM");
#[cfg(any(feature = "cbkem348864", feature = "cbkem348864f"))]
{
let result = provider.generate_keypair(alg, None);
match result {
Ok(_) => {}
Err(Error::NotImplemented { .. }) => {}
Err(Error::RandomGenerationFailed { .. }) => {}
Err(e) => panic!("Unexpected error type: {:?}", e),
}
}
}
#[test]
fn test_provider_full_kem_cycle() {
#[cfg(feature = "std")]
{
let provider = LibQCbKemProvider::new().unwrap();
let alg = compiled_cb_kem_algorithm();
#[cfg(any(feature = "cbkem348864", feature = "cbkem348864f"))]
{
let keypair = provider.generate_keypair(alg, None).unwrap();
let (ciphertext, shared_secret1) = provider
.encapsulate(alg, &keypair.public_key, None)
.unwrap();
let shared_secret2 = provider
.decapsulate(alg, &keypair.secret_key, &ciphertext)
.unwrap();
assert_eq!(
shared_secret1, shared_secret2,
"Shared secrets should match"
);
assert_eq!(
ciphertext.len(),
CRYPTO_CIPHERTEXTBYTES,
"Classical McEliece ciphertext should be {} bytes",
CRYPTO_CIPHERTEXTBYTES
);
assert_eq!(
shared_secret1.len(),
CRYPTO_BYTES,
"Shared secret should be {} bytes",
CRYPTO_BYTES
);
}
#[cfg(not(any(feature = "cbkem348864", feature = "cbkem348864f")))]
{
provider
.security_validator()
.validate_algorithm_category(alg, lib_q_core::api::AlgorithmCategory::Kem)
.expect("compiled CB-KEM algorithm should be a KEM");
}
}
}
#[test]
fn test_provider_derive_public_key_is_unsupported() {
let provider = LibQCbKemProvider::new().unwrap();
let secret_key = KemSecretKey::new(Vec::new());
let result = provider.derive_public_key(compiled_cb_kem_algorithm(), &secret_key);
assert!(matches!(result, Err(Error::UnsupportedOperation { .. })));
}
#[test]
fn test_crypto_provider_trait_exposes_only_kem_operations() {
let provider = LibQCbKemProvider::new().unwrap();
let crypto_provider: &dyn CryptoProvider = &provider;
assert!(crypto_provider.kem().is_some());
assert!(crypto_provider.signature().is_none());
assert!(crypto_provider.hash().is_none());
assert!(crypto_provider.aead().is_none());
}
}