Skip to content

Commit

Permalink
Do not implement Node.js crypto key generation methods
Browse files Browse the repository at this point in the history
  • Loading branch information
jasnell committed Jun 28, 2023
1 parent 14b5476 commit a1542bf
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 162 deletions.
10 changes: 5 additions & 5 deletions src/node/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,11 @@ export default {
// * Keys
// * [ ] crypto.createPrivateKey(key)
// * [ ] crypto.createPublicKey(key)
// * [ ] crypto.createSecretKey(key[, encoding])
// * [ ] crypto.generateKey(type, options, callback)
// * [ ] crypto.generateKeyPair(type, options, callback)
// * [ ] crypto.generateKeyPairSync(type, options)
// * [ ] crypto.generateKeySync(type, options)
// * [x] crypto.createSecretKey(key[, encoding])
// * [x] crypto.generateKey(type, options, callback)
// * [x] crypto.generateKeyPair(type, options, callback)
// * [x] crypto.generateKeyPairSync(type, options)
// * [x] crypto.generateKeySync(type, options)
// * Sign/Verify
// * [ ] crypto.createSign(algorithm[, options])
// * [ ] crypto.createVerify(algorithm[, options])
Expand Down
1 change: 0 additions & 1 deletion src/node/internal/crypto.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export function exportKey(key: CryptoKey, options?: InnerExportOptions): KeyExpo
export function equals(key: CryptoKey, otherKey: CryptoKey): boolean;
export function getAsymmetricKeyDetail(key: CryptoKey): AsymmetricKeyDetails;
export function getAsymmetricKeyType(key: CryptoKey): AsymmetricKeyType;
export function generateKeyPair(type: AsymmetricKeyType, options: GenerateKeyPairOptions): CryptoKeyPair;
export function createSecretKey(key: ArrayBuffer | ArrayBufferView): CryptoKey;
export function createPrivateKey(key: InnerCreateAsymmetricKeyOptions): CryptoKey;
export function createPublicKey(key: InnerCreateAsymmetricKeyOptions): CryptoKey;
Expand Down
174 changes: 33 additions & 141 deletions src/node/internal/crypto_keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@
/* eslint-disable */

import { Buffer } from 'node-internal:internal_buffer';
import { randomBytes } from 'node-internal:crypto_random';

import {
CryptoKey,
CryptoKeyPair,
JsonWebKey,
KeyData,
KeyObjectType,
Expand Down Expand Up @@ -66,15 +65,12 @@ import {
import {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_METHOD_NOT_IMPLEMENTED,
} from 'node-internal:internal_errors';

import {
validateFunction,
validateInteger,
validateObject,
validateOneOf,
validateString,
validateUint32,
} from 'node-internal:validators';

// In Node.js, the definition of KeyObject is a bit complicated because
Expand Down Expand Up @@ -393,48 +389,11 @@ export function createPublicKey(key: CreateAsymmetricKeyOptions | KeyData | Cryp
return KeyObject.from(cryptoImpl.createPublicKey(inner)) as PublicKeyObject;
}

export type GenerateKeyCallback = (err?: any, key?: KeyObject) => void;

function validateGenerateKeyOptions(type: SecretKeyType, options: GenerateKeyOptions) {
// While Node.js requires that we pass in a type, it uses that
// only for validation of the length. The secret key returned
// is not specific to either hmac or aes.
validateOneOf(type, 'type', ['hmac', 'aes']);
validateObject(options, 'options', {});
const { length } = options;
validateUint32(length, 'options.length');
if (type === 'aes') {
validateOneOf(length, 'options.length', [128, 192, 256]);
} else if (type === 'hmac') {
validateInteger(length, 'options.length', 8, 2147483647);
} else {
throw new ERR_INVALID_ARG_VALUE('type', type);
}
}

export function generateKey(type: SecretKeyType,
options: GenerateKeyOptions,
callback: GenerateKeyCallback) {
validateGenerateKeyOptions(type, options);
validateFunction(callback, 'callback');
new Promise<KeyObject>((resolve, reject) => {
try {
resolve(KeyObject.from(cryptoImpl.createSecretKey(randomBytes(options.length))));
} catch (err) {
reject(err);
}
}).then((key: KeyObject) => callback(null, key))
.catch((err: any) => callback(err));
}

export function generateKeySync(type: SecretKeyType,
options: GenerateKeyOptions) {
validateGenerateKeyOptions(type, options);
return KeyObject.from(cryptoImpl.createSecretKey(randomBytes(options.length)));
}
// ======================================================================================

export type PublicKeyResult = KeyExportResult | PublicKeyObject;
export type PrivateKeyResult = KeyExportResult | PrivateKeyObject;
export type GenerateKeyCallback = (err?: any, key?: KeyObject) => void;
export type GenerateKeyPairCallback =
(err?: any, publicKey?: PublicKeyResult, privateKey?: PrivateKeyResult) => void;

Expand All @@ -443,109 +402,42 @@ export interface KeyObjectPair {
privateKey: PrivateKeyResult;
}

function validateGenerateKeyPairOptions(
type : AsymmetricKeyType,
options: GenerateKeyPairOptions) {
validateOneOf(type, 'type', ['rsa', 'rsa-pss', 'dsa', 'ec', 'x25519', 'ed25519', 'dh']);
validateObject(options, 'options', {});
const {
modulusLength,
publicExponent,
hashAlgorithm,
mgf1HashAlgorithm,
saltLength,
divisorLength,
namedCurve,
prime,
primeLength,
generator,
groupName,
paramEncoding,
publicKeyEncoding,
privateKeyEncoding,
} = options;
if (modulusLength !== undefined) validateInteger(modulusLength, 'options.modulusLength', 0);
if (publicExponent !== undefined) {
if (typeof publicExponent !== 'bigint' && typeof publicExponent !== 'number')
throw new ERR_INVALID_ARG_TYPE('options.publicExponent', ['bigint', 'number'], publicExponent);
}
if (hashAlgorithm !== undefined) {
validateString(hashAlgorithm, 'options.hashAlgorithm');
}
if (mgf1HashAlgorithm !== undefined) {
validateString(mgf1HashAlgorithm, 'options.mgf1HashAlgorithm');
}
if (saltLength !== undefined) {
validateInteger(saltLength, 'options.saltLength', 0);
}
if (divisorLength !== undefined) {
validateInteger(divisorLength, 'options.divisorLength', 0);
}
if (namedCurve !== undefined) {
validateString(namedCurve, 'options.namedCurve');
}
if (prime !== undefined) {
if (!isUint8Array(prime))
throw new ERR_INVALID_ARG_TYPE('options.prime', 'Uint8Array', prime);
}
if (primeLength !== undefined) {
validateInteger(primeLength, 'options.primeLength', 0);
}
if (generator !== undefined) {
validateInteger(generator, 'options.generator', 0);
}
if (groupName !== undefined) {
validateString(groupName, 'options.groupName');
}
if (paramEncoding !== undefined) {
validateOneOf(paramEncoding, 'options.paramEncoding', ['named', 'explicit']);
}
if (publicKeyEncoding !== undefined) {
validateObject(publicKeyEncoding, 'options.publicKeyEncoding', {});
}
if (privateKeyEncoding !== undefined) {
validateObject(privateKeyEncoding, 'options.privateKeyEncoding', {});
}
export function generateKey(_type: SecretKeyType,
_options: GenerateKeyOptions,
callback: GenerateKeyCallback) {
// We intentionally have not implemented key generation up to this point.
// The reason is that generation of cryptographically safe keys is a CPU
// intensive operation that can often exceed limits on the amount of CPU
// time a worker is allowed.
callback(new ERR_METHOD_NOT_IMPLEMENTED('crypto.generateKeySync'));
}

function toExportedKeyPair(inner : CryptoKeyPair, options: GenerateKeyPairOptions) : KeyObjectPair {
const {
publicKey: pubkey,
privateKey: pvtkey,
} = inner;

let publicKey: PublicKeyResult = KeyObject.from(pubkey) as PublicKeyObject;
let privateKey: PrivateKeyResult = KeyObject.from(pvtkey) as PrivateKeyObject;

if (options.publicKeyEncoding !== undefined) {
publicKey = publicKey.export(options.publicKeyEncoding);
}
if (options.privateKeyEncoding !== undefined) {
privateKey = privateKey.export(options.privateKeyEncoding);
}

return { publicKey, privateKey };
export function generateKeySync(_type: SecretKeyType,
_options: GenerateKeyOptions) {
// We intentionally have not implemented key generation up to this point.
// The reason is that generation of cryptographically safe keys is a CPU
// intensive operation that can often exceed limits on the amount of CPU
// time a worker is allowed.
throw new ERR_METHOD_NOT_IMPLEMENTED('crypto.generateKeySync');
}

export function generateKeyPair(
type : AsymmetricKeyType,
options: GenerateKeyPairOptions,
_type : AsymmetricKeyType,
_options: GenerateKeyPairOptions,
callback: GenerateKeyPairCallback) {
validateGenerateKeyPairOptions(type, options);
validateFunction(callback, 'callback');
new Promise<KeyObjectPair>((resolve, reject) => {
try {
resolve(toExportedKeyPair(cryptoImpl.generateKeyPair(type, options), options));
} catch (err) {
reject(err);
}
}).then(({ publicKey, privateKey }) => callback(null, publicKey, privateKey))
.catch((err: any) => callback(err));
// We intentionally have not implemented key generation up to this point.
// The reason is that generation of cryptographically safe keys is a CPU
// intensive operation that can often exceed limits on the amount of CPU
// time a worker is allowed.
callback(new ERR_METHOD_NOT_IMPLEMENTED('crypto.generateKeyPair'));
}

export function generateKeyPairSync(
type : AsymmetricKeyType,
options: GenerateKeyPairOptions) : KeyObjectPair {
validateGenerateKeyPairOptions(type, options);
return toExportedKeyPair(cryptoImpl.generateKeyPair(type, options), options);
_type : AsymmetricKeyType,
_options: GenerateKeyPairOptions) : KeyObjectPair {
// We intentionally have not implemented key generation up to this point.
// The reason is that generation of cryptographically safe keys is a CPU
// intensive operation that can often exceed limits on the amount of CPU
// time a worker is allowed.
throw new ERR_METHOD_NOT_IMPLEMENTED('crypto.generateKeyPairSync');
}
7 changes: 0 additions & 7 deletions src/workerd/api/node/crypto-keys.c++
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,6 @@ kj::StringPtr CryptoImpl::getAsymmetricKeyType(jsg::Lock& js, jsg::Ref<CryptoKey
return key->getAlgorithmName();
}

CryptoKeyPair CryptoImpl::generateKeyPair(
jsg::Lock& js,
kj::String type,
CryptoImpl::GenerateKeyPairOptions options) {
KJ_UNIMPLEMENTED("not implemented");
}

jsg::Ref<CryptoKey> CryptoImpl::createSecretKey(jsg::Lock& js, kj::Array<kj::byte> keyData) {
return jsg::alloc<CryptoKey>(kj::heap<SecretKey>(kj::heapArray(keyData.asPtr())));
}
Expand Down
3 changes: 0 additions & 3 deletions src/workerd/api/node/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,6 @@ class CryptoImpl final: public jsg::Object {
CryptoKey::AsymmetricKeyDetails getAsymmetricKeyDetail(jsg::Lock& js, jsg::Ref<CryptoKey> key);
kj::StringPtr getAsymmetricKeyType(jsg::Lock& js, jsg::Ref<CryptoKey> key);

CryptoKeyPair generateKeyPair(jsg::Lock& js, kj::String type, GenerateKeyPairOptions options);

jsg::Ref<CryptoKey> createSecretKey(jsg::Lock& js, kj::Array<kj::byte>);
jsg::Ref<CryptoKey> createPrivateKey(jsg::Lock& js, CreateAsymmetricKeyOptions options);
jsg::Ref<CryptoKey> createPublicKey(jsg::Lock& js, CreateAsymmetricKeyOptions options);
Expand All @@ -146,7 +144,6 @@ class CryptoImpl final: public jsg::Object {
JSG_METHOD(equals);
JSG_METHOD(getAsymmetricKeyDetail);
JSG_METHOD(getAsymmetricKeyType);
JSG_METHOD(generateKeyPair);
JSG_METHOD(createSecretKey);
JSG_METHOD(createPrivateKey);
JSG_METHOD(createPublicKey);
Expand Down
5 changes: 0 additions & 5 deletions src/workerd/api/node/crypto_keys-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
KeyObject,
SecretKeyObject,
createSecretKey,
generateKeySync,
} from 'node:crypto';
import {
Buffer,
Expand Down Expand Up @@ -183,8 +182,6 @@ export const secret_key_test = {
const key1 = createSecretKey('hello');
const key2 = createSecretKey('hello');
const key3 = createSecretKey('there');
const key4 = generateKeySync('aes', { length: 128 });
const key5 = generateKeySync('hmac', { length: 128 });

ok(key1 instanceof SecretKeyObject);
ok(key2 instanceof SecretKeyObject);
Expand All @@ -194,8 +191,6 @@ export const secret_key_test = {
strictEqual(key1.type, 'secret');
strictEqual(key2.type, 'secret');
strictEqual(key3.type, 'secret');
strictEqual(key4.type, 'secret');
strictEqual(key5.type, 'secret');
ok(key1.equals(key2));
ok(!key1.equals(key3));
}
Expand Down

0 comments on commit a1542bf

Please sign in to comment.