summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2023-04-06 18:39:25 +0530
committerGitHub <noreply@github.com>2023-04-06 18:39:25 +0530
commit3b62a58818f83e32fc2644f44e75a5c8465b2003 (patch)
treecf8f0a6d7995fa0c632247f2679295b8ceea4460
parent339165bd9565806374fa842dfc217dcc5ebabac5 (diff)
fix(ext/node): add symmetric keygen (#18609)
Towards #18455
-rw-r--r--cli/tests/node_compat/config.json1
-rw-r--r--cli/tests/node_compat/test/parallel/test-crypto-secret-keygen.js137
-rw-r--r--ext/node/crypto/mod.rs17
-rw-r--r--ext/node/lib.rs2
-rw-r--r--ext/node/polyfills/internal/crypto/keygen.ts79
-rw-r--r--ext/node/polyfills/internal/crypto/keys.ts6
-rw-r--r--ext/node/polyfills/internal/crypto/util.ts5
7 files changed, 231 insertions, 16 deletions
diff --git a/cli/tests/node_compat/config.json b/cli/tests/node_compat/config.json
index cdfbf4677..e314c1958 100644
--- a/cli/tests/node_compat/config.json
+++ b/cli/tests/node_compat/config.json
@@ -230,6 +230,7 @@
"test-console-tty-colors.js",
"test-crypto-hmac.js",
"test-crypto-prime.js",
+ "test-crypto-secret-keygen.js",
"test-dgram-close-during-bind.js",
"test-dgram-close-signal.js",
"test-diagnostics-channel-has-subscribers.js",
diff --git a/cli/tests/node_compat/test/parallel/test-crypto-secret-keygen.js b/cli/tests/node_compat/test/parallel/test-crypto-secret-keygen.js
new file mode 100644
index 000000000..098882215
--- /dev/null
+++ b/cli/tests/node_compat/test/parallel/test-crypto-secret-keygen.js
@@ -0,0 +1,137 @@
+'use strict';
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const assert = require('assert');
+
+const {
+ generateKey,
+ generateKeySync
+} = require('crypto');
+
+[1, true, [], {}, Infinity, null, undefined].forEach((i) => {
+ assert.throws(() => generateKey(i, 1, common.mustNotCall()), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: /The "type" argument must be /
+ });
+ assert.throws(() => generateKeySync(i, 1), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: /The "type" argument must be /
+ });
+});
+
+['', true, [], null, undefined].forEach((i) => {
+ assert.throws(() => generateKey('aes', i, common.mustNotCall()), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: /The "options" argument must be /
+ });
+ assert.throws(() => generateKeySync('aes', i), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: /The "options" argument must be /
+ });
+});
+
+['', true, {}, [], null, undefined].forEach((length) => {
+ assert.throws(() => generateKey('hmac', { length }, common.mustNotCall()), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: /The "options\.length" property must be /
+ });
+ assert.throws(() => generateKeySync('hmac', { length }), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: /The "options\.length" property must be /
+ });
+});
+
+assert.throws(() => generateKey('aes', { length: 256 }), {
+ code: 'ERR_INVALID_ARG_TYPE'
+});
+
+assert.throws(() => generateKey('hmac', { length: -1 }, common.mustNotCall()), {
+ code: 'ERR_OUT_OF_RANGE'
+});
+
+assert.throws(() => generateKey('hmac', { length: 4 }, common.mustNotCall()), {
+ code: 'ERR_OUT_OF_RANGE'
+});
+
+assert.throws(() => generateKey('hmac', { length: 7 }, common.mustNotCall()), {
+ code: 'ERR_OUT_OF_RANGE'
+});
+
+assert.throws(
+ () => generateKey('hmac', { length: 2 ** 31 }, common.mustNotCall()), {
+ code: 'ERR_OUT_OF_RANGE'
+ });
+
+assert.throws(() => generateKeySync('hmac', { length: -1 }), {
+ code: 'ERR_OUT_OF_RANGE'
+});
+
+assert.throws(() => generateKeySync('hmac', { length: 4 }), {
+ code: 'ERR_OUT_OF_RANGE'
+});
+
+assert.throws(() => generateKeySync('hmac', { length: 7 }), {
+ code: 'ERR_OUT_OF_RANGE'
+});
+
+assert.throws(
+ () => generateKeySync('hmac', { length: 2 ** 31 }), {
+ code: 'ERR_OUT_OF_RANGE'
+ });
+
+assert.throws(() => generateKeySync('aes', { length: 123 }), {
+ code: 'ERR_INVALID_ARG_VALUE',
+ message: /The property 'options\.length' must be one of: 128, 192, 256/
+});
+
+{
+ const key = generateKeySync('aes', { length: 128 });
+ assert(key);
+ const keybuf = key.export();
+ assert.strictEqual(keybuf.byteLength, 128 / 8);
+
+ generateKey('aes', { length: 128 }, common.mustSucceed((key) => {
+ assert(key);
+ const keybuf = key.export();
+ assert.strictEqual(keybuf.byteLength, 128 / 8);
+ }));
+}
+
+{
+ const key = generateKeySync('aes', { length: 256 });
+ assert(key);
+ const keybuf = key.export();
+ assert.strictEqual(keybuf.byteLength, 256 / 8);
+
+ generateKey('aes', { length: 256 }, common.mustSucceed((key) => {
+ assert(key);
+ const keybuf = key.export();
+ assert.strictEqual(keybuf.byteLength, 256 / 8);
+ }));
+}
+
+{
+ const key = generateKeySync('hmac', { length: 123 });
+ assert(key);
+ const keybuf = key.export();
+ assert.strictEqual(keybuf.byteLength, Math.floor(123 / 8));
+
+ generateKey('hmac', { length: 123 }, common.mustSucceed((key) => {
+ assert(key);
+ const keybuf = key.export();
+ assert.strictEqual(keybuf.byteLength, Math.floor(123 / 8));
+ }));
+}
+
+assert.throws(
+ () => generateKey('unknown', { length: 123 }, common.mustNotCall()), {
+ code: 'ERR_INVALID_ARG_VALUE',
+ message: /The argument 'type' must be a supported key type/
+ });
+
+assert.throws(() => generateKeySync('unknown', { length: 123 }), {
+ code: 'ERR_INVALID_ARG_VALUE',
+ message: /The argument 'type' must be a supported key type/
+}); \ No newline at end of file
diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs
index 3529a3aa4..499e99fea 100644
--- a/ext/node/crypto/mod.rs
+++ b/ext/node/crypto/mod.rs
@@ -8,6 +8,7 @@ use deno_core::ResourceId;
use deno_core::StringOrBuffer;
use deno_core::ZeroCopyBuf;
use num_bigint::BigInt;
+use rand::Rng;
use std::future::Future;
use std::rc::Rc;
@@ -402,3 +403,19 @@ pub async fn op_node_pbkdf2_async(
})
.await?
}
+
+#[op]
+pub fn op_node_generate_secret(buf: &mut [u8]) {
+ rand::thread_rng().fill(buf);
+}
+
+#[op]
+pub async fn op_node_generate_secret_async(len: i32) -> ZeroCopyBuf {
+ tokio::task::spawn_blocking(move || {
+ let mut buf = vec![0u8; len as usize];
+ rand::thread_rng().fill(&mut buf[..]);
+ buf.into()
+ })
+ .await
+ .unwrap()
+}
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 04fd07cab..ec3e7ab25 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -170,6 +170,8 @@ deno_core::extension!(deno_node,
crypto::op_node_check_prime_bytes_async,
crypto::op_node_pbkdf2,
crypto::op_node_pbkdf2_async,
+ crypto::op_node_generate_secret,
+ crypto::op_node_generate_secret_async,
crypto::op_node_sign,
winerror::op_node_sys_to_uv_error,
v8::op_v8_cached_data_version_tag,
diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts
index dadc9c198..b490cedd7 100644
--- a/ext/node/polyfills/internal/crypto/keygen.ts
+++ b/ext/node/polyfills/internal/crypto/keygen.ts
@@ -2,18 +2,80 @@
// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license.
import { KeyObject } from "ext:deno_node/internal/crypto/keys.ts";
+import { kAesKeyLengths } from "ext:deno_node/internal/crypto/util.ts";
+import {
+ SecretKeyObject,
+ setOwnedKey,
+} from "ext:deno_node/internal/crypto/keys.ts";
import { notImplemented } from "ext:deno_node/_utils.ts";
+import { ERR_INVALID_ARG_VALUE } from "ext:deno_node/internal/errors.ts";
+import {
+ validateFunction,
+ validateInteger,
+ validateObject,
+ validateOneOf,
+ validateString,
+} from "ext:deno_node/internal/validators.mjs";
import { Buffer } from "ext:deno_node/buffer.ts";
import { KeyFormat, KeyType } from "ext:deno_node/internal/crypto/types.ts";
+const { core } = globalThis.__bootstrap;
+const { ops } = core;
+
+function validateGenerateKey(
+ type: "hmac" | "aes",
+ options: { length: number },
+) {
+ validateString(type, "type");
+ validateObject(options, "options");
+ const { length } = options;
+ switch (type) {
+ case "hmac":
+ validateInteger(length, "options.length", 8, 2 ** 31 - 1);
+ break;
+ case "aes":
+ validateOneOf(length, "options.length", kAesKeyLengths);
+ break;
+ default:
+ throw new ERR_INVALID_ARG_VALUE(
+ "type",
+ type,
+ "must be a supported key type",
+ );
+ }
+}
+
+export function generateKeySync(
+ type: "hmac" | "aes",
+ options: {
+ length: number;
+ },
+): KeyObject {
+ validateGenerateKey(type, options);
+ const { length } = options;
+
+ const key = new Uint8Array(Math.floor(length / 8));
+ ops.op_node_generate_secret(key);
+
+ return new SecretKeyObject(setOwnedKey(key));
+}
+
export function generateKey(
- _type: "hmac" | "aes",
- _options: {
+ type: "hmac" | "aes",
+ options: {
length: number;
},
- _callback: (err: Error | null, key: KeyObject) => void,
+ callback: (err: Error | null, key: KeyObject) => void,
) {
- notImplemented("crypto.generateKey");
+ validateGenerateKey(type, options);
+ validateFunction(callback, "callback");
+ const { length } = options;
+
+ core.opAsync("op_node_generate_secret_async", Math.floor(length / 8)).then(
+ (key) => {
+ callback(null, new SecretKeyObject(setOwnedKey(key)));
+ },
+ );
}
export interface BasePrivateKeyEncodingOptions<T extends KeyFormat> {
@@ -662,15 +724,6 @@ export function generateKeyPairSync(
notImplemented("crypto.generateKeyPairSync");
}
-export function generateKeySync(
- _type: "hmac" | "aes",
- _options: {
- length: number;
- },
-): KeyObject {
- notImplemented("crypto.generateKeySync");
-}
-
export default {
generateKey,
generateKeySync,
diff --git a/ext/node/polyfills/internal/crypto/keys.ts b/ext/node/polyfills/internal/crypto/keys.ts
index eb7990b06..ef2aba737 100644
--- a/ext/node/polyfills/internal/crypto/keys.ts
+++ b/ext/node/polyfills/internal/crypto/keys.ts
@@ -279,7 +279,7 @@ export function prepareSecretKey(
return getArrayBufferOrView(key, "key", encoding);
}
-class SecretKeyObject extends KeyObject {
+export class SecretKeyObject extends KeyObject {
constructor(handle: unknown) {
super("secret", handle);
}
@@ -313,7 +313,7 @@ class SecretKeyObject extends KeyObject {
}
}
-function setOwnedKey(key: Uint8Array): unknown {
+export function setOwnedKey(key: Uint8Array): unknown {
const handle = {};
KEY_STORE.set(handle, key);
return handle;
@@ -345,4 +345,6 @@ export default {
isCryptoKey,
KeyObject,
prepareSecretKey,
+ setOwnedKey,
+ SecretKeyObject,
};
diff --git a/ext/node/polyfills/internal/crypto/util.ts b/ext/node/polyfills/internal/crypto/util.ts
index 317c1d503..ccb772631 100644
--- a/ext/node/polyfills/internal/crypto/util.ts
+++ b/ext/node/polyfills/internal/crypto/util.ts
@@ -133,7 +133,9 @@ export function setEngine(_engine: string, _flags: typeof constants) {
notImplemented("crypto.setEngine");
}
-export { kHandle, kKeyObject };
+const kAesKeyLengths = [128, 192, 256];
+
+export { kAesKeyLengths, kHandle, kKeyObject };
export default {
getDefaultEncoding,
@@ -147,4 +149,5 @@ export default {
toBuf,
kHandle,
kKeyObject,
+ kAesKeyLengths,
};