deno/ext/crypto/generate_key.rs
Divy Srivastava bfd5f1598c
feat(ext/crypto): initial support for p521 in generateKey and importKey (#21815)
Part 1 of a potential 3 part series. Ref #13449 

The current implementation passes key material back and forth RustCrypto
group of crates and ring. ring does not implement p521 yet.

This PR adds support for P521 named curve in `generateKey` and
`importKey` where we use RustCrypto. Other parts should be moved over to
the RustGroup group of crates for consistency.
2024-01-06 16:48:31 +05:30

156 lines
4.3 KiB
Rust

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::unsync::spawn_blocking;
use deno_core::ToJsBuffer;
use elliptic_curve::rand_core::OsRng;
use num_traits::FromPrimitive;
use once_cell::sync::Lazy;
use ring::rand::SecureRandom;
use ring::signature::EcdsaKeyPair;
use rsa::pkcs1::EncodeRsaPrivateKey;
use rsa::BigUint;
use rsa::RsaPrivateKey;
use serde::Deserialize;
use crate::shared::*;
// Allowlist for RSA public exponents.
static PUB_EXPONENT_1: Lazy<BigUint> =
Lazy::new(|| BigUint::from_u64(3).unwrap());
static PUB_EXPONENT_2: Lazy<BigUint> =
Lazy::new(|| BigUint::from_u64(65537).unwrap());
#[derive(Deserialize)]
#[serde(rename_all = "camelCase", tag = "algorithm")]
pub enum GenerateKeyOptions {
#[serde(rename = "RSA", rename_all = "camelCase")]
Rsa {
modulus_length: u32,
#[serde(with = "serde_bytes")]
public_exponent: Vec<u8>,
},
#[serde(rename = "EC", rename_all = "camelCase")]
Ec { named_curve: EcNamedCurve },
#[serde(rename = "AES", rename_all = "camelCase")]
Aes { length: usize },
#[serde(rename = "HMAC", rename_all = "camelCase")]
Hmac {
hash: ShaHash,
length: Option<usize>,
},
}
#[op2(async)]
#[serde]
pub async fn op_crypto_generate_key(
#[serde] opts: GenerateKeyOptions,
) -> Result<ToJsBuffer, AnyError> {
let fun = || match opts {
GenerateKeyOptions::Rsa {
modulus_length,
public_exponent,
} => generate_key_rsa(modulus_length, &public_exponent),
GenerateKeyOptions::Ec { named_curve } => generate_key_ec(named_curve),
GenerateKeyOptions::Aes { length } => generate_key_aes(length),
GenerateKeyOptions::Hmac { hash, length } => {
generate_key_hmac(hash, length)
}
};
let buf = spawn_blocking(fun).await.unwrap()?;
Ok(buf.into())
}
fn generate_key_rsa(
modulus_length: u32,
public_exponent: &[u8],
) -> Result<Vec<u8>, AnyError> {
let exponent = BigUint::from_bytes_be(public_exponent);
if exponent != *PUB_EXPONENT_1 && exponent != *PUB_EXPONENT_2 {
return Err(operation_error("Bad public exponent"));
}
let mut rng = OsRng;
let private_key =
RsaPrivateKey::new_with_exp(&mut rng, modulus_length as usize, &exponent)
.map_err(|_| operation_error("Failed to generate RSA key"))?;
let private_key = private_key
.to_pkcs1_der()
.map_err(|_| operation_error("Failed to serialize RSA key"))?;
Ok(private_key.as_bytes().to_vec())
}
fn generate_key_ec_p521() -> Vec<u8> {
let mut rng = OsRng;
let key = p521::SecretKey::random(&mut rng);
key.to_nonzero_scalar().to_bytes().to_vec()
}
fn generate_key_ec(named_curve: EcNamedCurve) -> Result<Vec<u8>, AnyError> {
let curve = match named_curve {
EcNamedCurve::P256 => &ring::signature::ECDSA_P256_SHA256_FIXED_SIGNING,
EcNamedCurve::P384 => &ring::signature::ECDSA_P384_SHA384_FIXED_SIGNING,
EcNamedCurve::P521 => return Ok(generate_key_ec_p521()),
};
let rng = ring::rand::SystemRandom::new();
let pkcs8 = EcdsaKeyPair::generate_pkcs8(curve, &rng)
.map_err(|_| operation_error("Failed to generate EC key"))?;
Ok(pkcs8.as_ref().to_vec())
}
fn generate_key_aes(length: usize) -> Result<Vec<u8>, AnyError> {
if length % 8 != 0 || length > 256 {
return Err(operation_error("Invalid AES key length"));
}
let mut key = vec![0u8; length / 8];
let rng = ring::rand::SystemRandom::new();
rng
.fill(&mut key)
.map_err(|_| operation_error("Failed to generate key"))?;
Ok(key)
}
fn generate_key_hmac(
hash: ShaHash,
length: Option<usize>,
) -> Result<Vec<u8>, AnyError> {
let hash = match hash {
ShaHash::Sha1 => &ring::hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
ShaHash::Sha256 => &ring::hmac::HMAC_SHA256,
ShaHash::Sha384 => &ring::hmac::HMAC_SHA384,
ShaHash::Sha512 => &ring::hmac::HMAC_SHA512,
};
let length = if let Some(length) = length {
if length % 8 != 0 {
return Err(operation_error("Invalid HMAC key length"));
}
let length = length / 8;
if length > ring::digest::MAX_BLOCK_LEN {
return Err(operation_error("Invalid HMAC key length"));
}
length
} else {
hash.digest_algorithm().block_len()
};
let rng = ring::rand::SystemRandom::new();
let mut key = vec![0u8; length];
rng
.fill(&mut key)
.map_err(|_| operation_error("Failed to generate key"))?;
Ok(key)
}