-
Notifications
You must be signed in to change notification settings - Fork 30k
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
RFC: support for HSM private keys in TLS handshake #28973
Conversation
b020c1b
to
33cf16f
Compare
Needs documentation and a unit test |
@nodejs/crypto |
lib/_tls_common.js
Outdated
@@ -153,6 +153,38 @@ exports.createSecureContext = function createSecureContext(options) { | |||
} | |||
} | |||
|
|||
const engineKey = options.engineKey; | |||
const privateKeyEngine = options.privateKeyEngine; | |||
if (engineKey && privateKeyEngine) { |
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.
Node.js has traditionally been lazy when it comes to input validation, but new APIs should be stricter: Use !== undefined
or something similar to not ignore inputs such as engineKey: 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.
Also, I don't know if it would make sense to validate types even if only one option is given? Or to even throw if only one is given? Is there any use case for only specifying one of these options?
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 any use case for only specifying one of these options?
@tniessen From the OpenSSL point of view, you can extract multiple keys from one engine, but since we only have one key in the SecureContext, they should probably go together.
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.
I agree, but I would prefer to throw when only one of the options is given. (Currently, you are ignoring the options if only one is used.) If there is no legitimate use case for only specifying one of the two options, then it is most likely an error or a mistake, and we shouldn't silently ignore 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.
I'm not sure this comment was addressed, though the code was outdated, since if only one of engine or keyId is provided, they are both ignored.
I can't comment much on the matter, maybe @bnoordhuis or @sam-github can, I never used OpenSSL engines. These options would be for server keys then, not for client keys? The other addition, |
@tniessen Thank you for your review, your comments are extremely valuable for a newcomer like myself. I am addressing all of them in the coming version.
These options are for the communication party under control of Node.js. If it's a server (which is a more common scenario with Node.js, as far as I know), then it will be server keys, the example in my first comment is rather a client. |
Fixed hopefully all the issues, added documentation and tests. |
doc/api/tls.md
Outdated
@@ -1425,6 +1425,11 @@ changes: | |||
passphrase: <string>]}`. The object form can only occur in an array. | |||
`object.passphrase` is optional. Encrypted keys will be decrypted with | |||
`object.passphrase` if provided, or `options.passphrase` if it is not. | |||
* `privateKeyEngine` {string} Name of an OpenSSL engine to get private key | |||
from. Should be use together with `engineKey`, otherwise ignored. |
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:
from. Should be use together with `engineKey`, otherwise ignored. | |
from. Should be used together with `engineKey`, otherwise ignored. |
doc/api/tls.md
Outdated
* `privateKeyEngine` {string} Name of an OpenSSL engine to get private key | ||
from. Should be use together with `engineKey`, otherwise ignored. | ||
* `engineKey` {string} Identifier of a private key managed by OpenSSL engine. | ||
Should be use together with `privateKeyEngine`, otherwise ignored. Overrides |
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 (it seems this needs rewrapping to conform with 80 chars limit):
Should be use together with `privateKeyEngine`, otherwise ignored. Overrides | |
Should be used together with `privateKeyEngine`, otherwise ignored. Overrides |
/ping @nodejs/crypto @rvagg Any opinions on this? If we're definitely not going to merge this, I'd like to close it. But if we might merge it, it would be great to nudge it forward a bit.... |
I don't see a reason to reject this out of hand, seems like a reasonable ask if the code can get up to scratch. Solid tests are tricky to do for it though, without building and loading in a custom engine so I'm not sure how to view that problem—maybe in the same way that we view the problem of testing commits that deal with LibreSSL? Caveat emptor, it's expected the committer and users of the code in question do the testing IRL? Is that an OK standard for crypto? |
I'll add my 2 cents that it is the kind of thing we want one the PR is in good shape. |
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.
I'm fine with this. BUT, it's important to note here that the tests are only asserting (a) the existence of the API, (b) the API argument validation and (c) what happens when used with an OpenSSL compiled with -no-engines
which is not the normal mode we ship, so is perhaps more of an edge-case. The tests don't test anything about the keys coming in via the API. In coverage terms, this is adding a lot more uncovered code and not improving existing coverage. Such a test is possible (I think) but would involve creating a dummy engine and having it loaded during test time. The awkwardness of creating a test environment for this to happen might be significant, I'm not sure, but I'm fairly sure it's at least possible if someone felt the need to hold this up on needing proper tests.
Would like to hear additional thoughts. @sam-github @bnoordhuis @tniessen @addaleax?
@vsemozhetbyt @rvagg @mhdawson @jasnell Thank you for your reviews, everything should be fixed now (CI job should be restarted probably, it seems the failure is unrelated to the PR). I agree that a dummy engine is the way to test this functionality meaningfully. If you decide it's the way to go, I can try to implement that. |
I'd agree with @rvagg that it would be better to avoid adding code that we can't test. If you can try to create a test with a dummy engine that would be great. |
if you can give it a go that would be great @OYTIS, I don't imagine it'll be simple but if you come up with a mechanism to compile and load a custom engine during test time then we could go back and apply it to the custom engine use for certs. |
We kindof support engines, in that there is code and some docs, but we don't test the support, so its hard to know it works, what it works for, if the docs are correct, etc. All of which is to say, if someone has the time to add a dummy engine that would be great. Or possibly, if there is a software only engine available on Linux, perhaps the tests could search for it, and use it if available? That would allow testing locally for developers, and allow us to add a container test that uses engines. |
Sorry, I'm a bit short on time now, I'll try to finish that shortly. From what I can see the test in |
Signed-off-by: Anton Gerasimov <agerasimov@twilio.com>
Signed-off-by: Anton Gerasimov <agerasimov@twilio.com>
Rebased, probably Jenkins tests also need to be run. |
Is this ready to land? @jasnell @rvagg @tniessen @sam-github @OYTIS |
@Trott I've got no amendments from my side. |
Yes, landable now that CI is green. |
Add `privateKeyIdentifier` and `privateKeyEngine` options to get private key from an OpenSSL engine in tls.createSecureContext(). PR-URL: #28973 Reviewed-By: Rod Vagg <rod@vagg.org> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
Landed in c2ce8d0 |
Add `privateKeyIdentifier` and `privateKeyEngine` options to get private key from an OpenSSL engine in tls.createSecureContext(). PR-URL: #28973 Reviewed-By: Rod Vagg <rod@vagg.org> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
Notable changes: * build: * Add `--force-context-aware` flag to prevent usage of native node addons that aren't context aware #29631 * deprecations: * Add documentation-only deprecation for `process._tickCallback()` #29781 * esm: * Using JSON modules is experimental again #29754 * fs: * Introduce `opendir()` and `fs.Dir` to iterate through directories #29349 * process: * Add source-map support to stack traces by using `--source-map-support` #29564 * tls: * Honor `pauseOnConnect` option #29635 * Add option for private keys for OpenSSL engines #28973 PR-URL: #29919
Notable changes: * build: * Add `--force-context-aware` flag to prevent usage of native node addons that aren't context aware #29631 * deprecations: * Add documentation-only deprecation for `process._tickCallback()` #29781 * esm: * Using JSON modules is experimental again #29754 * fs: * Introduce `opendir()` and `fs.Dir` to iterate through directories #29349 * process: * Add source-map support to stack traces by using `--source-map-support` #29564 * tls: * Honor `pauseOnConnect` option #29635 * Add option for private keys for OpenSSL engines #28973 PR-URL: #29919
Notable changes: * build: * Add `--force-context-aware` flag to prevent usage of native node addons that aren't context aware #29631 * deprecations: * Add documentation-only deprecation for `process._tickCallback()` #29781 * esm: * Using JSON modules is experimental again #29754 * fs: * Introduce `opendir()` and `fs.Dir` to iterate through directories #29349 * process: * Add source-map support to stack traces by using `--source-map-support` #29564 * tls: * Honor `pauseOnConnect` option #29635 * Add option for private keys for OpenSSL engines #28973 PR-URL: #29919
Hi! I have a yubikey, and i guess this is the way to go for me when connecting with TLS? |
We have a few places where we individually forward each parameter to tls.createSecureContext(). In nodejs#28973 and others, we added new SecureContext options but forgot to keep these places up to date. As per https.Agent#getName, I understand that at least `privateKeyIdentifier` and `privateKeyEngine` should be added too, since they're a substitute for `key`. I've also added sigalgs. Fixes: nodejs#36322 Refs: nodejs#28973
We have a few places where we individually forward each parameter to tls.createSecureContext(). In #28973 and others, we added new SecureContext options but forgot to keep these places up to date. As per https.Agent#getName, I understand that at least `privateKeyIdentifier` and `privateKeyEngine` should be added too, since they're a substitute for `key`. I've also added sigalgs. Fixes: #36322 Refs: #28973 PR-URL: #36416 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
We have a few places where we individually forward each parameter to tls.createSecureContext(). In #28973 and others, we added new SecureContext options but forgot to keep these places up to date. As per https.Agent#getName, I understand that at least `privateKeyIdentifier` and `privateKeyEngine` should be added too, since they're a substitute for `key`. I've also added sigalgs. Fixes: #36322 Refs: #28973 PR-URL: #36416 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
We have a few places where we individually forward each parameter to tls.createSecureContext(). In #28973 and others, we added new SecureContext options but forgot to keep these places up to date. As per https.Agent#getName, I understand that at least `privateKeyIdentifier` and `privateKeyEngine` should be added too, since they're a substitute for `key`. I've also added sigalgs. Fixes: #36322 Refs: #28973 PR-URL: #36416 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
The use-case is briefly described here: #28921. I want to use a private key managed by an OpenSSL engine to establish a TLS connection.
Another option,
sigalgs
, is useful, among the other things, to adapt to the limitations of the hardware (e.g. if it doesn't support RSAPSS). I can make it a separate PR if it makes more sense.Usage example: