From 6c035487efa2d5879d0af5b0178d75e9f987f488 Mon Sep 17 00:00:00 2001 From: mbullington Date: Sun, 1 Mar 2015 17:15:48 -0500 Subject: [PATCH 1/2] 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 03687c75c3deda..5b038190f54553 100644 --- a/doc/api/crypto.markdown +++ b/doc/api/crypto.markdown @@ -550,6 +550,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.computeSecret(other_public_key[, input_encoding][, output_encoding]) Computes the shared secret using `other_public_key` as the other diff --git a/lib/crypto.js b/lib/crypto.js index 6033a85e57fd24..dd8d72b1161d33 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -535,6 +535,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 912320771e3f65..55871b353d3725 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4025,6 +4025,7 @@ void ECDH::Initialize(Environment* env, Handle 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); @@ -4062,10 +4063,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; @@ -4095,7 +4119,6 @@ EC_POINT* ECDH::BufferToPoint(char* data, size_t len) { return nullptr; } - void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -4124,7 +4147,6 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(Buffer::Use(env, out, out_len)); } - void ECDH::GetPublicKey(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -4133,9 +4155,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"); @@ -4168,9 +4187,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"); @@ -4224,7 +4240,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 75ffe4f31ddeea..88d35e192bea3f 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -635,7 +635,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); @@ -644,6 +643,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); @@ -652,7 +652,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 e9240477b35cbd..1f71fece6e6b17 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -178,3 +178,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 076917d2c6632e2008ca6003d965e1350458bf31 Mon Sep 17 00:00:00 2001 From: Michael Bullington Date: Mon, 2 Mar 2015 19:03:03 -0500 Subject: [PATCH 2/2] 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 dd8d72b1161d33..a0612aad9bba64 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -535,7 +535,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);