mirror of
https://github.com/denoland/deno.git
synced 2025-02-12 16:59:32 -05:00
fix(ext/crypto): export private x25519 JWK key (#27828)
Ref https://github.com/denoland/deno/issues/26431
This commit is contained in:
parent
21e8260cc9
commit
88490d0927
5 changed files with 42 additions and 27 deletions
|
@ -48,6 +48,7 @@ import {
|
||||||
op_crypto_verify_ed25519,
|
op_crypto_verify_ed25519,
|
||||||
op_crypto_verify_key,
|
op_crypto_verify_key,
|
||||||
op_crypto_wrap_key,
|
op_crypto_wrap_key,
|
||||||
|
op_crypto_x25519_public_key,
|
||||||
} from "ext:core/ops";
|
} from "ext:core/ops";
|
||||||
const {
|
const {
|
||||||
ArrayBufferIsView,
|
ArrayBufferIsView,
|
||||||
|
@ -4532,17 +4533,18 @@ function exportKeyX25519(format, key, innerKey) {
|
||||||
return TypedArrayPrototypeGetBuffer(pkcs8Der);
|
return TypedArrayPrototypeGetBuffer(pkcs8Der);
|
||||||
}
|
}
|
||||||
case "jwk": {
|
case "jwk": {
|
||||||
if (key[_type] === "private") {
|
|
||||||
throw new DOMException("Not implemented", "NotSupportedError");
|
|
||||||
}
|
|
||||||
const x = op_crypto_base64url_encode(innerKey);
|
|
||||||
const jwk = {
|
const jwk = {
|
||||||
kty: "OKP",
|
kty: "OKP",
|
||||||
crv: "X25519",
|
crv: "X25519",
|
||||||
x,
|
|
||||||
"key_ops": key.usages,
|
"key_ops": key.usages,
|
||||||
ext: key[_extractable],
|
ext: key[_extractable],
|
||||||
};
|
};
|
||||||
|
if (key[_type] === "private") {
|
||||||
|
jwk.x = op_crypto_x25519_public_key(innerKey);
|
||||||
|
jwk.d = op_crypto_base64url_encode(innerKey);
|
||||||
|
} else {
|
||||||
|
jwk.x = op_crypto_base64url_encode(innerKey);
|
||||||
|
}
|
||||||
return jwk;
|
return jwk;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -98,6 +98,7 @@ deno_core::extension!(deno_crypto,
|
||||||
op_crypto_base64url_decode,
|
op_crypto_base64url_decode,
|
||||||
op_crypto_base64url_encode,
|
op_crypto_base64url_encode,
|
||||||
x25519::op_crypto_generate_x25519_keypair,
|
x25519::op_crypto_generate_x25519_keypair,
|
||||||
|
x25519::op_crypto_x25519_public_key,
|
||||||
x25519::op_crypto_derive_bits_x25519,
|
x25519::op_crypto_derive_bits_x25519,
|
||||||
x25519::op_crypto_import_spki_x25519,
|
x25519::op_crypto_import_spki_x25519,
|
||||||
x25519::op_crypto_import_pkcs8_x25519,
|
x25519::op_crypto_import_pkcs8_x25519,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||||
|
|
||||||
|
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
|
||||||
use curve25519_dalek::montgomery::MontgomeryPoint;
|
use curve25519_dalek::montgomery::MontgomeryPoint;
|
||||||
use deno_core::op2;
|
use deno_core::op2;
|
||||||
use deno_core::ToJsBuffer;
|
use deno_core::ToJsBuffer;
|
||||||
|
@ -20,17 +21,16 @@ pub enum X25519Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Der(#[from] spki::der::Error),
|
Der(#[from] spki::der::Error),
|
||||||
}
|
}
|
||||||
|
// u-coordinate of the base point.
|
||||||
|
const X25519_BASEPOINT_BYTES: [u8; 32] = [
|
||||||
|
9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
];
|
||||||
#[op2(fast)]
|
#[op2(fast)]
|
||||||
pub fn op_crypto_generate_x25519_keypair(
|
pub fn op_crypto_generate_x25519_keypair(
|
||||||
#[buffer] pkey: &mut [u8],
|
#[buffer] pkey: &mut [u8],
|
||||||
#[buffer] pubkey: &mut [u8],
|
#[buffer] pubkey: &mut [u8],
|
||||||
) {
|
) {
|
||||||
// u-coordinate of the base point.
|
|
||||||
const X25519_BASEPOINT_BYTES: [u8; 32] = [
|
|
||||||
9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0,
|
|
||||||
];
|
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
rng.fill_bytes(pkey);
|
rng.fill_bytes(pkey);
|
||||||
// https://www.rfc-editor.org/rfc/rfc7748#section-6.1
|
// https://www.rfc-editor.org/rfc/rfc7748#section-6.1
|
||||||
|
@ -42,6 +42,17 @@ pub fn op_crypto_generate_x25519_keypair(
|
||||||
pubkey.copy_from_slice(&x25519_dalek::x25519(pkey, X25519_BASEPOINT_BYTES));
|
pubkey.copy_from_slice(&x25519_dalek::x25519(pkey, X25519_BASEPOINT_BYTES));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[op2]
|
||||||
|
#[string]
|
||||||
|
pub fn op_crypto_x25519_public_key(#[buffer] private_key: &[u8]) -> String {
|
||||||
|
use base64::Engine;
|
||||||
|
|
||||||
|
let private_key: [u8; 32] =
|
||||||
|
private_key.try_into().expect("Expected byteLength 32");
|
||||||
|
BASE64_URL_SAFE_NO_PAD
|
||||||
|
.encode(x25519_dalek::x25519(private_key, X25519_BASEPOINT_BYTES))
|
||||||
|
}
|
||||||
|
|
||||||
const MONTGOMERY_IDENTITY: MontgomeryPoint = MontgomeryPoint([0; 32]);
|
const MONTGOMERY_IDENTITY: MontgomeryPoint = MontgomeryPoint([0; 32]);
|
||||||
|
|
||||||
#[op2(fast)]
|
#[op2(fast)]
|
||||||
|
|
|
@ -2085,3 +2085,20 @@ Deno.test(async function x25519SharedSecret() {
|
||||||
assertEquals(sharedSecret1.byteLength, 16);
|
assertEquals(sharedSecret1.byteLength, 16);
|
||||||
assertEquals(new Uint8Array(sharedSecret1), new Uint8Array(sharedSecret2));
|
assertEquals(new Uint8Array(sharedSecret1), new Uint8Array(sharedSecret2));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test(async function x25519ExportJwk() {
|
||||||
|
const keyPair = await crypto.subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: "X25519",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
["deriveBits"],
|
||||||
|
) as CryptoKeyPair;
|
||||||
|
|
||||||
|
const jwk = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
|
||||||
|
|
||||||
|
assertEquals(jwk.kty, "OKP");
|
||||||
|
assertEquals(jwk.crv, "X25519");
|
||||||
|
assert(jwk.d);
|
||||||
|
assert(jwk.x);
|
||||||
|
});
|
||||||
|
|
|
@ -845,14 +845,6 @@
|
||||||
"Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign])",
|
"Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign])",
|
||||||
"Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, false, [sign, sign])",
|
"Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, false, [sign, sign])",
|
||||||
"Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign, sign])",
|
"Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign, sign])",
|
||||||
"Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey])",
|
|
||||||
"Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey])",
|
|
||||||
"Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits, deriveKey])",
|
|
||||||
"Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits, deriveKey])",
|
|
||||||
"Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])",
|
|
||||||
"Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])",
|
|
||||||
"Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])",
|
|
||||||
"Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])",
|
|
||||||
"Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])",
|
"Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])",
|
||||||
"Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])",
|
"Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])",
|
||||||
"Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])",
|
"Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])",
|
||||||
|
@ -902,14 +894,6 @@
|
||||||
"Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign])",
|
"Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign])",
|
||||||
"Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, false, [sign, sign])",
|
"Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, false, [sign, sign])",
|
||||||
"Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign, sign])",
|
"Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign, sign])",
|
||||||
"Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey])",
|
|
||||||
"Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey])",
|
|
||||||
"Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits, deriveKey])",
|
|
||||||
"Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits, deriveKey])",
|
|
||||||
"Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])",
|
|
||||||
"Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])",
|
|
||||||
"Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])",
|
|
||||||
"Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])",
|
|
||||||
"Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])",
|
"Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])",
|
||||||
"Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])",
|
"Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])",
|
||||||
"Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])",
|
"Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])",
|
||||||
|
|
Loading…
Add table
Reference in a new issue