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:
parent
38f1630223
commit
2926827726
4 changed files with 689 additions and 748 deletions
|
@ -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
578
ext/crypto/import_key.rs
Normal 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()),
|
||||
})
|
||||
}
|
|
@ -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
74
ext/crypto/shared.rs
Normal 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")
|
||||
}
|
Loading…
Reference in a new issue