summaryrefslogtreecommitdiff
path: root/ext/crypto/import_key.rs
diff options
context:
space:
mode:
authorLuca Casonato <hello@lcas.dev>2021-12-10 15:06:03 +0100
committerGitHub <noreply@github.com>2021-12-10 15:06:03 +0100
commit292682772691402bb3c1e4fee554d85746147621 (patch)
tree2df72a3edd51cf7bac39412a05f2c36cbf7de34a /ext/crypto/import_key.rs
parent38f163022373c9adb050f17140f7d29bb403abe2 (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.rs578
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()),
+ })
+}