-
Notifications
You must be signed in to change notification settings - Fork 29.9k
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: ECDH convertKey to convert public keys between different formats #19080
Conversation
9060702
to
7869581
Compare
lib/internal/crypto/diffiehellman.js
Outdated
if (outEnc && outEnc !== 'buffer') | ||
convertedKey = convertedKey.toString(outEnc); | ||
return convertedKey; | ||
}; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
lib/internal/crypto/diffiehellman.js
Outdated
return encode(convertedKey, outEnc); | ||
}; | ||
|
||
function encode(buffer, encoding) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bnoordhuis I also added the encode
function since I noticed that the test for 'buffer'
encoding happens a lot in this file. Should I also change that in this PR or should I submit another PR for that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, doing it in this PR is fine.
61fe99b
to
bd78e2c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly LGTM apart from some comments. Thanks for doing this.
lib/internal/crypto/diffiehellman.js
Outdated
return encode(convertedKey, outEnc); | ||
}; | ||
|
||
function encode(buffer, encoding) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, doing it in this PR is fine.
return nullptr; | ||
} | ||
|
||
r = EC_POINT_oct2point( | ||
group_, | ||
group, | ||
pub, | ||
reinterpret_cast<unsigned char*>(data), | ||
len, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you're making changes here, you might want to get rid of the goto
by moving the fatal:
block inside the if statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
src/node_crypto.cc
Outdated
@@ -5532,6 +5539,63 @@ void ExportChallenge(const FunctionCallbackInfo<Value>& args) { | |||
args.GetReturnValue().Set(outString); | |||
} | |||
|
|||
|
|||
// convert public key to compressed, uncompressed, hybrid format |
There was a problem hiding this comment.
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?
src/node_crypto.cc
Outdated
Buffer::Data(args[0]), | ||
len); | ||
if (pub == nullptr) | ||
return env->ThrowError("Failed to convert Buffer to EC_POINT"); |
There was a problem hiding this comment.
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.
src/node_crypto.cc
Outdated
point_conversion_form_t form = | ||
static_cast<point_conversion_form_t>(args[2]->Uint32Value()); | ||
|
||
size = EC_POINT_point2oct(group, pub, form, nullptr, 0, nullptr); |
There was a problem hiding this comment.
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 = ...
?
const cafebabeKey = 'cafebabe'.repeat(8); | ||
// Associated compressed and uncompressed public keys (points). | ||
const cafebabePubPtComp = | ||
'03672a31bfc59d3f04548ec9b7daeeba2f61814e8ccc40448045007f5479f693a3'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you indent these by four spaces? Four because line continuation.
Buffer::Data(args[0]), | ||
len); | ||
|
||
std::shared_ptr<void> cleanup(nullptr, [group, pub] (...) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bnoordhuis I am not sure if I am using shared_ptr
correctly. I created it before the pub === nullptr
check so that returns. the delete function will be called
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wuweiweiwu we also have OnScopeLeave
in util.h
, you could use that if you prefer? It might be a bit more self-documenting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@addaleax I will look at that! Is there a preference using onScopeLeave
v. shared_ptr
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wuweiweiwu The former is pretty new as an utility -- but basically, use whichever makes sense to you, or lets the code seem more readable. :)
(Also, probably doesn't matter much now that this has landed, unless you want to send another PR right behind this. ¯\_(ツ)_/¯)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good! I'll go over both so I am more familiar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, LGTM. CI: https://ci.nodejs.org/job/node-test-pull-request/13488/
This PR should probably get one or two more sign-offs because it adds a new API. Also, cc @nodejs/crypto.
node-test-commit failure looks unrelated
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well done!
doc/api/crypto.md
Outdated
@@ -656,6 +656,54 @@ assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex')); | |||
// OK | |||
``` | |||
|
|||
### ECDH.convertKey(key, curve[, inputEncoding][, outputEncoding][, format]) | |||
<!-- YAML | |||
added: v10.0.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be added: REPLACEME
(it might be a different version on other release branches, not necessarily v10.0.0
).
lib/internal/crypto/diffiehellman.js
Outdated
@@ -192,6 +181,28 @@ ECDH.prototype.generateKeys = function generateKeys(encoding, format) { | |||
}; | |||
|
|||
ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { | |||
var f = getFormat(format); | |||
var key = this._handle.getPublicKey(f); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason not to use const
here? In general, let
is preferred over var
and const
over let
if possible.
lib/internal/crypto/diffiehellman.js
Outdated
inEnc = inEnc || encoding; | ||
outEnc = outEnc || encoding; | ||
var f = getFormat(format); | ||
var convertedKey = _ECDHConvertKey(toBuf(key, inEnc), curve, f); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
const assert = require('assert'); | ||
const crypto = require('crypto'); | ||
|
||
const ECDH = crypto.ECDH; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I'd prefer destructuring here: const { ECDH } = crypto;
, maybe even const { ECDH, getCurves } = require('crypto');
.
doc/api/crypto.md
Outdated
@@ -656,6 +656,54 @@ assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex')); | |||
// OK | |||
``` | |||
|
|||
### ECDH.convertKey(key, curve[, inputEncoding][, outputEncoding][, format]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this signature correct? According to this signature, all of the following calls would be valid:
ECDH.convertKey(key, curve[, inputEncoding][, outputEncoding][, format])
ECDH.convertKey(key, curve[, inputEncoding][, outputEncoding])
ECDH.convertKey(key, curve[, inputEncoding])
ECDH.convertKey(key, curve)
ECDH.convertKey(key, curve[, inputEncoding][, format])
ECDH.convertKey(key, curve[, outputEncoding][, format])
ECDH.convertKey(key, curve[, format])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tniessen How would I structure the signature?
Function signature?
key
and curve
are required but the rest are optional. These are the available calls:
ECDH.convertKey(key, curve, inputEncoding, outputEncoding, format)
ECDH.convertKey(key, curve, inputEncoding, outputEncoding)
ECDH.convertKey(key, curve, inputEncoding)
ECDH.convertKey(key, curve)
Would it be: ECDH.convertKey(key, curve[, inputEncoding, outputEncoding, format])
?
or just ECDH.convertKey(key, curve, inputEncoding, outputEncoding, format)
I figured it out!
ECDH.convertKey(key, curve[, inputEncoding[, outputEncoding[, format]]])
doc/api/crypto.md
Outdated
Example (uncompressing a key): | ||
|
||
```js | ||
const ECDH = require('crypto').ECDH; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Use destructuring: const { ECDH } = require('crypto');
.
|
||
const availableCurves = new Set(getCurves()); | ||
|
||
if (availableCurves.has('secp256k1')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tiny nit: No need for a set, just use if (getCurves().includes('secp256k1')) {
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed! :)
const assert = require('assert'); | ||
const crypto = require('crypto'); | ||
|
||
const { ECDH, getCurves } = crypto; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tiny nit: This can be combined with the call to require:
const assert = require('assert');
const { ECDH, getCurves } = require('crypto');
src/node_crypto.cc
Outdated
|
||
CHECK_EQ(args.Length(), 3); | ||
|
||
THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Public key"); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! :)
@wuweiweiwu Please rebase your changes on top of # This is only necessary if you did not add the upstream remote before.
git remote add upstream https://github.com/nodejs/node.git
# Checkout your branch.
git checkout crypto-convert-key
# Undo the merge commit.
git reset --hard 7a921c56bd19cb2e7fa68862bcfa023fedc817b8
# Make sure your copy of our master branch is up-to-date.
git fetch upstream master
# Rebase on top of our changes.
git rebase upstream/master
|
ECDH.convertKey is used to convert public keys between different formats. Fixes: nodejs#18977
refactored ECDH::BufferToPoint and key formatting
Adding tests to verify that convertkey correctly converts ECDH keys to different formats
Adding docs and usage examples.
Using std::shared_ptr to clean up pointers. Formatting changes. Refactoring encode function.
Using const over var. Destructuring notation. Updated function signature.
Handle input typechecking in JavaScript using errors. Minor fixes and updated tests
9f2490f
to
c201c26
Compare
Using the new error throwing mechanism.
@tniessen Thanks for the tips! I have rebased and also updated my error throwing using the new errors. |
Thank you! CI: https://ci.nodejs.org/job/node-test-pull-request/13624/ |
Not sure why test-process-env-deprecation fails for those test cases09:16:20 not ok 1280 parallel/test-process-env-deprecation
09:16:20 ---
09:16:20 duration_ms: 0.297
09:16:20 severity: fail
09:16:20 stack: |-
09:16:20 Mismatched <anonymous> function calls. Expected exactly 1, actual 0.
09:16:20 at Object.exports.mustCall (/home/iojs/build/workspace/node-test-commit-linux-containered/nodes/ubuntu1604_sharedlibs_withoutintl_x64/test/common/index.js:427:10)
09:16:20 at expectWarning (/home/iojs/build/workspace/node-test-commit-linux-containered/nodes/ubuntu1604_sharedlibs_withoutintl_x64/test/common/index.js:615:18)
09:16:20 at expectWarningByName (/home/iojs/build/workspace/node-test-commit-linux-containered/nodes/ubuntu1604_sharedlibs_withoutintl_x64/test/common/index.js:629:25)
09:16:20 at Object.exports.expectWarning (/home/iojs/build/workspace/node-test-commit-linux-containered/nodes/ubuntu1604_sharedlibs_withoutintl_x64/test/common/index.js:649:5)
09:16:20 at Object.<anonymous> (/home/iojs/build/workspace/node-test-commit-linux-containered/nodes/ubuntu1604_sharedlibs_withoutintl_x64/test/parallel/test-process-env-deprecation.js:7:8)
09:16:20 at Module._compile (module.js:671:30)
09:16:20 at Object.Module._extensions..js (module.js:682:10)
09:16:20 at Module.load (module.js:582:32)
09:16:20 at tryModuleLoad (module.js:522:12)
09:16:20 ... It passes locally. @tniessen Is it something I need to fix? |
I think the CI problems were on master and not in this PR and that they've been fixed. Let's find out... |
Offending test: sequential/test-inspector-stop-profile-after-done
@Trott seems unrelated. But this test was passing for me locally. What should I do? |
@wuweiweiwu That test failing on Windows in CI is a known issue: #19263 I think this can be treated as "CI has passed" at this point. Also: "Node.js project needs to get CI back to green, this is ridiculous." But that's not your problem to solve (unless you want to). :-D |
/ping @nodejs/crypto |
@Trott just checking up on the status of this PR! |
@wuweiweiwu I didn't have time to review this thoroughly, but this should be ready to land nevertheless. I will try to review and land on Saturday if no one beats me to it. One more CI: https://ci.nodejs.org/job/node-test-pull-request/13826/ |
Re-running FreeBSD: https://ci.nodejs.org/job/node-test-commit-freebsd/16379/ Re-running arm-fanned: https://ci.nodejs.org/job/node-test-commit-arm-fanned/15339/ |
Both re-runs of CI were green. This can land. |
Landed in f2e0288, thank you @wuweiweiwu! 🎉 |
Awesome! Can't wait to contribute more :) |
ECDH.convertKey is used to convert public keys between different formats. PR-URL: nodejs#19080 Fixes: nodejs#18977 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
ECDH#convertKey is used to convert public keys between different formats, as per #18977
ECDH#convertKey(key, curve[, inputEncoding[, outputEncoding[, format]]])
key
<string>
|<Buffer>
|<TypedArray>
|<DataView>
curve
<string>
inputEncoding
<string>
outputEncoding
<string>
format
<string>
can be'uncompressed'
,'compressed'
,'hybrid'
Returns the input key converted to the specified encoding and format.
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
crypto, doc, test