mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
refactor(ext/crypto): generateKey rust cleanup (#13069)
This commit is contained in:
parent
308813ae29
commit
8fdade79da
6 changed files with 163 additions and 126 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -780,6 +780,7 @@ dependencies = [
|
|||
"ring",
|
||||
"rsa",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"sha-1",
|
||||
"sha2",
|
||||
"spki",
|
||||
|
|
|
@ -1468,7 +1468,7 @@
|
|||
const keyData = await core.opAsync(
|
||||
"op_crypto_generate_key",
|
||||
{
|
||||
name: algorithmName,
|
||||
algorithm: "RSA",
|
||||
modulusLength: normalizedAlgorithm.modulusLength,
|
||||
publicExponent: normalizedAlgorithm.publicExponent,
|
||||
},
|
||||
|
@ -1528,7 +1528,7 @@
|
|||
const keyData = await core.opAsync(
|
||||
"op_crypto_generate_key",
|
||||
{
|
||||
name: algorithmName,
|
||||
algorithm: "RSA",
|
||||
modulusLength: normalizedAlgorithm.modulusLength,
|
||||
publicExponent: normalizedAlgorithm.publicExponent,
|
||||
},
|
||||
|
@ -1590,7 +1590,7 @@
|
|||
)
|
||||
) {
|
||||
const keyData = await core.opAsync("op_crypto_generate_key", {
|
||||
name: algorithmName,
|
||||
algorithm: "EC",
|
||||
namedCurve,
|
||||
});
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
|
@ -1650,7 +1650,7 @@
|
|||
)
|
||||
) {
|
||||
const keyData = await core.opAsync("op_crypto_generate_key", {
|
||||
name: algorithmName,
|
||||
algorithm: "EC",
|
||||
namedCurve,
|
||||
});
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
|
@ -1745,7 +1745,7 @@
|
|||
|
||||
// 3-4.
|
||||
const keyData = await core.opAsync("op_crypto_generate_key", {
|
||||
name: algorithmName,
|
||||
algorithm: "HMAC",
|
||||
hash: normalizedAlgorithm.hash.name,
|
||||
length,
|
||||
});
|
||||
|
@ -2586,7 +2586,7 @@
|
|||
|
||||
// 3.
|
||||
const keyData = await core.opAsync("op_crypto_generate_key", {
|
||||
name: algorithmName,
|
||||
algorithm: "AES",
|
||||
length: normalizedAlgorithm.length,
|
||||
});
|
||||
const handle = {};
|
||||
|
|
|
@ -28,6 +28,7 @@ rand = "0.8.4"
|
|||
ring = { version = "0.16.20", features = ["std"] }
|
||||
rsa = { version = "0.5.0", default-features = false, features = ["std"] }
|
||||
serde = { version = "1.0.129", features = ["derive"] }
|
||||
serde_bytes = "0.11"
|
||||
sha-1 = "0.9.7"
|
||||
sha2 = "0.9.5"
|
||||
spki = "0.4.1"
|
||||
|
|
147
ext/crypto/generate_key.rs
Normal file
147
ext/crypto/generate_key.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::OpState;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use elliptic_curve::rand_core::OsRng;
|
||||
use num_traits::FromPrimitive;
|
||||
use ring::rand::SecureRandom;
|
||||
use ring::signature::EcdsaKeyPair;
|
||||
use rsa::pkcs1::ToRsaPrivateKey;
|
||||
use rsa::BigUint;
|
||||
use rsa::RsaPrivateKey;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::shared::*;
|
||||
|
||||
// Allowlist for RSA public exponents.
|
||||
lazy_static::lazy_static! {
|
||||
static ref PUB_EXPONENT_1: BigUint = BigUint::from_u64(3).unwrap();
|
||||
static ref PUB_EXPONENT_2: BigUint = 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>,
|
||||
},
|
||||
}
|
||||
|
||||
pub async fn op_crypto_generate_key(
|
||||
_state: Rc<RefCell<OpState>>,
|
||||
opts: GenerateKeyOptions,
|
||||
_: (),
|
||||
) -> Result<ZeroCopyBuf, 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 = tokio::task::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_ref().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,
|
||||
};
|
||||
|
||||
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)
|
||||
}
|
|
@ -10,7 +10,6 @@ use deno_core::op_sync;
|
|||
use deno_core::Extension;
|
||||
use deno_core::OpState;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use export_key::op_crypto_export_key;
|
||||
use serde::Deserialize;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
@ -31,7 +30,6 @@ use ring::hmac::Algorithm as HmacAlgorithm;
|
|||
use ring::hmac::Key as HmacKey;
|
||||
use ring::pbkdf2;
|
||||
use ring::rand as RingRand;
|
||||
use ring::rand::SecureRandom;
|
||||
use ring::signature::EcdsaKeyPair;
|
||||
use ring::signature::EcdsaSigningAlgorithm;
|
||||
use ring::signature::EcdsaVerificationAlgorithm;
|
||||
|
@ -41,7 +39,6 @@ use rsa::pkcs1::der::Decodable;
|
|||
use rsa::pkcs1::der::Encodable;
|
||||
use rsa::pkcs1::FromRsaPrivateKey;
|
||||
use rsa::pkcs1::FromRsaPublicKey;
|
||||
use rsa::pkcs1::ToRsaPrivateKey;
|
||||
use rsa::pkcs8::der::asn1;
|
||||
use rsa::pkcs8::FromPrivateKey;
|
||||
use rsa::BigUint;
|
||||
|
@ -58,16 +55,19 @@ use std::path::PathBuf;
|
|||
pub use rand; // Re-export rand
|
||||
|
||||
mod export_key;
|
||||
mod generate_key;
|
||||
mod import_key;
|
||||
mod key;
|
||||
mod shared;
|
||||
|
||||
pub use crate::export_key::op_crypto_export_key;
|
||||
pub use crate::generate_key::op_crypto_generate_key;
|
||||
pub use crate::import_key::op_crypto_import_key;
|
||||
use crate::key::Algorithm;
|
||||
use crate::key::CryptoHash;
|
||||
use crate::key::CryptoNamedCurve;
|
||||
use crate::key::HkdfOutput;
|
||||
|
||||
pub use crate::import_key::op_crypto_import_key;
|
||||
use crate::shared::ID_MFG1;
|
||||
use crate::shared::ID_P_SPECIFIED;
|
||||
use crate::shared::ID_SHA1_OID;
|
||||
|
@ -133,122 +133,6 @@ pub fn op_crypto_get_random_values(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AlgorithmArg {
|
||||
name: Algorithm,
|
||||
modulus_length: Option<u32>,
|
||||
public_exponent: Option<ZeroCopyBuf>,
|
||||
named_curve: Option<CryptoNamedCurve>,
|
||||
hash: Option<CryptoHash>,
|
||||
length: Option<usize>,
|
||||
}
|
||||
|
||||
pub async fn op_crypto_generate_key(
|
||||
_state: Rc<RefCell<OpState>>,
|
||||
args: AlgorithmArg,
|
||||
_: (),
|
||||
) -> Result<ZeroCopyBuf, AnyError> {
|
||||
let algorithm = args.name;
|
||||
|
||||
let key = match algorithm {
|
||||
Algorithm::RsassaPkcs1v15 | Algorithm::RsaPss | Algorithm::RsaOaep => {
|
||||
let public_exponent = args.public_exponent.ok_or_else(not_supported)?;
|
||||
let modulus_length = args.modulus_length.ok_or_else(not_supported)?;
|
||||
|
||||
let exponent = BigUint::from_bytes_be(&public_exponent);
|
||||
if exponent != *PUB_EXPONENT_1 && exponent != *PUB_EXPONENT_2 {
|
||||
return Err(custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"Bad public exponent",
|
||||
));
|
||||
}
|
||||
|
||||
let mut rng = OsRng;
|
||||
|
||||
let private_key: RsaPrivateKey = tokio::task::spawn_blocking(
|
||||
move || -> Result<RsaPrivateKey, rsa::errors::Error> {
|
||||
RsaPrivateKey::new_with_exp(
|
||||
&mut rng,
|
||||
modulus_length as usize,
|
||||
&exponent,
|
||||
)
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.map_err(|e| custom_error("DOMExceptionOperationError", e.to_string()))?;
|
||||
|
||||
private_key.to_pkcs1_der()?.as_ref().to_vec()
|
||||
}
|
||||
Algorithm::Ecdsa | Algorithm::Ecdh => {
|
||||
let curve: &EcdsaSigningAlgorithm =
|
||||
args.named_curve.ok_or_else(not_supported)?.into();
|
||||
let rng = RingRand::SystemRandom::new();
|
||||
let private_key: Vec<u8> = tokio::task::spawn_blocking(
|
||||
move || -> Result<Vec<u8>, ring::error::Unspecified> {
|
||||
let pkcs8 = EcdsaKeyPair::generate_pkcs8(curve, &rng)?;
|
||||
Ok(pkcs8.as_ref().to_vec())
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.map_err(|_| {
|
||||
custom_error("DOMExceptionOperationError", "Key generation failed")
|
||||
})?;
|
||||
|
||||
private_key
|
||||
}
|
||||
Algorithm::AesCtr
|
||||
| Algorithm::AesCbc
|
||||
| Algorithm::AesGcm
|
||||
| Algorithm::AesKw => {
|
||||
let length = args.length.ok_or_else(not_supported)?;
|
||||
// Caller must guarantee divisibility by 8
|
||||
let mut key_data = vec![0u8; length / 8];
|
||||
let rng = RingRand::SystemRandom::new();
|
||||
rng.fill(&mut key_data).map_err(|_| {
|
||||
custom_error("DOMExceptionOperationError", "Key generation failed")
|
||||
})?;
|
||||
key_data
|
||||
}
|
||||
Algorithm::Hmac => {
|
||||
let hash: HmacAlgorithm = args.hash.ok_or_else(not_supported)?.into();
|
||||
|
||||
let length = if let Some(length) = args.length {
|
||||
if (length % 8) != 0 {
|
||||
return Err(custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"hmac block length must be byte aligned",
|
||||
));
|
||||
}
|
||||
let length = length / 8;
|
||||
if length > ring::digest::MAX_BLOCK_LEN {
|
||||
return Err(custom_error(
|
||||
"DOMExceptionOperationError",
|
||||
"hmac block length is too large",
|
||||
));
|
||||
}
|
||||
length
|
||||
} else {
|
||||
hash.digest_algorithm().block_len
|
||||
};
|
||||
|
||||
let rng = RingRand::SystemRandom::new();
|
||||
let mut key_bytes = [0; ring::digest::MAX_BLOCK_LEN];
|
||||
let key_bytes = &mut key_bytes[..length];
|
||||
rng.fill(key_bytes).map_err(|_| {
|
||||
custom_error("DOMExceptionOperationError", "Key generation failed")
|
||||
})?;
|
||||
|
||||
key_bytes.to_vec()
|
||||
}
|
||||
_ => return Err(not_supported()),
|
||||
};
|
||||
|
||||
Ok(key.into())
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum KeyFormat {
|
||||
|
|
|
@ -100,6 +100,10 @@ pub fn not_supported_error(msg: impl Into<Cow<'static, str>>) -> AnyError {
|
|||
custom_error("DOMExceptionNotSupportedError", msg)
|
||||
}
|
||||
|
||||
pub fn operation_error(msg: impl Into<Cow<'static, str>>) -> AnyError {
|
||||
custom_error("DOMExceptionOperationError", msg)
|
||||
}
|
||||
|
||||
pub fn unsupported_format() -> AnyError {
|
||||
not_supported_error("unsupported format")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue