use std::ffi::CStr;
use deno_core::GarbageCollected;
use deno_core::OpState;
use deno_core::op2;
use deno_core::v8;
use deno_core::webidl::WebIdlInterfaceConverter;
use rand::Rng;
use rand::rngs::StdRng;
use rand::thread_rng;
use crate::CryptoError;
use crate::fast_uuid_v4_bytes;
use crate::shared::SharedError;
const UUID_BYTES: usize = 16;
const UUID_STRING_BYTES: usize = 36;
const UUID_BATCH_SIZE: usize = 128;
#[op2]
pub fn op_crypto_random_uuid_batch<'s>(
scope: &mut v8::PinScope<'s, '_>,
) -> Result<v8::Local<'s, v8::String>, CryptoError> {
let mut entropy = [0u8; UUID_BYTES * UUID_BATCH_SIZE];
let mut output = [0u8; UUID_STRING_BYTES * UUID_BATCH_SIZE];
thread_rng().fill(&mut entropy[..]);
for (bytes, uuid_output) in entropy
.chunks_exact_mut(UUID_BYTES)
.zip(output.chunks_exact_mut(UUID_STRING_BYTES))
{
let bytes: &mut [u8; UUID_BYTES] = bytes.try_into().unwrap();
let uuid = fast_uuid_v4_bytes(bytes);
uuid_output.copy_from_slice(&uuid);
}
v8::String::new_from_one_byte(scope, &output, v8::NewStringType::Normal)
.ok_or(CryptoError::UuidStringAllocation)
}
pub struct Crypto {
subtle: v8::Global<v8::Value>,
}
impl WebIdlInterfaceConverter for Crypto {
const NAME: &'static str = "Crypto";
}
unsafe impl GarbageCollected for Crypto {
fn trace(&self, _visitor: &mut v8::cppgc::Visitor) {}
fn get_name(&self) -> &'static CStr {
c"Crypto"
}
}
#[op2]
impl Crypto {
#[constructor]
#[cppgc]
fn constructor(_: bool) -> Result<Crypto, SharedError> {
Err(SharedError::IllegalConstructor)
}
#[required(1)]
#[static_method]
#[cppgc]
fn create(
scope: &mut v8::PinScope<'_, '_>,
subtle: v8::Local<v8::Value>,
) -> Crypto {
Crypto {
subtle: v8::Global::new(scope, subtle),
}
}
#[getter]
fn subtle<'s>(
&self,
scope: &mut v8::PinScope<'s, '_>,
) -> v8::Local<'s, v8::Value> {
v8::Local::new(scope, &self.subtle)
}
#[required(1)]
fn get_random_values<'s>(
&self,
state: &mut OpState,
scope: &mut v8::PinScope<'s, '_>,
typed_array: v8::Local<'s, v8::Value>,
) -> Result<v8::Local<'s, v8::Value>, CryptoError> {
let view = v8::Local::<v8::ArrayBufferView>::try_from(typed_array)
.map_err(|_| CryptoError::TypedArrayNotInteger)?;
if !(view.is_int8_array()
|| view.is_uint8_array()
|| view.is_uint8_clamped_array()
|| view.is_int16_array()
|| view.is_uint16_array()
|| view.is_int32_array()
|| view.is_uint32_array()
|| view.is_big_int64_array()
|| view.is_big_uint64_array())
{
return Err(CryptoError::TypedArrayNotInteger);
}
let byte_len = view.byte_length();
if byte_len > 65536 {
return Err(CryptoError::ArrayBufferViewLengthExceeded(byte_len));
}
if byte_len > 0 {
let byte_offset = view.byte_offset();
let ab = view.buffer(scope).unwrap();
let bytes = unsafe {
let ptr = (ab.data().unwrap().as_ptr() as *mut u8).add(byte_offset);
std::slice::from_raw_parts_mut(ptr, byte_len)
};
let maybe_seeded_rng = state.try_borrow_mut::<StdRng>();
if let Some(seeded_rng) = maybe_seeded_rng {
seeded_rng.fill(bytes);
} else {
let mut rng = thread_rng();
rng.fill(bytes);
}
}
Ok(typed_array)
}
#[fast]
#[rename("registerSymbols")]
#[static_method]
fn register_symbols<'s>(
scope: &mut v8::PinScope<'s, '_>,
webidl_brand: v8::Local<'s, v8::Value>,
k_key_object: v8::Local<'s, v8::Value>,
) -> bool {
crate::make_key::register_symbols(scope, webidl_brand, k_key_object)
}
#[rename("randomUUID")]
#[required(0)]
fn random_uuid<'s>(
&self,
state: &mut OpState,
scope: &mut v8::PinScope<'s, '_>,
) -> Result<v8::Local<'s, v8::String>, CryptoError> {
let maybe_seeded_rng = state.try_borrow_mut::<StdRng>();
let mut bytes = [0u8; UUID_BYTES];
if let Some(seeded_rng) = maybe_seeded_rng {
seeded_rng.fill(&mut bytes);
} else {
let mut rng = thread_rng();
rng.fill(&mut bytes);
}
let uuid = fast_uuid_v4_bytes(&mut bytes);
v8::String::new_from_one_byte(scope, &uuid, v8::NewStringType::Normal)
.ok_or(CryptoError::UuidStringAllocation)
}
}