diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/crypto/00_crypto.js | 106 | ||||
-rw-r--r-- | ext/crypto/ed25519.rs | 6 | ||||
-rw-r--r-- | ext/crypto/lib.rs | 15 | ||||
-rw-r--r-- | ext/crypto/x25519.rs | 37 |
4 files changed, 138 insertions, 26 deletions
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 9134f486b..5e918cc7d 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 a8060cae1..b7ff99d8b 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<ZeroCopyBuf, AnyError> { #[op] pub fn op_export_pkcs8_ed25519(pkey: &[u8]) -> Result<ZeroCopyBuf, AnyError> { + // 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 0f1ee565b..5f6445f49 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -100,7 +100,8 @@ pub fn init(maybe_seed: Option<u64>) -> 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<u64>) -> 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,13 +127,19 @@ pub fn init(maybe_seed: Option<u64>) -> Extension { } #[op] -pub fn op_crypto_base64url(data: String) -> ZeroCopyBuf { +pub fn op_crypto_base64url_decode(data: String) -> ZeroCopyBuf { let data: Vec<u8> = - 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, mut zero_copy: ZeroCopyBuf, diff --git a/ext/crypto/x25519.rs b/ext/crypto/x25519.rs index b606f4ca0..1e64e9909 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<ZeroCopyBuf, AnyError> { + 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<ZeroCopyBuf, AnyError> { + // 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()) +} |