use ring::{
digest::{self, Context as DigestContext},
hmac::{self, Context as HmacContext},
};
use rquickjs::{function::Opt, prelude::This, Class, Ctx, Exception, Result, Value};
use crate::utils::object::{bytes_to_typed_array, get_bytes};
use super::encoded_bytes;
#[rquickjs::class]
#[derive(rquickjs::class::Trace)]
pub struct Hmac {
#[qjs(skip_trace)]
context: HmacContext,
}
#[rquickjs::methods]
impl Hmac {
#[qjs(skip)]
pub fn new<'js>(ctx: Ctx<'js>, algorithm: String, secret: Value<'js>) -> Result<Self> {
let key_value = get_bytes(&ctx, secret)?;
let algorithm = match algorithm.to_lowercase().as_str() {
"sha1" => hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
"sha256" => hmac::HMAC_SHA256,
"sha384" => hmac::HMAC_SHA384,
"sha512" => hmac::HMAC_SHA512,
_ => {
return Err(Exception::throw_message(
&ctx,
&format!("Algorithm \"{}\" not supported", &algorithm),
))
}
};
Ok(Self {
context: HmacContext::with_key(&hmac::Key::new(algorithm, &key_value)),
})
}
fn digest<'js>(&self, ctx: Ctx<'js>, encoding: Opt<String>) -> Result<Value<'js>> {
let signature = self.context.clone().sign();
let bytes: &[u8] = signature.as_ref();
match encoding.into_inner() {
Some(encoding) => encoded_bytes(ctx, bytes, &encoding),
None => bytes_to_typed_array(ctx, bytes),
}
}
fn update<'js>(
this: This<Class<'js, Self>>,
ctx: Ctx<'js>,
value: Value<'js>,
) -> Result<Class<'js, Self>> {
let bytes = get_bytes(&ctx, value)?;
this.0.borrow_mut().context.update(&bytes);
Ok(this.0)
}
}
impl Clone for Hmac {
fn clone(&self) -> Self {
Self {
context: self.context.clone(),
}
}
}
#[rquickjs::class]
#[derive(rquickjs::class::Trace)]
pub struct Hash {
#[qjs(skip_trace)]
context: DigestContext,
}
#[rquickjs::methods]
impl Hash {
#[qjs(skip)]
pub fn new(ctx: Ctx<'_>, algorithm: String) -> Result<Self> {
let algorithm = match algorithm.to_lowercase().as_str() {
"sha1" => &digest::SHA1_FOR_LEGACY_USE_ONLY,
"sha256" => &digest::SHA256,
"sha512" => &digest::SHA512,
_ => {
return Err(Exception::throw_message(
&ctx,
&format!("Algorithm \"{}\" not supported", &algorithm),
))
}
};
Ok(Self {
context: DigestContext::new(algorithm),
})
}
#[qjs(rename = "digest")]
fn hash_digest<'js>(&self, ctx: Ctx<'js>, encoding: Opt<String>) -> Result<Value<'js>> {
let digest = self.context.clone().finish();
let bytes: &[u8] = digest.as_ref();
match encoding.0 {
Some(encoding) => encoded_bytes(ctx, bytes, &encoding),
None => bytes_to_typed_array(ctx, bytes),
}
}
#[qjs(rename = "update")]
fn hash_update<'js>(
this: This<Class<'js, Self>>,
ctx: Ctx<'js>,
value: Value<'js>,
) -> Result<Class<'js, Self>> {
let bytes = get_bytes(&ctx, value)?;
this.0.borrow_mut().context.update(&bytes);
Ok(this.0)
}
}
iterable_enum!(pub, ShaAlgorithm, SHA1, SHA256, SHA384, SHA512);
impl ShaAlgorithm {
pub fn class_name(&self) -> &'static str {
match self {
ShaAlgorithm::SHA1 => "Sha1",
ShaAlgorithm::SHA256 => "Sha256",
ShaAlgorithm::SHA384 => "Sha384",
ShaAlgorithm::SHA512 => "Sha512",
}
}
pub fn hmac_algorithm(&self) -> &'static hmac::Algorithm {
match self {
ShaAlgorithm::SHA1 => &hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
ShaAlgorithm::SHA256 => &hmac::HMAC_SHA256,
ShaAlgorithm::SHA384 => &hmac::HMAC_SHA384,
ShaAlgorithm::SHA512 => &hmac::HMAC_SHA512,
}
}
fn digest_algorithm(&self) -> &'static digest::Algorithm {
match self {
ShaAlgorithm::SHA1 => &digest::SHA1_FOR_LEGACY_USE_ONLY,
ShaAlgorithm::SHA256 => &digest::SHA256,
ShaAlgorithm::SHA384 => &digest::SHA384,
ShaAlgorithm::SHA512 => &digest::SHA512,
}
}
}
#[rquickjs::class]
#[derive(rquickjs::class::Trace)]
pub struct ShaHash {
#[qjs(skip_trace)]
secret: Option<Vec<u8>>,
#[qjs(skip_trace)]
bytes: Vec<u8>,
#[qjs(skip_trace)]
algorithm: ShaAlgorithm,
}
#[rquickjs::methods]
impl ShaHash {
#[qjs(skip)]
pub fn new<'js>(
ctx: Ctx<'js>,
algorithm: ShaAlgorithm,
secret: Opt<Value<'js>>,
) -> Result<Self> {
let secret = secret.0;
let secret = match secret {
Some(secret) => {
let bytes = get_bytes(&ctx, secret)?;
Some(bytes)
}
None => None,
};
Ok(ShaHash {
secret,
bytes: Vec::new(),
algorithm,
})
}
#[qjs(rename = "digest")]
fn sha_digest<'js>(&self, ctx: Ctx<'js>) -> Result<Value<'js>> {
if let Some(secret) = &self.secret {
let key_value = secret;
let key = hmac::Key::new(*self.algorithm.hmac_algorithm(), key_value);
return bytes_to_typed_array(ctx, hmac::sign(&key, &self.bytes).as_ref());
}
bytes_to_typed_array(
ctx,
digest::digest(self.algorithm.digest_algorithm(), &self.bytes).as_ref(),
)
}
#[qjs(rename = "update")]
fn sha_update<'js>(
this: This<Class<'js, Self>>,
ctx: Ctx<'js>,
value: Value<'js>,
) -> Result<Class<'js, Self>> {
let bytes = get_bytes(&ctx, value)?;
this.0.borrow_mut().bytes = bytes;
Ok(this.0)
}
}