1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-05 13:59:01 -05:00

fix(ext/node): add symmetric keygen (#18609)

Towards #18455
This commit is contained in:
Divy Srivastava 2023-04-06 18:39:25 +05:30 committed by Levente Kurusa
parent 16c1e023a5
commit 63454729a2
7 changed files with 232 additions and 17 deletions

View file

@ -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",

View file

@ -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/
});

View file

@ -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()
}

View file

@ -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,

View file

@ -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";
export function generateKey(
_type: "hmac" | "aes",
_options: {
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;
},
_callback: (err: Error | null, key: KeyObject) => void,
): 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: {
length: number;
},
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,

View file

@ -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,
};

View file

@ -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,
};