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: ecdh - generate public key when setting private key and more #3511

Closed
wants to merge 1 commit into from
Closed

Conversation

mruddy
Copy link

@mruddy mruddy commented Oct 25, 2015

crypto: ecdh updates to support using previously known private keys

  • generate public key when setting private key
  • remove "generated" checks from getters
  • added tests
  • updated docs
  • check the key pair before computing the secret to avoid computing a garbage shared secret
  • added ECDH.validKeyPair
  • marked ECDH.setPublicKey as deprecated

This pull request shares many similarities with #1020.

I decided not to add the public key generation as a separate method like was proposed in #1020.
Rather I made it part of setting the private key.
I liked this way more because then it leaves two main ways to use the class. Either generate the keys, or set the private key then compute the shared secret.

Originally I started looking at this code because the following error was annoying and unnecessary (and is why I removed the generated_ member from ECDH):

> node -p "require('crypto').createECDH('secp256k1').setPrivateKey(new Buffer('1111111111111111111111111111111111111111111111111111111111111111', 'hex')).getPublicKey()"
  crypto.js:526
    var key = this._handle.getPublicKey(f);
                           ^

  Error: You should generate ECDH keys first
      at Error (native)
      at ECDH.getPublicKey (crypto.js:526:26)

@mscdex mscdex added the crypto Issues and PRs related to the crypto subsystem. label Oct 25, 2015
@mruddy
Copy link
Author

mruddy commented Oct 25, 2015

Added ECDH.validKeyPair (and tests) since it's useful to be able to validate a key pair that was stored previously and used again. I added this as part of the public API, instead of just validating the key pair at the end of ECDH.setPrivateKey, since setPublicKey is still part of the public API and since having the ability to validate a key pair is also tangentially useful for allowing the creation and validation of EC key pairs in general using only the crypto module (a nice little side feature).

Also, marked ECDH.setPublicKey as deprecated in the docs since it's inclusion as part of the API is not useful. Either a previously stored private key should be set, which automatically generates the associated public key, or generateKeys should be called. The main drawback of ECDH.setPublicKey is that it can be used to put the key pair into an inconsistent state.

Updated docs and tests accordingly and squashed everything into one commit..

@jasnell jasnell added the semver-minor PRs that contain new features and should be released in the next minor version. label Oct 26, 2015
@bnoordhuis
Copy link
Member

Sorry, looks like this fell by the wayside. Can you rebase against master? I'll do a quick review.

/cc @nodejs/crypto

@@ -4624,6 +4620,26 @@ void ECDH::SetPrivateKey(const FunctionCallbackInfo<Value>& args) {
if (!result) {
return env->ThrowError("Failed to convert BN to a private key");
}

const BIGNUM* privKey = EC_KEY_get0_private_key(ecdh->key_);
Copy link
Member

Choose a reason for hiding this comment

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

Style issue: priv_key (but maybe prefer private_key.)

@mruddy
Copy link
Author

mruddy commented Nov 11, 2015

@bnoordhuis Thanks for the review. I've rebased, incorporated changes for all of your feedback, and squashed it all into a new commit.

About the one naming style issue, I went with "priv_key" to avoid a long line lint caused by using the name "private_key".

About the EC_KEY_check_key error stack side-effect possibility, thanks for catching that! That caught me by surprise. That wasn't mentioned in the library doc that I read. But, looking that the OpenSSL source, you're totally correct. Let me know if you want the error stack handled differently than I did. I could have used ClearErrorOnReturn or ERR_clear_error, but went with ERR_get_error to try to do as little as possible.

Yes, that hex pub key passed the linter and it still does :)

@mruddy
Copy link
Author

mruddy commented Nov 11, 2015

Also, I just did a quick dig through some more OpenSSL and noticed that the error stack can also be changed by the following call chains. I'll likely have to make more updates to ECDH::SetPrivateKey because of this. I'll work on that later, just noting as something todo for this PR.

  • EC_POINT_new
  • EC_KEY_set_public_key
    EC_POINT_dup
    EC_POINT_new

@mruddy
Copy link
Author

mruddy commented Nov 12, 2015

Just added a commit to add ClearErrorOnReturn to handle any errors being put on the openssl error stack within ECDH::SetPrivateKey. Handling this way seemed to fit the pattern for how other code in the file was already handling similar situations.

Note: ECDH::BufferToPoint could use a similar change to handle anything that it leaves on the openssl error stack too. I did not touch that code since I didn't want to grow my commit with tangential changes.

@jasnell
Copy link
Member

jasnell commented Nov 16, 2015

@shigeki @indutny @bnoordhuis ... ping
@mruddy ... unfortunately this is going to need another rebase before it could land :-/

This allows you to only store and provide the private part of the EC key pair.
To support verifying that the private key used is valid for the curve, the
method `ECDH.validKeyPair` was added. `ECDH.setPublicKey` was marked as
deprecated since it's inclusion as part of the API is not useful. Either a
Copy link
Member

Choose a reason for hiding this comment

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

Typo: s/it's/its/

@bnoordhuis
Copy link
Member

@mruddy Can you rebase against master?

I'd like at least one other member of @nodejs/crypto to take a look at this.

@mruddy
Copy link
Author

mruddy commented Nov 16, 2015

@bnoordhuis Thanks for the useful feedback! I rebased again and made the updates that were mentioned.

Thanks for the tip on ERR_set_mark + ERR_pop_to_mark. Those capture more what I wanted to do. I created MarkPopErrorOnReturn (using those two funcs) and use it from the two places where I needed to cleanup the OpenSSL error stack now.

@jasnell
Copy link
Member

jasnell commented Nov 17, 2015

@shigeki
Copy link
Contributor

shigeki commented Nov 17, 2015

Sorry for being late. Ben deeply takes care of reviewing code so no need to change it but I have three comments for this API change.

  • Deprecation of ECDH.setPublicKey()
    I agree that it has no uss but the deprecation here is only marked on the doc. Do we leave API until release 6.0?
  • Inconsistent behavior between ECDH and DH
    DH key exchange still need DH.generateKey() at first. Do we also change DH?
  • Check of private key length
    The current ECDH.setPrivateKey() does not check key length validity. I've tested that both smaller and larger keys can be accepted but large one cannot compute secret. One idea is to have a key length check in setPrivateKey() accroding to group_->order but most of curve defined it in bits with its name and not byte alignmented. On the other hand, ECDH.generateKeys() has its advantage to generate random up to its key length. I'm not strongly oppose this PR but we should take care of private key length since it affects its strength.

@mruddy
Copy link
Author

mruddy commented Nov 17, 2015

@jasnell Can we run the CI again? I looked through the failure logs and they were only for the ARM builds and only logged "TIMEOUT". I'm thinking maybe it was a build system problem, but can't be sure. 2 of the 3 failures name a script that I didn't touch for this PR and that doesn't appear to use my changes (directly at least). Here are the log messages that I found:

@bnoordhuis
Copy link
Member

I filed #3881 for the failing tests, I agree they're unrelated flakes.

@mruddy
Copy link
Author

mruddy commented Nov 17, 2015

To answer @shigeki:

  • Good question. I marked the deprecation of ECDH.setPublicKey in the documentation only because I was not sure of the release process that would be used. I know ECDH.setPublicKey has to be left around for some time after we first say that it's going away, to give people a chance to update. I wanted to be able to deploy these changes quickly. If we mark ECDH.setPublicKey as deprecated in a near dated upcoming release and actually remove it when these changes go in (delaying their deployment to a further future release), then it may not be necessary at all to ever add ECDH.isValid to the API (I basically kept ECDH.isValid because we allow setting the public key into an invalid state with ECDH.setPublicKey). Further direction is welcome.
  • Inconsistent behavior between ECDH and DH: Might make sense, but as a different PR.
  • I did not make ECDH::SetPrivateKey call ECDH::IsKeyPairValid because ECDH::SetPublicKey is still part of the API and can be used to put the object into a bad state. ECDH::ComputeSecret does call ECDH::IsKeyPairValid to fail out early in such an event. ECDH::IsKeyPairValid validates that the public point is on the curve and that the private key corresponds to the public point, all with EC_KEY_check_key. I could make ECDH::SetPrivateKey call ECDH::IsKeyPairValid, but being able to set the private key to an invalid value and then check it with ECDH.isValid is similar. If we go with removing ECDH.setPublicKey and never making ECDH.isValid part of the API, then this check needs to be added to ECDH::SetPrivateKey.

One other thing I was aiming to fix with these changes is that right now a secret can be computed with a private key value of zero, which is invalid and not good.

@shigeki
Copy link
Contributor

shigeki commented Nov 17, 2015

  • ECDH.setPublicKey deprecation.
    To be included in 5.x, I'd like to have a comment from release team.

    @jasnell @Fishrock123 ECDH.setPublicKey is a candidate of deprecation API for 6.0. This PR is only marking deprecation in the doc. Is it valid to be include 5.x?

  • DH API change
    @mruddy Are you planning to submit an another PR for DH key exchange?

  • key length check
    I missed to find that EC_KEY_check_key() has its private key length check. ECDH.isValid() can check when key length is larger than group order. I'm still not sure why it is not included in ECDH::SetPrivateKey(). Is there any reason to have an object in a bad state?

    And ECDH.isValid() still does not check a smaller key size. Using ECDH.generateKeys() can avoid the risk to use a small key size by mistake. I think it is better to add some warning in the description if a small key size is permitted. I agree that all zero of a private key is not good and should be checked.

@jasnell
Copy link
Member

jasnell commented Nov 17, 2015

Personally, I'm fine with the "soft" deprecation but printing the warning would likely be the best option. Would ask how other @nodejs/ctc members feel tho.

@mruddy
Copy link
Author

mruddy commented Nov 17, 2015

@shigeki No, I'm not planning to submit a PR for the non-EC DH API. Also, I just added another commit that adds the ECDH::IsKeyValidForCurve check into ECDH::SetPrivateKey. I also updated the unit tests accordingly. I left it as a separate commit for easy diff.

@shigeki
Copy link
Contributor

shigeki commented Nov 18, 2015

@mruddy Thanks. Checking a private kye by IsKeyValidForCurve is fine with me. But the author and message in your commit need to follow our contribution format. Please refer https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#step-3-commit and change them.
I will work DH API and doc change to be consistent with ECDH later.

@bnoordhuis One more commit is added after your review. PTAL.

@jasnell I agree it is better to add warning. The api is shared with DH api so that I will work it with DH api change later.

@mruddy
Copy link
Author

mruddy commented Nov 19, 2015

@shigeki @bnoordhuis I'm just working on the latest updates based on your feedback (and a couple minor things I noticed too). Looking at my changes, now that we check the validity of the private key and generate the public key in ECDH::SetPrivateKey, I'm leaning towards removing my addition of ECDH.isValid.

Before I put together a commit with that removed, do you agree?

I'm thinking that adding ECDH.isValid really will only help people check that ECDH.setPublicKey was not used to put the pair into an inconsistent state. ECDH.setPublicKey is being deprecated and the docs explain why. Plus the public key doesn't matter for the local secret computation. So, after ECDH.setPublicKey finally goes away, I don't see any reason for having added ECDH.isValid to the API. Plus it'll make this change set smaller.

@mruddy
Copy link
Author

mruddy commented Nov 22, 2015

Last CI build was some Jenkins failure soon after it was kicked it off.

@bnoordhuis
Copy link
Member

@mruddy
Copy link
Author

mruddy commented Nov 23, 2015

Build system failed again.

@targos
Copy link
Member

targos commented Nov 23, 2015

nodejs/build#263

@mscdex
Copy link
Contributor

mscdex commented Dec 4, 2015

FWIW CI is all green except some strange issue with node-test-binary-windows. Looks like a CI issue though.

@Trott
Copy link
Member

Trott commented Dec 5, 2015

CI, once more with feeling: https://ci.nodejs.org/job/node-test-pull-request/929/

@Trott
Copy link
Member

Trott commented Dec 5, 2015

Only red failure on Windows is known-flaky test-net-error-twice.js. (That should be fixed, by the way, if #4062 lands. Yes, that's thinly-veiled plea for a review on that one.)

ARM failure is a CI failure.

Everything else is green.

TL;DR: CI looks good.

@mruddy
Copy link
Author

mruddy commented Dec 5, 2015

Thanks for the extra eyes on the CI failures. I agree that the failures are not caused by this PR's changes.

bnoordhuis pushed a commit that referenced this pull request Dec 7, 2015
These changes simplify using ECDH with private keys that are not
dynamically generated with ECDH.generateKeys.

Support for computing the public key corresponding to the given private
key was added. Validity checks to reduce the possibility of computing
a weak or invalid shared secret were also added.

Finally, ECDH.setPublicKey was softly deprecated.

PR-URL: #3511
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Shigeki Ohtsu <ohtsu@iij.ad.jp>
@bnoordhuis
Copy link
Member

Thanks Michael, landed in 322b36c.

@bnoordhuis bnoordhuis closed this Dec 7, 2015
rvagg pushed a commit that referenced this pull request Dec 8, 2015
These changes simplify using ECDH with private keys that are not
dynamically generated with ECDH.generateKeys.

Support for computing the public key corresponding to the given private
key was added. Validity checks to reduce the possibility of computing
a weak or invalid shared secret were also added.

Finally, ECDH.setPublicKey was softly deprecated.

PR-URL: #3511
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Shigeki Ohtsu <ohtsu@iij.ad.jp>
rvagg added a commit that referenced this pull request Dec 9, 2015
Notable changes:

* build:
  - Add support for Intel's VTune JIT profiling when compiled with
    --enable-vtune-profiling. For more information about VTune, see
    https://software.intel.com/en-us/node/544211. (Chunyang Dai) #3785.
  - Properly enable V8 snapshots by default. Due to a configuration
    error, snapshots have been kept off by default when the intention
    is for the feature to be enabled. (Fedor Indutny) #3962.
* crypto:
  - Simplify use of ECDH (Elliptic Curve Diffie-Hellman) objects
    (created via crypto.createECDH(curve_name)) with private keys that
    are not dynamically generated via generateKeys(). The public key
    is now computed when explicitly setting a private key. Added
    validity checks to reduce the possibility of computing weak or
    invalid shared secrets. Also, deprecated the setPublicKey() method
    for ECDH objects as its usage is unnecessary and can lead to
    inconsistent state. (Michael Ruddy) #3511.
  - Update root certificates from the current list stored maintained
    by Mozilla NSS. (Ben Noordhuis) #3951.
  - Multiple CA certificates can now be passed with the ca option to
    TLS methods as an array of strings or in a single new-line
    separated string. (Ben Noordhuis) #4099
* tools: Include a tick processor in core, exposed via the
  --prof-process command-line argument which can be used to process V8
  profiling output files generated when using the --prof command-line
  argument. (Matt Loring) #4021.

PR-URL: #4181
rvagg added a commit that referenced this pull request Dec 9, 2015
Notable changes:

* build:
  - Add support for Intel's VTune JIT profiling when compiled with
    --enable-vtune-profiling. For more information about VTune, see
    https://software.intel.com/en-us/node/544211. (Chunyang Dai) #3785.
  - Properly enable V8 snapshots by default. Due to a configuration
    error, snapshots have been kept off by default when the intention
    is for the feature to be enabled. (Fedor Indutny) #3962.
* crypto:
  - Simplify use of ECDH (Elliptic Curve Diffie-Hellman) objects
    (created via crypto.createECDH(curve_name)) with private keys that
    are not dynamically generated via generateKeys(). The public key
    is now computed when explicitly setting a private key. Added
    validity checks to reduce the possibility of computing weak or
    invalid shared secrets. Also, deprecated the setPublicKey() method
    for ECDH objects as its usage is unnecessary and can lead to
    inconsistent state. (Michael Ruddy) #3511.
  - Update root certificates from the current list stored maintained
    by Mozilla NSS. (Ben Noordhuis) #3951.
  - Multiple CA certificates can now be passed with the ca option to
    TLS methods as an array of strings or in a single new-line
    separated string. (Ben Noordhuis) #4099
* tools: Include a tick processor in core, exposed via the
  --prof-process command-line argument which can be used to process V8
  profiling output files generated when using the --prof command-line
  argument. (Matt Loring) #4021.

PR-URL: #4181
scovetta pushed a commit to scovetta/node that referenced this pull request Apr 2, 2016
These changes simplify using ECDH with private keys that are not
dynamically generated with ECDH.generateKeys.

Support for computing the public key corresponding to the given private
key was added. Validity checks to reduce the possibility of computing
a weak or invalid shared secret were also added.

Finally, ECDH.setPublicKey was softly deprecated.

PR-URL: nodejs#3511
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Shigeki Ohtsu <ohtsu@iij.ad.jp>
scovetta pushed a commit to scovetta/node that referenced this pull request Apr 2, 2016
Notable changes:

* build:
  - Add support for Intel's VTune JIT profiling when compiled with
    --enable-vtune-profiling. For more information about VTune, see
    https://software.intel.com/en-us/node/544211. (Chunyang Dai) nodejs#3785.
  - Properly enable V8 snapshots by default. Due to a configuration
    error, snapshots have been kept off by default when the intention
    is for the feature to be enabled. (Fedor Indutny) nodejs#3962.
* crypto:
  - Simplify use of ECDH (Elliptic Curve Diffie-Hellman) objects
    (created via crypto.createECDH(curve_name)) with private keys that
    are not dynamically generated via generateKeys(). The public key
    is now computed when explicitly setting a private key. Added
    validity checks to reduce the possibility of computing weak or
    invalid shared secrets. Also, deprecated the setPublicKey() method
    for ECDH objects as its usage is unnecessary and can lead to
    inconsistent state. (Michael Ruddy) nodejs#3511.
  - Update root certificates from the current list stored maintained
    by Mozilla NSS. (Ben Noordhuis) nodejs#3951.
  - Multiple CA certificates can now be passed with the ca option to
    TLS methods as an array of strings or in a single new-line
    separated string. (Ben Noordhuis) nodejs#4099
* tools: Include a tick processor in core, exposed via the
  --prof-process command-line argument which can be used to process V8
  profiling output files generated when using the --prof command-line
  argument. (Matt Loring) nodejs#4021.

PR-URL: nodejs#4181
tniessen added a commit to tniessen/node that referenced this pull request Jan 30, 2022
The ECDH API changes were made more than six years ago and this
section is not helpful for new applications. The behavior of the ECDH
APIs should be explained in the relevant sections, not in a note.

Refs: nodejs#3511
nodejs-github-bot pushed a commit that referenced this pull request Jan 31, 2022
The ECDH API changes were made more than six years ago and this
section is not helpful for new applications. The behavior of the ECDH
APIs should be explained in the relevant sections, not in a note.

Refs: #3511

PR-URL: #41773
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
ruyadorno pushed a commit that referenced this pull request Feb 8, 2022
The ECDH API changes were made more than six years ago and this
section is not helpful for new applications. The behavior of the ECDH
APIs should be explained in the relevant sections, not in a note.

Refs: #3511

PR-URL: #41773
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
danielleadams pushed a commit that referenced this pull request Mar 2, 2022
The ECDH API changes were made more than six years ago and this
section is not helpful for new applications. The behavior of the ECDH
APIs should be explained in the relevant sections, not in a note.

Refs: #3511

PR-URL: #41773
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
danielleadams pushed a commit that referenced this pull request Mar 3, 2022
The ECDH API changes were made more than six years ago and this
section is not helpful for new applications. The behavior of the ECDH
APIs should be explained in the relevant sections, not in a note.

Refs: #3511

PR-URL: #41773
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
danielleadams pushed a commit that referenced this pull request Mar 14, 2022
The ECDH API changes were made more than six years ago and this
section is not helpful for new applications. The behavior of the ECDH
APIs should be explained in the relevant sections, not in a note.

Refs: #3511

PR-URL: #41773
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
crypto Issues and PRs related to the crypto subsystem. semver-minor PRs that contain new features and should be released in the next minor version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants