Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
crypto: ECDH function to convert public keys
ECDH.convertKey is used to convert public keys
between different formats.

Fixes: #18977
  • Loading branch information
wuweiweiwu committed Mar 10, 2018
commit 41a17db2df9e75ee336225675ff09768ddad970a
27 changes: 26 additions & 1 deletion lib/internal/crypto/diffiehellman.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const {
const {
DiffieHellman: _DiffieHellman,
DiffieHellmanGroup: _DiffieHellmanGroup,
ECDH: _ECDH
ECDH: _ECDH,
ECDHConvertKey: _ECDHConvertKey
} = process.binding('crypto');
const {
POINT_CONVERSION_COMPRESSED,
Expand Down Expand Up @@ -218,6 +219,30 @@ ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) {
return key;
};

ECDH.convertKey = function convertKey(key, curve, inEnc, outEnc, format) {
const encoding = getDefaultEncoding();
inEnc = inEnc || encoding;
outEnc = outEnc || encoding;
var f;
if (format) {
if (format === 'compressed')
f = POINT_CONVERSION_COMPRESSED;
else if (format === 'hybrid')
f = POINT_CONVERSION_HYBRID;
// Default
else if (format === 'uncompressed')
f = POINT_CONVERSION_UNCOMPRESSED;
else
throw new errors.TypeError('ERR_CRYPTO_ECDH_INVALID_FORMAT', format);
} else {
f = POINT_CONVERSION_UNCOMPRESSED;
}
var convertedKey = _ECDHConvertKey(toBuf(key, inEnc), curve, f);
if (outEnc && outEnc !== 'buffer')
convertedKey = convertedKey.toString(outEnc);
return convertedKey;
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't duplicate this logic, factor it out so it can be shared with ECDH#getPublicKey(). Likewise the C++ code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!


module.exports = {
DiffieHellman,
DiffieHellmanGroup,
Expand Down
68 changes: 68 additions & 0 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5550,6 +5550,72 @@ void ExportChallenge(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(outString);
}


// convert public key to compressed, uncompressed, hybrid format
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you capitalize and punctuate the comments?

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

CHECK_EQ(args.Length(), 3);

THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Public key");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is adding a new API, please handle the input type checking in javascript and use the internal/errors mechanism.

Copy link
Copy Markdown
Contributor Author

@wuweiweiwu wuweiweiwu Mar 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! :)


size_t len = Buffer::Length(args[0]);
if (len == 0)
return args.GetReturnValue().SetEmptyString();

THROW_AND_RETURN_IF_NOT_STRING(args[1], "ECDH curve name");
node::Utf8Value curve(env->isolate(), args[1]);

int nid = OBJ_sn2nid(*curve);
if (nid == NID_undef)
return env->ThrowTypeError("Second argument should be a valid curve name");

EC_GROUP* group = EC_GROUP_new_by_curve_name(nid);
if (group == nullptr)
return env->ThrowError("Failed to get EC_GROUP");

EC_POINT* pub = EC_POINT_new(group);
if (pub == nullptr)
return env->ThrowError("Failed to allocate EC_POINT for a public key");

int r = EC_POINT_oct2point(
group,
pub,
reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
len,
nullptr);
if (!r)
return env->ThrowError("Failed to convert key to point");

if (pub == nullptr)
return env->ThrowError("Failed to convert Buffer to EC_POINT");
Copy link
Copy Markdown
Member

@bnoordhuis bnoordhuis Mar 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaks group. group and pub are leaked on lines 5580 and 5587.

Suggestion: use a std::unique_ptr with a custom deleter or use the std::shared_ptr trick from 8ccd320.


// convert to the specified format
int size;
point_conversion_form_t form =
static_cast<point_conversion_form_t>(args[2]->Uint32Value());

size = EC_POINT_point2oct(group, pub, form, nullptr, 0, nullptr);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor thing but can you write this as int size = ...?

if (size == 0)
return env->ThrowError("Failed to get public key length");

unsigned char* out = node::Malloc<unsigned char>(size);

r = EC_POINT_point2oct(group, pub, form, out, size, nullptr);
if (r != size) {
free(out);
return env->ThrowError("Failed to get public key");
}

Local<Object> buf =
Buffer::New(env, reinterpret_cast<char*>(out), size).ToLocalChecked();
args.GetReturnValue().Set(buf);

EC_GROUP_free(group);
EC_POINT_free(pub);
}


void TimingSafeEqual(const FunctionCallbackInfo<Value>& args) {
CHECK(Buffer::HasInstance(args[0]));
CHECK(Buffer::HasInstance(args[1]));
Expand Down Expand Up @@ -5692,6 +5758,8 @@ void InitCrypto(Local<Object> target,
env->SetMethod(target, "certVerifySpkac", VerifySpkac);
env->SetMethod(target, "certExportPublicKey", ExportPublicKey);
env->SetMethod(target, "certExportChallenge", ExportChallenge);

env->SetMethod(target, "ECDHConvertKey", ConvertKey);
#ifndef OPENSSL_NO_ENGINE
env->SetMethod(target, "setEngine", SetEngine);
#endif // !OPENSSL_NO_ENGINE
Expand Down