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..a0612aad9bba64 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 generatePublicKey(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'));