-
Notifications
You must be signed in to change notification settings - Fork 376
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
Add support for JWKs with EC key type #371
Add support for JWKs with EC key type #371
Conversation
6346031
to
64bc94d
Compare
Looks great, thanks for your contribution. Wondering about this 'An import followed by an export does not preserve the "kid" value.', this behaviour was not intended and is fixed in #320 for the RSA keys. Maybe this feature could be perfect from the beginning in regards to that :) About not exporting private keys, I recall me thinking that it could be dangerous to export the private key by default. If we would like to support this we could add a parameter to the export method that would allow inclusion of the private parts. |
lib/jwt/jwk/ec.rb
Outdated
jwk_crv, jwk_x, jwk_y, jwk_kid = jwk_attrs(jwk_data, %i[crv x y kid]) | ||
raise Jwt::JWKError, 'Key format is invalid for EC' unless jwk_crv && jwk_x && jwk_y | ||
|
||
new(ec_pkey(jwk_crv, jwk_x, jwk_y), kid=jwk_kid) |
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.
Useless assignment to variable - kid
.
lib/jwt/jwk/ec.rb
Outdated
|
||
def export | ||
crv, x_octets, y_octets = keypair_components(keypair) | ||
sequence = OpenSSL::ASN1::Sequence([OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(x_octets, BINARY)), |
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.
Useless assignment to variable - sequence
.
lib/jwt/jwk/ec.rb
Outdated
jwk_crv, jwk_x, jwk_y, jwk_kid = jwk_attrs(jwk_data, %i[crv x y kid]) | ||
raise Jwt::JWKError, 'Key format is invalid for EC' unless jwk_crv && jwk_x && jwk_y | ||
|
||
new(ec_pkey(jwk_crv, jwk_x, jwk_y), kid=jwk_kid) |
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.
Surrounding space missing for operator =
.
6b6895e
to
7562cdf
Compare
lib/jwt/jwk/ec.rb
Outdated
crv = 'P-521' | ||
x_octets, y_octets = encoded_point.unpack('xa66a66') | ||
else | ||
raise "Unsupported curve '#{ec_keypair.group.curve_name}'" |
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.
Missed this the first time around. The JWT::JWKError
could be used here also
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.
My bad. Should be fixed now.
Hi @phlegx Think that it would be a nice feature. As mentioned before the main reason for it was to avoid accidental leaking of the private key, so still think that including the private key in the export should not be the default behaviour. In other words the private key should only be included when asked for. |
@richardlarocque Looks good. |
@anakinj the RFC describes JWK with: A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. So, JWK is only a data structure that represents a cryptographic key. How a user expose a JWK to a web endpoint is not part of the RFC7517 and should not affect the behavior or structure of a JWK. |
The need until now has been focused on the public parts of the keys (signature verification and exports in tests to simulate public JWK endpoints). So therefore the private parts have been totally left out. Think that if we would support exporting of the private keys we should protect the uneducated user from leaking sensitive information and require the user to actually ask for the private parts to be included. Made a quick proposal on how the private parts of the key could be included in the export #373 |
@anakinj it is clear for me thx! It is a good compromise for me to add an option if private key should be exported or not. We should focus to add this feature to RSA, EC and HMAC and if possible release a new version. |
@richardlarocque I suggest to merge master branch to your PR, that includes the abstract class. Then you can adapt your PR to the abstract structure and the |
Merges in upstream changes to JWK code that occurred while jwt#371 was under review.
Merged in upstream changes to fix conflicts. Please take another look and merge if desired. (I don't have permission to hit the "merge" button, even if there is an approving review.) |
lib/jwt/jwk/ec.rb
Outdated
|
||
module JWT | ||
module JWK | ||
class EC |
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.
You have to inherit from abstract class here:
class EC < KeyAbstract
lib/jwt/jwk/ec.rb
Outdated
attr_reader :keypair | ||
attr_reader :kid |
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 can be removed, because already included from abstract class.
# remove this two lines.
attr_reader :keypair
attr_reader :kid
lib/jwt/jwk/ec.rb
Outdated
def initialize(keypair, kid = nil) | ||
raise ArgumentError, 'keypair must be of type OpenSSL::PKey::EC' unless keypair.is_a?(OpenSSL::PKey::EC) | ||
|
||
@keypair = keypair |
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.
Remove the line @keypair = keypair
and add super
instead.
lib/jwt/jwk/ec.rb
Outdated
@kid = kid || generate_kid(@keypair) | ||
end | ||
|
||
def export |
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.
Add parameter options
like: def export(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.
lib/jwt/jwk/ec.rb
Outdated
|
||
def export | ||
crv, x_octets, y_octets = keypair_components(keypair) | ||
{ |
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.
Sorry, I got distracted with other things and ended up not paying much attention to this PR. I find that abstract classes are not very common in Ruby. There's no need to define stub methods that throw exceptions when they're not overridden; that's what But I'm not the one who has to live with this codebase. I'll defer to the maintainers on this one. @excpt, @anakinj, do you have opinions on @phlegx's suggestions? I'm happy to implement it however you prefer. |
I somewhat agree about the empty stubs. The naming is always hard, maybe in Ruby base classes are more common than abstracts. But then again something that would bind these different types of keys together would be of benefit. Had a thought when this was discussed was that the list of supported types could be generated using something shared between these classes. Is that shared thing a base class or mixin i have no strong opinion on. |
Added something small to the thing I'm currently working on in regards to the Base class for the different key types. And as @richardlarocque suggested I also removed the empty method stubs. |
@richardlarocque can you please adapt your code to @anakinj base class and solve review issues? |
I have reservations about rebasing on top of an uncommitted branch. My goal in opening this PR was to get this functionality into the ruby-jwt upstream. I built this PR so that it fit the style of the project as it existed at the time, so that it would be easy to merge. My preference is that we merge this more or less as it is, in conformance with the old style. If this project is in the middle of a refactor, and as a result we can't accept the PR as-is right now, that's OK. I just had bad timing, I guess. But I'd rather not get involved with the refactoring myself. I'd prefer to wait until that refactoring lands in master and then rebase my change against that. I don't want this feature's fate to be tied to that of some refactoring work. I'd like to keep the two things separate. I am willing to add support for the |
Just a comment to say thank you so much for doing this. We need this functionality too and exploring the alternative JWT gems, I didn't like what I saw in some of the solutions. Would love to see this one get across the line. I'm happy to help in any way I can do too ❤️ |
@richardlarocque all other PR have been merged and are in master. Can you please adapt your code style and options to RSA and HMAC? Thank you so much. |
@richardlarocque do you mind someone continue with this to get it into the next release or are you planning on continuing on this on your own? Would be really great to get this into master. |
Sorry I'm late to the follow-up! I can try to rebase in the next few days, but if one of you wants to beat me to it, feel free. |
Adds support for JWKs with "kty" value "EC". This commit includes support for curves ("crv") "P-256", "P-384", "P-521". For additional details on these JWKs and their contents, see https://tools.ietf.org/html/rfc7518#section-6.2.1. This implementation adheres closely to the pattern set by `JWT::JWK::RSA`. It keeps the same coding style and method names. It also inherits a few quirks: - It ignores any private key ("d") values when importing JWKs. - It never emits private key ("d") values when exporting JWKs. - An import followed by an export does not preserve the "kid" value. These behaviors seem strange to me, but I worry that changing the convention would be surprising and potentially dangerous to existing users of this library.
Updates the implementation of JWT:JWK::EC so it will import and export custom "kid" values. See also jwt#320, which proposes doing the same for JWT::JWK::RSA.
50a1500
to
17967f1
Compare
lib/jwt/jwk/ec.rb
Outdated
def initialize(keypair, kid = nil) | ||
raise ArgumentError, 'keypair must be of type OpenSSL::PKey::EC' unless keypair.is_a?(OpenSSL::PKey::EC) | ||
|
||
kid = kid || generate_kid(keypair) |
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.
Use self-assignment shorthand ||=
.
Updates `JWT::JWK::EC` to extend `KeyBase` and support other conventions like the `JWT::JWK::KTYS` array. Adds support for `#export(include_private: true)` to `JWT::JWK::EC` and updates tests to match the new behavior.
17967f1
to
162f256
Compare
Nice work @richardlarocque! Thx |
Thanks @richardlarocque for the contribution and patience. |
@anakinj is a release planned? |
Adds support for JWKs with "kty" value "EC". This commit includes
support for curves ("crv") "P-256", "P-384", "P-521".
For additional details on these JWKs and their contents, see
https://tools.ietf.org/html/rfc7518#section-6.2.1.
This implementation adheres closely to the pattern set by
JWT::JWK::RSA
. It keeps the same coding style and method names.It also inherits a few quirks:
These behaviors seem strange to me, but I worry that changing the
convention would be surprising and potentially dangerous to existing
users of this library.