ferrijs-std 0.2.1

Node and web standard library for the ferrijs QuickJS runtime: WHATWG Streams, Events, AbortController, Buffer, crypto, fs, os, url, zlib and the capability model they enforce (partly derived from awslabs/llrt, Apache-2.0).
Documentation
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use crate::exceptions::DOMException;
use crate::utils::{bytes::ObjectBytes, object::ObjectExt};
use rquickjs::{Ctx, FromJs, Result, Value};

use crate::crypto::{hash::HashAlgorithm, provider::MlDsaVariant};

use super::{
    algorithm_not_supported_error, enforce_range_u32, get_required_dictionary_value,
    key_algorithm::extract_sha_hash, normalize_algorithm_name, to_name_and_maybe_object,
};

#[derive(Debug)]
pub enum SigningAlgorithm {
    Ecdsa {
        hash: HashAlgorithm,
    },
    Ed25519,
    RsaPss {
        salt_length: u32,
    },
    RsassaPkcs1v15,
    Hmac,
    MlDsa {
        variant: MlDsaVariant,
        context: Box<[u8]>,
    },
}

impl<'js> FromJs<'js> for SigningAlgorithm {
    fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
        let (name, obj) = to_name_and_maybe_object(ctx, value)?;
        let name = normalize_algorithm_name(&name);

        let algorithm = match name.as_str() {
            "Ed25519" => SigningAlgorithm::Ed25519,
            "HMAC" => SigningAlgorithm::Hmac,
            "RSASSA-PKCS1-v1_5" => SigningAlgorithm::RsassaPkcs1v15,
            "ECDSA" => {
                let obj = obj?;
                let hash = extract_sha_hash(ctx, &obj)?;
                SigningAlgorithm::Ecdsa { hash }
            },
            "RSA-PSS" => {
                let value = get_required_dictionary_value(&obj?, "saltLength", "algorithm")?;
                let salt_length = enforce_range_u32(ctx, value, "saltLength")?;

                SigningAlgorithm::RsaPss { salt_length }
            },
            _ => {
                let Ok(variant) = MlDsaVariant::try_from(name.as_str()) else {
                    return algorithm_not_supported_error(ctx);
                };
                let context = if let Ok(obj) = obj {
                    obj.get_optional::<_, ObjectBytes>("context")?
                        .map(|value| value.into_bytes(ctx))
                        .transpose()?
                        .unwrap_or_default()
                } else {
                    Vec::new()
                };
                if context.len() > 255 {
                    return Err(DOMException::operation_error(
                        ctx,
                        "ML-DSA context must not exceed 255 bytes",
                    ));
                }
                SigningAlgorithm::MlDsa {
                    variant,
                    context: context.into_boxed_slice(),
                }
            },
        };
        Ok(algorithm)
    }
}

impl SigningAlgorithm {
    pub fn name(&self) -> &'static str {
        match self {
            SigningAlgorithm::Ecdsa { .. } => "ECDSA",
            SigningAlgorithm::Ed25519 => "Ed25519",
            SigningAlgorithm::RsaPss { .. } => "RSA-PSS",
            SigningAlgorithm::RsassaPkcs1v15 => "RSASSA-PKCS1-v1_5",
            SigningAlgorithm::Hmac => "HMAC",
            SigningAlgorithm::MlDsa { variant, .. } => variant.as_str(),
        }
    }
}