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

feat: add ed25519 support to JWK (public keys) #452

Merged
merged 2 commits into from Jun 14, 2023

Conversation

edudobay
Copy link

Ed25519 key support has been added in #343 for the "standard" key format, but not yet for JWKs. (I'm opening a new PR, as I had previously opened #414 against a different base branch.)

Reference documentation: RFC8037

Copy link
Collaborator

@bshaffer bshaffer left a comment

Choose a reason for hiding this comment

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

Thank you for submitting this! I have some technical questions, as well as some issues with the implementation here. Hopefully we can get this feature merged!

tests/JWKTest.php Show resolved Hide resolved
src/JWT.php Outdated
* @return string A Base64 encoded string with standard characters (+/) and padding (=), when
* needed.
*/
public static function urlsafeToStandardB64(string $input): string
Copy link
Collaborator

Choose a reason for hiding this comment

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

This naming is confusing to me. For key type OKP, the spec says

The parameter "x" MUST be present and contain the public key
encoded using the base64url [RFC4648] encoding.

This links to another spec which says

This encoding may be referred to as "base64url". This encoding
should not be regarded as the same as the "base64" encoding and
should not be referred to as only "base64"

I think it would be better to call this base64UrlDecode

Copy link
Author

Choose a reason for hiding this comment

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

I've renamed the function to convertBase64urlToBase64. Do you think it's clearer?

It doesn't actually decode base64 data, it just translates from base64url to base64 so that the PHP base64_decode function can be used.

src/JWK.php Outdated
Comment on lines 104 to 110
$ktyNotRequiringAlg = [
// In Octet Key Pair (OKP) keys, the signing algorithm (alg) will not be read from the
// JWK, as it can be inferred directly from the curve type (crv).
// @see https://datatracker.ietf.org/doc/html/rfc8037#section-3.1
'OKP',
];
if (!isset($jwk['alg']) && !\in_array($jwk['kty'], $ktyNotRequiringAlg, true)) {
Copy link
Collaborator

@bshaffer bshaffer Oct 5, 2022

Choose a reason for hiding this comment

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

I am unfamiliar with OKPs, but by looking at this example, those values contain an "alg" of EdDSA instead of using the clunky mapping. I'd prefer to keep the algorithm explicit, and require it like we do everywhere else.

Copy link
Author

Choose a reason for hiding this comment

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

I've reverted to the original version where the algorithm is always required. I see that the library opted for always requiring an explicit algorithm.

src/JWK.php Outdated Show resolved Hide resolved
src/JWK.php Outdated
@@ -30,6 +30,11 @@ class JWK
// 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
];

// 'crv' identifier => JWT 'alg'
private const OKP_CURVES = [
Copy link
Collaborator

@bshaffer bshaffer Oct 5, 2022

Choose a reason for hiding this comment

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

This is strange to me, as the values here aren't curves, but algorithms. I am not convinced this is the correct implementation.

Copy link
Author

Choose a reason for hiding this comment

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

I've rephrased the concepts to call these "key subtypes", as section 2 of RFC 8037 calls it.

src/JWK.php Outdated
Comment on lines 176 to 177
$publicKey = JWT::urlsafeToStandardB64($jwk['x']);
$alg = self::OKP_CURVES[$jwk['crv']];
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am confused because there is no action taken as far as implementing decoding a key with ed25519. The JWK class is base64url-decoding the key, and (I am assuming) relying on the to default behavior of sodium_crypto to accept this as ed25519. There are other possible values here, however, so I think at the very least this needs to throw an exception if crv is not ed25519.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, the sodium_crypto functions that are used by the library work specifically with ed25519.

If an unsupported crv is provided, an exception will be thrown a few lines above with the message Unrecognised or unsupported OKP key subtype.

@alecsammon
Copy link

This PR would be really useful for us.

@edudobay - do you have time to get this updated and the questions responded to, or instead would you be happy if I took it forward?

I've tested the branch with our code bases and everything looks to work correctly.

Thank you!

@edudobay
Copy link
Author

edudobay commented May 21, 2023

Hi! Sorry for the delay, I've addressed the issues you commented, and I think it's clearer and cleaner now.

I was wondering if it would be useful to throw an exception if the provided alg is not supported for the given kty (for example, alg=RS256 and kty=OKP). But this verification does not yet exist for the other key types, so I guess this would be the scope of a future PR.

In that case, the mismatch would fall down to the crypto function layer — it would either give an error because the given key is invalid for the given algorithm, or encrypt with the wrong algorithm if by chance the same key is valid for different algorithms.

Copy link
Collaborator

@bshaffer bshaffer left a comment

Choose a reason for hiding this comment

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

This looks great! Just one minor change and we can merge this!

Thanks again for your contribution.

src/JWT.php Outdated Show resolved Hide resolved
"kid": "jwk1",
"kty": "OKP",
"crv": "Ed25519",
"x": "uOSJMhbKSG4V5xUHS7B9YHmVg_1yVd-G-Io6oBFhSfY"
Copy link
Collaborator

Choose a reason for hiding this comment

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

The tests say this needs to contain an "alg" parameter! Either that or you can refactor the tests to use the $defaultAlg argument

Copy link
Author

Choose a reason for hiding this comment

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

Added the parameter.

@edudobay
Copy link
Author

@bshaffer I've addressed the final comments, so I think this is ready now :)

@bshaffer bshaffer merged commit e53979a into firebase:main Jun 14, 2023
7 checks passed
@edudobay edudobay deleted the add-ed25519-jwk-support branch June 14, 2023 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants