-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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: expose crypto.isKeyObject() helper #26200
Conversation
🤔 shall i expose this under crypto instead? |
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.
Instead of exposing the internal isKeyObject
function, it would be good to expose the KeyObject
class itself (as done with all other crypto classes). The function is only a helper for obj instanceof KeyObject
and that can be done as soon as the class is exposed.
@nodejs/crypto @tniessen was there a specific reason KeyObject
was not exposed in the first place?
@BridgeAR this was my very original question to @tniessen as well, quoting my communication with him
Now the constructor can only be called with internal handles so I tend to agree with not exposing it. Putting it under utils tho fails CI for ?reasons? (see https://travis-ci.com/nodejs/node/builds/101419655). I have it passing under |
@BridgeAR would you mind changing the PR label? |
@BridgeAR I'd prefer not to expose I suggested to add |
I couldn't get around See the two different approaches |
@tniessen I didn’t design much of that API, I just exposed it :)
|
Thanks for the input, @addaleax! :) I prefer |
I'm not totally clear on the purpose. Why is it necessary to make this check specifically for key objects? What about SecureContext, which has an Object form, or can be an actual SecureContext (like key objects)? Or ReadStream vs WriteStream? Etc. My concern there is about whether there is something specific to crypto KeyObjects that run-time checking like this is necessary (as opposed to just using the object, and letting the method error if its incorrect), and if its not specific to KeyObjects, rather than growing a crypto-wart API, maybe a more general pattern is required? For example, if I wanted a js function to check if its arg is a key object I would prefer to do something like this rather than is-checking:
For is-checking, I'm not opposed to this, but it feels a bit like an ad-hoc solution to an unspecified problem. |
Fail-fast and harmonizing the object a library will be working with. The exact use for me, is importing keys to a key store, specifically a JWKS and the input will not be used for crypto operations straight away. You can see the snippet using the constructors pulled from my hack in the description in this gist. Using an already validated key material is appealing to that use case since the key object may already be present. It may be in an already existing keystore that i'm copying keys from or any other means, knowing its an immutable internal KeyObject is why it's okay to just use the input as-is and avoid calling I can see from that code the helper could be omitted IF an existing KeyObject instance was valid input to createSecretKey/createPublicKey/createPrivateKey (it is not today). But this is just one use, there may be more. |
doc/api/crypto.md
Outdated
* `value` {any} | ||
* Returns: {boolean} | ||
|
||
Returns `true` if the value is a [`KeyObject`] instance. |
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.
Should we add a note that this does not work with other contexts / realms?
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 afraid I do not follow :(
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.
The function uses obj instanceof KeyObject
internally. This does not work in case some code is run in e.g. the vm
.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_context_(e.g._frames_or_windows) for further details.
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 suggest the language? I'm happy to add it.
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.
Maybe:
Returns `true` if the value is a [`KeyObject`][] instance from the [current execution context][].
// ...
[current execution context]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_context_(e.g._frames_or_windows)
But this might be confusing to others. So it's maybe something we do not have to add?
Change itself LGTM. Remaining questions:
Regarding (1) I guess it does no harm to have this function and I believe there are instances where it might be useful. |
Regarding (2) i wasn’t able to get tests to pass under util.types and would need someone’s help to make it happen. |
The more I think about it, the more uncertain I am this API is a good idea. I'm not sure it "does no harm" to add it if it adds an idiosyncratic appendage to an already large and complex crypto API. Why is there not a family of APIs, I looks to me because they all expose the class, so instanceof checks can be made. So, why don't we expose the class here? Perhaps because we want API users to not use classes directly? But this leads me back to the first question, why is there not a family of APIs so we recommend people away from direct class usage? I also quite prefer my suggestion that input be sanitized by passing it through its expected "create" function. It ensures that its converted to its preferred pre-parsed object (for performance) and also asserts that its of the correct type/object form as a side effect, and can be used as a pure assertion of type, if that's all that's wanted. |
indeed it is in my case |
@tniessen @sam-github Any further thoughts on this? |
@addaleax There has been no response to my last comments, so no further thoughts, my thoughts remain the same: I think the need for a one-off is-function for key types indicates an underlying problem that should be fixed. I think we should expose the class. At that point the is-function becomes unnecessary. If we want people to stop using the classes (all the classes), we should either provide a family of is-functions simultaneously for ALL the classes, or (and?) change the create APIs so they can take an object of their create type, and return the arg. |
I'm fine with all the options
There's already a PR to support privateKeyObject instance as argument for createPublicKey. My preference would be both exposing the class and accepting existing instances. Whichever comes first will help me to get rid of this, unstable looking export to aid with normalizing key import |
@tniessen what do you think? I assume you didn't expose the class partly because all the other classes have "do not use this" in their docs, but is there any other reason? The next week is busy, but I think I can expose the class pretty easily, and look at the input args later. Unless @panva you would be willing to do that? Assuming @tniessen is OK with it. |
Sorry Sam, I was away for a couple of days.
Exactly, I did not expose the class to prevent people from attempting to use it directly. In my opinion, exposing the classes just to allow
I can easily add support for calling
I guess there are very few use cases where one needs such checks. While there are different ways to represent key objects, there is only one way to represent an HMAC object (within node core), and no other object can be converted to an HMAC object. (Sadly, we also still have two classes for To be honest, I'm not sure what's the best way here. If the only use case is to sanitize user inputs, accepting instances in |
I like things when the API is the "right" way, the way we think it should be, but not so much when only part of the API is the right way, and other parts aren't, that just taxes my memory systems. Ideally, none of the classes should have been exposed, but for now, I think we should expose the KeyObject base-class (just the base, as you say). Sometime in the future, when we have a solid way for people do instanceof checks, either through passing through a sanitization function that returns identify if its already a key object or i How's that for a plan? |
I understand that this might be appealing for reasons of consistency and simplicity. Just for my own understanding, does this actually solve any problems that wouldn't be solved by allowing to pass existing instances to
I am still wondering whether people are actually doing checks like |
While that would solve ONE of my uses, i can solve more by having access to the constructor or the is* helper. Passing every user input to create*Key can get costly. There are now 2 PRs solving a real application problem based on two member recommendations. Please pick one ;) |
I am not convinced either of the PRs is a good idea, that's why I didn't "pick one" yet. I agree with @sam-github though, so if it's this or the other, I prefer exposing the class as a whole. |
I am marking this as blocked based on my and @sam-github's opinion and our preference towards exposing the class itself. @BridgeAR and @benjamingr, feel free to chime in. |
I do not have a strong opinion about it. For consistency reasons, I'd go and expose the class but I very well understand why that's not ideal either. So I'll just abstain. |
Indeed. |
Exposes
crypto.isKeyObject
to be able to check that a provided input is a prepared crypto key object from the crypto module add in v11.6.0 (#24234)This is to get around a dirty practice we have to use now (example below) that allows to use
instanceof
.With this check in place this can be replaced with
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes