Repository: awslabs/llrt
File: llrt_core/src/modules/crypto/mod.rs (Rust)

Code before:
            .ok_or("ArrayBuffer is detached")
            .or_throw(&ctx)?;
        let bytes = unsafe { slice::from_raw_parts_mut(raw.ptr.as_ptr(), raw.len) };

        SYSTEM_RANDOM
            .fill(&mut bytes[offset..offset + checked_len])
            .unwrap();
    }

    Ok(obj)
}

macro_rules! fill_typed_array {
    ($ty:ty, $bytes:expr, $rng:expr) => {{
        let size = mem::size_of::<$ty>();
        for chunk in $bytes.chunks_exact_mut(size) {
            let val_bytes = $rng.gen::<$ty>().to_ne_bytes();
            chunk.copy_from_slice(&val_bytes[..size]);
        }
    }};
}

fn get_random_values<'js>(ctx: Ctx<'js>, obj: Object<'js>) -> Result<Object<'js>> {
    let mut rng = rand::thread_rng();

    if let Some(array_buffer) = obj_to_array_buffer(&ctx, &obj)? {
        let raw = array_buffer
            .as_raw()
            .ok_or("ArrayBuffer is detached")
            .or_throw(&ctx)?;

        if raw.len > 65536 {
            return Err(Exception::throw_message(&ctx, "QuotaExceededError"));
        }

        let bytes = unsafe { std::slice::from_raw_parts_mut(raw.ptr.as_ptr(), raw.len) };

        match get_class_name(&obj)?.unwrap().as_str() {
            "Int8Array" => fill_typed_array!(i8, bytes, rng),
            "Uint8Array" | "Uint8ClampedArray" => fill_typed_array!(u8, bytes, rng),
            "Int16Array" => fill_typed_array!(i16, bytes, rng),
            "Uint16Array" => fill_typed_array!(u16, bytes, rng),
            "Int32Array" => fill_typed_array!(i32, bytes, rng),
            "Uint32Array" => fill_typed_array!(u32, bytes, rng),
            "Float32Array" => fill_typed_array!(f32, bytes, rng),
            "Float64Array" => fill_typed_array!(f64, bytes, rng),
            "BigInt64Array" => fill_typed_array!(i64, bytes, rng),
            "BigUint64Array" => fill_typed_array!(u64, bytes, rng),
            _ => return Err(Exception::throw_message(&ctx, "Unsupported TypedArray")),
        }
    }

Review comment (refactor):
I would suggest to refactor this method:
https://github.com/nabetti1720/llrt/blob/724f74f198fb93f3f674810d1728e203a4e8f75c/llrt_core/src/utils/object.rs#L142 to avoid matching on class names and instead to `if let Ok(array) = TypedArray::<i8>::from_object(obj.clone())` for each type and return the buffer.

Then use the same principle below.


Code after:
            .ok_or("ArrayBuffer is detached")
            .or_throw(&ctx)?;
        let bytes = unsafe { slice::from_raw_parts_mut(raw.ptr.as_ptr(), raw.len) };

        SYSTEM_RANDOM
            .fill(&mut bytes[offset..offset + checked_len])
            .unwrap();
    }

    Ok(obj)
}

fn get_random_values<'js>(ctx: Ctx<'js>, obj: Object<'js>) -> Result<Object<'js>> {
    if let Some(array_buffer) = obj_to_array_buffer(&obj)? {
        let raw = array_buffer
            .as_raw()
            .ok_or("ArrayBuffer is detached")
            .or_throw(&ctx)?;

        if raw.len > 65536 {
            return Err(Exception::throw_message(&ctx, "QuotaExceededError"));
        }

        let bytes = unsafe { std::slice::from_raw_parts_mut(raw.ptr.as_ptr(), raw.len) };

        match get_class_name(&obj)?.unwrap().as_str() {
            "Int8Array" | "Uint8Array" | "Uint8ClampedArray" | "Int16Array" | "Uint16Array"
            | "Int32Array" | "Uint32Array" | "BigInt64Array" | "BigUint64Array" => {
                SYSTEM_RANDOM.fill(&mut bytes[..]).unwrap()
            },
            _ => return Err(Exception::throw_message(&ctx, "Unsupported TypedArray")),
        }
    }

    Ok(obj)
}

pub fn init(ctx: &Ctx<'_>) -> Result<()> {
    let globals = ctx.globals();

    let crypto = Object::new(ctx.clone())?;

    crypto.set("createHash", Func::from(Hash::new))?;
    crypto.set("createHmac", Func::from(Hmac::new))?;
    crypto.set("randomBytes", Func::from(get_random_bytes))?;
    crypto.set("randomInt", Func::from(get_random_int))?;
    crypto.set("randomUUID", Func::from(uuidv4))?;
    crypto.set("randomFillSync", Func::from(random_fill_sync))?;
    crypto.set("randomFill", Func::from(random_fill))?;
    crypto.set("getRandomValues", Func::from(get_random_values))?;