#![allow(clippy::unused_unit)]
use std::{
collections::HashMap,
convert::From,
sync::{
atomic::{AtomicI32, Ordering},
RwLock,
},
};
use cosmian_crypto_base::{
symmetric_crypto::{aes_256_gcm_pure::Aes256GcmCrypto, SymmetricCrypto},
KeyTrait,
};
use lazy_static::lazy_static;
use wasm_bindgen::prelude::*;
use crate::{
core::{
bilinear_map::bls12_381::Bls12_381,
gpsw::{scheme::GpswDecryptionKey, AbeScheme, AsBytes, Gpsw},
},
interfaces::hybrid_crypto::{decrypt_hybrid_block, decrypt_hybrid_header, ClearTextHeader},
};
pub const MAX_CLEAR_TEXT_SIZE: usize = 1 << 30;
type UserDecryptionKey = <Gpsw<Bls12_381> as AbeScheme>::UserDecryptionKey;
#[wasm_bindgen]
pub fn webassembly_get_encrypted_header_size(
encrypted_bytes: js_sys::Uint8Array,
) -> Result<u32, JsValue> {
if encrypted_bytes.length() < 4 {
return Err(JsValue::from_str(
"Encrypted value must be at least 4-bytes long",
));
}
let mut header_size_bytes = [0; 4];
header_size_bytes.copy_from_slice(&encrypted_bytes.to_vec()[0..4]);
let header_size = u32::from_be_bytes(header_size_bytes);
Ok(header_size)
}
#[wasm_bindgen]
pub fn webassembly_decrypt_hybrid_header(
user_decryption_key_bytes: js_sys::Uint8Array,
encrypted_header_bytes: js_sys::Uint8Array,
) -> Result<js_sys::Uint8Array, JsValue> {
if user_decryption_key_bytes.length() == 0 {
return Err(JsValue::from_str("User decryption key is empty"));
}
if encrypted_header_bytes.length() < 4 {
return Err(JsValue::from_str("Encrypted value is empty"));
}
let user_decryption_key = UserDecryptionKey::try_from_bytes(
user_decryption_key_bytes.to_vec().as_slice(),
)
.map_err(|e| JsValue::from_str(&format!("Error deserializing user decryption key: {e}")))?;
let cleartext_header: ClearTextHeader<Aes256GcmCrypto> =
decrypt_hybrid_header::<Gpsw<Bls12_381>, Aes256GcmCrypto>(
&user_decryption_key,
encrypted_header_bytes.to_vec().as_slice(),
)
.map_err(|e| JsValue::from_str(&format!("Error decrypting hybrid header: {e}")))?;
let cleartext_header_bytes = cleartext_header
.try_into_bytes()
.map_err(|e| JsValue::from_str(&format!("Error serializing cleartext header: {e}")))?;
Ok(js_sys::Uint8Array::from(&cleartext_header_bytes[..]))
}
lazy_static! {
static ref DECRYPTION_CACHE_MAP: RwLock<HashMap<i32, DecryptionCache>> =
RwLock::new(HashMap::new());
static ref NEXT_DECRYPTION_CACHE_ID: std::sync::atomic::AtomicI32 = AtomicI32::new(0);
}
pub struct DecryptionCache {
user_decryption_key: GpswDecryptionKey<Bls12_381>,
}
#[wasm_bindgen]
pub fn webassembly_create_decryption_cache(
user_decryption_key: js_sys::Uint8Array,
) -> Result<i32, JsValue> {
if user_decryption_key.length() == 0 {
return Err(JsValue::from_str("User decryption key is empty"));
}
let user_decryption_key = UserDecryptionKey::try_from_bytes(
user_decryption_key.to_vec().as_slice(),
)
.map_err(|e| JsValue::from_str(&format!("Error deserializing user decryption key: {e}")))?;
let cache = DecryptionCache {
user_decryption_key,
};
let id = NEXT_DECRYPTION_CACHE_ID.fetch_add(1, Ordering::Acquire);
let mut map = DECRYPTION_CACHE_MAP
.write()
.expect("A write mutex on decryption cache failed");
map.insert(id, cache);
Ok(id)
}
#[wasm_bindgen]
pub fn webassembly_destroy_decryption_cache(cache_handle: i32) -> Result<(), JsValue> {
let mut map = DECRYPTION_CACHE_MAP
.write()
.expect("A write mutex on decryption cache failed");
map.remove(&cache_handle);
Ok(())
}
#[wasm_bindgen]
pub fn webassembly_decrypt_hybrid_header_using_cache(
cache_handle: i32,
encrypted_header: js_sys::Uint8Array,
) -> Result<js_sys::Uint8Array, JsValue> {
let map = DECRYPTION_CACHE_MAP
.read()
.expect("a read mutex on the decryption cache failed");
let cache = map
.get(&cache_handle)
.expect("Hybrid Cipher: no decryption cache with handle");
let cleartext_header: ClearTextHeader<Aes256GcmCrypto> =
decrypt_hybrid_header::<Gpsw<Bls12_381>, Aes256GcmCrypto>(
&cache.user_decryption_key,
encrypted_header.to_vec().as_slice(),
)
.map_err(|e| JsValue::from_str(&format!("Error decrypting hybrid header: {e}")))?;
let cleartext_header_bytes = cleartext_header
.try_into_bytes()
.map_err(|e| JsValue::from_str(&format!("Error serializing cleartext header: {e}")))?;
Ok(js_sys::Uint8Array::from(&cleartext_header_bytes[..]))
}
#[wasm_bindgen]
pub fn webassembly_decrypt_hybrid_block(
symmetric_key_bytes: js_sys::Uint8Array,
uid_bytes: Option<js_sys::Uint8Array>,
block_number: Option<usize>,
encrypted_bytes: js_sys::Uint8Array,
) -> Result<js_sys::Uint8Array, JsValue> {
if symmetric_key_bytes.length() != 32 {
return Err(JsValue::from_str("Symmetric key must be 32-bytes long"));
}
if encrypted_bytes.length() == 0 {
return Err(JsValue::from_str("Encrypted value is empty"));
}
let symmetric_key =
<Aes256GcmCrypto as SymmetricCrypto>::Key::try_from_bytes(&symmetric_key_bytes.to_vec())
.map_err(|e| JsValue::from_str(&format!("Error parsing symmetric key: {e}")))?;
let uid = uid_bytes.map_or(vec![], |v| v.to_vec());
let block_number_value = block_number.unwrap_or(0);
let cleartext = decrypt_hybrid_block::<Gpsw<Bls12_381>, Aes256GcmCrypto, MAX_CLEAR_TEXT_SIZE>(
&symmetric_key,
&uid,
block_number_value as usize,
&encrypted_bytes.to_vec(),
)
.map_err(|e| JsValue::from_str(&format!("Error decrypting block: {e}")))?;
Ok(js_sys::Uint8Array::from(&cleartext[..]))
}