| // 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) | |
| } |