summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--ext/crypto/00_crypto.js12
-rw-r--r--ext/crypto/Cargo.toml1
-rw-r--r--ext/crypto/generate_key.rs147
-rw-r--r--ext/crypto/lib.rs124
-rw-r--r--ext/crypto/shared.rs4
6 files changed, 163 insertions, 126 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 8932ca2fe..8c58bfe22 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -780,6 +780,7 @@ dependencies = [
"ring",
"rsa",
"serde",
+ "serde_bytes",
"sha-1",
"sha2",
"spki",
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js
index 818061387..258363586 100644
--- a/ext/crypto/00_crypto.js
+++ b/ext/crypto/00_crypto.js
@@ -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 = {};
diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml
index daa809422..d5c2a1acb 100644
--- a/ext/crypto/Cargo.toml
+++ b/ext/crypto/Cargo.toml
@@ -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"
diff --git a/ext/crypto/generate_key.rs b/ext/crypto/generate_key.rs
new file mode 100644
index 000000000..3f9df44a1
--- /dev/null
+++ b/ext/crypto/generate_key.rs
@@ -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)
+}
diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs
index 971b32bbb..a15f2d986 100644
--- a/ext/crypto/lib.rs
+++ b/ext/crypto/lib.rs
@@ -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;
@@ -134,122 +134,6 @@ pub fn op_crypto_get_random_values(
}
#[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 {
Raw,
diff --git a/ext/crypto/shared.rs b/ext/crypto/shared.rs
index 1af0169ef..0b70db24e 100644
--- a/ext/crypto/shared.rs
+++ b/ext/crypto/shared.rs
@@ -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")
}