From 41a17db2df9e75ee336225675ff09768ddad970a Mon Sep 17 00:00:00 2001 From: Wei-Wei Wu Date: Thu, 1 Mar 2018 17:13:12 -0800 Subject: [PATCH 1/8] crypto: ECDH function to convert public keys ECDH.convertKey is used to convert public keys between different formats. Fixes: https://github.com/nodejs/node/issues/18977 --- lib/internal/crypto/diffiehellman.js | 27 ++++++++++- src/node_crypto.cc | 68 ++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index f6d525c215ca63..8d4149174513aa 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -14,7 +14,8 @@ const { const { DiffieHellman: _DiffieHellman, DiffieHellmanGroup: _DiffieHellmanGroup, - ECDH: _ECDH + ECDH: _ECDH, + ECDHConvertKey: _ECDHConvertKey } = process.binding('crypto'); const { POINT_CONVERSION_COMPRESSED, @@ -218,6 +219,30 @@ ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { return key; }; +ECDH.convertKey = function convertKey(key, curve, inEnc, outEnc, format) { + const encoding = getDefaultEncoding(); + inEnc = inEnc || encoding; + outEnc = outEnc || encoding; + var f; + if (format) { + if (format === 'compressed') + f = POINT_CONVERSION_COMPRESSED; + else if (format === 'hybrid') + f = POINT_CONVERSION_HYBRID; + // Default + else if (format === 'uncompressed') + f = POINT_CONVERSION_UNCOMPRESSED; + else + throw new errors.TypeError('ERR_CRYPTO_ECDH_INVALID_FORMAT', format); + } else { + f = POINT_CONVERSION_UNCOMPRESSED; + } + var convertedKey = _ECDHConvertKey(toBuf(key, inEnc), curve, f); + if (outEnc && outEnc !== 'buffer') + convertedKey = convertedKey.toString(outEnc); + return convertedKey; +}; + module.exports = { DiffieHellman, DiffieHellmanGroup, diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 5c5981b76b3688..2fd6a65ef8b1f2 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -5550,6 +5550,72 @@ void ExportChallenge(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(outString); } + +// convert public key to compressed, uncompressed, hybrid format +void ConvertKey(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK_EQ(args.Length(), 3); + + THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Public key"); + + size_t len = Buffer::Length(args[0]); + if (len == 0) + return args.GetReturnValue().SetEmptyString(); + + THROW_AND_RETURN_IF_NOT_STRING(args[1], "ECDH curve name"); + node::Utf8Value curve(env->isolate(), args[1]); + + int nid = OBJ_sn2nid(*curve); + if (nid == NID_undef) + return env->ThrowTypeError("Second argument should be a valid curve name"); + + EC_GROUP* group = EC_GROUP_new_by_curve_name(nid); + if (group == nullptr) + return env->ThrowError("Failed to get EC_GROUP"); + + EC_POINT* pub = EC_POINT_new(group); + if (pub == nullptr) + return env->ThrowError("Failed to allocate EC_POINT for a public key"); + + int r = EC_POINT_oct2point( + group, + pub, + reinterpret_cast(Buffer::Data(args[0])), + len, + nullptr); + if (!r) + return env->ThrowError("Failed to convert key to point"); + + if (pub == nullptr) + return env->ThrowError("Failed to convert Buffer to EC_POINT"); + + // convert to the specified format + int size; + point_conversion_form_t form = + static_cast(args[2]->Uint32Value()); + + size = EC_POINT_point2oct(group, pub, form, nullptr, 0, nullptr); + if (size == 0) + return env->ThrowError("Failed to get public key length"); + + unsigned char* out = node::Malloc(size); + + r = EC_POINT_point2oct(group, pub, form, out, size, nullptr); + if (r != size) { + free(out); + return env->ThrowError("Failed to get public key"); + } + + Local buf = + Buffer::New(env, reinterpret_cast(out), size).ToLocalChecked(); + args.GetReturnValue().Set(buf); + + EC_GROUP_free(group); + EC_POINT_free(pub); +} + + void TimingSafeEqual(const FunctionCallbackInfo& args) { CHECK(Buffer::HasInstance(args[0])); CHECK(Buffer::HasInstance(args[1])); @@ -5692,6 +5758,8 @@ void InitCrypto(Local target, env->SetMethod(target, "certVerifySpkac", VerifySpkac); env->SetMethod(target, "certExportPublicKey", ExportPublicKey); env->SetMethod(target, "certExportChallenge", ExportChallenge); + + env->SetMethod(target, "ECDHConvertKey", ConvertKey); #ifndef OPENSSL_NO_ENGINE env->SetMethod(target, "setEngine", SetEngine); #endif // !OPENSSL_NO_ENGINE From 9ded80cc0dd854788979b7c1118283598adaaf0c Mon Sep 17 00:00:00 2001 From: Wei-Wei Wu Date: Fri, 2 Mar 2018 13:08:03 -0800 Subject: [PATCH 2/8] crypto: refactoring shared code refactored ECDH::BufferToPoint and key formatting --- lib/internal/crypto/diffiehellman.js | 38 ++++++++++++---------------- src/node_crypto.cc | 38 +++++++++++++--------------- src/node_crypto.h | 6 +++-- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index 8d4149174513aa..7224c84441f911 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -198,31 +198,28 @@ ECDH.prototype.generateKeys = function generateKeys(encoding, format) { }; ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { - var f; - if (format) { - if (format === 'compressed') - f = POINT_CONVERSION_COMPRESSED; - else if (format === 'hybrid') - f = POINT_CONVERSION_HYBRID; - // Default - else if (format === 'uncompressed') - f = POINT_CONVERSION_UNCOMPRESSED; - else - throw new ERR_CRYPTO_ECDH_INVALID_FORMAT(format); - } else { - f = POINT_CONVERSION_UNCOMPRESSED; - } + var f = getFormat(format); var key = this._handle.getPublicKey(f); encoding = encoding || getDefaultEncoding(); - if (encoding && encoding !== 'buffer') - key = key.toString(encoding); - return key; + return encode(key, encoding); }; ECDH.convertKey = function convertKey(key, curve, inEnc, outEnc, format) { const encoding = getDefaultEncoding(); inEnc = inEnc || encoding; outEnc = outEnc || encoding; + var f = getFormat(format); + var 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) { var f; if (format) { if (format === 'compressed') @@ -237,11 +234,8 @@ ECDH.convertKey = function convertKey(key, curve, inEnc, outEnc, format) { } else { f = POINT_CONVERSION_UNCOMPRESSED; } - var convertedKey = _ECDHConvertKey(toBuf(key, inEnc), curve, f); - if (outEnc && outEnc !== 'buffer') - convertedKey = convertedKey.toString(outEnc); - return convertedKey; -}; + return f; +} module.exports = { DiffieHellman, diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 2fd6a65ef8b1f2..646f17fc779765 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4684,18 +4684,21 @@ void ECDH::GenerateKeys(const FunctionCallbackInfo& args) { } -EC_POINT* ECDH::BufferToPoint(char* data, size_t len) { +EC_POINT* ECDH::BufferToPoint(Environment* env, + const EC_GROUP* group, + char* data, + size_t len) { EC_POINT* pub; int r; - pub = EC_POINT_new(group_); + pub = EC_POINT_new(group); if (pub == nullptr) { - env()->ThrowError("Failed to allocate EC_POINT for a public key"); + env->ThrowError("Failed to allocate EC_POINT for a public key"); return nullptr; } r = EC_POINT_oct2point( - group_, + group, pub, reinterpret_cast(data), len, @@ -4725,7 +4728,9 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { if (!ecdh->IsKeyPairValid()) return env->ThrowError("Invalid key pair"); - EC_POINT* pub = ecdh->BufferToPoint(Buffer::Data(args[0]), + EC_POINT* pub = ECDH::BufferToPoint(env, + ecdh->group_, + Buffer::Data(args[0]), Buffer::Length(args[0])); if (pub == nullptr) { args.GetReturnValue().Set( @@ -4874,7 +4879,9 @@ void ECDH::SetPublicKey(const FunctionCallbackInfo& args) { MarkPopErrorOnReturn mark_pop_error_on_return; - EC_POINT* pub = ecdh->BufferToPoint(Buffer::Data(args[0].As()), + EC_POINT* pub = ECDH::BufferToPoint(env, + ecdh->group_, + Buffer::Data(args[0].As()), Buffer::Length(args[0].As())); if (pub == nullptr) return env->ThrowError("Failed to convert Buffer to EC_POINT"); @@ -5574,19 +5581,10 @@ void ConvertKey(const FunctionCallbackInfo& args) { if (group == nullptr) return env->ThrowError("Failed to get EC_GROUP"); - EC_POINT* pub = EC_POINT_new(group); - if (pub == nullptr) - return env->ThrowError("Failed to allocate EC_POINT for a public key"); - - int r = EC_POINT_oct2point( - group, - pub, - reinterpret_cast(Buffer::Data(args[0])), - len, - nullptr); - if (!r) - return env->ThrowError("Failed to convert key to point"); - + EC_POINT* pub = ECDH::BufferToPoint(env, + group, + Buffer::Data(args[0]), + len); if (pub == nullptr) return env->ThrowError("Failed to convert Buffer to EC_POINT"); @@ -5601,7 +5599,7 @@ void ConvertKey(const FunctionCallbackInfo& args) { unsigned char* out = node::Malloc(size); - r = EC_POINT_point2oct(group, pub, form, out, size, nullptr); + int r = EC_POINT_point2oct(group, pub, form, out, size, nullptr); if (r != size) { free(out); return env->ThrowError("Failed to get public key"); diff --git a/src/node_crypto.h b/src/node_crypto.h index 05ea79f71f0565..a7c7205e519365 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -625,6 +625,10 @@ class ECDH : public BaseObject { } static void Initialize(Environment* env, v8::Local target); + static EC_POINT* BufferToPoint(Environment* env, + const EC_GROUP* group, + char* data, + size_t len); protected: ECDH(Environment* env, v8::Local wrap, EC_KEY* key) @@ -643,8 +647,6 @@ class ECDH : public BaseObject { static void GetPublicKey(const v8::FunctionCallbackInfo& args); static void SetPublicKey(const v8::FunctionCallbackInfo& args); - EC_POINT* BufferToPoint(char* data, size_t len); - bool IsKeyPairValid(); bool IsKeyValidForCurve(const BIGNUM* private_key); From e876f36d92891a78c340edd92fba92961060fd3d Mon Sep 17 00:00:00 2001 From: Wei-Wei Wu Date: Fri, 2 Mar 2018 15:33:29 -0800 Subject: [PATCH 3/8] test: adding tests for ECDH#convertKey Adding tests to verify that convertkey correctly converts ECDH keys to different formats --- src/node_crypto.cc | 2 +- test/parallel/test-crypto-ecdh-convert-key.js | 104 ++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-crypto-ecdh-convert-key.js diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 646f17fc779765..11345e955ebb32 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -5575,7 +5575,7 @@ void ConvertKey(const FunctionCallbackInfo& args) { int nid = OBJ_sn2nid(*curve); if (nid == NID_undef) - return env->ThrowTypeError("Second argument should be a valid curve name"); + return env->ThrowTypeError("Invalid ECDH curve name"); EC_GROUP* group = EC_GROUP_new_by_curve_name(nid); if (group == nullptr) diff --git a/test/parallel/test-crypto-ecdh-convert-key.js b/test/parallel/test-crypto-ecdh-convert-key.js new file mode 100644 index 00000000000000..5c29af63fbe2eb --- /dev/null +++ b/test/parallel/test-crypto-ecdh-convert-key.js @@ -0,0 +1,104 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +const ECDH = crypto.ECDH; + +// A valid private key for the secp256k1 curve. +const cafebabeKey = 'cafebabe'.repeat(8); +// Associated compressed and uncompressed public keys (points). +const cafebabePubPtComp = +'03672a31bfc59d3f04548ec9b7daeeba2f61814e8ccc40448045007f5479f693a3'; +const cafebabePubPtUnComp = +'04672a31bfc59d3f04548ec9b7daeeba2f61814e8ccc40448045007f5479f693a3' + +'2e02c7f93d13dc2732b760ca377a5897b9dd41a1c1b29dc0442fdce6d0a04d1d'; + +// invalid test: key argument is undefined +common.expectsError( + () => ECDH.convertKey(), + { + type: TypeError, + message: 'Public key must be a buffer' + }); + +// invalid test: curve argument is undefined +common.expectsError( + () => ECDH.convertKey(cafebabePubPtComp), + { + type: TypeError, + message: 'ECDH curve name must be a string' + }); + +// invalid test: curve argument is invalid +common.expectsError( + () => ECDH.convertKey(cafebabePubPtComp, 'badcurve'), + { + type: TypeError, + message: 'Invalid ECDH curve name' + }); + +const availableCurves = new Set(crypto.getCurves()); + +if (availableCurves.has('secp256k1')) { + // invalid test: format argument is undefined + common.expectsError( + () => ECDH.convertKey(cafebabePubPtComp, 'secp256k1', 'hex', 'hex', 10), + { + code: 'ERR_CRYPTO_ECDH_INVALID_FORMAT', + type: TypeError, + message: 'Invalid ECDH format: 10' + }); + + // Point formats + let uncompressed = ECDH.convertKey(cafebabePubPtComp, + 'secp256k1', + 'hex', + 'buffer', + 'uncompressed'); + let compressed = ECDH.convertKey(cafebabePubPtComp, + 'secp256k1', + 'hex', + 'buffer', + 'compressed'); + let hybrid = ECDH.convertKey(cafebabePubPtComp, + 'secp256k1', + 'hex', + 'buffer', + 'hybrid'); + assert.strictEqual(uncompressed[0], 4); + let firstByte = compressed[0]; + assert(firstByte === 2 || firstByte === 3); + firstByte = hybrid[0]; + assert(firstByte === 6 || firstByte === 7); + + // format conversion + uncompressed = ECDH.convertKey(cafebabePubPtComp, + 'secp256k1', + 'hex', + 'hex', + 'uncompressed'); + compressed = ECDH.convertKey(cafebabePubPtComp, + 'secp256k1', + 'hex', + 'hex', + 'compressed'); + hybrid = ECDH.convertKey(cafebabePubPtComp, + 'secp256k1', + 'hex', + 'hex', + 'hybrid'); + assert.strictEqual(uncompressed, cafebabePubPtUnComp); + assert.strictEqual(compressed, cafebabePubPtComp); + + // compare to getPublicKey + const ecdh1 = ECDH('secp256k1'); + ecdh1.generateKeys(); + ecdh1.setPrivateKey(cafebabeKey, 'hex'); + assert.strictEqual(ecdh1.getPublicKey('hex', 'uncompressed'), uncompressed); + assert.strictEqual(ecdh1.getPublicKey('hex', 'compressed'), compressed); + assert.strictEqual(ecdh1.getPublicKey('hex', 'hybrid'), hybrid); +} From 810cb370d8173491bffe0c8d32f9954450f7b86a Mon Sep 17 00:00:00 2001 From: Wei-Wei Wu Date: Fri, 2 Mar 2018 16:45:30 -0800 Subject: [PATCH 4/8] doc: adding docs for ECDH#convertKey Adding docs and usage examples. --- doc/api/crypto.md | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 94e7dcd15734b3..1c5dba0a74e315 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -656,6 +656,54 @@ assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex')); // OK ``` +### ECDH.convertKey(key, curve[, inputEncoding][, outputEncoding][, format]) + + +- `key` {string | Buffer | TypedArray | DataView} +- `curve` {string} +- `inputEncoding` {string} +- `outputEncoding` {string} +- `format` {string} Defaults to `uncompressed`. + +Converts the EC Diffie-Hellman public key specified by `key` and `curve` to the +format specified by `format`. The `format` argument specifies point encoding +and can be `'compressed'`, `'uncompressed'` or `'hybrid'`. The supplied key is +interpreted using the specified `inputEncoding`, and the returned key is encoded +using the specified `outputEncoding`. Encodings can be `'latin1'`, `'hex'`, +or `'base64'`. + +Use [`crypto.getCurves()`][] to obtain a list of available curve names. +On recent OpenSSL releases, `openssl ecparam -list_curves` will also display +the name and description of each available elliptic curve. + +If `format` is not specified the point will be returned in `'uncompressed'` +format. + +If the `inputEncoding` is not provided, `key` is expected to be a [`Buffer`][], +`TypedArray`, or `DataView`. + +Example (uncompressing a key): + +```js +const ECDH = require('crypto').ECDH; + +const ecdh = ECDH('secp256k1'); +ecdh.generateKeys(); + +const compressedKey = ecdh.getPublicKey('hex', 'compressed'); + +const uncompressedKey = ECDH.convertKey(compressedKey, + 'secp256k1', + 'hex', + 'hex', + 'uncompressed'); + +// the converted key and the uncompressed public key should be the same +console.log(uncompressedKey === ecdh.getPublicKey('hex')); +``` + ### ecdh.computeSecret(otherPublicKey[, inputEncoding][, outputEncoding]) - `key` {string | Buffer | TypedArray | DataView} @@ -687,7 +687,7 @@ If the `inputEncoding` is not provided, `key` is expected to be a [`Buffer`][], Example (uncompressing a key): ```js -const ECDH = require('crypto').ECDH; +const { ECDH } = require('crypto'); const ecdh = ECDH('secp256k1'); ecdh.generateKeys(); diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index 5fe0389b187118..788bf380b24e31 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -85,7 +85,7 @@ DiffieHellmanGroup.prototype.generateKeys = dhGenerateKeys; function dhGenerateKeys(encoding) { - var keys = this._handle.generateKeys(); + const keys = this._handle.generateKeys(); encoding = encoding || getDefaultEncoding(); return encode(keys, encoding); } @@ -99,7 +99,7 @@ function dhComputeSecret(key, inEnc, outEnc) { const encoding = getDefaultEncoding(); inEnc = inEnc || encoding; outEnc = outEnc || encoding; - var ret = this._handle.computeSecret(toBuf(key, inEnc)); + const ret = this._handle.computeSecret(toBuf(key, inEnc)); if (typeof ret === 'string') throw new ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY(); return encode(ret, outEnc); @@ -111,7 +111,7 @@ DiffieHellmanGroup.prototype.getPrime = dhGetPrime; function dhGetPrime(encoding) { - var prime = this._handle.getPrime(); + const prime = this._handle.getPrime(); encoding = encoding || getDefaultEncoding(); return encode(prime, encoding); } @@ -122,7 +122,7 @@ DiffieHellmanGroup.prototype.getGenerator = dhGetGenerator; function dhGetGenerator(encoding) { - var generator = this._handle.getGenerator(); + const generator = this._handle.getGenerator(); encoding = encoding || getDefaultEncoding(); return encode(generator, encoding); } @@ -133,7 +133,7 @@ DiffieHellmanGroup.prototype.getPublicKey = dhGetPublicKey; function dhGetPublicKey(encoding) { - var key = this._handle.getPublicKey(); + const key = this._handle.getPublicKey(); encoding = encoding || getDefaultEncoding(); return encode(key, encoding); } @@ -144,7 +144,7 @@ DiffieHellmanGroup.prototype.getPrivateKey = dhGetPrivateKey; function dhGetPrivateKey(encoding) { - var key = this._handle.getPrivateKey(); + const key = this._handle.getPrivateKey(); encoding = encoding || getDefaultEncoding(); return encode(key, encoding); } @@ -186,8 +186,8 @@ ECDH.prototype.generateKeys = function generateKeys(encoding, format) { }; ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { - var f = getFormat(format); - var key = this._handle.getPublicKey(f); + const f = getFormat(format); + const key = this._handle.getPublicKey(f); encoding = encoding || getDefaultEncoding(); return encode(key, encoding); }; @@ -196,8 +196,8 @@ ECDH.convertKey = function convertKey(key, curve, inEnc, outEnc, format) { const encoding = getDefaultEncoding(); inEnc = inEnc || encoding; outEnc = outEnc || encoding; - var f = getFormat(format); - var convertedKey = _ECDHConvertKey(toBuf(key, inEnc), curve, f); + const f = getFormat(format); + const convertedKey = _ECDHConvertKey(toBuf(key, inEnc), curve, f); return encode(convertedKey, outEnc); }; @@ -208,7 +208,7 @@ function encode(buffer, encoding) { } function getFormat(format) { - var f; + let f; if (format) { if (format === 'compressed') f = POINT_CONVERSION_COMPRESSED; diff --git a/test/parallel/test-crypto-ecdh-convert-key.js b/test/parallel/test-crypto-ecdh-convert-key.js index af8eb0e10fb5d3..857b44423d1ae0 100644 --- a/test/parallel/test-crypto-ecdh-convert-key.js +++ b/test/parallel/test-crypto-ecdh-convert-key.js @@ -6,7 +6,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); -const ECDH = crypto.ECDH; +const { ECDH, getCurves } = crypto; // A valid private key for the secp256k1 curve. const cafebabeKey = 'cafebabe'.repeat(8); @@ -41,7 +41,7 @@ common.expectsError( message: 'Invalid ECDH curve name' }); -const availableCurves = new Set(crypto.getCurves()); +const availableCurves = new Set(getCurves()); if (availableCurves.has('secp256k1')) { // Invalid test: format argument is undefined. From c201c264d76b87c64b4a27699ecfdc3ec83760f3 Mon Sep 17 00:00:00 2001 From: Wei-Wei Wu Date: Mon, 5 Mar 2018 11:09:42 -0800 Subject: [PATCH 7/8] crypto: input typechecking Handle input typechecking in JavaScript using errors. Minor fixes and updated tests --- lib/internal/crypto/diffiehellman.js | 9 +++++++++ src/node_crypto.cc | 3 --- test/parallel/test-crypto-ecdh-convert-key.js | 11 ++++------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index 788bf380b24e31..731721b0f0c1c8 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -193,6 +193,15 @@ ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { }; ECDH.convertKey = function convertKey(key, curve, inEnc, outEnc, format) { + if (typeof key !== 'string' && !isArrayBufferView(key)) { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'key', + ['string', 'Buffer', 'TypedArray', 'DataView']); + } + + if (typeof curve !== 'string') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'curve', 'string'); + } + const encoding = getDefaultEncoding(); inEnc = inEnc || encoding; outEnc = outEnc || encoding; diff --git a/src/node_crypto.cc b/src/node_crypto.cc index b4091f577dfc0d..08fa3dad87a8cf 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -5561,13 +5561,10 @@ void ConvertKey(const FunctionCallbackInfo& args) { CHECK_EQ(args.Length(), 3); - THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Public key"); - size_t len = Buffer::Length(args[0]); if (len == 0) return args.GetReturnValue().SetEmptyString(); - THROW_AND_RETURN_IF_NOT_STRING(args[1], "ECDH curve name"); node::Utf8Value curve(env->isolate(), args[1]); int nid = OBJ_sn2nid(*curve); diff --git a/test/parallel/test-crypto-ecdh-convert-key.js b/test/parallel/test-crypto-ecdh-convert-key.js index 857b44423d1ae0..407178a1953250 100644 --- a/test/parallel/test-crypto-ecdh-convert-key.js +++ b/test/parallel/test-crypto-ecdh-convert-key.js @@ -4,9 +4,8 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const crypto = require('crypto'); -const { ECDH, getCurves } = crypto; +const { ECDH, getCurves } = require('crypto'); // A valid private key for the secp256k1 curve. const cafebabeKey = 'cafebabe'.repeat(8); @@ -21,16 +20,16 @@ const cafebabePubPtUnComp = common.expectsError( () => ECDH.convertKey(), { + code: 'ERR_INVALID_ARG_TYPE', type: TypeError, - message: 'Public key must be a buffer' }); // Invalid test: curve argument is undefined. common.expectsError( () => ECDH.convertKey(cafebabePubPtComp), { + code: 'ERR_INVALID_ARG_TYPE', type: TypeError, - message: 'ECDH curve name must be a string' }); // Invalid test: curve argument is invalid. @@ -41,9 +40,7 @@ common.expectsError( message: 'Invalid ECDH curve name' }); -const availableCurves = new Set(getCurves()); - -if (availableCurves.has('secp256k1')) { +if (getCurves().includes('secp256k1')) { // Invalid test: format argument is undefined. common.expectsError( () => ECDH.convertKey(cafebabePubPtComp, 'secp256k1', 'hex', 'hex', 10), From 541a651d568ab0b103ee4b68bf355a63236835dc Mon Sep 17 00:00:00 2001 From: Wei-Wei Wu Date: Sat, 10 Mar 2018 13:06:17 -0800 Subject: [PATCH 8/8] crypto: using the updated errors Using the new error throwing mechanism. --- lib/internal/crypto/diffiehellman.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index 731721b0f0c1c8..793f3fb351460e 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -194,12 +194,14 @@ ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { ECDH.convertKey = function convertKey(key, curve, inEnc, outEnc, format) { if (typeof key !== 'string' && !isArrayBufferView(key)) { - throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'key', - ['string', 'Buffer', 'TypedArray', 'DataView']); + throw new ERR_INVALID_ARG_TYPE( + 'key', + ['string', 'Buffer', 'TypedArray', 'DataView'] + ); } if (typeof curve !== 'string') { - throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'curve', 'string'); + throw new ERR_INVALID_ARG_TYPE('curve', 'string'); } const encoding = getDefaultEncoding(); @@ -227,7 +229,7 @@ function getFormat(format) { else if (format === 'uncompressed') f = POINT_CONVERSION_UNCOMPRESSED; else - throw new errors.TypeError('ERR_CRYPTO_ECDH_INVALID_FORMAT', format); + throw new ERR_CRYPTO_ECDH_INVALID_FORMAT(format); } else { f = POINT_CONVERSION_UNCOMPRESSED; }