#[cfg(feature = "wasm")]
extern crate alloc;
#[cfg(feature = "wasm")]
use alloc::{
boxed::Box,
format,
string::{
String,
ToString,
},
vec::Vec,
};
#[cfg(feature = "wasm")]
use js_sys::Uint8Array;
#[cfg(feature = "wasm")]
use serde_json;
#[cfg(feature = "wasm")]
use serde_wasm_bindgen;
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
use crate::api::{
Algorithm,
AlgorithmCategory,
};
use crate::contexts::{
AeadContext,
HashContext,
KemContext,
SignatureContext,
};
use crate::providers::LibQCryptoProvider;
use crate::security::SecurityValidator;
use crate::traits::{
AeadKey,
Nonce,
};
use crate::wasm::conversions::WASM_SIGNATURE_ALGORITHM_IDS;
use crate::wasm::error::{
convert_result,
error_to_js_value,
parse_algorithm_wasm,
};
#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub struct WasmKemContext {
inner: KemContext,
security_validator: SecurityValidator,
}
impl WasmKemContext {
pub fn new() -> WasmKemContext {
WasmKemContext {
inner: KemContext::with_default_provider(),
security_validator: SecurityValidator::new()
.unwrap_or_else(|_| SecurityValidator::new().unwrap()),
}
}
pub fn with_provider(provider: &WasmCryptoProvider) -> WasmKemContext {
WasmKemContext {
inner: KemContext::with_provider(Box::new(provider.inner.clone())),
security_validator: SecurityValidator::new()
.unwrap_or_else(|_| SecurityValidator::new().unwrap()),
}
}
}
impl Default for WasmKemContext {
fn default() -> Self {
Self::new()
}
}
impl WasmKemContext {
pub fn generate_keypair(
&mut self,
algorithm: &str,
randomness: Option<Uint8Array>,
) -> Result<JsValue, JsValue> {
let algorithm = self
.parse_kem_algorithm(algorithm)
.map_err(error_to_js_value)?;
convert_result(
self.security_validator
.validate_algorithm_category(algorithm, algorithm.category()),
)?;
let randomness_vec = randomness.map(|rand| rand.to_vec());
let randomness_bytes = randomness_vec.as_deref();
let keypair = self
.inner
.generate_keypair(algorithm, randomness_bytes)
.map_err(error_to_js_value)?;
#[cfg(feature = "wasm")]
{
let result = serde_json::json!({
"public_key": keypair.public_key.data,
"secret_key": keypair.secret_key.data,
"algorithm": algorithm.to_string(),
"security_level": 256 });
serde_wasm_bindgen::to_value(&result)
.map_err(|e| JsValue::from_str(&format!("Serialization error: {:?}", e)))
}
#[cfg(not(feature = "wasm"))]
{
Err(JsValue::from_str("WASM feature not enabled"))
}
}
pub fn encapsulate(
&self,
algorithm: &str,
public_key_data: &Uint8Array,
randomness: Option<Uint8Array>,
) -> Result<JsValue, JsValue> {
let algorithm = self
.parse_kem_algorithm(algorithm)
.map_err(error_to_js_value)?;
if public_key_data.length() == 0 {
return Err(JsValue::from_str("Invalid KEM public key: empty key"));
}
let randomness_vec = randomness.map(|rand| rand.to_vec());
let randomness_bytes = randomness_vec.as_deref();
let public_key = crate::traits::KemPublicKey::new(public_key_data.to_vec());
let (ciphertext, shared_secret) = self
.inner
.encapsulate(algorithm, &public_key, randomness_bytes)
.map_err(error_to_js_value)?;
#[cfg(feature = "wasm")]
{
let result = serde_json::json!({
"ciphertext": ciphertext,
"shared_secret": shared_secret,
"algorithm": algorithm.to_string(),
"security_level": 256 });
serde_wasm_bindgen::to_value(&result)
.map_err(|e| JsValue::from_str(&format!("Serialization error: {:?}", e)))
}
#[cfg(not(feature = "wasm"))]
{
Err(JsValue::from_str("WASM feature not enabled"))
}
}
pub fn decapsulate(
&self,
algorithm: &str,
secret_key_data: &Uint8Array,
ciphertext: &Uint8Array,
) -> Result<Vec<u8>, JsValue> {
let algorithm = self
.parse_kem_algorithm(algorithm)
.map_err(error_to_js_value)?;
self.security_validator
.validate_algorithm_category(algorithm, AlgorithmCategory::Kem)
.map_err(error_to_js_value)?;
if secret_key_data.length() == 0 {
return Err(JsValue::from_str("Invalid KEM secret key: empty key"));
}
if ciphertext.length() == 0 {
return Err(JsValue::from_str("Invalid message size: empty data"));
}
let secret_key = crate::traits::KemSecretKey::new(secret_key_data.to_vec());
self.security_validator
.validate_secret_key(algorithm, secret_key.as_bytes())
.map_err(error_to_js_value)?;
self.security_validator
.validate_ciphertext(algorithm, &ciphertext.to_vec())
.map_err(error_to_js_value)?;
let shared_secret = self
.inner
.decapsulate(algorithm, &secret_key, &ciphertext.to_vec())
.map_err(error_to_js_value)?;
Ok(shared_secret)
}
pub fn security_level(&self) -> u32 {
256 }
pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
self.parse_kem_algorithm(algorithm).is_ok()
}
pub fn supported_algorithms(&self) -> String {
#[allow(unused_mut)] let mut algorithms = alloc::vec!["ml-kem-512", "ml-kem-768", "ml-kem-1024"];
#[cfg(feature = "wasm")]
{
serde_json::to_string(&algorithms).unwrap_or_else(|_| "[]".to_string())
}
#[cfg(not(feature = "wasm"))]
{
"[]".to_string()
}
}
fn parse_kem_algorithm(&self, algorithm: &str) -> Result<Algorithm, crate::error::Error> {
parse_algorithm_wasm(algorithm).map_err(|_| crate::error::Error::InvalidAlgorithm {
algorithm: "Invalid algorithm name",
})
}
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub struct WasmSignatureContext {
inner: SignatureContext,
security_validator: SecurityValidator,
}
impl WasmSignatureContext {
pub fn new() -> WasmSignatureContext {
WasmSignatureContext {
inner: SignatureContext::with_default_provider(),
security_validator: SecurityValidator::new()
.unwrap_or_else(|_| SecurityValidator::new().unwrap()),
}
}
pub fn from_signature_context(inner: SignatureContext) -> WasmSignatureContext {
WasmSignatureContext {
inner,
security_validator: SecurityValidator::new()
.unwrap_or_else(|_| SecurityValidator::new().unwrap()),
}
}
}
impl Default for WasmSignatureContext {
fn default() -> Self {
Self::new()
}
}
impl WasmSignatureContext {
pub fn with_provider(provider: &WasmCryptoProvider) -> WasmSignatureContext {
WasmSignatureContext {
inner: SignatureContext::with_provider(Box::new(provider.inner.clone())),
security_validator: SecurityValidator::new()
.unwrap_or_else(|_| SecurityValidator::new().unwrap()),
}
}
fn parse_signature_algorithm(&self, algorithm: &str) -> Result<Algorithm, crate::error::Error> {
parse_algorithm_wasm(algorithm).map_err(|_| crate::error::Error::InvalidAlgorithm {
algorithm: "Invalid algorithm name",
})
}
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
impl WasmSignatureContext {
pub fn generate_keypair(
&mut self,
algorithm: &str,
randomness: Option<Uint8Array>,
) -> Result<JsValue, JsValue> {
let algorithm = self
.parse_signature_algorithm(algorithm)
.map_err(error_to_js_value)?;
convert_result(
self.security_validator
.validate_algorithm_category(algorithm, algorithm.category()),
)?;
let randomness_vec = randomness.map(|rand| rand.to_vec());
let randomness_bytes = randomness_vec.as_deref();
let keypair = self
.inner
.generate_keypair(algorithm, randomness_bytes)
.map_err(error_to_js_value)?;
#[cfg(feature = "wasm")]
{
let result = serde_json::json!({
"public_key": keypair.public_key.data,
"secret_key": keypair.secret_key.data,
"algorithm": algorithm.to_string(),
"security_level": 256 });
serde_wasm_bindgen::to_value(&result)
.map_err(|e| JsValue::from_str(&format!("Serialization error: {:?}", e)))
}
#[cfg(not(feature = "wasm"))]
{
Err(JsValue::from_str("WASM feature not enabled"))
}
}
pub fn sign(
&self,
algorithm: &str,
secret_key_data: &Uint8Array,
message: &Uint8Array,
randomness: Option<Uint8Array>,
) -> Result<Vec<u8>, JsValue> {
let algorithm = self
.parse_signature_algorithm(algorithm)
.map_err(error_to_js_value)?;
if secret_key_data.length() == 0 {
return Err(JsValue::from_str("Invalid signature secret key: empty key"));
}
if message.length() == 0 {
return Err(JsValue::from_str("Invalid message size: empty data"));
}
let randomness_vec = randomness.map(|rand| rand.to_vec());
let randomness_bytes = randomness_vec.as_deref();
let secret_key = crate::traits::SigSecretKey::new(secret_key_data.to_vec());
let signature = self
.inner
.sign(algorithm, &secret_key, &message.to_vec(), randomness_bytes)
.map_err(error_to_js_value)?;
Ok(signature)
}
pub fn verify(
&self,
algorithm: &str,
public_key_data: &Uint8Array,
message: &Uint8Array,
signature: &Uint8Array,
) -> Result<bool, JsValue> {
let algorithm = self
.parse_signature_algorithm(algorithm)
.map_err(error_to_js_value)?;
if public_key_data.length() == 0 {
return Err(JsValue::from_str("Invalid signature public key: empty key"));
}
if message.length() == 0 {
return Err(JsValue::from_str("Invalid message size: empty data"));
}
if signature.length() == 0 {
return Err(JsValue::from_str("Invalid signature: empty data"));
}
let public_key = crate::traits::SigPublicKey::new(public_key_data.to_vec());
let is_valid = self
.inner
.verify(
algorithm,
&public_key,
&message.to_vec(),
&signature.to_vec(),
)
.map_err(error_to_js_value)?;
Ok(is_valid)
}
pub fn security_level(&self) -> u32 {
256 }
pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
self.parse_signature_algorithm(algorithm).is_ok()
}
pub fn supported_algorithms(&self) -> String {
#[cfg(feature = "wasm")]
{
serde_json::to_string(&WASM_SIGNATURE_ALGORITHM_IDS)
.unwrap_or_else(|_| "[]".to_string())
}
#[cfg(not(feature = "wasm"))]
{
"[]".to_string()
}
}
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub struct WasmHashContext {
inner: HashContext,
security_validator: SecurityValidator,
}
impl WasmHashContext {
pub fn new() -> WasmHashContext {
WasmHashContext {
inner: HashContext::with_default_provider(),
security_validator: SecurityValidator::new()
.unwrap_or_else(|_| SecurityValidator::new().unwrap()),
}
}
pub fn from_hash_context(inner: HashContext) -> WasmHashContext {
WasmHashContext {
inner,
security_validator: SecurityValidator::new()
.unwrap_or_else(|_| SecurityValidator::new().unwrap()),
}
}
}
impl Default for WasmHashContext {
fn default() -> Self {
Self::new()
}
}
impl WasmHashContext {
pub fn with_provider(provider: &WasmCryptoProvider) -> WasmHashContext {
WasmHashContext {
inner: HashContext::with_provider(Box::new(provider.inner.clone())),
security_validator: SecurityValidator::new()
.unwrap_or_else(|_| SecurityValidator::new().unwrap()),
}
}
pub fn hash(&mut self, algorithm: &str, data: &Uint8Array) -> Result<JsValue, JsValue> {
let algorithm = self
.parse_hash_algorithm(algorithm)
.map_err(error_to_js_value)?;
self.security_validator
.validate_algorithm_category(algorithm, AlgorithmCategory::Hash)
.map_err(error_to_js_value)?;
self.security_validator
.validate_hash_input(&data.to_vec())
.map_err(error_to_js_value)?;
let hash = self
.inner
.hash(algorithm, &data.to_vec())
.map_err(error_to_js_value)?;
#[cfg(feature = "wasm")]
{
let result = serde_json::json!({
"hash": hash,
"algorithm": algorithm.to_string(),
"security_level": 256 });
match serde_wasm_bindgen::to_value(&result) {
Ok(value) => Ok(value),
Err(e) => Err(JsValue::from_str(&format!("Serialization error: {:?}", e))),
}
}
#[cfg(not(feature = "wasm"))]
{
Err(JsValue::from_str("WASM feature not enabled"))
}
}
pub fn security_level(&self) -> u32 {
256 }
pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
self.parse_hash_algorithm(algorithm).is_ok()
}
pub fn supported_algorithms(&self) -> String {
let algorithms = alloc::vec![
"sha3-224",
"sha3-256",
"sha3-384",
"sha3-512",
"shake128",
"shake256",
"sha-224",
"sha-256",
"sha-384",
"sha-512",
"sha-512/224",
"sha-512/256",
"cshake128",
"cshake256",
"keccak-224",
"keccak-256",
"keccak-384",
"keccak-512",
"kangarootwelve",
"turboshake128",
"turboshake256",
"kmac128",
"kmac256",
"tuplehash128",
"tuplehash256",
"parallelhash128",
"parallelhash256",
];
#[cfg(feature = "wasm")]
{
serde_json::to_string(&algorithms).unwrap_or_else(|_| "[]".to_string())
}
#[cfg(not(feature = "wasm"))]
{
"[]".to_string()
}
}
fn parse_hash_algorithm(&self, algorithm: &str) -> Result<Algorithm, crate::error::Error> {
parse_algorithm_wasm(algorithm).map_err(|_| crate::error::Error::InvalidAlgorithm {
algorithm: "Invalid algorithm name",
})
}
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub struct WasmAeadContext {
inner: AeadContext,
security_validator: SecurityValidator,
}
impl WasmAeadContext {
pub fn new() -> WasmAeadContext {
WasmAeadContext {
inner: AeadContext::new(),
security_validator: SecurityValidator::new()
.unwrap_or_else(|_| SecurityValidator::new().unwrap()),
}
}
pub fn from_aead_context(inner: AeadContext) -> WasmAeadContext {
WasmAeadContext {
inner,
security_validator: SecurityValidator::new()
.unwrap_or_else(|_| SecurityValidator::new().unwrap()),
}
}
}
impl Default for WasmAeadContext {
fn default() -> Self {
Self::new()
}
}
impl WasmAeadContext {
pub fn with_provider(provider: &WasmCryptoProvider) -> WasmAeadContext {
WasmAeadContext {
inner: AeadContext::with_provider(Box::new(provider.inner.clone())),
security_validator: SecurityValidator::new()
.unwrap_or_else(|_| SecurityValidator::new().unwrap()),
}
}
pub fn encrypt(
&mut self,
algorithm: &str,
key: &Uint8Array,
nonce: &Uint8Array,
plaintext: &Uint8Array,
aad: Option<Uint8Array>,
) -> Result<Vec<u8>, JsValue> {
let algorithm = self
.parse_aead_algorithm(algorithm)
.map_err(error_to_js_value)?;
self.security_validator
.validate_algorithm_category(algorithm, AlgorithmCategory::Aead)
.map_err(error_to_js_value)?;
self.security_validator
.validate_key_size(algorithm, &key.to_vec(), true)
.map_err(error_to_js_value)?;
self.security_validator
.validate_nonce(&nonce.to_vec())
.map_err(error_to_js_value)?;
self.security_validator
.validate_aead_message(&plaintext.to_vec())
.map_err(error_to_js_value)?;
let aad_bytes = aad.map(|aad_data| aad_data.to_vec());
if let Some(ref aad_data) = aad_bytes {
self.security_validator
.validate_aead_message(aad_data)
.map_err(error_to_js_value)?;
}
let aead_key = AeadKey::new(key.to_vec());
let nonce_obj = Nonce::new(nonce.to_vec());
let ciphertext = self
.inner
.encrypt(
algorithm,
&aead_key,
&nonce_obj,
&plaintext.to_vec(),
aad_bytes.as_deref(),
)
.map_err(error_to_js_value)?;
Ok(ciphertext)
}
pub fn decrypt(
&self,
algorithm: &str,
key: &Uint8Array,
nonce: &Uint8Array,
ciphertext: &Uint8Array,
aad: Option<Uint8Array>,
) -> Result<Vec<u8>, JsValue> {
let algorithm = self
.parse_aead_algorithm(algorithm)
.map_err(error_to_js_value)?;
self.security_validator
.validate_algorithm_category(algorithm, AlgorithmCategory::Aead)
.map_err(error_to_js_value)?;
self.security_validator
.validate_key_size(algorithm, &key.to_vec(), true)
.map_err(error_to_js_value)?;
self.security_validator
.validate_nonce(&nonce.to_vec())
.map_err(error_to_js_value)?;
self.security_validator
.validate_ciphertext(algorithm, &ciphertext.to_vec())
.map_err(error_to_js_value)?;
let aad_bytes = aad.map(|aad_data| aad_data.to_vec());
if let Some(ref aad_data) = aad_bytes {
self.security_validator
.validate_aead_message(aad_data)
.map_err(error_to_js_value)?;
}
let aead_key = AeadKey::new(key.to_vec());
let nonce_obj = Nonce::new(nonce.to_vec());
let plaintext = self
.inner
.decrypt(
algorithm,
&aead_key,
&nonce_obj,
&ciphertext.to_vec(),
aad_bytes.as_deref(),
)
.map_err(error_to_js_value)?;
Ok(plaintext)
}
pub fn security_level(&self) -> u32 {
256 }
pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
self.parse_aead_algorithm(algorithm).is_ok()
}
pub fn supported_algorithms(&self) -> String {
let algorithms = alloc::vec!["saturnin", "shake256-aead"];
#[cfg(feature = "wasm")]
{
serde_json::to_string(&algorithms).unwrap_or_else(|_| "[]".to_string())
}
#[cfg(not(feature = "wasm"))]
{
"[]".to_string()
}
}
fn parse_aead_algorithm(&self, algorithm: &str) -> Result<Algorithm, crate::error::Error> {
parse_algorithm_wasm(algorithm).map_err(|_| crate::error::Error::InvalidAlgorithm {
algorithm: "Invalid algorithm name",
})
}
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub struct WasmCryptoProvider {
inner: LibQCryptoProvider,
}
impl WasmCryptoProvider {
pub fn new() -> WasmCryptoProvider {
WasmCryptoProvider {
inner: LibQCryptoProvider::new().unwrap_or_else(|_| LibQCryptoProvider::new().unwrap()),
}
}
}
impl Default for WasmCryptoProvider {
fn default() -> Self {
Self::new()
}
}
impl WasmCryptoProvider {
pub fn info(&self) -> String {
#[cfg(feature = "wasm")]
{
serde_json::json!({
"name": "lib-Q Crypto Provider",
"version": crate::VERSION,
"features": {
"kem": true,
"signature": true,
"hash": true,
"aead": true,
"security_hardened": true
}
})
.to_string()
}
#[cfg(not(feature = "wasm"))]
{
"{}".to_string()
}
}
pub fn is_algorithm_supported(&self, _algorithm: &str) -> bool {
true }
pub fn supported_algorithms(&self) -> String {
#[cfg(feature = "wasm")]
{
#[allow(unused_mut)] let mut kem_algorithms = alloc::vec!["ml-kem-512", "ml-kem-768", "ml-kem-1024"];
let algorithms = serde_json::json!({
"kem": kem_algorithms,
"signature": WASM_SIGNATURE_ALGORITHM_IDS,
"hash": [
"sha3-224", "sha3-256", "sha3-384", "sha3-512", "shake128", "shake256",
"sha-224", "sha-256", "sha-384", "sha-512", "sha-512/224", "sha-512/256",
"cshake128", "cshake256", "keccak-224", "keccak-256", "keccak-384", "keccak-512",
"kangarootwelve", "turboshake128", "turboshake256", "kmac128", "kmac256",
"tuplehash128", "tuplehash256", "parallelhash128", "parallelhash256",
],
"aead": ["saturnin", "shake256-aead"]
});
algorithms.to_string()
}
#[cfg(not(feature = "wasm"))]
{
"{}".to_string()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wasm_kem_context_creation() {
let context = WasmKemContext::new();
assert_eq!(context.security_level(), 256);
}
#[test]
fn test_wasm_signature_context_creation() {
let context = WasmSignatureContext::new();
assert_eq!(context.security_level(), 256);
}
#[test]
fn test_wasm_hash_context_creation() {
let context = WasmHashContext::new();
assert_eq!(context.security_level(), 256);
}
#[test]
fn test_wasm_aead_context_creation() {
let context = WasmAeadContext::new();
assert_eq!(context.security_level(), 256);
}
#[test]
fn test_wasm_crypto_provider_creation() {
let provider = WasmCryptoProvider::new();
let info = provider.info();
assert!(info.contains("lib-Q") || info == "{}");
}
#[test]
#[cfg(target_arch = "wasm32")]
fn test_wasm_kem_context_operations() {
let mut context = WasmKemContext::new();
let result = context.generate_keypair("ml-kem-512", None);
assert!(result.is_err());
if let Err(error) = result {
let error_str = error.as_string().unwrap_or_default();
assert!(error_str.contains("NotImplemented") || error_str.contains("WASM"));
}
}
#[test]
#[cfg(target_arch = "wasm32")]
fn test_wasm_signature_context_operations() {
let mut context = WasmSignatureContext::new();
let result = context.generate_keypair("ml-dsa-65", None);
assert!(result.is_err());
if let Err(error) = result {
let error_str = error.as_string().unwrap_or_default();
assert!(error_str.contains("NotImplemented") || error_str.contains("WASM"));
}
}
#[test]
#[cfg(target_arch = "wasm32")]
fn test_wasm_hash_context_operations() {
let mut context = WasmHashContext::new();
let data = Uint8Array::new_with_length(10);
let result = context.hash("sha3-256", &data);
assert!(result.is_err());
if let Err(error) = result {
let error_str = error.as_string().unwrap_or_default();
assert!(error_str.contains("NotImplemented") || error_str.contains("WASM"));
}
}
#[test]
#[cfg(target_arch = "wasm32")]
fn test_wasm_aead_context_operations() {
let mut context = WasmAeadContext::new();
let key = Uint8Array::new_with_length(32);
let nonce = Uint8Array::new_with_length(16);
let plaintext = Uint8Array::new_with_length(10);
let result = context.encrypt("saturnin", &key, &nonce, &plaintext, None);
assert!(result.is_err());
if let Err(error) = result {
let error_str = error.as_string().unwrap_or_default();
assert!(
error_str.contains("Provider not configured") ||
error_str.contains("NotImplemented") ||
error_str.contains("WASM")
);
}
}
}