diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index ddc88bdcb2..16c8628295 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -759,6 +759,7 @@ * @param {KeyUsages[]} keyUsages * @returns {Promise} */ + // deno-lint-ignore require-await async importKey(format, keyData, algorithm, extractable, keyUsages) { webidl.assertBranded(this, SubtleCrypto); const prefix = "Failed to execute 'importKey' on 'SubtleCrypto'"; @@ -812,7 +813,7 @@ ); } case "ECDSA": { - return await importKeyECDSA( + return importKeyECDSA( format, normalizedAlgorithm, keyData, @@ -823,7 +824,7 @@ case "RSASSA-PKCS1-v1_5": case "RSA-PSS": case "RSA-OAEP": { - return await importKeyRSA( + return importKeyRSA( format, normalizedAlgorithm, keyData, @@ -850,7 +851,7 @@ ); } case "AES-KW": { - return await importKeyAES( + return importKeyAES( format, normalizedAlgorithm, keyData, @@ -2194,7 +2195,7 @@ return key; } - async function importKeyECDSA( + function importKeyECDSA( format, normalizedAlgorithm, keyData, @@ -2227,16 +2228,13 @@ } // 3. - const { data } = await core.opAsync("op_crypto_import_key", { + const { rawData } = core.opSync("op_crypto_import_key", { algorithm: "ECDSA", namedCurve: normalizedAlgorithm.namedCurve, - }, keyData); + }, { raw: keyData }); const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "public", - data, - }); + WeakMapPrototypeSet(KEY_STORE, handle, rawData); // 4-5. const algorithm = { @@ -2275,7 +2273,7 @@ }, }; - async function importKeyRSA( + function importKeyRSA( format, normalizedAlgorithm, keyData, @@ -2299,23 +2297,18 @@ } // 2-9. - const { modulusLength, publicExponent, data } = await core - .opAsync( - "op_crypto_import_key", - { - algorithm: normalizedAlgorithm.name, - format: "pkcs8", - // Needed to perform step 7 without normalization. - hash: normalizedAlgorithm.hash.name, - }, - keyData, - ); + const { modulusLength, publicExponent, rawData } = core.opSync( + "op_crypto_import_key", + { + algorithm: normalizedAlgorithm.name, + // Needed to perform step 7 without normalization. + hash: normalizedAlgorithm.hash.name, + }, + { pkcs8: keyData }, + ); const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "private", - data, - }); + WeakMapPrototypeSet(KEY_STORE, handle, rawData); const algorithm = { name: normalizedAlgorithm.name, @@ -2350,23 +2343,18 @@ } // 2-9. - const { modulusLength, publicExponent, data } = await core - .opAsync( - "op_crypto_import_key", - { - algorithm: normalizedAlgorithm.name, - format: "spki", - // Needed to perform step 7 without normalization. - hash: normalizedAlgorithm.hash.name, - }, - keyData, - ); + const { modulusLength, publicExponent, rawData } = core.opSync( + "op_crypto_import_key", + { + algorithm: normalizedAlgorithm.name, + // Needed to perform step 7 without normalization. + hash: normalizedAlgorithm.hash.name, + }, + { spki: keyData }, + ); const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "public", - data, - }); + WeakMapPrototypeSet(KEY_STORE, handle, rawData); const algorithm = { name: normalizedAlgorithm.name, diff --git a/ext/crypto/import_key.rs b/ext/crypto/import_key.rs new file mode 100644 index 0000000000..16a8d155f8 --- /dev/null +++ b/ext/crypto/import_key.rs @@ -0,0 +1,578 @@ +use deno_core::error::AnyError; +use deno_core::OpState; +use deno_core::ZeroCopyBuf; +use serde::Deserialize; +use serde::Serialize; +use spki::der::Decodable; +use spki::der::Encodable; + +use crate::shared::*; +use crate::OaepPrivateKeyParameters; +use crate::PssPrivateKeyParameters; + +#[derive(Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum KeyData { + Spki(ZeroCopyBuf), + Pkcs8(ZeroCopyBuf), + Raw(ZeroCopyBuf), + Jwk { k: String }, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase", tag = "algorithm")] +pub enum ImportKeyOptions { + #[serde(rename = "RSASSA-PKCS1-v1_5")] + RsassaPkcs1v15 { hash: ShaHash }, + #[serde(rename = "RSA-PSS")] + RsaPss { hash: ShaHash }, + #[serde(rename = "RSA-OAEP")] + RsaOaep { hash: ShaHash }, + #[serde(rename = "ECDSA", rename_all = "camelCase")] + Ecdsa { named_curve: EcNamedCurve }, + #[serde(rename = "ECDH", rename_all = "camelCase")] + Ecdh { named_curve: EcNamedCurve }, +} + +#[derive(Serialize)] +#[serde(untagged)] +pub enum ImportKeyResult { + #[serde(rename_all = "camelCase")] + Rsa { + raw_data: RawKeyData, + modulus_length: usize, + public_exponent: ZeroCopyBuf, + }, + #[serde(rename_all = "camelCase")] + Ec { raw_data: RawKeyData }, +} + +pub fn op_crypto_import_key( + _state: &mut OpState, + opts: ImportKeyOptions, + key_data: KeyData, +) -> Result { + match opts { + ImportKeyOptions::RsassaPkcs1v15 { hash } => { + import_key_rsassa(key_data, hash) + } + ImportKeyOptions::RsaPss { hash } => import_key_rsapss(key_data, hash), + ImportKeyOptions::RsaOaep { hash } => import_key_rsaoaep(key_data, hash), + ImportKeyOptions::Ecdsa { named_curve } + | ImportKeyOptions::Ecdh { named_curve } => { + import_key_ec(key_data, named_curve) + } + } +} + +fn import_key_rsassa( + key_data: KeyData, + hash: ShaHash, +) -> Result { + match key_data { + KeyData::Spki(data) => { + // 2-3. + let pk_info = spki::SubjectPublicKeyInfo::from_der(&data) + .map_err(|e| data_error(e.to_string()))?; + + // 4-5. + let alg = pk_info.algorithm.oid; + + // 6. + let pk_hash = match alg { + // rsaEncryption + RSA_ENCRYPTION_OID => None, + // sha1WithRSAEncryption + SHA1_RSA_ENCRYPTION_OID => Some(ShaHash::Sha1), + // sha256WithRSAEncryption + SHA256_RSA_ENCRYPTION_OID => Some(ShaHash::Sha256), + // sha384WithRSAEncryption + SHA384_RSA_ENCRYPTION_OID => Some(ShaHash::Sha384), + // sha512WithRSAEncryption + SHA512_RSA_ENCRYPTION_OID => Some(ShaHash::Sha512), + _ => return Err(data_error("unsupported algorithm")), + }; + + // 7. + if let Some(pk_hash) = pk_hash { + if pk_hash != hash { + return Err(data_error("hash mismatch")); + } + } + + // 8-9. + let public_key = + rsa::pkcs1::RsaPublicKey::from_der(pk_info.subject_public_key) + .map_err(|e| data_error(e.to_string()))?; + + let bytes_consumed = public_key + .encoded_len() + .map_err(|e| data_error(e.to_string()))?; + + if bytes_consumed + != spki::der::Length::new(pk_info.subject_public_key.len() as u16) + { + return Err(data_error("public key is invalid (too long)")); + } + + let data = pk_info.subject_public_key.to_vec().into(); + let public_exponent = + public_key.public_exponent.as_bytes().to_vec().into(); + let modulus_length = public_key.modulus.as_bytes().len() * 8; + + Ok(ImportKeyResult::Rsa { + raw_data: RawKeyData::Public(data), + modulus_length, + public_exponent, + }) + } + KeyData::Pkcs8(data) => { + // 2-3. + let pk_info = rsa::pkcs8::PrivateKeyInfo::from_der(&data) + .map_err(|e| data_error(e.to_string()))?; + + // 4-5. + let alg = pk_info.algorithm.oid; + + // 6. + let pk_hash = match alg { + // rsaEncryption + RSA_ENCRYPTION_OID => None, + // sha1WithRSAEncryption + SHA1_RSA_ENCRYPTION_OID => Some(ShaHash::Sha1), + // sha256WithRSAEncryption + SHA256_RSA_ENCRYPTION_OID => Some(ShaHash::Sha256), + // sha384WithRSAEncryption + SHA384_RSA_ENCRYPTION_OID => Some(ShaHash::Sha384), + // sha512WithRSAEncryption + SHA512_RSA_ENCRYPTION_OID => Some(ShaHash::Sha512), + _ => return Err(data_error("unsupported algorithm")), + }; + + // 7. + if let Some(pk_hash) = pk_hash { + if pk_hash != hash { + return Err(data_error("hash mismatch")); + } + } + + // 8-9. + let private_key = + rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key) + .map_err(|e| data_error(e.to_string()))?; + + let bytes_consumed = private_key + .encoded_len() + .map_err(|e| data_error(e.to_string()))?; + + if bytes_consumed + != spki::der::Length::new(pk_info.private_key.len() as u16) + { + return Err(data_error("private key is invalid (too long)")); + } + + let data = pk_info.private_key.to_vec().into(); + let public_exponent = + private_key.public_exponent.as_bytes().to_vec().into(); + let modulus_length = private_key.modulus.as_bytes().len() * 8; + + Ok(ImportKeyResult::Rsa { + raw_data: RawKeyData::Private(data), + modulus_length, + public_exponent, + }) + } + // TODO(lucacasonato): support JWK encoding + _ => Err(unsupported_format()), + } +} + +fn import_key_rsapss( + key_data: KeyData, + hash: ShaHash, +) -> Result { + match key_data { + KeyData::Spki(data) => { + // 2-3. + let pk_info = spki::SubjectPublicKeyInfo::from_der(&data) + .map_err(|e| data_error(e.to_string()))?; + + // 4-5. + let alg = pk_info.algorithm.oid; + + // 6. + let pk_hash = match alg { + // rsaEncryption + RSA_ENCRYPTION_OID => None, + // id-RSASSA-PSS + RSASSA_PSS_OID => { + let params = PssPrivateKeyParameters::try_from( + pk_info + .algorithm + .parameters + .ok_or_else(|| data_error("malformed parameters"))?, + ) + .map_err(|_| data_error("malformed parameters"))?; + + let hash_alg = params.hash_algorithm; + let hash = match hash_alg.oid { + // id-sha1 + ID_SHA1_OID => Some(ShaHash::Sha1), + // id-sha256 + ID_SHA256_OID => Some(ShaHash::Sha256), + // id-sha384 + ID_SHA384_OID => Some(ShaHash::Sha384), + // id-sha256 + ID_SHA512_OID => Some(ShaHash::Sha512), + _ => return Err(data_error("unsupported hash algorithm")), + }; + + if params.mask_gen_algorithm.oid != ID_MFG1 { + return Err(not_supported_error("unsupported hash algorithm")); + } + + // TODO(lucacasonato): + // If the parameters field of the maskGenAlgorithm field of params + // is not an instance of the HashAlgorithm ASN.1 type that is + // identical in content to the hashAlgorithm field of params, + // throw a NotSupportedError. + + hash + } + _ => return Err(data_error("unsupported algorithm")), + }; + + // 7. + if let Some(pk_hash) = pk_hash { + if pk_hash != hash { + return Err(data_error("hash mismatch")); + } + } + + // 8-9. + let public_key = + rsa::pkcs1::RsaPublicKey::from_der(pk_info.subject_public_key) + .map_err(|e| data_error(e.to_string()))?; + + let bytes_consumed = public_key + .encoded_len() + .map_err(|e| data_error(e.to_string()))?; + + if bytes_consumed + != spki::der::Length::new(pk_info.subject_public_key.len() as u16) + { + return Err(data_error("public key is invalid (too long)")); + } + + let data = pk_info.subject_public_key.to_vec().into(); + let public_exponent = + public_key.public_exponent.as_bytes().to_vec().into(); + let modulus_length = public_key.modulus.as_bytes().len() * 8; + + Ok(ImportKeyResult::Rsa { + raw_data: RawKeyData::Public(data), + modulus_length, + public_exponent, + }) + } + KeyData::Pkcs8(data) => { + // 2-3. + let pk_info = rsa::pkcs8::PrivateKeyInfo::from_der(&data) + .map_err(|e| data_error(e.to_string()))?; + + // 4-5. + let alg = pk_info.algorithm.oid; + + // 6. + // 6. + let pk_hash = match alg { + // rsaEncryption + RSA_ENCRYPTION_OID => None, + // id-RSASSA-PSS + RSASSA_PSS_OID => { + let params = PssPrivateKeyParameters::try_from( + pk_info + .algorithm + .parameters + .ok_or_else(|| not_supported_error("malformed parameters"))?, + ) + .map_err(|_| not_supported_error("malformed parameters"))?; + + let hash_alg = params.hash_algorithm; + let hash = match hash_alg.oid { + // id-sha1 + ID_SHA1_OID => Some(ShaHash::Sha1), + // id-sha256 + ID_SHA256_OID => Some(ShaHash::Sha256), + // id-sha384 + ID_SHA384_OID => Some(ShaHash::Sha384), + // id-sha256 + ID_SHA512_OID => Some(ShaHash::Sha512), + _ => return Err(data_error("unsupported hash algorithm")), + }; + + if params.mask_gen_algorithm.oid != ID_MFG1 { + return Err(not_supported_error("unsupported mask gen algorithm")); + } + + // TODO(lucacasonato): + // If the parameters field of the maskGenAlgorithm field of params + // is not an instance of the HashAlgorithm ASN.1 type that is + // identical in content to the hashAlgorithm field of params, + // throw a NotSupportedError. + + hash + } + _ => return Err(data_error("unsupported algorithm")), + }; + + // 7. + if let Some(pk_hash) = pk_hash { + if pk_hash != hash { + return Err(data_error("hash mismatch")); + } + } + + // 8-9. + let private_key = + rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key) + .map_err(|e| data_error(e.to_string()))?; + + let bytes_consumed = private_key + .encoded_len() + .map_err(|e| data_error(e.to_string()))?; + + if bytes_consumed + != spki::der::Length::new(pk_info.private_key.len() as u16) + { + return Err(data_error("private key is invalid (too long)")); + } + + let data = pk_info.private_key.to_vec().into(); + let public_exponent = + private_key.public_exponent.as_bytes().to_vec().into(); + let modulus_length = private_key.modulus.as_bytes().len() * 8; + + Ok(ImportKeyResult::Rsa { + raw_data: RawKeyData::Private(data), + modulus_length, + public_exponent, + }) + } + // TODO(lucacasonato): support JWK encoding + _ => Err(unsupported_format()), + } +} + +fn import_key_rsaoaep( + key_data: KeyData, + hash: ShaHash, +) -> Result { + match key_data { + KeyData::Spki(data) => { + // 2-3. + let pk_info = spki::SubjectPublicKeyInfo::from_der(&data) + .map_err(|e| data_error(e.to_string()))?; + + // 4-5. + let alg = pk_info.algorithm.oid; + + // 6. + let pk_hash = match alg { + // rsaEncryption + RSA_ENCRYPTION_OID => None, + // id-RSAES-OAEP + RSAES_OAEP_OID => { + let params = OaepPrivateKeyParameters::try_from( + pk_info + .algorithm + .parameters + .ok_or_else(|| data_error("malformed parameters"))?, + ) + .map_err(|_| data_error("malformed parameters"))?; + + let hash_alg = params.hash_algorithm; + let hash = match hash_alg.oid { + // id-sha1 + ID_SHA1_OID => Some(ShaHash::Sha1), + // id-sha256 + ID_SHA256_OID => Some(ShaHash::Sha256), + // id-sha384 + ID_SHA384_OID => Some(ShaHash::Sha384), + // id-sha256 + ID_SHA512_OID => Some(ShaHash::Sha512), + _ => return Err(data_error("unsupported hash algorithm")), + }; + + if params.mask_gen_algorithm.oid != ID_MFG1 { + return Err(not_supported_error("unsupported hash algorithm")); + } + + // TODO(lucacasonato): + // If the parameters field of the maskGenAlgorithm field of params + // is not an instance of the HashAlgorithm ASN.1 type that is + // identical in content to the hashAlgorithm field of params, + // throw a NotSupportedError. + + hash + } + _ => return Err(data_error("unsupported algorithm")), + }; + + // 7. + if let Some(pk_hash) = pk_hash { + if pk_hash != hash { + return Err(data_error("hash mismatch")); + } + } + + // 8-9. + let public_key = + rsa::pkcs1::RsaPublicKey::from_der(pk_info.subject_public_key) + .map_err(|e| data_error(e.to_string()))?; + + let bytes_consumed = public_key + .encoded_len() + .map_err(|e| data_error(e.to_string()))?; + + if bytes_consumed + != spki::der::Length::new(pk_info.subject_public_key.len() as u16) + { + return Err(data_error("public key is invalid (too long)")); + } + + let data = pk_info.subject_public_key.to_vec().into(); + let public_exponent = + public_key.public_exponent.as_bytes().to_vec().into(); + let modulus_length = public_key.modulus.as_bytes().len() * 8; + + Ok(ImportKeyResult::Rsa { + raw_data: RawKeyData::Public(data), + modulus_length, + public_exponent, + }) + } + KeyData::Pkcs8(data) => { + // 2-3. + let pk_info = rsa::pkcs8::PrivateKeyInfo::from_der(&data) + .map_err(|e| data_error(e.to_string()))?; + + // 4-5. + let alg = pk_info.algorithm.oid; + + // 6. + // 6. + let pk_hash = match alg { + // rsaEncryption + RSA_ENCRYPTION_OID => None, + // id-RSAES-OAEP + RSAES_OAEP_OID => { + let params = OaepPrivateKeyParameters::try_from( + pk_info + .algorithm + .parameters + .ok_or_else(|| not_supported_error("malformed parameters"))?, + ) + .map_err(|_| not_supported_error("malformed parameters"))?; + + let hash_alg = params.hash_algorithm; + let hash = match hash_alg.oid { + // id-sha1 + ID_SHA1_OID => Some(ShaHash::Sha1), + // id-sha256 + ID_SHA256_OID => Some(ShaHash::Sha256), + // id-sha384 + ID_SHA384_OID => Some(ShaHash::Sha384), + // id-sha256 + ID_SHA512_OID => Some(ShaHash::Sha512), + _ => return Err(data_error("unsupported hash algorithm")), + }; + + if params.mask_gen_algorithm.oid != ID_MFG1 { + return Err(not_supported_error("unsupported mask gen algorithm")); + } + + // TODO(lucacasonato): + // If the parameters field of the maskGenAlgorithm field of params + // is not an instance of the HashAlgorithm ASN.1 type that is + // identical in content to the hashAlgorithm field of params, + // throw a NotSupportedError. + + hash + } + _ => return Err(data_error("unsupported algorithm")), + }; + + // 7. + if let Some(pk_hash) = pk_hash { + if pk_hash != hash { + return Err(data_error("hash mismatch")); + } + } + + // 8-9. + let private_key = + rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key) + .map_err(|e| data_error(e.to_string()))?; + + let bytes_consumed = private_key + .encoded_len() + .map_err(|e| data_error(e.to_string()))?; + + if bytes_consumed + != spki::der::Length::new(pk_info.private_key.len() as u16) + { + return Err(data_error("private key is invalid (too long)")); + } + + let data = pk_info.private_key.to_vec().into(); + let public_exponent = + private_key.public_exponent.as_bytes().to_vec().into(); + let modulus_length = private_key.modulus.as_bytes().len() * 8; + + Ok(ImportKeyResult::Rsa { + raw_data: RawKeyData::Private(data), + modulus_length, + public_exponent, + }) + } + // TODO(lucacasonato): support JWK encoding + _ => Err(unsupported_format()), + } +} + +fn import_key_ec( + key_data: KeyData, + named_curve: EcNamedCurve, +) -> Result { + Ok(match key_data { + KeyData::Raw(data) => { + // The point is parsed and validated, ultimately the original data is + // returned though. + match named_curve { + EcNamedCurve::P256 => { + // 1-2. + let point = p256::EncodedPoint::from_bytes(&data) + .map_err(|_| data_error("invalid P-256 eliptic curve point"))?; + // 3. + if point.is_identity() { + return Err(data_error("invalid P-256 eliptic curve point")); + } + } + EcNamedCurve::P384 => { + // 1-2. + let point = p384::EncodedPoint::from_bytes(&data) + .map_err(|_| data_error("invalid P-384 eliptic curve point"))?; + // 3. + if point.is_identity() { + return Err(data_error("invalid P-384 eliptic curve point")); + } + } + }; + ImportKeyResult::Ec { + raw_data: RawKeyData::Public(data), + } + } + _ => return Err(unsupported_format()), + }) +} diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index d811e504db..379b101a29 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -11,7 +11,6 @@ use deno_core::Extension; use deno_core::OpState; use deno_core::ZeroCopyBuf; use serde::Deserialize; -use serde::Serialize; use std::cell::RefCell; use std::num::NonZeroU32; @@ -57,46 +56,26 @@ use std::path::PathBuf; pub use rand; // Re-export rand +mod import_key; mod key; +mod shared; 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; + // Allowlist for RSA public exponents. lazy_static! { static ref PUB_EXPONENT_1: BigUint = BigUint::from_u64(3).unwrap(); static ref PUB_EXPONENT_2: BigUint = BigUint::from_u64(65537).unwrap(); } -const RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.1"); -const SHA1_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.5"); -const SHA256_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.11"); -const SHA384_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.12"); -const SHA512_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.13"); -const RSASSA_PSS_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.10"); -const ID_SHA1_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("1.3.14.3.2.26"); -const ID_SHA256_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("2.16.840.1.101.3.4.2.1"); -const ID_SHA384_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("2.16.840.1.101.3.4.2.2"); -const ID_SHA512_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("2.16.840.1.101.3.4.2.3"); -const ID_MFG1: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.8"); -const RSAES_OAEP_OID: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.7"); -const ID_P_SPECIFIED: rsa::pkcs8::ObjectIdentifier = - rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.9"); - pub fn init(maybe_seed: Option) -> Extension { Extension::builder() .js(include_js_files!( @@ -113,7 +92,7 @@ pub fn init(maybe_seed: Option) -> Extension { ("op_crypto_sign_key", op_async(op_crypto_sign_key)), ("op_crypto_verify_key", op_async(op_crypto_verify_key)), ("op_crypto_derive_bits", op_async(op_crypto_derive_bits)), - ("op_crypto_import_key", op_async(op_crypto_import_key)), + ("op_crypto_import_key", op_sync(op_crypto_import_key)), ("op_crypto_export_key", op_async(op_crypto_export_key)), ("op_crypto_encrypt_key", op_async(op_crypto_encrypt_key)), ("op_crypto_decrypt_key", op_async(op_crypto_decrypt_key)), @@ -1176,684 +1155,6 @@ impl<'a> TryFrom> } } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ImportKeyArg { - algorithm: Algorithm, - format: KeyFormat, - // RSASSA-PKCS1-v1_5 - hash: Option, - // ECDSA - named_curve: Option, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ImportKeyResult { - data: ZeroCopyBuf, - // RSASSA-PKCS1-v1_5 - public_exponent: Option, - modulus_length: Option, -} - -pub async fn op_crypto_import_key( - _state: Rc>, - args: ImportKeyArg, - zero_copy: ZeroCopyBuf, -) -> Result { - let data = &*zero_copy; - let algorithm = args.algorithm; - - match algorithm { - Algorithm::Ecdsa => { - let curve = args.named_curve.ok_or_else(|| { - type_error("Missing argument named_curve".to_string()) - })?; - - match curve { - CryptoNamedCurve::P256 => { - // 1-2. - let point = p256::EncodedPoint::from_bytes(data)?; - // 3. - if point.is_identity() { - return Err(type_error("Invalid key data".to_string())); - } - } - CryptoNamedCurve::P384 => { - // 1-2. - let point = p384::EncodedPoint::from_bytes(data)?; - // 3. - if point.is_identity() { - return Err(type_error("Invalid key data".to_string())); - } - } - }; - - Ok(ImportKeyResult { - data: zero_copy, - modulus_length: None, - public_exponent: None, - }) - } - Algorithm::RsassaPkcs1v15 => { - match args.format { - KeyFormat::Pkcs8 => { - let hash = args - .hash - .ok_or_else(|| type_error("Missing argument hash".to_string()))?; - - // 2-3. - let pk_info = - rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - // 4-5. - let alg = pk_info.algorithm.oid; - - // 6. - let pk_hash = match alg { - // rsaEncryption - RSA_ENCRYPTION_OID => None, - // sha1WithRSAEncryption - SHA1_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha1), - // sha256WithRSAEncryption - SHA256_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha256), - // sha384WithRSAEncryption - SHA384_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha384), - // sha512WithRSAEncryption - SHA512_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha512), - _ => { - return Err(custom_error( - "DOMExceptionDataError", - "Unsupported algorithm".to_string(), - )) - } - }; - - // 7. - if let Some(pk_hash) = pk_hash { - if pk_hash != hash { - return Err(custom_error( - "DOMExceptionDataError", - "Hash mismatch".to_string(), - )); - } - } - - // 8-9. - let private_key = rsa::pkcs1::RsaPrivateKey::from_der( - pk_info.private_key, - ) - .map_err(|e| custom_error("DOMExceptionDataError", e.to_string()))?; - - let bytes_consumed = private_key.encoded_len().map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - if bytes_consumed - != spki::der::Length::new(pk_info.private_key.len() as u16) - { - return Err(custom_error( - "DOMExceptionDataError", - "Some bytes were not consumed".to_string(), - )); - } - - Ok(ImportKeyResult { - data: pk_info.private_key.to_vec().into(), - public_exponent: Some( - private_key.public_exponent.as_bytes().to_vec().into(), - ), - modulus_length: Some(private_key.modulus.as_bytes().len() * 8), - }) - } - KeyFormat::Spki => { - let hash = args - .hash - .ok_or_else(|| type_error("Missing argument hash".to_string()))?; - - // 2-3. - let pk_info = - spki::SubjectPublicKeyInfo::from_der(data).map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - // 4-5. - let alg = pk_info.algorithm.oid; - - // 6. - let pk_hash = match alg { - // rsaEncryption - RSA_ENCRYPTION_OID => None, - // sha1WithRSAEncryption - SHA1_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha1), - // sha256WithRSAEncryption - SHA256_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha256), - // sha384WithRSAEncryption - SHA384_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha384), - // sha512WithRSAEncryption - SHA512_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha512), - _ => { - return Err(custom_error( - "DOMExceptionDataError", - "Unsupported algorithm".to_string(), - )) - } - }; - - // 7. - if let Some(pk_hash) = pk_hash { - if pk_hash != hash { - return Err(custom_error( - "DOMExceptionDataError", - "Hash mismatch".to_string(), - )); - } - } - - // 8-9. - let public_key = - rsa::pkcs1::RsaPublicKey::from_der(pk_info.subject_public_key) - .map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - let bytes_consumed = public_key.encoded_len().map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - if bytes_consumed - != spki::der::Length::new(pk_info.subject_public_key.len() as u16) - { - return Err(custom_error( - "DOMExceptionDataError", - "Some bytes were not consumed".to_string(), - )); - } - - Ok(ImportKeyResult { - data: pk_info.subject_public_key.to_vec().into(), - public_exponent: Some( - public_key.public_exponent.as_bytes().to_vec().into(), - ), - modulus_length: Some(public_key.modulus.as_bytes().len() * 8), - }) - } - // TODO(@littledivy): jwk - _ => Err(type_error("Unsupported format".to_string())), - } - } - Algorithm::RsaPss => { - match args.format { - KeyFormat::Pkcs8 => { - let hash = args - .hash - .ok_or_else(|| type_error("Missing argument hash".to_string()))?; - - // 2-3. - let pk_info = - rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - // 4-5. - let alg = pk_info.algorithm.oid; - - // 6. - let pk_hash = match alg { - // rsaEncryption - RSA_ENCRYPTION_OID => None, - // id-RSASSA-PSS - RSASSA_PSS_OID => { - let params = PssPrivateKeyParameters::try_from( - pk_info.algorithm.parameters.ok_or_else(|| { - custom_error( - "DOMExceptionNotSupportedError", - "Malformed parameters".to_string(), - ) - })?, - ) - .map_err(|_| { - custom_error( - "DOMExceptionNotSupportedError", - "Malformed parameters".to_string(), - ) - })?; - - let hash_alg = params.hash_algorithm; - let hash = match hash_alg.oid { - // id-sha1 - ID_SHA1_OID => Some(CryptoHash::Sha1), - // id-sha256 - ID_SHA256_OID => Some(CryptoHash::Sha256), - // id-sha384 - ID_SHA384_OID => Some(CryptoHash::Sha384), - // id-sha256 - ID_SHA512_OID => Some(CryptoHash::Sha512), - _ => { - return Err(custom_error( - "DOMExceptionDataError", - "Unsupported hash algorithm".to_string(), - )) - } - }; - - if params.mask_gen_algorithm.oid != ID_MFG1 { - return Err(custom_error( - "DOMExceptionNotSupportedError", - "Unsupported hash algorithm".to_string(), - )); - } - - // TODO(lucacasonato): - // If the parameters field of the maskGenAlgorithm field of params - // is not an instance of the HashAlgorithm ASN.1 type that is - // identical in content to the hashAlgorithm field of params, - // throw a NotSupportedError. - - hash - } - _ => { - return Err(custom_error( - "DOMExceptionDataError", - "Unsupported algorithm".to_string(), - )) - } - }; - - // 7. - if let Some(pk_hash) = pk_hash { - if pk_hash != hash { - return Err(custom_error( - "DOMExceptionDataError", - "Hash mismatch".to_string(), - )); - } - } - - // 8-9. - let private_key = rsa::pkcs1::RsaPrivateKey::from_der( - pk_info.private_key, - ) - .map_err(|e| custom_error("DOMExceptionDataError", e.to_string()))?; - - let bytes_consumed = private_key.encoded_len().map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - if bytes_consumed - != spki::der::Length::new(pk_info.private_key.len() as u16) - { - return Err(custom_error( - "DOMExceptionDataError", - "Some bytes were not consumed".to_string(), - )); - } - - Ok(ImportKeyResult { - data: pk_info.private_key.to_vec().into(), - public_exponent: Some( - private_key.public_exponent.as_bytes().to_vec().into(), - ), - modulus_length: Some(private_key.modulus.as_bytes().len() * 8), - }) - } - KeyFormat::Spki => { - let hash = args - .hash - .ok_or_else(|| type_error("Missing argument hash".to_string()))?; - - // 2-3. - let pk_info = - spki::SubjectPublicKeyInfo::from_der(data).map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - // 4-5. - let alg = pk_info.algorithm.oid; - - // 6. - let pk_hash = match alg { - // rsaEncryption - RSA_ENCRYPTION_OID => None, - // id-RSASSA-PSS - RSASSA_PSS_OID => { - let params = PssPrivateKeyParameters::try_from( - pk_info.algorithm.parameters.ok_or_else(|| { - custom_error( - "DOMExceptionDataError", - "Malformed parameters".to_string(), - ) - })?, - ) - .map_err(|_| { - custom_error( - "DOMExceptionDataError", - "Malformed parameters".to_string(), - ) - })?; - - let hash_alg = params.hash_algorithm; - let hash = match hash_alg.oid { - // id-sha1 - ID_SHA1_OID => Some(CryptoHash::Sha1), - // id-sha256 - ID_SHA256_OID => Some(CryptoHash::Sha256), - // id-sha384 - ID_SHA384_OID => Some(CryptoHash::Sha384), - // id-sha256 - ID_SHA512_OID => Some(CryptoHash::Sha512), - _ => { - return Err(custom_error( - "DOMExceptionDataError", - "Unsupported hash algorithm".to_string(), - )) - } - }; - - if params.mask_gen_algorithm.oid != ID_MFG1 { - return Err(custom_error( - "DOMExceptionNotSupportedError", - "Unsupported hash algorithm".to_string(), - )); - } - - // TODO(lucacasonato): - // If the parameters field of the maskGenAlgorithm field of params - // is not an instance of the HashAlgorithm ASN.1 type that is - // identical in content to the hashAlgorithm field of params, - // throw a NotSupportedError. - - hash - } - _ => { - return Err(custom_error( - "DOMExceptionDataError", - "Unsupported algorithm".to_string(), - )) - } - }; - - // 7. - if let Some(pk_hash) = pk_hash { - if pk_hash != hash { - return Err(custom_error( - "DOMExceptionDataError", - "Hash mismatch".to_string(), - )); - } - } - - // 8-9. - let public_key = - rsa::pkcs1::RsaPublicKey::from_der(pk_info.subject_public_key) - .map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - let bytes_consumed = public_key.encoded_len().map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - if bytes_consumed - != spki::der::Length::new(pk_info.subject_public_key.len() as u16) - { - return Err(custom_error( - "DOMExceptionDataError", - "Some bytes were not consumed".to_string(), - )); - } - - Ok(ImportKeyResult { - data: pk_info.subject_public_key.to_vec().into(), - public_exponent: Some( - public_key.public_exponent.as_bytes().to_vec().into(), - ), - modulus_length: Some(public_key.modulus.as_bytes().len() * 8), - }) - } - // TODO(@littledivy): jwk - _ => Err(type_error("Unsupported format".to_string())), - } - } - Algorithm::RsaOaep => { - match args.format { - KeyFormat::Pkcs8 => { - let hash = args - .hash - .ok_or_else(|| type_error("Missing argument hash".to_string()))?; - - // 2-3. - let pk_info = - rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - // 4-5. - let alg = pk_info.algorithm.oid; - - // 6. - let pk_hash = match alg { - // rsaEncryption - RSA_ENCRYPTION_OID => None, - // id-RSAES-OAEP - RSAES_OAEP_OID => { - let params = OaepPrivateKeyParameters::try_from( - pk_info.algorithm.parameters.ok_or_else(|| { - custom_error( - "DOMExceptionNotSupportedError", - "Malformed parameters".to_string(), - ) - })?, - ) - .map_err(|_| { - custom_error( - "DOMExceptionNotSupportedError", - "Malformed parameters".to_string(), - ) - })?; - - let hash_alg = params.hash_algorithm; - let hash = match hash_alg.oid { - // id-sha1 - ID_SHA1_OID => Some(CryptoHash::Sha1), - // id-sha256 - ID_SHA256_OID => Some(CryptoHash::Sha256), - // id-sha384 - ID_SHA384_OID => Some(CryptoHash::Sha384), - // id-sha256 - ID_SHA512_OID => Some(CryptoHash::Sha512), - _ => { - return Err(custom_error( - "DOMExceptionDataError", - "Unsupported hash algorithm".to_string(), - )) - } - }; - - if params.mask_gen_algorithm.oid != ID_MFG1 { - return Err(custom_error( - "DOMExceptionNotSupportedError", - "Unsupported hash algorithm".to_string(), - )); - } - - // TODO(lucacasonato): - // If the parameters field of the maskGenAlgorithm field of params - // is not an instance of the HashAlgorithm ASN.1 type that is - // identical in content to the hashAlgorithm field of params, - // throw a NotSupportedError. - - hash - } - _ => { - return Err(custom_error( - "DOMExceptionDataError", - "Unsupported algorithm".to_string(), - )) - } - }; - - // 7. - if let Some(pk_hash) = pk_hash { - if pk_hash != hash { - return Err(custom_error( - "DOMExceptionDataError", - "Hash mismatch".to_string(), - )); - } - } - - // 8-9. - let private_key = rsa::pkcs1::RsaPrivateKey::from_der( - pk_info.private_key, - ) - .map_err(|e| custom_error("DOMExceptionDataError", e.to_string()))?; - - let bytes_consumed = private_key.encoded_len().map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - if bytes_consumed - != spki::der::Length::new(pk_info.private_key.len() as u16) - { - return Err(custom_error( - "DOMExceptionDataError", - "Some bytes were not consumed".to_string(), - )); - } - - Ok(ImportKeyResult { - data: pk_info.private_key.to_vec().into(), - public_exponent: Some( - private_key.public_exponent.as_bytes().to_vec().into(), - ), - modulus_length: Some(private_key.modulus.as_bytes().len() * 8), - }) - } - KeyFormat::Spki => { - let hash = args - .hash - .ok_or_else(|| type_error("Missing argument hash".to_string()))?; - - // 2-3. - let pk_info = - spki::SubjectPublicKeyInfo::from_der(data).map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - // 4-5. - let alg = pk_info.algorithm.oid; - - // 6. - let pk_hash = match alg { - // rsaEncryption - RSA_ENCRYPTION_OID => None, - // id-RSAES-OAEP - RSAES_OAEP_OID => { - let params = OaepPrivateKeyParameters::try_from( - pk_info.algorithm.parameters.ok_or_else(|| { - custom_error( - "DOMExceptionDataError", - "Malformed parameters".to_string(), - ) - })?, - ) - .map_err(|_| { - custom_error( - "DOMExceptionDataError", - "Malformed parameters".to_string(), - ) - })?; - - let hash_alg = params.hash_algorithm; - let hash = match hash_alg.oid { - // id-sha1 - ID_SHA1_OID => Some(CryptoHash::Sha1), - // id-sha256 - ID_SHA256_OID => Some(CryptoHash::Sha256), - // id-sha384 - ID_SHA384_OID => Some(CryptoHash::Sha384), - // id-sha256 - ID_SHA512_OID => Some(CryptoHash::Sha512), - _ => { - return Err(custom_error( - "DOMExceptionDataError", - "Unsupported hash algorithm".to_string(), - )) - } - }; - - if params.mask_gen_algorithm.oid != ID_MFG1 { - return Err(custom_error( - "DOMExceptionDataError", - "Unsupported hash algorithm".to_string(), - )); - } - - // TODO(lucacasonato): - // If the parameters field of the maskGenAlgorithm field of params - // is not an instance of the HashAlgorithm ASN.1 type that is - // identical in content to the hashAlgorithm field of params, - // throw a NotSupportedError. - - hash - } - _ => { - return Err(custom_error( - "DOMExceptionDataError", - "Unsupported algorithm".to_string(), - )) - } - }; - - // 7. - if let Some(pk_hash) = pk_hash { - if pk_hash != hash { - return Err(custom_error( - "DOMExceptionDataError", - "Hash mismatch".to_string(), - )); - } - } - - // 8-9. - let public_key = - rsa::pkcs1::RsaPublicKey::from_der(pk_info.subject_public_key) - .map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - let bytes_consumed = public_key.encoded_len().map_err(|e| { - custom_error("DOMExceptionDataError", e.to_string()) - })?; - - if bytes_consumed - != spki::der::Length::new(pk_info.subject_public_key.len() as u16) - { - return Err(custom_error( - "DOMExceptionDataError", - "Some bytes were not consumed".to_string(), - )); - } - - Ok(ImportKeyResult { - data: pk_info.subject_public_key.to_vec().into(), - public_exponent: Some( - public_key.public_exponent.as_bytes().to_vec().into(), - ), - modulus_length: Some(public_key.modulus.as_bytes().len() * 8), - }) - } - // TODO(@littledivy): jwk - _ => Err(type_error("Unsupported format".to_string())), - } - } - _ => Err(type_error("Unsupported algorithm".to_string())), - } -} - #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct DecryptArg { diff --git a/ext/crypto/shared.rs b/ext/crypto/shared.rs new file mode 100644 index 0000000000..696b1c087c --- /dev/null +++ b/ext/crypto/shared.rs @@ -0,0 +1,74 @@ +use std::borrow::Cow; + +use deno_core::error::custom_error; +use deno_core::error::AnyError; +use deno_core::ZeroCopyBuf; +use serde::Deserialize; +use serde::Serialize; + +pub const RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.1"); +pub const SHA1_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.5"); +pub const SHA256_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.11"); +pub const SHA384_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.12"); +pub const SHA512_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.13"); +pub const RSASSA_PSS_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.10"); +pub const ID_SHA1_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.3.14.3.2.26"); +pub const ID_SHA256_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("2.16.840.1.101.3.4.2.1"); +pub const ID_SHA384_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("2.16.840.1.101.3.4.2.2"); +pub const ID_SHA512_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("2.16.840.1.101.3.4.2.3"); +pub const ID_MFG1: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.8"); +pub const RSAES_OAEP_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.7"); +pub const ID_P_SPECIFIED: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.9"); + +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] +pub enum ShaHash { + #[serde(rename = "SHA-1")] + Sha1, + #[serde(rename = "SHA-256")] + Sha256, + #[serde(rename = "SHA-384")] + Sha384, + #[serde(rename = "SHA-512")] + Sha512, +} + +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] +pub enum EcNamedCurve { + #[serde(rename = "P-256")] + P256, + #[serde(rename = "P-384")] + P384, +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "lowercase", tag = "type", content = "data")] +pub enum RawKeyData { + Secret(ZeroCopyBuf), + Private(ZeroCopyBuf), + Public(ZeroCopyBuf), +} + +pub fn data_error(msg: impl Into>) -> AnyError { + custom_error("DOMExceptionDataError", msg) +} + +pub fn not_supported_error(msg: impl Into>) -> AnyError { + custom_error("DOMExceptionNotSupportedError", msg) +} + +pub fn unsupported_format() -> AnyError { + not_supported_error("unsupported format") +}