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..00a975058ed12d 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(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..c9c65fc42cf5ce 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..e2fb2bcaae8caa 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,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 ComputeSecret(const v8::FunctionCallbackInfo& args); static void GetPrivateKey(const v8::FunctionCallbackInfo& args); static void SetPrivateKey(const v8::FunctionCallbackInfo& args); @@ -710,7 +711,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..2ec60839b2182e 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.strictEqual(ecdh5.generatePublicKey('hex'), ecdh5.getPublicKey('hex'));