1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-24 08:09:08 -05:00

refactor(ext/crypto): clean up rust side importKey (#13036)

This commit cleans up the Rust side of `import_key` by using a bunch of
enums instead of structs with "type" and "data" fields.

This commit does add some duplicated code for the time being, because
a lot of the other ops still need to get the same cleanup treatment.
This commit is contained in:
Luca Casonato 2021-12-10 15:06:03 +01:00 committed by GitHub
parent 38f1630223
commit 2926827726
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 689 additions and 748 deletions

View file

@ -759,6 +759,7 @@
* @param {KeyUsages[]} keyUsages
* @returns {Promise<any>}
*/
// 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(
const { modulusLength, publicExponent, rawData } = core.opSync(
"op_crypto_import_key",
{
algorithm: normalizedAlgorithm.name,
format: "pkcs8",
// Needed to perform step 7 without normalization.
hash: normalizedAlgorithm.hash.name,
},
keyData,
{ 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(
const { modulusLength, publicExponent, rawData } = core.opSync(
"op_crypto_import_key",
{
algorithm: normalizedAlgorithm.name,
format: "spki",
// Needed to perform step 7 without normalization.
hash: normalizedAlgorithm.hash.name,
},
keyData,
{ spki: keyData },
);
const handle = {};
WeakMapPrototypeSet(KEY_STORE, handle, {
type: "public",
data,
});
WeakMapPrototypeSet(KEY_STORE, handle, rawData);
const algorithm = {
name: normalizedAlgorithm.name,

578
ext/crypto/import_key.rs Normal file
View file

@ -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<ImportKeyResult, AnyError> {
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<ImportKeyResult, deno_core::anyhow::Error> {
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<ImportKeyResult, deno_core::anyhow::Error> {
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<ImportKeyResult, deno_core::anyhow::Error> {
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<ImportKeyResult, AnyError> {
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()),
})
}

View file

@ -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<u64>) -> Extension {
Extension::builder()
.js(include_js_files!(
@ -113,7 +92,7 @@ pub fn init(maybe_seed: Option<u64>) -> 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<rsa::pkcs8::der::asn1::Any<'a>>
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ImportKeyArg {
algorithm: Algorithm,
format: KeyFormat,
// RSASSA-PKCS1-v1_5
hash: Option<CryptoHash>,
// ECDSA
named_curve: Option<CryptoNamedCurve>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ImportKeyResult {
data: ZeroCopyBuf,
// RSASSA-PKCS1-v1_5
public_exponent: Option<ZeroCopyBuf>,
modulus_length: Option<usize>,
}
pub async fn op_crypto_import_key(
_state: Rc<RefCell<OpState>>,
args: ImportKeyArg,
zero_copy: ZeroCopyBuf,
) -> Result<ImportKeyResult, AnyError> {
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 {

74
ext/crypto/shared.rs Normal file
View file

@ -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<Cow<'static, str>>) -> AnyError {
custom_error("DOMExceptionDataError", msg)
}
pub fn not_supported_error(msg: impl Into<Cow<'static, str>>) -> AnyError {
custom_error("DOMExceptionNotSupportedError", msg)
}
pub fn unsupported_format() -> AnyError {
not_supported_error("unsupported format")
}