diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/crypto/00_crypto.js | 271 | ||||
-rw-r--r-- | ext/crypto/01_webidl.js | 12 | ||||
-rw-r--r-- | ext/crypto/Cargo.toml | 2 | ||||
-rw-r--r-- | ext/crypto/generate_key.rs | 1 | ||||
-rw-r--r-- | ext/crypto/import_key.rs | 321 | ||||
-rw-r--r-- | ext/crypto/lib.deno_crypto.d.ts | 16 | ||||
-rw-r--r-- | ext/crypto/lib.rs | 52 | ||||
-rw-r--r-- | ext/crypto/shared.rs | 9 |
8 files changed, 661 insertions, 23 deletions
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index c8c7d9810..f15238b3a 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -108,6 +108,8 @@ "RSASSA-PKCS1-v1_5": "RsaHashedImportParams", "RSA-PSS": "RsaHashedImportParams", "RSA-OAEP": "RsaHashedImportParams", + "ECDSA": "EcImportParams", + "ECDH": "EcImportParams", "HMAC": "HmacImportParams", "HKDF": null, "PBKDF2": null, @@ -796,8 +798,9 @@ keyUsages, ); } + case "ECDH": case "ECDSA": { - return importKeyECDSA( + return importKeyEC( format, normalizedAlgorithm, keyData, @@ -1144,6 +1147,7 @@ } // 2. const hash = normalizedAlgorithm.hash.name; + // 3-8. return await core.opAsync("op_crypto_verify_key", { key: keyData, @@ -2195,13 +2199,28 @@ return key; } - function importKeyECDSA( + const SUPPORTED_EC_KEY_USAGES = { + "ECDSA": { + public: ["verify"], + private: ["sign"], + jwtUse: "sig", + }, + "ECDH": { + public: [], + private: ["deriveKey", "deriveBits"], + jwtUse: "enc", + }, + }; + + function importKeyEC( format, normalizedAlgorithm, keyData, extractable, keyUsages, ) { + const supportedUsages = SUPPORTED_EC_KEY_USAGES[normalizedAlgorithm.name]; + switch (format) { case "raw": { // 1. @@ -2221,7 +2240,7 @@ if ( ArrayPrototypeFind( keyUsages, - (u) => !ArrayPrototypeIncludes(["verify"], u), + (u) => !ArrayPrototypeIncludes(supportedUsages.public, u), ) !== undefined ) { throw new DOMException("Invalid key usages", "SyntaxError"); @@ -2229,7 +2248,7 @@ // 3. const { rawData } = core.opSync("op_crypto_import_key", { - algorithm: "ECDSA", + algorithm: normalizedAlgorithm.name, namedCurve: normalizedAlgorithm.namedCurve, }, { raw: keyData }); @@ -2238,7 +2257,7 @@ // 4-5. const algorithm = { - name: "ECDSA", + name: normalizedAlgorithm.name, namedCurve: normalizedAlgorithm.namedCurve, }; @@ -2253,6 +2272,248 @@ return key; } + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(supportedUsages.private, u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + // 2-9. + const { rawData } = core.opSync("op_crypto_import_key", { + algorithm: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }, { pkcs8: keyData }); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, rawData); + + const algorithm = { + name: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }; + + const key = constructKey( + "private", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } + case "spki": { + // 1. + if (normalizedAlgorithm.name == "ECDSA") { + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(supportedUsages.public, u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + } else if (keyUsages.length != 0) { + throw new DOMException("Key usage must be empty", "SyntaxError"); + } + + // 2-12 + const { rawData } = core.opSync("op_crypto_import_key", { + algorithm: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }, { spki: keyData }); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, rawData); + + const algorithm = { + name: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }; + + // 6-8. + const key = constructKey( + "public", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } + case "jwk": { + const jwk = keyData; + + const keyType = (jwk.d !== undefined) ? "private" : "public"; + + // 2. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(supportedUsages[keyType], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + // 3. + if (jwk.kty !== "EC") { + throw new DOMException( + "'kty' property of JsonWebKey must be 'EC'", + "DataError", + ); + } + + // 4. + if ( + keyUsages.length > 0 && jwk.use !== undefined && + jwk.use !== supportedUsages.jwkUse + ) { + throw new DOMException( + `'use' property of JsonWebKey must be '${supportedUsages.jwkUse}'`, + "DataError", + ); + } + + // 5. + // Section 4.3 of RFC7517 + if (jwk.key_ops !== undefined) { + if ( + ArrayPrototypeFind( + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), + ) !== undefined + ) { + throw new DOMException( + "'key_ops' member of JsonWebKey is invalid", + "DataError", + ); + } + + if ( + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) + ) { + throw new DOMException( + "'key_ops' member of JsonWebKey is invalid", + "DataError", + ); + } + } + + // 6. + if (jwk.ext === false && extractable === true) { + throw new DOMException( + "'ext' property of JsonWebKey must not be false if extractable is true", + "DataError", + ); + } + + // 9. + if (jwk.alg !== undefined && normalizedAlgorithm.name == "ECDSA") { + let algNamedCurve; + + switch (jwk.alg) { + case "ES256": { + algNamedCurve = "P-256"; + break; + } + case "ES384": { + algNamedCurve = "P-384"; + break; + } + case "ES512": { + algNamedCurve = "P-521"; + break; + } + default: + throw new DOMException( + "Curve algorithm not supported", + "DataError", + ); + } + + if (algNamedCurve) { + if (algNamedCurve !== normalizedAlgorithm.namedCurve) { + throw new DOMException( + "Mismatched curve algorithm", + "DataError", + ); + } + } + } + + // Validate that this is a valid public key. + if (jwk.x === undefined) { + throw new DOMException( + "'x' property of JsonWebKey is required for EC keys", + "DataError", + ); + } + if (jwk.y === undefined) { + throw new DOMException( + "'y' property of JsonWebKey is required for EC keys", + "DataError", + ); + } + + if (jwk.d !== undefined) { + // it's also a Private key + const { rawData } = core.opSync("op_crypto_import_key", { + algorithm: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }, { jwkPrivateEc: jwk }); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, rawData); + + const algorithm = { + name: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }; + + const key = constructKey( + "private", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } else { + const { rawData } = core.opSync("op_crypto_import_key", { + algorithm: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }, { jwkPublicEc: jwk }); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, rawData); + + const algorithm = { + name: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }; + + const key = constructKey( + "public", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } + } default: throw new DOMException("Not implemented", "NotSupportedError"); } diff --git a/ext/crypto/01_webidl.js b/ext/crypto/01_webidl.js index 7b1fc4e08..a6470ce9e 100644 --- a/ext/crypto/01_webidl.js +++ b/ext/crypto/01_webidl.js @@ -130,6 +130,18 @@ webidl.converters.EcKeyGenParams = webidl .createDictionaryConverter("EcKeyGenParams", dictEcKeyGenParams); + const dictEcImportParams = [ + ...dictAlgorithm, + { + key: "namedCurve", + converter: webidl.converters.NamedCurve, + required: true, + }, + ]; + + webidl.converters.EcImportParams = webidl + .createDictionaryConverter("EcImportParams", dictEcImportParams); + const dictAesKeyGenParams = [ ...dictAlgorithm, { diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index d5c2a1acb..ced20fa7d 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -19,7 +19,7 @@ base64 = "0.13.0" block-modes = "0.8.1" deno_core = { version = "0.110.0", path = "../../core" } deno_web = { version = "0.59.0", path = "../web" } -elliptic-curve = "0.10.6" +elliptic-curve = { version = "0.10.6", features = ["std", "pem"] } lazy_static = "1.4.0" num-traits = "0.2.14" p256 = { version = "0.9.0", features = ["ecdh"] } diff --git a/ext/crypto/generate_key.rs b/ext/crypto/generate_key.rs index 3f9df44a1..7ed841297 100644 --- a/ext/crypto/generate_key.rs +++ b/ext/crypto/generate_key.rs @@ -87,6 +87,7 @@ fn generate_key_ec(named_curve: EcNamedCurve) -> Result<Vec<u8>, AnyError> { let curve = match named_curve { EcNamedCurve::P256 => &ring::signature::ECDSA_P256_SHA256_FIXED_SIGNING, EcNamedCurve::P384 => &ring::signature::ECDSA_P384_SHA384_FIXED_SIGNING, + _ => return Err(not_supported_error("Unsupported named curve")), }; let rng = ring::rand::SystemRandom::new(); diff --git a/ext/crypto/import_key.rs b/ext/crypto/import_key.rs index 5b5a707a6..c658d7c12 100644 --- a/ext/crypto/import_key.rs +++ b/ext/crypto/import_key.rs @@ -1,10 +1,14 @@ use deno_core::error::AnyError; use deno_core::OpState; use deno_core::ZeroCopyBuf; +use elliptic_curve::pkcs8::der::Decodable as Pkcs8Decodable; +use elliptic_curve::pkcs8::PrivateKeyInfo; +use elliptic_curve::sec1::ToEncodedPoint; +use p256::pkcs8::FromPrivateKey; +use p256::pkcs8::ToPrivateKey; use rsa::pkcs1::UIntBytes; use serde::Deserialize; use serde::Serialize; -use spki::der::Decodable; use spki::der::Encodable; use crate::shared::*; @@ -34,6 +38,15 @@ pub enum KeyData { dq: String, qi: String, }, + JwkPublicEc { + x: String, + y: String, + }, + JwkPrivateEc { + x: String, + y: String, + d: String, + }, } #[derive(Deserialize)] @@ -238,7 +251,7 @@ fn import_key_rsassa( } KeyData::Pkcs8(data) => { // 2-3. - let pk_info = rsa::pkcs8::PrivateKeyInfo::from_der(&data) + let pk_info = PrivateKeyInfo::from_der(&data) .map_err(|e| data_error(e.to_string()))?; // 4-5. @@ -389,7 +402,7 @@ fn import_key_rsapss( } KeyData::Pkcs8(data) => { // 2-3. - let pk_info = rsa::pkcs8::PrivateKeyInfo::from_der(&data) + let pk_info = PrivateKeyInfo::from_der(&data) .map_err(|e| data_error(e.to_string()))?; // 4-5. @@ -568,7 +581,7 @@ fn import_key_rsaoaep( } KeyData::Pkcs8(data) => { // 2-3. - let pk_info = rsa::pkcs8::PrivateKeyInfo::from_der(&data) + let pk_info = PrivateKeyInfo::from_der(&data) .map_err(|e| data_error(e.to_string()))?; // 4-5. @@ -657,11 +670,137 @@ fn import_key_rsaoaep( } } +fn decode_b64url_to_field_bytes<C: elliptic_curve::Curve>( + b64: &str, +) -> Result<elliptic_curve::FieldBytes<C>, deno_core::anyhow::Error> { + jwt_b64_int_or_err!(val, b64, "invalid b64 coordinate"); + + let mut bytes = elliptic_curve::FieldBytes::<C>::default(); + let val = val.as_bytes(); + if val.len() != bytes.len() { + return Err(data_error("invalid b64 coordinate")); + } + bytes.copy_from_slice(val); + + Ok(bytes) +} + +fn import_key_ec_jwk_to_point( + x: String, + y: String, + named_curve: EcNamedCurve, +) -> Result<Vec<u8>, deno_core::anyhow::Error> { + let point_bytes = match named_curve { + EcNamedCurve::P256 => { + let x = decode_b64url_to_field_bytes::<p256::NistP256>(&x)?; + let y = decode_b64url_to_field_bytes::<p256::NistP256>(&y)?; + + p256::EncodedPoint::from_affine_coordinates(&x, &y, false).to_bytes() + } + EcNamedCurve::P384 => { + let x = decode_b64url_to_field_bytes::<p384::NistP384>(&x)?; + let y = decode_b64url_to_field_bytes::<p384::NistP384>(&y)?; + + p384::EncodedPoint::from_affine_coordinates(&x, &y, false).to_bytes() + } + _ => return Err(not_supported_error("Unsupported named curve")), + }; + + Ok(point_bytes.to_vec()) +} + +fn import_key_ec_jwk( + key_data: KeyData, + named_curve: EcNamedCurve, +) -> Result<ImportKeyResult, deno_core::anyhow::Error> { + match key_data { + KeyData::JwkPublicEc { x, y } => { + let point_bytes = import_key_ec_jwk_to_point(x, y, named_curve)?; + + Ok(ImportKeyResult::Ec { + raw_data: RawKeyData::Public(point_bytes.to_vec().into()), + }) + } + KeyData::JwkPrivateEc { d, x, y } => { + let point_bytes = import_key_ec_jwk_to_point(x, y, named_curve)?; + + let secret_key_der = match named_curve { + EcNamedCurve::P256 => { + let d = decode_b64url_to_field_bytes::<p256::NistP256>(&d)?; + let secret_key = p256::SecretKey::from_bytes(&d)?; + ToPrivateKey::to_pkcs8_der(&secret_key).unwrap() + } + //@todo(sean) - build p384 secret key from jwk, when crate implements to_pkcs8_der + //Problem: p384 crate does not implement ProjectiveArithmetic + /*EcNamedCurve::P384 => { + let secret_key = p384::SecretKey::from_be_bytes(&d)?; + + secret_key.to_pkcs8_der().unwrap() + }*/ + _ => return Err(not_supported_error("Unsupported named curve")), + }; + + let oid = + <p256::NistP256 as p256::elliptic_curve::AlgorithmParameters>::OID; + + let pki = p256::pkcs8::PrivateKeyInfo::new( + p256::pkcs8::AlgorithmIdentifier { + oid, + parameters: None, + }, + secret_key_der.as_ref(), + ); + + let pki = p256::pkcs8::PrivateKeyInfo { + public_key: Some(&point_bytes), + ..pki + }; + + Ok(ImportKeyResult::Ec { + raw_data: RawKeyData::Private(pki.private_key.to_vec().into()), + }) + } + _ => unreachable!(), + } +} + +pub struct ECParametersPkcs8 { + pub named_curve_alg: p256::pkcs8::der::asn1::ObjectIdentifier, +} + +impl<'a> TryFrom<p256::pkcs8::der::asn1::Any<'a>> for ECParametersPkcs8 { + type Error = p256::pkcs8::der::Error; + + fn try_from( + any: p256::pkcs8::der::asn1::Any<'a>, + ) -> p256::pkcs8::der::Result<ECParametersPkcs8> { + let x = any.oid()?; + + Ok(Self { named_curve_alg: x }) + } +} + +pub struct ECParametersSpki { + pub named_curve_alg: spki::der::asn1::ObjectIdentifier, +} + +impl<'a> TryFrom<spki::der::asn1::Any<'a>> for ECParametersSpki { + type Error = spki::der::Error; + + fn try_from( + any: spki::der::asn1::Any<'a>, + ) -> spki::der::Result<ECParametersSpki> { + let x = any.oid()?; + + Ok(Self { named_curve_alg: x }) + } +} + fn import_key_ec( key_data: KeyData, named_curve: EcNamedCurve, ) -> Result<ImportKeyResult, AnyError> { - Ok(match key_data { + match key_data { KeyData::Raw(data) => { // The point is parsed and validated, ultimately the original data is // returned though. @@ -684,13 +823,179 @@ fn import_key_ec( return Err(data_error("invalid P-384 eliptic curve point")); } } + _ => return Err(not_supported_error("Unsupported named curve")), }; - ImportKeyResult::Ec { + Ok(ImportKeyResult::Ec { raw_data: RawKeyData::Public(data), + }) + } + KeyData::Pkcs8(data) => { + // 2-3. + let pk_info = PrivateKeyInfo::from_der(&data) + .map_err(|e| data_error(e.to_string()))?; + + // 4-5. + let alg = pk_info.algorithm.oid; + // id-ecPublicKey + if alg != elliptic_curve::ALGORITHM_OID { + return Err(data_error("unsupported algorithm")); } + + // 5-7. + let params = ECParametersPkcs8::try_from( + pk_info + .algorithm + .parameters + .ok_or_else(|| data_error("malformed parameters"))?, + ) + .map_err(|_| data_error("malformed parameters"))?; + + // 8-9. + let pk_named_curve = match params.named_curve_alg { + // id-secp256r1 + ID_SECP256R1_OID => Some(EcNamedCurve::P256), + // id-secp384r1 + ID_SECP384R1_OID => Some(EcNamedCurve::P384), + // id-secp384r1 + ID_SECP521R1_OID => Some(EcNamedCurve::P521), + _ => None, + }; + + // 10. + if let Some(pk_named_curve) = pk_named_curve { + match pk_named_curve { + EcNamedCurve::P256 => { + let secret_key = + p256::SecretKey::from_pkcs8_der(&data).map_err(|_| { + data_error("invalid P-256 elliptic curve PKCS8 data") + })?; + + let point = + secret_key.public_key().as_affine().to_encoded_point(false); + + // 12 - not sure if this is correct. + if point.is_identity() { + return Err(data_error("Invalid key data")); + } + } + //@todo(sean) Validate P384 secret-key on import(pkcs8) + //Problem: Nist384 Curve from p384 crate does not implement ProjectiveArithmetic + //so cannot extract PublicKey from SecretKey. + /*EcNamedCurve::P384 => { + let secret_key = + p384::SecretKey::from_pkcs8_der(&data).unwrap(); + + let point = + secret_key.public_key().as_affine().to_encoded_point(false); + // 3. + if point.is_identity() { + return Err(type_error("Invalid key data".to_string())); + } + }*/ + _ => return Err(data_error("Unsupported named curve")), + } + // 11. + if named_curve != pk_named_curve { + return Err(data_error("curve mismatch")); + } + } else { + return Err(data_error("Unsupported named curve")); + } + + Ok(ImportKeyResult::Ec { + raw_data: RawKeyData::Private(data), + }) } - _ => return Err(unsupported_format()), - }) + KeyData::Spki(data) => { + // 2-3. + let pk_info = spki::SubjectPublicKeyInfo::from_der(&data) + .map_err(|e| data_error(e.to_string()))?; + + // 4. + let alg = pk_info.algorithm.oid; + // id-ecPublicKey + if alg != elliptic_curve::ALGORITHM_OID { + return Err(data_error("unsupported algorithm")); + } + + // 5-7. + let params = ECParametersSpki::try_from( + pk_info + .algorithm + .parameters + .ok_or_else(|| data_error("malformed parameters"))?, + ) + .map_err(|_| data_error("malformed parameters"))?; + + // 8-9. + let named_curve_alg = params.named_curve_alg; + let pk_named_curve = match named_curve_alg { + // id-secp256r1 + ID_SECP256R1_OID => Some(EcNamedCurve::P256), + // id-secp384r1 + ID_SECP384R1_OID => Some(EcNamedCurve::P384), + // id-secp521r1 + ID_SECP521R1_OID => Some(EcNamedCurve::P521), + _ => None, + }; + + // 10. + let encoded_key; + + if let Some(pk_named_curve) = pk_named_curve { + let pk = pk_info.subject_public_key; + + encoded_key = pk.to_vec(); + + let bytes_consumed = match named_curve { + EcNamedCurve::P256 => { + let point = + p256::EncodedPoint::from_bytes(&*encoded_key).map_err(|_| { + data_error("invalid P-256 eliptic curve SPKI data") + })?; + + if point.is_identity() { + return Err(data_error("invalid P-256 eliptic curve point")); + } + + point.as_bytes().len() + } + EcNamedCurve::P384 => { + let point = + p384::EncodedPoint::from_bytes(&*encoded_key).map_err(|_| { + data_error("invalid P-384 eliptic curve SPKI data") + })?; + + if point.is_identity() { + return Err(data_error("invalid P-384 eliptic curve point")); + } + + point.as_bytes().len() + } + _ => return Err(not_supported_error("Unsupported named curve")), + }; + + if bytes_consumed != pk_info.subject_public_key.len() { + return Err(data_error("public key is invalid (too long)")); + } + + // 11. + if named_curve != pk_named_curve { + return Err(data_error("curve mismatch")); + } + } else { + return Err(data_error("Unsupported named curve")); + } + + Ok(ImportKeyResult::Ec { + raw_data: RawKeyData::Public(encoded_key.to_vec().into()), + }) + } + KeyData::JwkPublicEc { .. } | KeyData::JwkPrivateEc { .. } => { + import_key_ec_jwk(key_data, named_curve) + } + _ => Err(unsupported_format()), + } } fn import_key_aes(key_data: KeyData) -> Result<ImportKeyResult, AnyError> { diff --git a/ext/crypto/lib.deno_crypto.d.ts b/ext/crypto/lib.deno_crypto.d.ts index 7681a56ab..6a3255745 100644 --- a/ext/crypto/lib.deno_crypto.d.ts +++ b/ext/crypto/lib.deno_crypto.d.ts @@ -71,6 +71,10 @@ interface EcKeyGenParams extends Algorithm { namedCurve: NamedCurve; } +interface EcImportParams extends Algorithm { + namedCurve: NamedCurve; +} + interface EcdsaParams extends Algorithm { hash: HashAlgorithmIdentifier; } @@ -195,14 +199,22 @@ interface SubtleCrypto { importKey( format: "jwk", keyData: JsonWebKey, - algorithm: AlgorithmIdentifier | HmacImportParams | RsaHashedImportParams, + algorithm: + | AlgorithmIdentifier + | HmacImportParams + | RsaHashedImportParams + | EcImportParams, extractable: boolean, keyUsages: KeyUsage[], ): Promise<CryptoKey>; importKey( format: Exclude<KeyFormat, "jwk">, keyData: BufferSource, - algorithm: AlgorithmIdentifier | HmacImportParams | RsaHashedImportParams, + algorithm: + | AlgorithmIdentifier + | HmacImportParams + | RsaHashedImportParams + | EcImportParams, extractable: boolean, keyUsages: KeyUsage[], ): Promise<CryptoKey>; diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index bb7ad5f32..b46b27d74 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -19,6 +19,8 @@ use std::rc::Rc; use block_modes::BlockMode; use lazy_static::lazy_static; use num_traits::cast::FromPrimitive; +use p256::elliptic_curve::sec1::FromEncodedPoint; +use p256::pkcs8::FromPrivateKey; use rand::rngs::OsRng; use rand::rngs::StdRng; use rand::thread_rng; @@ -40,7 +42,6 @@ use rsa::pkcs1::der::Encodable; use rsa::pkcs1::FromRsaPrivateKey; use rsa::pkcs1::FromRsaPublicKey; use rsa::pkcs8::der::asn1; -use rsa::pkcs8::FromPrivateKey; use rsa::BigUint; use rsa::PublicKey; use rsa::RsaPrivateKey; @@ -443,8 +444,18 @@ pub async fn op_crypto_verify_key( let verify_alg: &EcdsaVerificationAlgorithm = args.named_curve.ok_or_else(not_supported)?.try_into()?; - let private_key = EcdsaKeyPair::from_pkcs8(signing_alg, &*args.key.data)?; - let public_key_bytes = private_key.public_key().as_ref(); + let private_key; + + let public_key_bytes = match args.key.r#type { + KeyType::Private => { + private_key = EcdsaKeyPair::from_pkcs8(signing_alg, &*args.key.data)?; + + private_key.public_key().as_ref() + } + KeyType::Public => &*args.key.data, + _ => return Err(type_error("Invalid Key format".to_string())), + }; + let public_key = ring::signature::UnparsedPublicKey::new(verify_alg, public_key_bytes); @@ -507,13 +518,40 @@ pub async fn op_crypto_derive_bits( let public_key = args .public_key - .ok_or_else(|| type_error("Missing argument publicKey".to_string()))?; + .ok_or_else(|| type_error("Missing argument publicKey"))?; match named_curve { CryptoNamedCurve::P256 => { - let secret_key = p256::SecretKey::from_pkcs8_der(&args.key.data)?; - let public_key = - p256::SecretKey::from_pkcs8_der(&public_key.data)?.public_key(); + let secret_key = p256::SecretKey::from_pkcs8_der(&args.key.data) + .map_err(|_| type_error("Unexpected error decoding private key"))?; + + let public_key = match public_key.r#type { + KeyType::Private => { + p256::SecretKey::from_pkcs8_der(&public_key.data) + .map_err(|_| { + type_error("Unexpected error decoding private key") + })? + .public_key() + } + KeyType::Public => { + let point = p256::EncodedPoint::from_bytes(public_key.data) + .map_err(|_| { + type_error("Unexpected error decoding private key") + })?; + + let pk: Option<p256::PublicKey> = + p256::PublicKey::from_encoded_point(&point); + + if let Some(pk) = pk { + pk + } else { + return Err(type_error( + "Unexpected error decoding private key", + )); + } + } + _ => unreachable!(), + }; let shared_secret = p256::elliptic_curve::ecdh::diffie_hellman( secret_key.to_secret_scalar(), diff --git a/ext/crypto/shared.rs b/ext/crypto/shared.rs index 1a0703b26..3b32bb2a2 100644 --- a/ext/crypto/shared.rs +++ b/ext/crypto/shared.rs @@ -37,6 +37,13 @@ pub const RSAES_OAEP_OID: rsa::pkcs8::ObjectIdentifier = pub const ID_P_SPECIFIED: rsa::pkcs8::ObjectIdentifier = rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.9"); +pub const ID_SECP256R1_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.10045.3.1.7"); +pub const ID_SECP384R1_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.3.132.0.34"); +pub const ID_SECP521R1_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.3.132.0.35"); + #[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] pub enum ShaHash { #[serde(rename = "SHA-1")] @@ -55,6 +62,8 @@ pub enum EcNamedCurve { P256, #[serde(rename = "P-384")] P384, + #[serde(rename = "P-521")] + P521, } #[derive(Serialize, Deserialize)] |