summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/internal/crypto/keygen.ts
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2023-04-19 22:27:34 +0530
committerGitHub <noreply@github.com>2023-04-19 22:27:34 +0530
commit9496dfc68558a0d6e9fa0a3bf1fbde9883a88d07 (patch)
treefd37f0f325e288c7cf4a48426481d8553c38f6ef /ext/node/polyfills/internal/crypto/keygen.ts
parent53c9f5918cd07237c20b086945d4604baf1900fb (diff)
fix(ext/node): implement asymmetric keygen (#18651)
Towards #18455 This commit implements the keypair generation for asymmetric keys for the `generateKeyPair` API. See how key material is managed in this implementation: https://www.notion.so/denolandinc/node-crypto-design-99fc33f568d24e47a5e4b36002c5325d?pvs=4 Private and public key encoding depend on `KeyObject#export` which is not implemented. I've also skipped ED448 and X448 since we need a crate for that in WebCrypto too.
Diffstat (limited to 'ext/node/polyfills/internal/crypto/keygen.ts')
-rw-r--r--ext/node/polyfills/internal/crypto/keygen.ts275
1 files changed, 265 insertions, 10 deletions
diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts
index b490cedd7..cdc616db6 100644
--- a/ext/node/polyfills/internal/crypto/keygen.ts
+++ b/ext/node/polyfills/internal/crypto/keygen.ts
@@ -1,3 +1,5 @@
+// deno-lint-ignore-file no-explicit-any
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license.
@@ -8,13 +10,20 @@ import {
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 {
+ ERR_INCOMPATIBLE_OPTION_PAIR,
+ ERR_INVALID_ARG_VALUE,
+ ERR_MISSING_OPTION,
+} from "ext:deno_node/internal/errors.ts";
+import {
+ validateBuffer,
validateFunction,
+ validateInt32,
validateInteger,
validateObject,
validateOneOf,
validateString,
+ validateUint32,
} 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";
@@ -529,17 +538,34 @@ export function generateKeyPair(
) => void,
): void;
export function generateKeyPair(
- _type: KeyType,
- _options: unknown,
- _callback: (
+ type: KeyType,
+ options: unknown,
+ callback: (
err: Error | null,
- // deno-lint-ignore no-explicit-any
publicKey: any,
- // deno-lint-ignore no-explicit-any
privateKey: any,
) => void,
) {
- notImplemented("crypto.generateKeyPair");
+ createJob(kAsync, type, options).then(([privateKey, publicKey]) => {
+ privateKey = new KeyObject("private", setOwnedKey(privateKey));
+ publicKey = new KeyObject("public", setOwnedKey(publicKey));
+
+ if (typeof options === "object" && options !== null) {
+ const { publicKeyEncoding, privateKeyEncoding } = options as any;
+
+ if (publicKeyEncoding) {
+ publicKey = publicKey.export(publicKeyEncoding);
+ }
+
+ if (privateKeyEncoding) {
+ privateKey = privateKey.export(privateKeyEncoding);
+ }
+ }
+
+ callback(null, publicKey, privateKey);
+ }).catch((err) => {
+ callback(err, null, null);
+ });
}
export interface KeyPairKeyObjectResult {
@@ -716,12 +742,241 @@ export function generateKeyPairSync(
options?: X448KeyPairKeyObjectOptions,
): KeyPairKeyObjectResult;
export function generateKeyPairSync(
- _type: KeyType,
- _options: unknown,
+ type: KeyType,
+ options: unknown,
):
| KeyPairKeyObjectResult
| KeyPairSyncResult<string | Buffer, string | Buffer> {
- notImplemented("crypto.generateKeyPairSync");
+ let [privateKey, publicKey] = createJob(kSync, type, options);
+
+ privateKey = new KeyObject("private", setOwnedKey(privateKey));
+ publicKey = new KeyObject("public", setOwnedKey(publicKey));
+
+ if (typeof options === "object" && options !== null) {
+ const { publicKeyEncoding, privateKeyEncoding } = options as any;
+
+ if (publicKeyEncoding) {
+ publicKey = publicKey.export(publicKeyEncoding);
+ }
+
+ if (privateKeyEncoding) {
+ privateKey = privateKey.export(privateKeyEncoding);
+ }
+ }
+
+ return { publicKey, privateKey };
+}
+
+const kSync = 0;
+const kAsync = 1;
+
+function createJob(mode, type, options) {
+ validateString(type, "type");
+
+ if (options !== undefined) {
+ validateObject(options, "options");
+ }
+
+ switch (type) {
+ case "rsa":
+ case "rsa-pss": {
+ validateObject(options, "options");
+ const { modulusLength } = options;
+ validateUint32(modulusLength, "options.modulusLength");
+
+ let { publicExponent } = options;
+ if (publicExponent == null) {
+ publicExponent = 0x10001;
+ } else {
+ validateUint32(publicExponent, "options.publicExponent");
+ }
+
+ if (type === "rsa") {
+ if (mode === kSync) {
+ return ops.op_node_generate_rsa(
+ modulusLength,
+ publicExponent,
+ );
+ } else {
+ return core.opAsync(
+ "op_node_generate_rsa_async",
+ modulusLength,
+ publicExponent,
+ );
+ }
+ }
+
+ const {
+ hash,
+ mgf1Hash,
+ hashAlgorithm,
+ mgf1HashAlgorithm,
+ saltLength,
+ } = options;
+
+ if (saltLength !== undefined) {
+ validateInt32(saltLength, "options.saltLength", 0);
+ }
+ if (hashAlgorithm !== undefined) {
+ validateString(hashAlgorithm, "options.hashAlgorithm");
+ }
+ if (mgf1HashAlgorithm !== undefined) {
+ validateString(mgf1HashAlgorithm, "options.mgf1HashAlgorithm");
+ }
+ if (hash !== undefined) {
+ process.emitWarning(
+ '"options.hash" is deprecated, ' +
+ 'use "options.hashAlgorithm" instead.',
+ "DeprecationWarning",
+ "DEP0154",
+ );
+ validateString(hash, "options.hash");
+ if (hashAlgorithm && hash !== hashAlgorithm) {
+ throw new ERR_INVALID_ARG_VALUE("options.hash", hash);
+ }
+ }
+ if (mgf1Hash !== undefined) {
+ process.emitWarning(
+ '"options.mgf1Hash" is deprecated, ' +
+ 'use "options.mgf1HashAlgorithm" instead.',
+ "DeprecationWarning",
+ "DEP0154",
+ );
+ validateString(mgf1Hash, "options.mgf1Hash");
+ if (mgf1HashAlgorithm && mgf1Hash !== mgf1HashAlgorithm) {
+ throw new ERR_INVALID_ARG_VALUE("options.mgf1Hash", mgf1Hash);
+ }
+ }
+
+ if (mode === kSync) {
+ return ops.op_node_generate_rsa(
+ modulusLength,
+ publicExponent,
+ );
+ } else {
+ return core.opAsync(
+ "op_node_generate_rsa_async",
+ modulusLength,
+ publicExponent,
+ );
+ }
+ }
+ case "dsa": {
+ validateObject(options, "options");
+ const { modulusLength } = options;
+ validateUint32(modulusLength, "options.modulusLength");
+
+ let { divisorLength } = options;
+ if (divisorLength == null) {
+ divisorLength = 256;
+ } else {
+ validateInt32(divisorLength, "options.divisorLength", 0);
+ }
+
+ if (mode === kSync) {
+ return ops.op_node_dsa_generate(modulusLength, divisorLength);
+ }
+ return core.opAsync(
+ "op_node_dsa_generate_async",
+ modulusLength,
+ divisorLength,
+ );
+ }
+ case "ec": {
+ validateObject(options, "options");
+ const { namedCurve } = options;
+ validateString(namedCurve, "options.namedCurve");
+ const { paramEncoding } = options;
+ if (paramEncoding == null || paramEncoding === "named") {
+ // pass.
+ } else if (paramEncoding === "explicit") {
+ // TODO(@littledivy): Explicit param encoding is very rarely used, and not supported by the ring crate.
+ throw new TypeError("Explicit encoding is not supported");
+ } else {
+ throw new ERR_INVALID_ARG_VALUE("options.paramEncoding", paramEncoding);
+ }
+
+ if (mode === kSync) {
+ return ops.op_node_ec_generate(namedCurve);
+ } else {
+ return core.opAsync("op_node_ec_generate_async", namedCurve);
+ }
+ }
+ case "ed25519": {
+ if (mode === kSync) {
+ return ops.op_node_ed25519_generate();
+ }
+ return core.opAsync("op_node_ed25519_generate_async");
+ }
+ case "x25519": {
+ if (mode === kSync) {
+ return ops.op_node_x25519_generate();
+ }
+ return core.opAsync("op_node_x25519_generate_async");
+ }
+ case "ed448":
+ case "x448": {
+ notImplemented(type);
+ break;
+ }
+ case "dh": {
+ validateObject(options, "options");
+ const { group, primeLength, prime, generator } = options;
+ if (group != null) {
+ if (prime != null) {
+ throw new ERR_INCOMPATIBLE_OPTION_PAIR("group", "prime");
+ }
+ if (primeLength != null) {
+ throw new ERR_INCOMPATIBLE_OPTION_PAIR("group", "primeLength");
+ }
+ if (generator != null) {
+ throw new ERR_INCOMPATIBLE_OPTION_PAIR("group", "generator");
+ }
+
+ validateString(group, "options.group");
+
+ if (mode === kSync) {
+ return ops.op_node_dh_generate_group(group);
+ } else {
+ return core.opAsync("op_node_dh_generate_group_async", group);
+ }
+ }
+
+ if (prime != null) {
+ if (primeLength != null) {
+ throw new ERR_INCOMPATIBLE_OPTION_PAIR("prime", "primeLength");
+ }
+
+ validateBuffer(prime, "options.prime");
+ } else if (primeLength != null) {
+ validateInt32(primeLength, "options.primeLength", 0);
+ } else {
+ throw new ERR_MISSING_OPTION(
+ "At least one of the group, prime, or primeLength options",
+ );
+ }
+
+ if (generator != null) {
+ validateInt32(generator, "options.generator", 0);
+ }
+
+ const g = generator == null ? 2 : generator;
+
+ if (mode === kSync) {
+ return ops.op_node_dh_generate(prime, primeLength ?? 0, g);
+ } else {
+ return core.opAsync(
+ "op_node_dh_generate_async",
+ prime,
+ primeLength ?? 0,
+ g,
+ );
+ }
+ }
+ default:
+ // Fall through
+ }
+ throw new ERR_INVALID_ARG_VALUE("type", type, "must be a supported key type");
}
export default {