diff options
author | Luca Casonato <hello@lcas.dev> | 2021-12-10 15:06:03 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-10 15:06:03 +0100 |
commit | 292682772691402bb3c1e4fee554d85746147621 (patch) | |
tree | 2df72a3edd51cf7bac39412a05f2c36cbf7de34a /ext/crypto/import_key.rs | |
parent | 38f163022373c9adb050f17140f7d29bb403abe2 (diff) |
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.
Diffstat (limited to 'ext/crypto/import_key.rs')
-rw-r--r-- | ext/crypto/import_key.rs | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/ext/crypto/import_key.rs b/ext/crypto/import_key.rs new file mode 100644 index 000000000..16a8d155f --- /dev/null +++ b/ext/crypto/import_key.rs @@ -0,0 +1,578 @@ +use deno_core::error::AnyError; +use deno_core::OpState; +use deno_core::ZeroCopyBuf; +use serde::Deserialize; +use serde::Serialize; +use spki::der::Decodable; +use spki::der::Encodable; + +use crate::shared::*; +use crate::OaepPrivateKeyParameters; +use crate::PssPrivateKeyParameters; + +#[derive(Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum KeyData { + Spki(ZeroCopyBuf), + Pkcs8(ZeroCopyBuf), + Raw(ZeroCopyBuf), + Jwk { k: String }, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase", tag = "algorithm")] +pub enum ImportKeyOptions { + #[serde(rename = "RSASSA-PKCS1-v1_5")] + RsassaPkcs1v15 { hash: ShaHash }, + #[serde(rename = "RSA-PSS")] + RsaPss { hash: ShaHash }, + #[serde(rename = "RSA-OAEP")] + RsaOaep { hash: ShaHash }, + #[serde(rename = "ECDSA", rename_all = "camelCase")] + Ecdsa { named_curve: EcNamedCurve }, + #[serde(rename = "ECDH", rename_all = "camelCase")] + Ecdh { named_curve: EcNamedCurve }, +} + +#[derive(Serialize)] +#[serde(untagged)] +pub enum ImportKeyResult { + #[serde(rename_all = "camelCase")] + Rsa { + raw_data: RawKeyData, + modulus_length: usize, + public_exponent: ZeroCopyBuf, + }, + #[serde(rename_all = "camelCase")] + Ec { raw_data: RawKeyData }, +} + +pub fn op_crypto_import_key( + _state: &mut OpState, + opts: ImportKeyOptions, + key_data: KeyData, +) -> Result<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()), + }) +} |