From 7a112418a3b208bc913f2ae529983dca6f950714 Mon Sep 17 00:00:00 2001 From: mbullington Date: Sun, 1 Mar 2015 17:15:48 -0500 Subject: [PATCH 1/3] crypto: add generatePublicKey to ECDH, fix corner cases ECDH.generatePublicKey can get the public key using the curve and private key. This allows usage of the crypto module where a developer imports a private key, and then generates a public key without needing it stored. Removed the generated_ boolean from the ECDH class, and stopped checking for it with getPrivateKey() and getPublicKey(). This allows you to import public and private keys without having to generate first, which would just rewrite the generated keys regardless. An error message was changed to accurately reflect what the error was. --- doc/api/crypto.markdown | 12 +++++++++++ lib/crypto.js | 6 ++++++ src/node_crypto.cc | 38 +++++++++++++++++++++++---------- src/node_crypto.h | 3 +-- test/parallel/test-crypto-dh.js | 6 ++++++ 5 files changed, 52 insertions(+), 13 deletions(-) diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown index 60231b17072692..107b88885b24d3 100644 --- a/doc/api/crypto.markdown +++ b/doc/api/crypto.markdown @@ -239,6 +239,18 @@ Format specifies point encoding and can be `'compressed'`, `'uncompressed'`, or Encoding can be `'binary'`, `'hex'`, or `'base64'`. If no encoding is provided, then a buffer is returned. +### ECDH.generatePublicKey([encoding[, format]]) + +Generates public EC Diffie-Hellman key value based on the provided private key +value. Returns the public key in the specified format and encoding. + +Format specifies point encoding and can be `'compressed'`, `'uncompressed'`, or +`'hybrid'`. If no format is provided - the point will be returned in +`'uncompressed'` format. + +Encoding can be `'binary'`, `'hex'`, or `'base64'`. If no encoding is provided, +then a buffer is returned. + ### ECDH.getPrivateKey([encoding]) Returns the EC Diffie-Hellman private key in the specified encoding, diff --git a/lib/crypto.js b/lib/crypto.js index bed7d7764e3ad5..4cc4afbb68eed9 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -506,6 +506,12 @@ ECDH.prototype.generateKeys = function generateKeys(encoding, format) { return this.getPublicKey(encoding, format); }; +ECDH.prototype.generatePublicKey = function generateKeys(encoding, format) { + this._handle.generatePublicKey(); + + return this.getPublicKey(encoding, format); +}; + ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { var f; if (format) { diff --git a/src/node_crypto.cc b/src/node_crypto.cc index bd7314c9db902c..1f1a8a34764280 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4619,6 +4619,7 @@ void ECDH::Initialize(Environment* env, Local target) { t->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(t, "generateKeys", GenerateKeys); + env->SetProtoMethod(t, "generatePublicKey", GeneratePublicKey); env->SetProtoMethod(t, "computeSecret", ComputeSecret); env->SetProtoMethod(t, "getPublicKey", GetPublicKey); env->SetProtoMethod(t, "getPrivateKey", GetPrivateKey); @@ -4656,10 +4657,33 @@ void ECDH::GenerateKeys(const FunctionCallbackInfo& args) { if (!EC_KEY_generate_key(ecdh->key_)) return env->ThrowError("Failed to generate EC_KEY"); - - ecdh->generated_ = true; } +void ECDH::GeneratePublicKey(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + ECDH* ecdh = Unwrap(args.Holder()); + + const BIGNUM* priv = EC_KEY_get0_private_key(ecdh->key_); + if (priv == nullptr) + return env->ThrowError("Failed to get ECDH private key"); + + EC_POINT* pub = EC_POINT_new(ecdh->group_); + if (pub == nullptr) + return env->ThrowError("Failed to allocate EC_POINT for a public key"); + + if(EC_POINT_mul(ecdh->group_, pub, priv, nullptr, nullptr, nullptr) <= 0) { + EC_POINT_free(pub); + return env->ThrowError("Failed to generate ECDH public key"); + } + + if(!EC_KEY_set_public_key(ecdh->key_, pub)) { + EC_POINT_free(pub); + return env->ThrowError("Failed to convert EC_POINT to a public key"); + } + + EC_POINT_free(pub); +} EC_POINT* ECDH::BufferToPoint(char* data, size_t len) { EC_POINT* pub; @@ -4689,7 +4713,6 @@ EC_POINT* ECDH::BufferToPoint(char* data, size_t len) { return nullptr; } - void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -4719,7 +4742,6 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(buf); } - void ECDH::GetPublicKey(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -4728,9 +4750,6 @@ void ECDH::GetPublicKey(const FunctionCallbackInfo& args) { ECDH* ecdh = Unwrap(args.Holder()); - if (!ecdh->generated_) - return env->ThrowError("You should generate ECDH keys first"); - const EC_POINT* pub = EC_KEY_get0_public_key(ecdh->key_); if (pub == nullptr) return env->ThrowError("Failed to get ECDH public key"); @@ -4763,9 +4782,6 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo& args) { ECDH* ecdh = Unwrap(args.Holder()); - if (!ecdh->generated_) - return env->ThrowError("You should generate ECDH keys first"); - const BIGNUM* b = EC_KEY_get0_private_key(ecdh->key_); if (b == nullptr) return env->ThrowError("Failed to get ECDH private key"); @@ -4823,7 +4839,7 @@ void ECDH::SetPublicKey(const FunctionCallbackInfo& args) { int r = EC_KEY_set_public_key(ecdh->key_, pub); EC_POINT_free(pub); if (!r) - return env->ThrowError("Failed to convert BN to a private key"); + return env->ThrowError("Failed to convert EC_POINT to a public key"); } diff --git a/src/node_crypto.h b/src/node_crypto.h index beeb2fb4458267..e77b59b82cb09d 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -693,7 +693,6 @@ class ECDH : public BaseObject { protected: ECDH(Environment* env, v8::Local wrap, EC_KEY* key) : BaseObject(env, wrap), - generated_(false), key_(key), group_(EC_KEY_get0_group(key_)) { MakeWeak(this); @@ -702,6 +701,7 @@ class ECDH : public BaseObject { static void New(const v8::FunctionCallbackInfo& args); static void GenerateKeys(const v8::FunctionCallbackInfo& args); + static void GeneratePublicKey(const v8::FunctionCallbackInfo& args); static void ComputeSecret(const v8::FunctionCallbackInfo& args); static void GetPrivateKey(const v8::FunctionCallbackInfo& args); static void SetPrivateKey(const v8::FunctionCallbackInfo& args); @@ -710,7 +710,6 @@ class ECDH : public BaseObject { EC_POINT* BufferToPoint(char* data, size_t len); - bool generated_; EC_KEY* key_; const EC_GROUP* group_; }; diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index f3e54c46a8e16b..d815897a17b9f4 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -183,3 +183,9 @@ ecdh4.setPublicKey(ecdh1.getPublicKey()); assert.throws(function() { ecdh4.setPublicKey(ecdh3.getPublicKey()); }); + +// ECDH should be able to generate public key from private key +var ecdh5 = crypto.createECDH('prime256v1'); +ecdh5.setPrivateKey(ecdh4.getPrivateKey()); + +assert(ecdh5.generatePublicKey('hex') === ecdh5.getPublicKey('hex')); From 15df59976a060b05b32177cdbb70cce2103148bb Mon Sep 17 00:00:00 2001 From: Michael Bullington Date: Mon, 2 Mar 2015 19:03:03 -0500 Subject: [PATCH 2/3] crypto: change function name Change the function name of generatePublicKey to be generatePublicKey instead of generateKeys. --- lib/crypto.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crypto.js b/lib/crypto.js index 4cc4afbb68eed9..4ed365874741d2 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -506,7 +506,7 @@ ECDH.prototype.generateKeys = function generateKeys(encoding, format) { return this.getPublicKey(encoding, format); }; -ECDH.prototype.generatePublicKey = function generateKeys(encoding, format) { +ECDH.prototype.generatePublicKey = function generatePublicKey(encoding, format) { this._handle.generatePublicKey(); return this.getPublicKey(encoding, format); From e43de84cab839d5cb71bac03e465253ef19f9ecb Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 2 Dec 2015 16:04:20 -0800 Subject: [PATCH 3/3] fixup --- lib/crypto.js | 2 +- src/node_crypto.cc | 4 ++-- src/node_crypto.h | 3 ++- test/parallel/test-crypto-dh.js | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/crypto.js b/lib/crypto.js index 4ed365874741d2..00a975058ed12d 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -506,7 +506,7 @@ ECDH.prototype.generateKeys = function generateKeys(encoding, format) { return this.getPublicKey(encoding, format); }; -ECDH.prototype.generatePublicKey = function generatePublicKey(encoding, format) { +ECDH.prototype.generatePublicKey = function(encoding, format) { this._handle.generatePublicKey(); return this.getPublicKey(encoding, format); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 1f1a8a34764280..c9c65fc42cf5ce 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4672,12 +4672,12 @@ void ECDH::GeneratePublicKey(const FunctionCallbackInfo& args) { if (pub == nullptr) return env->ThrowError("Failed to allocate EC_POINT for a public key"); - if(EC_POINT_mul(ecdh->group_, pub, priv, nullptr, nullptr, nullptr) <= 0) { + if (EC_POINT_mul(ecdh->group_, pub, priv, nullptr, nullptr, nullptr) <= 0) { EC_POINT_free(pub); return env->ThrowError("Failed to generate ECDH public key"); } - if(!EC_KEY_set_public_key(ecdh->key_, pub)) { + if (!EC_KEY_set_public_key(ecdh->key_, pub)) { EC_POINT_free(pub); return env->ThrowError("Failed to convert EC_POINT to a public key"); } diff --git a/src/node_crypto.h b/src/node_crypto.h index e77b59b82cb09d..e2fb2bcaae8caa 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -701,7 +701,8 @@ class ECDH : public BaseObject { static void New(const v8::FunctionCallbackInfo& args); static void GenerateKeys(const v8::FunctionCallbackInfo& args); - static void GeneratePublicKey(const v8::FunctionCallbackInfo& args); + static + void GeneratePublicKey(const v8::FunctionCallbackInfo& args); static void ComputeSecret(const v8::FunctionCallbackInfo& args); static void GetPrivateKey(const v8::FunctionCallbackInfo& args); static void SetPrivateKey(const v8::FunctionCallbackInfo& args); diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index d815897a17b9f4..2ec60839b2182e 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -188,4 +188,4 @@ assert.throws(function() { var ecdh5 = crypto.createECDH('prime256v1'); ecdh5.setPrivateKey(ecdh4.getPrivateKey()); -assert(ecdh5.generatePublicKey('hex') === ecdh5.getPublicKey('hex')); +assert.strictEqual(ecdh5.generatePublicKey('hex'), ecdh5.getPublicKey('hex'));