-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add a script to enhance JWKs in preparation for move from pyjwk…
…est (#32089) * feat: Add a script to enhance JWKs in preparation for move from pyjwkest This script accepts a signing JWK (presumably `JWT_PRIVATE_SIGNING_JWK`) and ensures that it has all of the precomputed private numbers that are required for top performance. This is necessary before moving away from pyjwkest to PyJWT for signing JWTs. See issue <openedx/edx-drf-extensions#290>. (Alternatively, one could remove the p, q, dp, dq, and qi params, but there is an unknown performance cost to doing so as we are not currently caching these keys, and the precompution happens on every load due to the way pyjwkest's API works.) * fixup! Upgrade devstack at the same time
- Loading branch information
Showing
2 changed files
with
69 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#!/usr/bin/env python3 | ||
# Enhance a JWK to have all of its optional performance parameters. | ||
# Intended to be used on an RSA `JWT_PRIVATE_SIGNING_JWK`. | ||
# | ||
# This is needed for the change from pyjwkest to PyJWT, since the | ||
# former accepts a partial list of optional parameters but the latter | ||
# requires that they are either all present or all absent. The optional | ||
# parameters provide a performance boost. | ||
# | ||
# Usage: Key JSON accepted on stdin; enhanced key printed to stdout. | ||
|
||
import json | ||
import sys | ||
|
||
from jwt.algorithms import RSAAlgorithm | ||
|
||
|
||
print("Paste the key's JSON, followed by a new line and Ctrl-D:\n", file=sys.stderr) | ||
old_jwk_data = json.loads(sys.stdin.read()) | ||
|
||
# Clear out all of the precomputed private numbers | ||
for param_key in ['p', 'q', 'dp', 'dq', 'qi']: | ||
if param_key in old_jwk_data: | ||
del old_jwk_data[param_key] | ||
|
||
# Ensure that there aren't any unexpected parameters | ||
expected_remaining = {'kty', 'e', 'd', 'n', 'kid', 'key_ops'} | ||
unexpected_params = set(old_jwk_data.keys()) - expected_remaining | ||
if len(unexpected_params): | ||
print( | ||
f"Unexpected parameters {unexpected_params} would be lost. Aborting script. " | ||
"If your key has additional parameters that are unrelated to the precomputed " | ||
"private numbers, then please add them to the `expected_remaining` variable " | ||
"and re-run the script. Please consider making a PR as well.", | ||
file=sys.stderr | ||
) | ||
sys.exit(1) | ||
|
||
# Recompute private numbers | ||
new_jwk_data = json.loads(RSAAlgorithm.to_jwk(RSAAlgorithm.from_jwk(old_jwk_data))) | ||
|
||
# Restore the kid (key ID) param, which gets lost in the process. This adds it | ||
# to the front of the dict. The params are actually in a really nice order in | ||
# the native ordering that comes out of the JWK, with metadata first, then the | ||
# core params (n, e, d), and then the precomputed values. | ||
for restore_param in ['kid']: | ||
if restore_param in old_jwk_data: | ||
new_jwk_data = {restore_param: old_jwk_data[restore_param], **new_jwk_data} | ||
|
||
# Pretty-print so that the kid and modulus can be confirmed easily | ||
print("\n\nEnhanced private key:\n", file=sys.stderr) | ||
print(json.dumps(new_jwk_data, indent=4)) |