summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/crypto/00_crypto.js271
-rw-r--r--ext/crypto/01_webidl.js12
-rw-r--r--ext/crypto/Cargo.toml2
-rw-r--r--ext/crypto/generate_key.rs1
-rw-r--r--ext/crypto/import_key.rs321
-rw-r--r--ext/crypto/lib.deno_crypto.d.ts16
-rw-r--r--ext/crypto/lib.rs52
-rw-r--r--ext/crypto/shared.rs9
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)]