mirror of
https://github.com/denoland/deno.git
synced 2025-01-08 15:19:40 -05:00
feat(crypto): support importKey
in SPKI format (#12921)
This commit adds support for `spki` key format for `crypto.subtle.importKey` for the RSA* algorithms.
This commit is contained in:
parent
e70dc53460
commit
a3d024ac2e
3 changed files with 458 additions and 795 deletions
|
@ -270,7 +270,7 @@
|
|||
|
||||
/**
|
||||
* @param {ArrayBufferView | ArrayBuffer} input
|
||||
* @returns
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
function copyBuffer(input) {
|
||||
return TypedArrayPrototypeSlice(
|
||||
|
@ -826,7 +826,6 @@
|
|||
keyData,
|
||||
extractable,
|
||||
keyUsages,
|
||||
["sign"],
|
||||
);
|
||||
}
|
||||
case "RSA-OAEP": {
|
||||
|
@ -836,7 +835,6 @@
|
|||
keyData,
|
||||
extractable,
|
||||
keyUsages,
|
||||
["decrypt", "unwrapKey"],
|
||||
);
|
||||
}
|
||||
case "HKDF": {
|
||||
|
@ -1495,8 +1493,7 @@
|
|||
);
|
||||
const handle = {};
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
// PKCS#1 for RSA
|
||||
type: "raw",
|
||||
type: "private",
|
||||
data: keyData,
|
||||
});
|
||||
|
||||
|
@ -1556,8 +1553,7 @@
|
|||
);
|
||||
const handle = {};
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
// PKCS#1 for RSA
|
||||
type: "raw",
|
||||
type: "private",
|
||||
data: keyData,
|
||||
});
|
||||
|
||||
|
@ -1614,7 +1610,7 @@
|
|||
namedCurve: normalizedAlgorithm.namedCurve,
|
||||
});
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
type: "pkcs8",
|
||||
type: "private",
|
||||
data: keyData,
|
||||
});
|
||||
} else {
|
||||
|
@ -1672,7 +1668,7 @@
|
|||
namedCurve: normalizedAlgorithm.namedCurve,
|
||||
});
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
type: "pkcs8",
|
||||
type: "private",
|
||||
data: keyData,
|
||||
});
|
||||
} else {
|
||||
|
@ -1768,7 +1764,10 @@
|
|||
length,
|
||||
});
|
||||
const handle = {};
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, { type: "raw", data: keyData });
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
type: "secret",
|
||||
data: keyData,
|
||||
});
|
||||
|
||||
// 6-10.
|
||||
const algorithm = {
|
||||
|
@ -1980,7 +1979,7 @@
|
|||
|
||||
const handle = {};
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
type: "raw",
|
||||
type: "secret",
|
||||
data,
|
||||
});
|
||||
|
||||
|
@ -2165,13 +2164,9 @@
|
|||
length = normalizedAlgorithm.length;
|
||||
}
|
||||
|
||||
if (keyUsages.length == 0) {
|
||||
throw new DOMException("Key usage is empty", "SyntaxError");
|
||||
}
|
||||
|
||||
const handle = {};
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
type: "raw",
|
||||
type: "secret",
|
||||
data,
|
||||
});
|
||||
|
||||
|
@ -2232,7 +2227,7 @@
|
|||
|
||||
const handle = {};
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
type: "raw",
|
||||
type: "public",
|
||||
data,
|
||||
});
|
||||
|
||||
|
@ -2258,13 +2253,27 @@
|
|||
}
|
||||
}
|
||||
|
||||
const SUPPORTED_RSA_KEY_USAGES = {
|
||||
"RSASSA-PKCS1-v1_5": {
|
||||
spki: ["verify"],
|
||||
pkcs8: ["sign"],
|
||||
},
|
||||
"RSA-PSS": {
|
||||
spki: ["verify"],
|
||||
pkcs8: ["sign"],
|
||||
},
|
||||
"RSA-OAEP": {
|
||||
spki: ["encrypt", "wrapKey"],
|
||||
pkcs8: ["decrypt", "unwrapKey"],
|
||||
},
|
||||
};
|
||||
|
||||
async function importKeyRSA(
|
||||
format,
|
||||
normalizedAlgorithm,
|
||||
keyData,
|
||||
extractable,
|
||||
keyUsages,
|
||||
supportedKeyUsages,
|
||||
) {
|
||||
switch (format) {
|
||||
case "pkcs8": {
|
||||
|
@ -2272,16 +2281,16 @@
|
|||
if (
|
||||
ArrayPrototypeFind(
|
||||
keyUsages,
|
||||
(u) => !ArrayPrototypeIncludes(supportedKeyUsages, u),
|
||||
(u) =>
|
||||
!ArrayPrototypeIncludes(
|
||||
SUPPORTED_RSA_KEY_USAGES[normalizedAlgorithm.name].pkcs8,
|
||||
u,
|
||||
),
|
||||
) !== undefined
|
||||
) {
|
||||
throw new DOMException("Invalid key usages", "SyntaxError");
|
||||
}
|
||||
|
||||
if (keyUsages.length == 0) {
|
||||
throw new DOMException("Key usage is empty", "SyntaxError");
|
||||
}
|
||||
|
||||
// 2-9.
|
||||
const { modulusLength, publicExponent, data } = await core
|
||||
.opAsync(
|
||||
|
@ -2297,8 +2306,7 @@
|
|||
|
||||
const handle = {};
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
// PKCS#1 for RSA
|
||||
type: "raw",
|
||||
type: "private",
|
||||
data,
|
||||
});
|
||||
|
||||
|
@ -2319,6 +2327,57 @@
|
|||
|
||||
return key;
|
||||
}
|
||||
case "spki": {
|
||||
// 1.
|
||||
if (
|
||||
ArrayPrototypeFind(
|
||||
keyUsages,
|
||||
(u) =>
|
||||
!ArrayPrototypeIncludes(
|
||||
SUPPORTED_RSA_KEY_USAGES[normalizedAlgorithm.name].spki,
|
||||
u,
|
||||
),
|
||||
) !== undefined
|
||||
) {
|
||||
throw new DOMException("Invalid key usages", "SyntaxError");
|
||||
}
|
||||
|
||||
// 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 handle = {};
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
type: "public",
|
||||
data,
|
||||
});
|
||||
|
||||
const algorithm = {
|
||||
name: normalizedAlgorithm.name,
|
||||
modulusLength,
|
||||
publicExponent,
|
||||
hash: normalizedAlgorithm.hash,
|
||||
};
|
||||
|
||||
const key = constructKey(
|
||||
"public",
|
||||
extractable,
|
||||
usageIntersection(keyUsages, recognisedUsages),
|
||||
algorithm,
|
||||
handle,
|
||||
);
|
||||
|
||||
return key;
|
||||
}
|
||||
default:
|
||||
throw new DOMException("Not implemented", "NotSupportedError");
|
||||
}
|
||||
|
@ -2355,7 +2414,7 @@
|
|||
// 3.
|
||||
const handle = {};
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
type: "raw",
|
||||
type: "secret",
|
||||
data: keyData,
|
||||
});
|
||||
|
||||
|
@ -2407,7 +2466,7 @@
|
|||
// 4.
|
||||
const handle = {};
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
type: "raw",
|
||||
type: "secret",
|
||||
data: keyData,
|
||||
});
|
||||
|
||||
|
@ -2545,7 +2604,7 @@
|
|||
});
|
||||
const handle = {};
|
||||
WeakMapPrototypeSet(KEY_STORE, handle, {
|
||||
type: "raw",
|
||||
type: "secret",
|
||||
data: keyData,
|
||||
});
|
||||
|
||||
|
@ -2634,7 +2693,7 @@
|
|||
) {
|
||||
const baseKeyhandle = baseKey[_handle];
|
||||
const baseKeyData = WeakMapPrototypeGet(KEY_STORE, baseKeyhandle);
|
||||
const publicKeyhandle = baseKey[_handle];
|
||||
const publicKeyhandle = publicKey[_handle];
|
||||
const publicKeyData = WeakMapPrototypeGet(KEY_STORE, publicKeyhandle);
|
||||
|
||||
const buf = await core.opAsync("op_crypto_derive_bits", {
|
||||
|
@ -2705,6 +2764,7 @@
|
|||
key: keyData,
|
||||
algorithm: "RSA-OAEP",
|
||||
hash: hashAlgorithm,
|
||||
label: normalizedAlgorithm.label,
|
||||
}, data);
|
||||
|
||||
// 6.
|
||||
|
|
|
@ -40,6 +40,7 @@ use rsa::padding::PaddingScheme;
|
|||
use rsa::pkcs1::der::Decodable;
|
||||
use rsa::pkcs1::der::Encodable;
|
||||
use rsa::pkcs1::FromRsaPrivateKey;
|
||||
use rsa::pkcs1::FromRsaPublicKey;
|
||||
use rsa::pkcs1::ToRsaPrivateKey;
|
||||
use rsa::pkcs8::der::asn1;
|
||||
use rsa::pkcs8::FromPrivateKey;
|
||||
|
@ -275,12 +276,18 @@ pub enum KeyFormat {
|
|||
Spki,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum KeyType {
|
||||
Secret,
|
||||
Private,
|
||||
Public,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub struct KeyData {
|
||||
// TODO(littledivy): Kept here to be used to importKey() in future.
|
||||
#[allow(dead_code)]
|
||||
r#type: KeyFormat,
|
||||
r#type: KeyType,
|
||||
data: ZeroCopyBuf,
|
||||
}
|
||||
|
||||
|
@ -458,8 +465,7 @@ pub async fn op_crypto_verify_key(
|
|||
|
||||
let verification = match algorithm {
|
||||
Algorithm::RsassaPkcs1v15 => {
|
||||
let public_key: RsaPublicKey =
|
||||
RsaPrivateKey::from_pkcs1_der(&*args.key.data)?.to_public_key();
|
||||
let public_key = read_rsa_public_key(args.key)?;
|
||||
let (padding, hashed) = match args
|
||||
.hash
|
||||
.ok_or_else(|| type_error("Missing argument hash".to_string()))?
|
||||
|
@ -515,8 +521,7 @@ pub async fn op_crypto_verify_key(
|
|||
.salt_length
|
||||
.ok_or_else(|| type_error("Missing argument saltLength".to_string()))?
|
||||
as usize;
|
||||
let public_key: RsaPublicKey =
|
||||
RsaPrivateKey::from_pkcs1_der(&*args.key.data)?.to_public_key();
|
||||
let public_key = read_rsa_public_key(args.key)?;
|
||||
|
||||
let rng = OsRng;
|
||||
let (padding, hashed) = match args
|
||||
|
@ -903,6 +908,17 @@ pub struct EncryptArg {
|
|||
length: Option<usize>,
|
||||
}
|
||||
|
||||
fn read_rsa_public_key(key_data: KeyData) -> Result<RsaPublicKey, AnyError> {
|
||||
let public_key = match key_data.r#type {
|
||||
KeyType::Private => {
|
||||
RsaPrivateKey::from_pkcs1_der(&*key_data.data)?.to_public_key()
|
||||
}
|
||||
KeyType::Public => RsaPublicKey::from_pkcs1_der(&*key_data.data)?,
|
||||
KeyType::Secret => unreachable!("unexpected KeyType::Secret"),
|
||||
};
|
||||
Ok(public_key)
|
||||
}
|
||||
|
||||
pub async fn op_crypto_encrypt_key(
|
||||
_state: Rc<RefCell<OpState>>,
|
||||
args: EncryptArg,
|
||||
|
@ -913,8 +929,7 @@ pub async fn op_crypto_encrypt_key(
|
|||
|
||||
match algorithm {
|
||||
Algorithm::RsaOaep => {
|
||||
let public_key: RsaPublicKey =
|
||||
RsaPrivateKey::from_pkcs1_der(&*args.key.data)?.to_public_key();
|
||||
let public_key = read_rsa_public_key(args.key)?;
|
||||
let label = args.label.map(|l| String::from_utf8_lossy(&*l).to_string());
|
||||
let mut rng = OsRng;
|
||||
let padding = match args
|
||||
|
@ -1230,7 +1245,7 @@ pub async fn op_crypto_import_key(
|
|||
// 2-3.
|
||||
let pk_info =
|
||||
rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| {
|
||||
custom_error("DOMExceptionOperationError", e.to_string())
|
||||
custom_error("DOMExceptionDataError", e.to_string())
|
||||
})?;
|
||||
|
||||
// 4-5.
|
||||
|
@ -1248,7 +1263,12 @@ pub async fn op_crypto_import_key(
|
|||
SHA384_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha384),
|
||||
// sha512WithRSAEncryption
|
||||
SHA512_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha512),
|
||||
_ => return Err(type_error("Unsupported algorithm".to_string())),
|
||||
_ => {
|
||||
return Err(custom_error(
|
||||
"DOMExceptionDataError",
|
||||
"Unsupported algorithm".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// 7.
|
||||
|
@ -1262,17 +1282,17 @@ pub async fn op_crypto_import_key(
|
|||
}
|
||||
|
||||
// 8-9.
|
||||
let private_key =
|
||||
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key).map_err(
|
||||
|e| custom_error("DOMExceptionOperationError", e.to_string()),
|
||||
)?;
|
||||
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
|
||||
!= rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
|
||||
!= spki::der::Length::new(pk_info.private_key.len() as u16)
|
||||
{
|
||||
return Err(custom_error(
|
||||
"DOMExceptionDataError",
|
||||
|
@ -1288,7 +1308,78 @@ pub async fn op_crypto_import_key(
|
|||
modulus_length: Some(private_key.modulus.as_bytes().len() * 8),
|
||||
})
|
||||
}
|
||||
// TODO(@littledivy): spki
|
||||
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())),
|
||||
}
|
||||
|
@ -1303,7 +1394,7 @@ pub async fn op_crypto_import_key(
|
|||
// 2-3.
|
||||
let pk_info =
|
||||
rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| {
|
||||
custom_error("DOMExceptionOperationError", e.to_string())
|
||||
custom_error("DOMExceptionDataError", e.to_string())
|
||||
})?;
|
||||
|
||||
// 4-5.
|
||||
|
@ -1355,6 +1446,12 @@ pub async fn op_crypto_import_key(
|
|||
));
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
_ => {
|
||||
|
@ -1376,17 +1473,17 @@ pub async fn op_crypto_import_key(
|
|||
}
|
||||
|
||||
// 8-9.
|
||||
let private_key =
|
||||
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key).map_err(
|
||||
|e| custom_error("DOMExceptionOperationError", e.to_string()),
|
||||
)?;
|
||||
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("DataError", e.to_string()))?;
|
||||
let bytes_consumed = private_key.encoded_len().map_err(|e| {
|
||||
custom_error("DOMExceptionDataError", e.to_string())
|
||||
})?;
|
||||
|
||||
if bytes_consumed
|
||||
!= rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
|
||||
!= spki::der::Length::new(pk_info.private_key.len() as u16)
|
||||
{
|
||||
return Err(custom_error(
|
||||
"DOMExceptionDataError",
|
||||
|
@ -1402,7 +1499,120 @@ pub async fn op_crypto_import_key(
|
|||
modulus_length: Some(private_key.modulus.as_bytes().len() * 8),
|
||||
})
|
||||
}
|
||||
// TODO(@littledivy): spki
|
||||
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())),
|
||||
}
|
||||
|
@ -1417,7 +1627,7 @@ pub async fn op_crypto_import_key(
|
|||
// 2-3.
|
||||
let pk_info =
|
||||
rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| {
|
||||
custom_error("DOMExceptionOperationError", e.to_string())
|
||||
custom_error("DOMExceptionDataError", e.to_string())
|
||||
})?;
|
||||
|
||||
// 4-5.
|
||||
|
@ -1469,6 +1679,12 @@ pub async fn op_crypto_import_key(
|
|||
));
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
_ => {
|
||||
|
@ -1490,17 +1706,17 @@ pub async fn op_crypto_import_key(
|
|||
}
|
||||
|
||||
// 8-9.
|
||||
let private_key =
|
||||
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key).map_err(
|
||||
|e| custom_error("DOMExceptionOperationError", e.to_string()),
|
||||
)?;
|
||||
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
|
||||
!= rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
|
||||
!= spki::der::Length::new(pk_info.private_key.len() as u16)
|
||||
{
|
||||
return Err(custom_error(
|
||||
"DOMExceptionDataError",
|
||||
|
@ -1516,7 +1732,120 @@ pub async fn op_crypto_import_key(
|
|||
modulus_length: Some(private_key.modulus.as_bytes().len() * 8),
|
||||
})
|
||||
}
|
||||
// TODO(@littledivy): spki
|
||||
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())),
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue