Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: add generatePublicKey to ECDH, fix corner cases #4124

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/api/crypto.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions lib/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
38 changes: 27 additions & 11 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4619,6 +4619,7 @@ void ECDH::Initialize(Environment* env, Local<Object> 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);
Expand Down Expand Up @@ -4656,10 +4657,33 @@ void ECDH::GenerateKeys(const FunctionCallbackInfo<Value>& args) {

if (!EC_KEY_generate_key(ecdh->key_))
return env->ThrowError("Failed to generate EC_KEY");

ecdh->generated_ = true;
}

void ECDH::GeneratePublicKey(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

ECDH* ecdh = Unwrap<ECDH>(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;
Expand Down Expand Up @@ -4689,7 +4713,6 @@ EC_POINT* ECDH::BufferToPoint(char* data, size_t len) {
return nullptr;
}


void ECDH::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Expand Down Expand Up @@ -4719,7 +4742,6 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(buf);
}


void ECDH::GetPublicKey(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Expand All @@ -4728,9 +4750,6 @@ void ECDH::GetPublicKey(const FunctionCallbackInfo<Value>& args) {

ECDH* ecdh = Unwrap<ECDH>(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");
Expand Down Expand Up @@ -4763,9 +4782,6 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo<Value>& args) {

ECDH* ecdh = Unwrap<ECDH>(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");
Expand Down Expand Up @@ -4823,7 +4839,7 @@ void ECDH::SetPublicKey(const FunctionCallbackInfo<Value>& 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");
}


Expand Down
4 changes: 2 additions & 2 deletions src/node_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,6 @@ class ECDH : public BaseObject {
protected:
ECDH(Environment* env, v8::Local<v8::Object> wrap, EC_KEY* key)
: BaseObject(env, wrap),
generated_(false),
key_(key),
group_(EC_KEY_get0_group(key_)) {
MakeWeak<ECDH>(this);
Expand All @@ -702,6 +701,8 @@ class ECDH : public BaseObject {

static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GenerateKeys(const v8::FunctionCallbackInfo<v8::Value>& args);
static
void GeneratePublicKey(const v8::FunctionCallbackInfo<v8::Value>& args);
static void ComputeSecret(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetPrivateKey(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetPrivateKey(const v8::FunctionCallbackInfo<v8::Value>& args);
Expand All @@ -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_;
};
Expand Down
6 changes: 6 additions & 0 deletions test/parallel/test-crypto-dh.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'));