diff options
author | Filip Skokan <panva.ip@gmail.com> | 2022-10-04 14:07:59 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-04 17:37:59 +0530 |
commit | 0d042d8e54684b85ce557017ced20d867a77904f (patch) | |
tree | be3c55982823278b4a526b765ce7beecc7317a86 /ext | |
parent | aa710aac98885356cfb5bdfd237d2be8265ed2e6 (diff) |
fix(ext/crypto): interoperable import/export (#16153)
This PR updates RSA key import/export to a state which is interoperable
with other implementations.
For RSA the only OID in and out is `rsaEncryption`.
For EC the only OID in and out is `id-ecpublickey` (fixed in #16152).
see https://github.com/w3c/webcrypto/issues/307#issuecomment-995813032
see https://github.com/w3c/webcrypto/issues/307
see https://github.com/w3c/webcrypto/pull/305
see https://github.com/nodejs/node/pull/42816
Diffstat (limited to 'ext')
-rw-r--r-- | ext/crypto/import_key.rs | 225 | ||||
-rw-r--r-- | ext/crypto/lib.rs | 206 | ||||
-rw-r--r-- | ext/crypto/shared.rs | 24 |
3 files changed, 24 insertions, 431 deletions
diff --git a/ext/crypto/import_key.rs b/ext/crypto/import_key.rs index 225950aa7..07c7f3f6f 100644 --- a/ext/crypto/import_key.rs +++ b/ext/crypto/import_key.rs @@ -1,7 +1,5 @@ use crate::key::CryptoNamedCurve; use crate::shared::*; -use crate::OaepPrivateKeyParameters; -use crate::PssPrivateKeyParameters; use deno_core::error::AnyError; use deno_core::op; use deno_core::ZeroCopyBuf; @@ -52,11 +50,11 @@ pub enum KeyData { #[serde(rename_all = "camelCase", tag = "algorithm")] pub enum ImportKeyOptions { #[serde(rename = "RSASSA-PKCS1-v1_5")] - RsassaPkcs1v15 { hash: ShaHash }, + RsassaPkcs1v15 {}, #[serde(rename = "RSA-PSS")] - RsaPss { hash: ShaHash }, + RsaPss {}, #[serde(rename = "RSA-OAEP")] - RsaOaep { hash: ShaHash }, + RsaOaep {}, #[serde(rename = "ECDSA", rename_all = "camelCase")] Ecdsa { named_curve: EcNamedCurve }, #[serde(rename = "ECDH", rename_all = "camelCase")] @@ -91,11 +89,9 @@ pub fn op_crypto_import_key( 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::RsassaPkcs1v15 {} => import_key_rsassa(key_data), + ImportKeyOptions::RsaPss {} => import_key_rsapss(key_data), + ImportKeyOptions::RsaOaep {} => import_key_rsaoaep(key_data), ImportKeyOptions::Ecdsa { named_curve } | ImportKeyOptions::Ecdh { named_curve } => { import_key_ec(key_data, named_curve) @@ -193,7 +189,6 @@ fn import_key_rsa_jwk( fn import_key_rsassa( key_data: KeyData, - hash: ShaHash, ) -> Result<ImportKeyResult, deno_core::anyhow::Error> { match key_data { KeyData::Spki(data) => { @@ -204,26 +199,9 @@ fn import_key_rsassa( // 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")); - } + // 6-7. (skipped, only support rsaEncryption for interoperability) + if alg != RSA_ENCRYPTION_OID { + return Err(data_error("unsupported algorithm")); } // 8-9. @@ -260,26 +238,9 @@ fn import_key_rsassa( // 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")); - } + // 6-7. (skipped, only support rsaEncryption for interoperability) + if alg != RSA_ENCRYPTION_OID { + return Err(data_error("unsupported algorithm")); } // 8-9. @@ -317,7 +278,6 @@ fn import_key_rsassa( fn import_key_rsapss( key_data: KeyData, - hash: ShaHash, ) -> Result<ImportKeyResult, deno_core::anyhow::Error> { match key_data { KeyData::Spki(data) => { @@ -328,47 +288,9 @@ fn import_key_rsapss( // 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")); - } - - 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")); - } + // 6-7. (skipped, only support rsaEncryption for interoperability) + if alg != RSA_ENCRYPTION_OID { + return Err(data_error("unsupported algorithm")); } // 8-9. @@ -405,42 +327,9 @@ fn import_key_rsapss( // 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; - 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")), - } - } - _ => return Err(data_error("unsupported algorithm")), - }; - - // 7. - if let Some(pk_hash) = pk_hash { - if pk_hash != hash { - return Err(data_error("hash mismatch")); - } + // 6-7. (skipped, only support rsaEncryption for interoperability) + if alg != RSA_ENCRYPTION_OID { + return Err(data_error("unsupported algorithm")); } // 8-9. @@ -478,7 +367,6 @@ fn import_key_rsapss( fn import_key_rsaoaep( key_data: KeyData, - hash: ShaHash, ) -> Result<ImportKeyResult, deno_core::anyhow::Error> { match key_data { KeyData::Spki(data) => { @@ -489,41 +377,9 @@ fn import_key_rsaoaep( // 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; - 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")), - } - } - _ => return Err(data_error("unsupported algorithm")), - }; - - // 7. - if let Some(pk_hash) = pk_hash { - if pk_hash != hash { - return Err(data_error("hash mismatch")); - } + // 6-7. (skipped, only support rsaEncryption for interoperability) + if alg != RSA_ENCRYPTION_OID { + return Err(data_error("unsupported algorithm")); } // 8-9. @@ -560,42 +416,9 @@ fn import_key_rsaoaep( // 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; - 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")), - } - } - _ => return Err(data_error("unsupported algorithm")), - }; - - // 7. - if let Some(pk_hash) = pk_hash { - if pk_hash != hash { - return Err(data_error("hash mismatch")); - } + // 6-7. (skipped, only support rsaEncryption for interoperability) + if alg != RSA_ENCRYPTION_OID { + return Err(data_error("unsupported algorithm")); } // 8-9. diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index 5f6445f49..d949135e7 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -35,11 +35,8 @@ use ring::signature::EcdsaSigningAlgorithm; use ring::signature::EcdsaVerificationAlgorithm; use ring::signature::KeyPair; use rsa::padding::PaddingScheme; -use rsa::pkcs1::der::Decode; -use rsa::pkcs1::der::Encode; use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1::DecodeRsaPublicKey; -use rsa::pkcs8::der::asn1; use rsa::PublicKey; use rsa::RsaPrivateKey; use rsa::RsaPublicKey; @@ -74,10 +71,6 @@ use crate::key::CryptoHash; use crate::key::CryptoNamedCurve; use crate::key::HkdfOutput; use crate::shared::RawKeyData; -use crate::shared::ID_MFG1; -use crate::shared::ID_P_SPECIFIED; -use crate::shared::ID_SHA1_OID; -use once_cell::sync::Lazy; pub fn init(maybe_seed: Option<u64>) -> Extension { Extension::builder() @@ -674,205 +667,6 @@ fn read_rsa_public_key(key_data: KeyData) -> Result<RsaPublicKey, AnyError> { Ok(public_key) } -// The parameters field associated with OID id-RSASSA-PSS -// Defined in RFC 3447, section A.2.3 -// -// RSASSA-PSS-params ::= SEQUENCE { -// hashAlgorithm [0] HashAlgorithm DEFAULT sha1, -// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, -// saltLength [2] INTEGER DEFAULT 20, -// trailerField [3] TrailerField DEFAULT trailerFieldBC -// } -pub struct PssPrivateKeyParameters<'a> { - pub hash_algorithm: rsa::pkcs8::AlgorithmIdentifier<'a>, - pub mask_gen_algorithm: rsa::pkcs8::AlgorithmIdentifier<'a>, - pub salt_length: u32, -} - -// Context-specific tag number for hashAlgorithm. -const HASH_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber = - rsa::pkcs8::der::TagNumber::new(0); - -// Context-specific tag number for maskGenAlgorithm. -const MASK_GEN_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber = - rsa::pkcs8::der::TagNumber::new(1); - -// Context-specific tag number for saltLength. -const SALT_LENGTH_TAG: rsa::pkcs8::der::TagNumber = - rsa::pkcs8::der::TagNumber::new(2); - -// Context-specific tag number for pSourceAlgorithm -const P_SOURCE_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber = - rsa::pkcs8::der::TagNumber::new(2); - -// Default HashAlgorithm for RSASSA-PSS-params (sha1) -// -// sha1 HashAlgorithm ::= { -// algorithm id-sha1, -// parameters SHA1Parameters : NULL -// } -// -// SHA1Parameters ::= NULL -static SHA1_HASH_ALGORITHM: Lazy<rsa::pkcs8::AlgorithmIdentifier<'static>> = - Lazy::new(|| { - rsa::pkcs8::AlgorithmIdentifier { - // id-sha1 - oid: ID_SHA1_OID, - // NULL - parameters: Some(asn1::AnyRef::from(asn1::Null)), - } - }); - -// TODO(@littledivy): `pkcs8` should provide AlgorithmIdentifier to Any conversion. -static ENCODED_SHA1_HASH_ALGORITHM: Lazy<Vec<u8>> = - Lazy::new(|| SHA1_HASH_ALGORITHM.to_vec().unwrap()); -// Default MaskGenAlgrithm for RSASSA-PSS-params (mgf1SHA1) -// -// mgf1SHA1 MaskGenAlgorithm ::= { -// algorithm id-mgf1, -// parameters HashAlgorithm : sha1 -// } -static MGF1_SHA1_MASK_ALGORITHM: Lazy< - rsa::pkcs8::AlgorithmIdentifier<'static>, -> = Lazy::new(|| { - rsa::pkcs8::AlgorithmIdentifier { - // id-mgf1 - oid: ID_MFG1, - // sha1 - parameters: Some( - asn1::AnyRef::from_der(&ENCODED_SHA1_HASH_ALGORITHM).unwrap(), - ), - } -}); - -// Default PSourceAlgorithm for RSAES-OAEP-params -// The default label is an empty string. -// -// pSpecifiedEmpty PSourceAlgorithm ::= { -// algorithm id-pSpecified, -// parameters EncodingParameters : emptyString -// } -// -// emptyString EncodingParameters ::= ''H -static P_SPECIFIED_EMPTY: Lazy<rsa::pkcs8::AlgorithmIdentifier<'static>> = - Lazy::new(|| { - rsa::pkcs8::AlgorithmIdentifier { - // id-pSpecified - oid: ID_P_SPECIFIED, - // EncodingParameters - parameters: Some(asn1::AnyRef::from( - asn1::OctetStringRef::new(b"").unwrap(), - )), - } - }); - -fn decode_content_tag<'a, T>( - decoder: &mut rsa::pkcs8::der::SliceReader<'a>, - tag: rsa::pkcs8::der::TagNumber, -) -> rsa::pkcs8::der::Result<Option<T>> -where - T: rsa::pkcs8::der::Decode<'a>, -{ - Ok( - rsa::pkcs8::der::asn1::ContextSpecific::<T>::decode_explicit(decoder, tag)? - .map(|field| field.value), - ) -} - -impl<'a> TryFrom<rsa::pkcs8::der::asn1::AnyRef<'a>> - for PssPrivateKeyParameters<'a> -{ - type Error = rsa::pkcs8::der::Error; - - fn try_from( - any: rsa::pkcs8::der::asn1::AnyRef<'a>, - ) -> rsa::pkcs8::der::Result<PssPrivateKeyParameters<'a>> { - any.sequence(|decoder| { - let hash_algorithm = - decode_content_tag::<rsa::pkcs8::AlgorithmIdentifier>( - decoder, - HASH_ALGORITHM_TAG, - )? - .map(TryInto::try_into) - .transpose()? - .unwrap_or(*SHA1_HASH_ALGORITHM); - - let mask_gen_algorithm = decode_content_tag::< - rsa::pkcs8::AlgorithmIdentifier, - >(decoder, MASK_GEN_ALGORITHM_TAG)? - .map(TryInto::try_into) - .transpose()? - .unwrap_or(*MGF1_SHA1_MASK_ALGORITHM); - - let salt_length = decode_content_tag::<u32>(decoder, SALT_LENGTH_TAG)? - .map(TryInto::try_into) - .transpose()? - .unwrap_or(20); - - Ok(Self { - hash_algorithm, - mask_gen_algorithm, - salt_length, - }) - }) - } -} - -// The parameters field associated with OID id-RSAES-OAEP -// Defined in RFC 3447, section A.2.1 -// -// RSAES-OAEP-params ::= SEQUENCE { -// hashAlgorithm [0] HashAlgorithm DEFAULT sha1, -// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, -// pSourceAlgorithm [2] PSourceAlgorithm DEFAULT pSpecifiedEmpty -// } -pub struct OaepPrivateKeyParameters<'a> { - pub hash_algorithm: rsa::pkcs8::AlgorithmIdentifier<'a>, - pub mask_gen_algorithm: rsa::pkcs8::AlgorithmIdentifier<'a>, - pub p_source_algorithm: rsa::pkcs8::AlgorithmIdentifier<'a>, -} - -impl<'a> TryFrom<rsa::pkcs8::der::asn1::AnyRef<'a>> - for OaepPrivateKeyParameters<'a> -{ - type Error = rsa::pkcs8::der::Error; - - fn try_from( - any: rsa::pkcs8::der::asn1::AnyRef<'a>, - ) -> rsa::pkcs8::der::Result<OaepPrivateKeyParameters<'a>> { - any.sequence(|decoder| { - let hash_algorithm = - decode_content_tag::<rsa::pkcs8::AlgorithmIdentifier>( - decoder, - HASH_ALGORITHM_TAG, - )? - .map(TryInto::try_into) - .transpose()? - .unwrap_or(*SHA1_HASH_ALGORITHM); - - let mask_gen_algorithm = decode_content_tag::< - rsa::pkcs8::AlgorithmIdentifier, - >(decoder, MASK_GEN_ALGORITHM_TAG)? - .map(TryInto::try_into) - .transpose()? - .unwrap_or(*MGF1_SHA1_MASK_ALGORITHM); - - let p_source_algorithm = decode_content_tag::< - rsa::pkcs8::AlgorithmIdentifier, - >(decoder, P_SOURCE_ALGORITHM_TAG)? - .map(TryInto::try_into) - .transpose()? - .unwrap_or(*P_SPECIFIED_EMPTY); - - Ok(Self { - hash_algorithm, - mask_gen_algorithm, - p_source_algorithm, - }) - }) - } -} - #[op] pub fn op_crypto_random_uuid(state: &mut OpState) -> Result<String, AnyError> { let maybe_seeded_rng = state.try_borrow_mut::<StdRng>(); diff --git a/ext/crypto/shared.rs b/ext/crypto/shared.rs index dec9c6d4a..2e4eabdaf 100644 --- a/ext/crypto/shared.rs +++ b/ext/crypto/shared.rs @@ -14,30 +14,6 @@ use serde::Serialize; pub const RSA_ENCRYPTION_OID: const_oid::ObjectIdentifier = const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"); -pub const SHA1_RSA_ENCRYPTION_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.5"); -pub const SHA256_RSA_ENCRYPTION_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.11"); -pub const SHA384_RSA_ENCRYPTION_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.12"); -pub const SHA512_RSA_ENCRYPTION_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.13"); -pub const RSASSA_PSS_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"); -pub const ID_SHA1_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.3.14.3.2.26"); -pub const ID_SHA256_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1"); -pub const ID_SHA384_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2"); -pub const ID_SHA512_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3"); -pub const ID_MFG1: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.8"); -pub const RSAES_OAEP_OID: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.7"); -pub const ID_P_SPECIFIED: const_oid::ObjectIdentifier = - const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.9"); pub const ID_SECP256R1_OID: const_oid::ObjectIdentifier = const_oid::ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7"); |