diff options
Diffstat (limited to 'ext/crypto/lib.rs')
-rw-r--r-- | ext/crypto/lib.rs | 151 |
1 files changed, 142 insertions, 9 deletions
diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index 319f26c22..cf2c379a0 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -37,8 +37,9 @@ use ring::signature::EcdsaSigningAlgorithm; use ring::signature::EcdsaVerificationAlgorithm; use ring::signature::KeyPair; use rsa::padding::PaddingScheme; -use rsa::pkcs8::FromPrivateKey; -use rsa::pkcs8::ToPrivateKey; +use rsa::pkcs1::FromRsaPrivateKey; +use rsa::pkcs1::ToRsaPrivateKey; +use rsa::pkcs8::der::asn1; use rsa::BigUint; use rsa::PublicKey; use rsa::RsaPrivateKey; @@ -81,6 +82,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_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)), ("op_crypto_subtle_digest", op_async(op_crypto_subtle_digest)), @@ -164,7 +166,7 @@ pub async fn op_crypto_generate_key( .unwrap() .map_err(|e| custom_error("DOMExceptionOperationError", e.to_string()))?; - private_key.to_pkcs8_der()?.as_ref().to_vec() + private_key.to_pkcs1_der()?.as_ref().to_vec() } Algorithm::Ecdsa => { let curve: &EcdsaSigningAlgorithm = @@ -270,7 +272,7 @@ pub async fn op_crypto_sign_key( let signature = match algorithm { Algorithm::RsassaPkcs1v15 => { - let private_key = RsaPrivateKey::from_pkcs8_der(&*args.key.data)?; + let private_key = RsaPrivateKey::from_pkcs1_der(&*args.key.data)?; let (padding, hashed) = match args .hash .ok_or_else(|| type_error("Missing argument hash".to_string()))? @@ -320,7 +322,7 @@ pub async fn op_crypto_sign_key( private_key.sign(padding, &hashed)? } Algorithm::RsaPss => { - let private_key = RsaPrivateKey::from_pkcs8_der(&*args.key.data)?; + let private_key = RsaPrivateKey::from_pkcs1_der(&*args.key.data)?; let salt_len = args .salt_length @@ -426,7 +428,7 @@ pub async fn op_crypto_verify_key( let verification = match algorithm { Algorithm::RsassaPkcs1v15 => { let public_key: RsaPublicKey = - RsaPrivateKey::from_pkcs8_der(&*args.key.data)?.to_public_key(); + RsaPrivateKey::from_pkcs1_der(&*args.key.data)?.to_public_key(); let (padding, hashed) = match args .hash .ok_or_else(|| type_error("Missing argument hash".to_string()))? @@ -483,7 +485,7 @@ pub async fn op_crypto_verify_key( .ok_or_else(|| type_error("Missing argument saltLength".to_string()))? as usize; let public_key: RsaPublicKey = - RsaPrivateKey::from_pkcs8_der(&*args.key.data)?.to_public_key(); + RsaPrivateKey::from_pkcs1_der(&*args.key.data)?.to_public_key(); let rng = OsRng; let (padding, hashed) = match args @@ -554,6 +556,137 @@ pub async fn op_crypto_verify_key( #[derive(Deserialize)] #[serde(rename_all = "camelCase")] +pub struct ExportKeyArg { + key: KeyData, + algorithm: Algorithm, + format: KeyFormat, + // RSA-PSS + hash: Option<CryptoHash>, +} + +pub async fn op_crypto_export_key( + _state: Rc<RefCell<OpState>>, + args: ExportKeyArg, + _zero_copy: Option<ZeroCopyBuf>, +) -> Result<ZeroCopyBuf, AnyError> { + let algorithm = args.algorithm; + match algorithm { + Algorithm::RsassaPkcs1v15 => { + match args.format { + KeyFormat::Pkcs8 => { + // private_key is a PKCS#1 DER-encoded private key + + let private_key = &args.key.data; + + // the PKCS#8 v1 structure + // PrivateKeyInfo ::= SEQUENCE { + // version Version, + // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + // privateKey PrivateKey, + // attributes [0] IMPLICIT Attributes OPTIONAL } + + // version is 0 when publickey is None + + let pk_info = rsa::pkcs8::PrivateKeyInfo { + attributes: None, + public_key: None, + algorithm: rsa::pkcs8::AlgorithmIdentifier { + // rsaEncryption(1) + oid: rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.1"), + // parameters field should not be ommited (None). + // It MUST have ASN.1 type NULL as per defined in RFC 3279 Section 2.3.1 + parameters: Some(asn1::Any::from(asn1::Null)), + }, + private_key, + }; + + Ok(pk_info.to_der().as_ref().to_vec().into()) + } + // TODO(@littledivy): spki + // TODO(@littledivy): jwk + _ => unreachable!(), + } + } + Algorithm::RsaPss => { + match args.format { + KeyFormat::Pkcs8 => { + // Intentionally unused but required. Not encoded into PKCS#8 (see below). + let _hash = args + .hash + .ok_or_else(|| type_error("Missing argument hash".to_string()))?; + + // private_key is a PKCS#1 DER-encoded private key + let private_key = &args.key.data; + + // version is 0 when publickey is None + + let pk_info = rsa::pkcs8::PrivateKeyInfo { + attributes: None, + public_key: None, + algorithm: rsa::pkcs8::AlgorithmIdentifier { + // Spec wants the OID to be id-RSASSA-PSS (1.2.840.113549.1.1.10) but ring and RSA do not support it. + // Instead, we use rsaEncryption (1.2.840.113549.1.1.1) as specified in RFC 3447. + // Node, Chromium and Firefox also use rsaEncryption (1.2.840.113549.1.1.1) and do not support id-RSASSA-PSS. + + // parameters are set to NULL opposed to what spec wants (see above) + oid: rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.1"), + // parameters field should not be ommited (None). + // It MUST have ASN.1 type NULL as per defined in RFC 3279 Section 2.3.1 + parameters: Some(asn1::Any::from(asn1::Null)), + }, + private_key, + }; + + Ok(pk_info.to_der().as_ref().to_vec().into()) + } + // TODO(@littledivy): spki + // TODO(@littledivy): jwk + _ => unreachable!(), + } + } + Algorithm::RsaOaep => { + match args.format { + KeyFormat::Pkcs8 => { + // Intentionally unused but required. Not encoded into PKCS#8 (see below). + let _hash = args + .hash + .ok_or_else(|| type_error("Missing argument hash".to_string()))?; + + // private_key is a PKCS#1 DER-encoded private key + let private_key = &args.key.data; + + // version is 0 when publickey is None + + let pk_info = rsa::pkcs8::PrivateKeyInfo { + attributes: None, + public_key: None, + algorithm: rsa::pkcs8::AlgorithmIdentifier { + // Spec wants the OID to be id-RSAES-OAEP (1.2.840.113549.1.1.10) but ring and RSA crate do not support it. + // Instead, we use rsaEncryption (1.2.840.113549.1.1.1) as specified in RFC 3447. + // Chromium and Firefox also use rsaEncryption (1.2.840.113549.1.1.1) and do not support id-RSAES-OAEP. + + // parameters are set to NULL opposed to what spec wants (see above) + oid: rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.1"), + // parameters field should not be ommited (None). + // It MUST have ASN.1 type NULL as per defined in RFC 3279 Section 2.3.1 + parameters: Some(asn1::Any::from(asn1::Null)), + }, + private_key, + }; + + Ok(pk_info.to_der().as_ref().to_vec().into()) + } + // TODO(@littledivy): spki + // TODO(@littledivy): jwk + _ => unreachable!(), + } + } + _ => Err(type_error("Unsupported algorithm".to_string())), + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] pub struct DeriveKeyArg { key: KeyData, algorithm: Algorithm, @@ -642,7 +775,7 @@ pub async fn op_crypto_encrypt_key( match algorithm { Algorithm::RsaOaep => { let public_key: RsaPublicKey = - RsaPrivateKey::from_pkcs8_der(&*args.key.data)?.to_public_key(); + RsaPrivateKey::from_pkcs1_der(&*args.key.data)?.to_public_key(); let label = args.label.map(|l| String::from_utf8_lossy(&*l).to_string()); let mut rng = OsRng; let padding = match args @@ -705,7 +838,7 @@ pub async fn op_crypto_decrypt_key( match algorithm { Algorithm::RsaOaep => { let private_key: RsaPrivateKey = - RsaPrivateKey::from_pkcs8_der(&*args.key.data)?; + RsaPrivateKey::from_pkcs1_der(&*args.key.data)?; let label = args.label.map(|l| String::from_utf8_lossy(&*l).to_string()); let padding = match args .hash |