mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 21:58:48 +02:00

This will be a start to generalize all argument validation errors. As currently we throw ARG/OPT, OUT_OF_RANGE, and other more specific errors. The OPT errors didn't bring much to the errors as it's just another variant of ARG error which is sometimes more confusing (some of our code used OPT errors to denote just argument validation errors presumably because of similarity of OPT to 'option' and not 'options-object') and they don't specify the name of the options object where the invalid value is located. Much better approach would be to just specify path to the invalid value in the name of the value as it is done in this PR (i.e. 'options.format', 'options.publicKey.type' etc) Also since this decreases a variety of errors we have it'd be easier to reuse validation code across the codebase. Refs: https://github.com/nodejs/node/pull/31251 Refs: https://github.com/nodejs/node/pull/34070#discussion_r467251009 Signed-off-by: Denys Otrishko <shishugi@gmail.com> PR-URL: https://github.com/nodejs/node/pull/34682 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
289 lines
8 KiB
JavaScript
289 lines
8 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
ObjectDefineProperty,
|
|
Set
|
|
} = primordials;
|
|
|
|
const { Buffer } = require('buffer');
|
|
const {
|
|
ERR_CRYPTO_ECDH_INVALID_FORMAT,
|
|
ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY,
|
|
ERR_CRYPTO_INCOMPATIBLE_KEY,
|
|
ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE,
|
|
ERR_INVALID_ARG_TYPE,
|
|
ERR_INVALID_ARG_VALUE,
|
|
} = require('internal/errors').codes;
|
|
const {
|
|
validateString,
|
|
validateInt32,
|
|
} = require('internal/validators');
|
|
const { isArrayBufferView } = require('internal/util/types');
|
|
const { KeyObject } = require('internal/crypto/keys');
|
|
const {
|
|
getDefaultEncoding,
|
|
kHandle,
|
|
toBuf
|
|
} = require('internal/crypto/util');
|
|
const {
|
|
DiffieHellman: _DiffieHellman,
|
|
DiffieHellmanGroup: _DiffieHellmanGroup,
|
|
ECDH: _ECDH,
|
|
ECDHConvertKey: _ECDHConvertKey,
|
|
statelessDH
|
|
} = internalBinding('crypto');
|
|
const {
|
|
POINT_CONVERSION_COMPRESSED,
|
|
POINT_CONVERSION_HYBRID,
|
|
POINT_CONVERSION_UNCOMPRESSED
|
|
} = internalBinding('constants').crypto;
|
|
|
|
const DH_GENERATOR = 2;
|
|
|
|
function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) {
|
|
if (!(this instanceof DiffieHellman))
|
|
return new DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding);
|
|
|
|
if (typeof sizeOrKey !== 'number' &&
|
|
typeof sizeOrKey !== 'string' &&
|
|
!isArrayBufferView(sizeOrKey)) {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'sizeOrKey',
|
|
['number', 'string', 'Buffer', 'TypedArray', 'DataView'],
|
|
sizeOrKey
|
|
);
|
|
}
|
|
|
|
// Sizes < 0 don't make sense but they _are_ accepted (and subsequently
|
|
// rejected with ERR_OSSL_BN_BITS_TOO_SMALL) by OpenSSL. The glue code
|
|
// in node_crypto.cc accepts values that are IsInt32() for that reason
|
|
// and that's why we do that here too.
|
|
if (typeof sizeOrKey === 'number')
|
|
validateInt32(sizeOrKey, 'sizeOrKey');
|
|
|
|
if (keyEncoding && !Buffer.isEncoding(keyEncoding) &&
|
|
keyEncoding !== 'buffer') {
|
|
genEncoding = generator;
|
|
generator = keyEncoding;
|
|
keyEncoding = false;
|
|
}
|
|
|
|
const encoding = getDefaultEncoding();
|
|
keyEncoding = keyEncoding || encoding;
|
|
genEncoding = genEncoding || encoding;
|
|
|
|
if (typeof sizeOrKey !== 'number')
|
|
sizeOrKey = toBuf(sizeOrKey, keyEncoding);
|
|
|
|
if (!generator)
|
|
generator = DH_GENERATOR;
|
|
else if (typeof generator === 'number')
|
|
validateInt32(generator, 'generator');
|
|
else
|
|
generator = toBuf(generator, genEncoding);
|
|
|
|
this[kHandle] = new _DiffieHellman(sizeOrKey, generator);
|
|
ObjectDefineProperty(this, 'verifyError', {
|
|
enumerable: true,
|
|
value: this[kHandle].verifyError,
|
|
writable: false
|
|
});
|
|
}
|
|
|
|
|
|
function DiffieHellmanGroup(name) {
|
|
if (!(this instanceof DiffieHellmanGroup))
|
|
return new DiffieHellmanGroup(name);
|
|
this[kHandle] = new _DiffieHellmanGroup(name);
|
|
ObjectDefineProperty(this, 'verifyError', {
|
|
enumerable: true,
|
|
value: this[kHandle].verifyError,
|
|
writable: false
|
|
});
|
|
}
|
|
|
|
|
|
DiffieHellmanGroup.prototype.generateKeys =
|
|
DiffieHellman.prototype.generateKeys =
|
|
dhGenerateKeys;
|
|
|
|
function dhGenerateKeys(encoding) {
|
|
const keys = this[kHandle].generateKeys();
|
|
encoding = encoding || getDefaultEncoding();
|
|
return encode(keys, encoding);
|
|
}
|
|
|
|
|
|
DiffieHellmanGroup.prototype.computeSecret =
|
|
DiffieHellman.prototype.computeSecret =
|
|
dhComputeSecret;
|
|
|
|
function dhComputeSecret(key, inEnc, outEnc) {
|
|
const encoding = getDefaultEncoding();
|
|
inEnc = inEnc || encoding;
|
|
outEnc = outEnc || encoding;
|
|
const ret = this[kHandle].computeSecret(toBuf(key, inEnc));
|
|
if (typeof ret === 'string')
|
|
throw new ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY();
|
|
return encode(ret, outEnc);
|
|
}
|
|
|
|
|
|
DiffieHellmanGroup.prototype.getPrime =
|
|
DiffieHellman.prototype.getPrime =
|
|
dhGetPrime;
|
|
|
|
function dhGetPrime(encoding) {
|
|
const prime = this[kHandle].getPrime();
|
|
encoding = encoding || getDefaultEncoding();
|
|
return encode(prime, encoding);
|
|
}
|
|
|
|
|
|
DiffieHellmanGroup.prototype.getGenerator =
|
|
DiffieHellman.prototype.getGenerator =
|
|
dhGetGenerator;
|
|
|
|
function dhGetGenerator(encoding) {
|
|
const generator = this[kHandle].getGenerator();
|
|
encoding = encoding || getDefaultEncoding();
|
|
return encode(generator, encoding);
|
|
}
|
|
|
|
|
|
DiffieHellmanGroup.prototype.getPublicKey =
|
|
DiffieHellman.prototype.getPublicKey =
|
|
dhGetPublicKey;
|
|
|
|
function dhGetPublicKey(encoding) {
|
|
const key = this[kHandle].getPublicKey();
|
|
encoding = encoding || getDefaultEncoding();
|
|
return encode(key, encoding);
|
|
}
|
|
|
|
|
|
DiffieHellmanGroup.prototype.getPrivateKey =
|
|
DiffieHellman.prototype.getPrivateKey =
|
|
dhGetPrivateKey;
|
|
|
|
function dhGetPrivateKey(encoding) {
|
|
const key = this[kHandle].getPrivateKey();
|
|
encoding = encoding || getDefaultEncoding();
|
|
return encode(key, encoding);
|
|
}
|
|
|
|
|
|
DiffieHellman.prototype.setPublicKey = function setPublicKey(key, encoding) {
|
|
encoding = encoding || getDefaultEncoding();
|
|
this[kHandle].setPublicKey(toBuf(key, encoding));
|
|
return this;
|
|
};
|
|
|
|
|
|
DiffieHellman.prototype.setPrivateKey = function setPrivateKey(key, encoding) {
|
|
encoding = encoding || getDefaultEncoding();
|
|
this[kHandle].setPrivateKey(toBuf(key, encoding));
|
|
return this;
|
|
};
|
|
|
|
|
|
function ECDH(curve) {
|
|
if (!(this instanceof ECDH))
|
|
return new ECDH(curve);
|
|
|
|
validateString(curve, 'curve');
|
|
this[kHandle] = new _ECDH(curve);
|
|
}
|
|
|
|
ECDH.prototype.computeSecret = DiffieHellman.prototype.computeSecret;
|
|
ECDH.prototype.setPrivateKey = DiffieHellman.prototype.setPrivateKey;
|
|
ECDH.prototype.setPublicKey = DiffieHellman.prototype.setPublicKey;
|
|
ECDH.prototype.getPrivateKey = DiffieHellman.prototype.getPrivateKey;
|
|
|
|
ECDH.prototype.generateKeys = function generateKeys(encoding, format) {
|
|
this[kHandle].generateKeys();
|
|
|
|
return this.getPublicKey(encoding, format);
|
|
};
|
|
|
|
ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) {
|
|
const f = getFormat(format);
|
|
const key = this[kHandle].getPublicKey(f);
|
|
encoding = encoding || getDefaultEncoding();
|
|
return encode(key, encoding);
|
|
};
|
|
|
|
ECDH.convertKey = function convertKey(key, curve, inEnc, outEnc, format) {
|
|
if (typeof key !== 'string' && !isArrayBufferView(key)) {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'key',
|
|
['string', 'Buffer', 'TypedArray', 'DataView'],
|
|
key
|
|
);
|
|
}
|
|
|
|
validateString(curve, 'curve');
|
|
|
|
const encoding = getDefaultEncoding();
|
|
inEnc = inEnc || encoding;
|
|
outEnc = outEnc || encoding;
|
|
const f = getFormat(format);
|
|
const convertedKey = _ECDHConvertKey(toBuf(key, inEnc), curve, f);
|
|
return encode(convertedKey, outEnc);
|
|
};
|
|
|
|
function encode(buffer, encoding) {
|
|
if (encoding && encoding !== 'buffer')
|
|
buffer = buffer.toString(encoding);
|
|
return buffer;
|
|
}
|
|
|
|
function getFormat(format) {
|
|
if (format) {
|
|
if (format === 'compressed')
|
|
return POINT_CONVERSION_COMPRESSED;
|
|
if (format === 'hybrid')
|
|
return POINT_CONVERSION_HYBRID;
|
|
if (format !== 'uncompressed')
|
|
throw new ERR_CRYPTO_ECDH_INVALID_FORMAT(format);
|
|
}
|
|
return POINT_CONVERSION_UNCOMPRESSED;
|
|
}
|
|
|
|
const dhEnabledKeyTypes = new Set(['dh', 'ec', 'x448', 'x25519']);
|
|
|
|
function diffieHellman(options) {
|
|
if (typeof options !== 'object')
|
|
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
|
|
|
|
const { privateKey, publicKey } = options;
|
|
if (!(privateKey instanceof KeyObject))
|
|
throw new ERR_INVALID_ARG_VALUE('options.privateKey', privateKey);
|
|
|
|
if (!(publicKey instanceof KeyObject))
|
|
throw new ERR_INVALID_ARG_VALUE('options.publicKey', publicKey);
|
|
|
|
if (privateKey.type !== 'private')
|
|
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(privateKey.type, 'private');
|
|
|
|
if (publicKey.type !== 'public' && publicKey.type !== 'private') {
|
|
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(publicKey.type,
|
|
'private or public');
|
|
}
|
|
|
|
const privateType = privateKey.asymmetricKeyType;
|
|
const publicType = publicKey.asymmetricKeyType;
|
|
if (privateType !== publicType || !dhEnabledKeyTypes.has(privateType)) {
|
|
throw new ERR_CRYPTO_INCOMPATIBLE_KEY('key types for Diffie-Hellman',
|
|
`${privateType} and ${publicType}`);
|
|
}
|
|
|
|
return statelessDH(privateKey[kHandle], publicKey[kHandle]);
|
|
}
|
|
|
|
module.exports = {
|
|
DiffieHellman,
|
|
DiffieHellmanGroup,
|
|
ECDH,
|
|
diffieHellman
|
|
};
|