blob: fa65a76fa5c690e3e69df714950a619ee2d904c7 [file]
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use llrt_exceptions::DOMException;
use rquickjs::{object::Property, Array, Class, Ctx, Object, Result, Value};
use crate::{hash::HashAlgorithm, provider::CryptoProvider, CRYPTO_PROVIDER};
use super::{
algorithm_not_supported_error,
crypto_key::{CryptoKey, KeyKind},
key_algorithm::{KeyAlgorithm, KeyAlgorithmMode, KeyAlgorithmWithUsages},
util::ResultDomExt,
};
pub async fn subtle_generate_key<'js>(
ctx: Ctx<'js>,
algorithm: Value<'js>,
extractable: Value<'js>,
key_usages: Array<'js>,
) -> Result<Value<'js>> {
let KeyAlgorithmWithUsages {
name,
algorithm: key_algorithm,
private_usages,
public_usages,
} = KeyAlgorithm::from_js(&ctx, KeyAlgorithmMode::Generate, algorithm, key_usages)?;
let (private_key, public_or_secret_key) = generate_key(&ctx, &key_algorithm)?;
let Some(extractable) = extractable.as_bool() else {
return Err(DOMException::not_supported_error(&ctx, "Invalid parameter"));
};
if matches!(
key_algorithm,
KeyAlgorithm::Aes { .. } | KeyAlgorithm::Hmac { .. }
) {
return Ok(Class::instance(
ctx,
CryptoKey::new(
KeyKind::Secret,
name,
extractable,
key_algorithm,
public_usages,
public_or_secret_key,
),
)?
.into_value());
}
let private_key = Class::instance(
ctx.clone(),
CryptoKey::new(
KeyKind::Private,
name.clone(),
extractable,
key_algorithm.clone(),
private_usages,
private_key,
),
)?;
let public_key = Class::instance(
ctx.clone(),
CryptoKey::new(
KeyKind::Public,
name,
true,
key_algorithm,
public_usages,
public_or_secret_key,
),
)?;
let key_pair = Object::new(ctx.clone())?;
key_pair.prop("privateKey", Property::from(private_key).enumerable())?;
key_pair.prop("publicKey", Property::from(public_key).enumerable())?;
Ok(key_pair.into_value())
}
fn generate_key(ctx: &Ctx<'_>, algorithm: &KeyAlgorithm) -> Result<(Vec<u8>, Vec<u8>)> {
match algorithm {
KeyAlgorithm::Aes { length, .. } => {
// Default to AES-256
let key = CRYPTO_PROVIDER
.generate_aes_key(*length)
.or_throw_dom_with_msg(ctx, "AES key generation failed")?;
Ok((vec![], key))
},
KeyAlgorithm::Hmac { hash, length } => {
let key = CRYPTO_PROVIDER
.generate_hmac_key(*hash, *length)
.or_throw_dom_with_msg(ctx, "HMAC key generation failed")?;
Ok((vec![], key))
},
KeyAlgorithm::Ec { curve, .. } => CRYPTO_PROVIDER
.generate_ec_key(*curve)
.or_throw_dom_with_msg(ctx, "EC key generation failed"),
KeyAlgorithm::Ed25519 => CRYPTO_PROVIDER
.generate_ed25519_key()
.or_throw_dom_with_msg(ctx, "Ed25519 key generation failed"),
KeyAlgorithm::X25519 => CRYPTO_PROVIDER
.generate_x25519_key()
.or_throw_dom_with_msg(ctx, "X25519 key generation failed"),
KeyAlgorithm::Rsa {
modulus_length,
public_exponent,
..
} => CRYPTO_PROVIDER
.generate_rsa_key(*modulus_length, public_exponent.as_ref())
.or_throw_dom_with_msg(ctx, "RSA key generation failed"),
_ => algorithm_not_supported_error(ctx),
}
}
#[allow(dead_code)]
fn generate_symmetric_key(_ctx: &Ctx<'_>, length: usize) -> Result<Vec<u8>> {
Ok(crate::random_byte_array(length))
}
#[allow(dead_code)]
pub fn get_hash_length(ctx: &Ctx, hash: &HashAlgorithm, length: u16) -> Result<usize> {
if length == 0 {
return Ok(hash.block_len());
}
if !length.is_multiple_of(8) || (length / 8) as usize > 128 {
return Err(DOMException::not_supported_error(
ctx,
"Invalid HMAC key length",
));
}
Ok((length / 8) as usize)
}