diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 9134f486bf..5e918cc7d4 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -128,6 +128,7 @@ "AES-CBC": null, "AES-GCM": null, "AES-KW": null, + "Ed25519": null, "X25519": null, }, "deriveBits": { @@ -1049,6 +1050,10 @@ result = exportKeyEd25519(format, key, innerKey); break; } + case "X25519": { + result = exportKeyX25519(format, key, innerKey); + break; + } case "AES-CTR": case "AES-CBC": case "AES-GCM": @@ -2142,7 +2147,7 @@ return constructKey( "public", extractable, - [], + usageIntersection(keyUsages, recognisedUsages), algorithm, handle, ); @@ -2173,7 +2178,7 @@ return constructKey( "public", extractable, - [], + usageIntersection(keyUsages, recognisedUsages), algorithm, handle, ); @@ -2204,7 +2209,7 @@ return constructKey( "private", extractable, - [], + usageIntersection(keyUsages, recognisedUsages), algorithm, handle, ); @@ -2299,7 +2304,7 @@ // 9. if (jwk.d !== undefined) { // https://www.rfc-editor.org/rfc/rfc8037#section-2 - const privateKeyData = ops.op_crypto_base64url(jwk.d); + const privateKeyData = ops.op_crypto_base64url_decode(jwk.d); const handle = {}; WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); @@ -2311,13 +2316,13 @@ return constructKey( "private", extractable, - [], + usageIntersection(keyUsages, recognisedUsages), algorithm, handle, ); } else { // https://www.rfc-editor.org/rfc/rfc8037#section-2 - const publicKeyData = ops.op_crypto_base64url(jwk.d); + const publicKeyData = ops.op_crypto_base64url_decode(jwk.x); const handle = {}; WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); @@ -2329,7 +2334,7 @@ return constructKey( "public", extractable, - [], + usageIntersection(keyUsages, recognisedUsages), algorithm, handle, ); @@ -2422,7 +2427,7 @@ return constructKey( "private", extractable, - [], + usageIntersection(keyUsages, recognisedUsages), algorithm, handle, ); @@ -2438,7 +2443,7 @@ keyUsages, (u) => !ArrayPrototypeIncludes( - SUPPORTED_KEY_USAGES["X25519"].private, + ["deriveKey", "deriveBits"], u, ), ) !== undefined @@ -2504,7 +2509,7 @@ // 9. if (jwk.d !== undefined) { // https://www.rfc-editor.org/rfc/rfc8037#section-2 - const privateKeyData = ops.op_crypto_base64url(jwk.d); + const privateKeyData = ops.op_crypto_base64url_decode(jwk.d); const handle = {}; WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); @@ -2516,13 +2521,13 @@ return constructKey( "private", extractable, - [], + usageIntersection(keyUsages, ["deriveKey", "deriveBits"]), algorithm, handle, ); } else { // https://www.rfc-editor.org/rfc/rfc8037#section-2 - const publicKeyData = ops.op_crypto_base64url(jwk.d); + const publicKeyData = ops.op_crypto_base64url_decode(jwk.x); const handle = {}; WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); @@ -3310,9 +3315,6 @@ private: ["deriveKey", "deriveBits"], jwkUse: "enc", }, - "X25519": { - private: ["deriveKey", "deriveBits"], - }, }; function importKeyRSA( @@ -4046,13 +4048,16 @@ ); } - const pkcs8Der = ops.op_export_pkcs8_ed25519(innerKey); + const pkcs8Der = ops.op_export_pkcs8_ed25519( + new Uint8Array([0x04, 0x22, ...innerKey]), + ); + pkcs8Der[15] = 0x20; return pkcs8Der.buffer; } case "jwk": { const x = key[_type] === "private" ? ops.op_jwk_x_ed25519(innerKey) - : ops.op_crypto_base64url(innerKey); + : ops.op_crypto_base64url_encode(innerKey); const jwk = { kty: "OKP", alg: "EdDSA", @@ -4062,7 +4067,7 @@ ext: key[_extractable], }; if (key[_type] === "private") { - jwk.d = ops.op_crypto_base64url(innerKey); + jwk.d = ops.op_crypto_base64url_encode(innerKey); } return jwk; } @@ -4071,6 +4076,66 @@ } } + function exportKeyX25519(format, key, innerKey) { + switch (format) { + case "raw": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + // 2-3. + return innerKey.buffer; + } + case "spki": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + const spkiDer = ops.op_export_spki_x25519(innerKey); + return spkiDer.buffer; + } + case "pkcs8": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + const pkcs8Der = ops.op_export_pkcs8_x25519( + new Uint8Array([0x04, 0x22, ...innerKey]), + ); + pkcs8Der[15] = 0x20; + return pkcs8Der.buffer; + } + case "jwk": { + if (key[_type] === "private") { + throw new DOMException("Not implemented", "NotSupportedError"); + } + const x = ops.op_crypto_base64url_encode(innerKey); + const jwk = { + kty: "OKP", + crv: "X25519", + x, + "key_ops": key.usages, + ext: key[_extractable], + }; + return jwk; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + function exportKeyEC(format, key, innerKey) { switch (format) { case "raw": { @@ -4391,7 +4456,10 @@ // 7. if (length === null) { return secret.buffer; - } else if (secret.length * 8 < length) { + } else if ( + length === 0 || secret.buffer.byteLength * 8 < length || + secret.length * 8 < length + ) { throw new DOMException("Invalid length", "OperationError"); } else { return secret.subarray(0, length / 8).buffer; diff --git a/ext/crypto/ed25519.rs b/ext/crypto/ed25519.rs index a8060cae1d..b7ff99d8b1 100644 --- a/ext/crypto/ed25519.rs +++ b/ext/crypto/ed25519.rs @@ -66,6 +66,7 @@ pub fn op_import_spki_ed25519(key_data: &[u8], out: &mut [u8]) -> bool { #[op(fast)] pub fn op_import_pkcs8_ed25519(key_data: &[u8], out: &mut [u8]) -> bool { // 2-3. + // This should probably use OneAsymmetricKey instead let pk_info = match PrivateKeyInfo::from_der(key_data) { Ok(pk_info) => pk_info, Err(_) => return false, @@ -81,10 +82,10 @@ pub fn op_import_pkcs8_ed25519(key_data: &[u8], out: &mut [u8]) -> bool { } // 6. // CurvePrivateKey ::= OCTET STRING - if pk_info.private_key.len() != 32 { + if pk_info.private_key.len() != 34 { return false; } - out.copy_from_slice(pk_info.private_key); + out.copy_from_slice(&pk_info.private_key[2..]); true } @@ -103,6 +104,7 @@ pub fn op_export_spki_ed25519(pubkey: &[u8]) -> Result { #[op] pub fn op_export_pkcs8_ed25519(pkey: &[u8]) -> Result { + // This should probably use OneAsymmetricKey instead let pk_info = rsa::pkcs8::PrivateKeyInfo { public_key: None, algorithm: rsa::pkcs8::AlgorithmIdentifier { diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index 0f1ee565bf..5f6445f49a 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -100,7 +100,8 @@ pub fn init(maybe_seed: Option) -> Extension { op_crypto_random_uuid::decl(), op_crypto_wrap_key::decl(), op_crypto_unwrap_key::decl(), - op_crypto_base64url::decl(), + op_crypto_base64url_decode::decl(), + op_crypto_base64url_encode::decl(), x25519::op_generate_x25519_keypair::decl(), x25519::op_derive_bits_x25519::decl(), x25519::op_import_spki_x25519::decl(), @@ -113,6 +114,8 @@ pub fn init(maybe_seed: Option) -> Extension { ed25519::op_export_spki_ed25519::decl(), ed25519::op_export_pkcs8_ed25519::decl(), ed25519::op_jwk_x_ed25519::decl(), + x25519::op_export_spki_x25519::decl(), + x25519::op_export_pkcs8_x25519::decl(), ]) .state(move |state| { if let Some(seed) = maybe_seed { @@ -124,12 +127,18 @@ pub fn init(maybe_seed: Option) -> Extension { } #[op] -pub fn op_crypto_base64url(data: String) -> ZeroCopyBuf { +pub fn op_crypto_base64url_decode(data: String) -> ZeroCopyBuf { let data: Vec = - base64::encode_config(data, base64::URL_SAFE_NO_PAD).into(); + base64::decode_config(data, base64::URL_SAFE_NO_PAD).unwrap(); data.into() } +#[op] +pub fn op_crypto_base64url_encode(data: ZeroCopyBuf) -> String { + let data: String = base64::encode_config(data, base64::URL_SAFE_NO_PAD); + data +} + #[op] pub fn op_crypto_get_random_values( state: &mut OpState, diff --git a/ext/crypto/x25519.rs b/ext/crypto/x25519.rs index b606f4ca0b..1e64e99092 100644 --- a/ext/crypto/x25519.rs +++ b/ext/crypto/x25519.rs @@ -1,10 +1,13 @@ use curve25519_dalek::montgomery::MontgomeryPoint; +use deno_core::error::AnyError; use deno_core::op; +use deno_core::ZeroCopyBuf; use elliptic_curve::pkcs8::PrivateKeyInfo; use elliptic_curve::subtle::ConstantTimeEq; use rand::rngs::OsRng; use rand::RngCore; use spki::der::Decode; +use spki::der::Encode; #[op(fast)] pub fn op_generate_x25519_keypair(pkey: &mut [u8], pubkey: &mut [u8]) { @@ -66,6 +69,7 @@ pub fn op_import_spki_x25519(key_data: &[u8], out: &mut [u8]) -> bool { #[op(fast)] pub fn op_import_pkcs8_x25519(key_data: &[u8], out: &mut [u8]) -> bool { // 2-3. + // This should probably use OneAsymmetricKey instead let pk_info = match PrivateKeyInfo::from_der(key_data) { Ok(pk_info) => pk_info, Err(_) => return false, @@ -81,9 +85,38 @@ pub fn op_import_pkcs8_x25519(key_data: &[u8], out: &mut [u8]) -> bool { } // 6. // CurvePrivateKey ::= OCTET STRING - if pk_info.private_key.len() != 32 { + if pk_info.private_key.len() != 34 { return false; } - out.copy_from_slice(pk_info.private_key); + out.copy_from_slice(&pk_info.private_key[2..]); true } + +#[op] +pub fn op_export_spki_x25519(pubkey: &[u8]) -> Result { + let key_info = spki::SubjectPublicKeyInfo { + algorithm: spki::AlgorithmIdentifier { + // id-X25519 + oid: X25519_OID, + parameters: None, + }, + subject_public_key: pubkey, + }; + Ok(key_info.to_vec()?.into()) +} + +#[op] +pub fn op_export_pkcs8_x25519(pkey: &[u8]) -> Result { + // This should probably use OneAsymmetricKey instead + let pk_info = rsa::pkcs8::PrivateKeyInfo { + public_key: None, + algorithm: rsa::pkcs8::AlgorithmIdentifier { + // id-X25519 + oid: X25519_OID, + parameters: None, + }, + private_key: pkey, // OCTET STRING + }; + + Ok(pk_info.to_vec()?.into()) +} diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json index f79310d07f..98ce80dbff 100644 --- a/tools/wpt/expectation.json +++ b/tools/wpt/expectation.json @@ -780,14 +780,6 @@ "symmetric_importKey.https.any.html": true, "symmetric_importKey.https.any.worker.html": true, "okp_importKey.https.any.html": [ - "Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, true, [])", - "Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, true, [])", - "Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, true, [sign])", - "Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign])", - "Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, false, [])", - "Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, false, [])", - "Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, false, [sign])", - "Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), {name: Ed25519}, false, [sign])", "Good parameters: Ed448 bits (spki, buffer(69), {name: Ed448}, true, [])", "Good parameters: Ed448 bits (jwk, object(kty, crv, x), {name: Ed448}, true, [])", "Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, true, [sign])", @@ -796,18 +788,9 @@ "Good parameters: Ed448 bits (jwk, object(kty, crv, x), {name: Ed448}, false, [])", "Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, false, [sign])", "Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign])", - "Good parameters: X25519 bits (spki, buffer(44), {name: X25519}, true, [])", - "Good parameters: X25519 bits (jwk, object(kty, crv, x), {name: X25519}, true, [])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveKey])", "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveBits, deriveKey])", "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits, deriveKey])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveBits])", "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])", - "Good parameters: X25519 bits (jwk, object(kty, crv, x), {name: X25519}, false, [])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveKey])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveBits, deriveKey])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveBits])", "Good parameters: X448 bits (spki, buffer(68), {name: X448}, true, [])", "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, true, [])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])", @@ -826,14 +809,6 @@ "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits])" ], "okp_importKey.https.any.worker.html": [ - "Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, true, [])", - "Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, true, [])", - "Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, true, [sign])", - "Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign])", - "Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, false, [])", - "Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, false, [])", - "Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, false, [sign])", - "Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), {name: Ed25519}, false, [sign])", "Good parameters: Ed448 bits (spki, buffer(69), {name: Ed448}, true, [])", "Good parameters: Ed448 bits (jwk, object(kty, crv, x), {name: Ed448}, true, [])", "Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, true, [sign])", @@ -842,18 +817,9 @@ "Good parameters: Ed448 bits (jwk, object(kty, crv, x), {name: Ed448}, false, [])", "Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, false, [sign])", "Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign])", - "Good parameters: X25519 bits (spki, buffer(44), {name: X25519}, true, [])", - "Good parameters: X25519 bits (jwk, object(kty, crv, x), {name: X25519}, true, [])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveKey])", "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveBits, deriveKey])", "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits, deriveKey])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveBits])", "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])", - "Good parameters: X25519 bits (jwk, object(kty, crv, x), {name: X25519}, false, [])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveKey])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveBits, deriveKey])", - "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveBits])", "Good parameters: X448 bits (spki, buffer(68), {name: X448}, true, [])", "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, true, [])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])", @@ -948,10 +914,10 @@ "importVectorKeys step: ECDSA P-521 with SHA-256 verification failure due to altered plaintext", "importVectorKeys step: ECDSA P-521 with SHA-384 verification failure due to altered plaintext", "importVectorKeys step: ECDSA P-521 with SHA-512 verification failure due to altered plaintext", - "importVectorKeys step: ECDSA P-521 with SHA-512 signing with wrong algorithm name", "importVectorKeys step: ECDSA P-521 with SHA-1 signing with wrong algorithm name", "importVectorKeys step: ECDSA P-521 with SHA-256 signing with wrong algorithm name", "importVectorKeys step: ECDSA P-521 with SHA-384 signing with wrong algorithm name", + "importVectorKeys step: ECDSA P-521 with SHA-512 signing with wrong algorithm name", "importVectorKeys step: ECDSA P-521 with SHA-1 verifying with wrong algorithm name", "importVectorKeys step: ECDSA P-521 with SHA-256 verifying with wrong algorithm name", "importVectorKeys step: ECDSA P-521 with SHA-384 verifying with wrong algorithm name", @@ -1046,55 +1012,31 @@ "rsa_pss.https.any.html": true, "rsa_pss.https.any.worker.html": true, "eddsa.https.any.html": [ - "importVectorKeys step: EdDSA Ed25519 verification", "importVectorKeys step: EdDSA Ed448 verification", - "importVectorKeys step: EdDSA Ed25519 verification with altered signature after call", "importVectorKeys step: EdDSA Ed448 verification with altered signature after call", - "importVectorKeys step: EdDSA Ed25519 with altered data after call", "importVectorKeys step: EdDSA Ed448 with altered data after call", - "importVectorKeys step: EdDSA Ed25519 using privateKey to verify", "importVectorKeys step: EdDSA Ed448 using privateKey to verify", - "importVectorKeys step: EdDSA Ed25519 using publicKey to sign", "importVectorKeys step: EdDSA Ed448 using publicKey to sign", - "importVectorKeys step: EdDSA Ed25519 no verify usage", "importVectorKeys step: EdDSA Ed448 no verify usage", - "importVectorKeys step: EdDSA Ed25519 round trip", "importVectorKeys step: EdDSA Ed448 round trip", - "importVectorKeys step: EdDSA Ed25519 verification failure due to altered signature", "importVectorKeys step: EdDSA Ed448 verification failure due to altered signature", - "importVectorKeys step: EdDSA Ed25519 verification failure due to shortened signature", "importVectorKeys step: EdDSA Ed448 verification failure due to shortened signature", - "importVectorKeys step: EdDSA Ed25519 verification failure due to altered data", "importVectorKeys step: EdDSA Ed448 verification failure due to altered data", - "importVectorKeys step: EdDSA Ed25519 signing with wrong algorithm name", "importVectorKeys step: EdDSA Ed448 signing with wrong algorithm name", - "importVectorKeys step: EdDSA Ed25519 verifying with wrong algorithm name", "importVectorKeys step: EdDSA Ed448 verifying with wrong algorithm name" ], "eddsa.https.any.worker.html": [ - "importVectorKeys step: EdDSA Ed25519 verification", "importVectorKeys step: EdDSA Ed448 verification", - "importVectorKeys step: EdDSA Ed25519 verification with altered signature after call", "importVectorKeys step: EdDSA Ed448 verification with altered signature after call", - "importVectorKeys step: EdDSA Ed25519 with altered data after call", "importVectorKeys step: EdDSA Ed448 with altered data after call", - "importVectorKeys step: EdDSA Ed25519 using privateKey to verify", "importVectorKeys step: EdDSA Ed448 using privateKey to verify", - "importVectorKeys step: EdDSA Ed25519 using publicKey to sign", "importVectorKeys step: EdDSA Ed448 using publicKey to sign", - "importVectorKeys step: EdDSA Ed25519 no verify usage", "importVectorKeys step: EdDSA Ed448 no verify usage", - "importVectorKeys step: EdDSA Ed25519 round trip", "importVectorKeys step: EdDSA Ed448 round trip", - "importVectorKeys step: EdDSA Ed25519 verification failure due to altered signature", "importVectorKeys step: EdDSA Ed448 verification failure due to altered signature", - "importVectorKeys step: EdDSA Ed25519 verification failure due to shortened signature", "importVectorKeys step: EdDSA Ed448 verification failure due to shortened signature", - "importVectorKeys step: EdDSA Ed25519 verification failure due to altered data", "importVectorKeys step: EdDSA Ed448 verification failure due to altered data", - "importVectorKeys step: EdDSA Ed25519 signing with wrong algorithm name", "importVectorKeys step: EdDSA Ed448 signing with wrong algorithm name", - "importVectorKeys step: EdDSA Ed25519 verifying with wrong algorithm name", "importVectorKeys step: EdDSA Ed448 verifying with wrong algorithm name" ] },