Skip to content

Commit 2798504

Browse files
committed
fix: reject DER public keys as HMAC secrets
1 parent 8b4e233 commit 2798504

2 files changed

Lines changed: 65 additions & 1 deletion

File tree

jwt/algorithms.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
)
3434

3535
try:
36+
from cryptography import x509
3637
from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm
3738
from cryptography.hazmat.backends import default_backend
3839
from cryptography.hazmat.primitives import hashes
@@ -72,6 +73,7 @@
7273
NoEncryption,
7374
PrivateFormat,
7475
PublicFormat,
76+
load_der_public_key,
7577
load_pem_private_key,
7678
load_pem_public_key,
7779
load_ssh_public_key,
@@ -322,13 +324,36 @@ class HMACAlgorithm(Algorithm):
322324
def __init__(self, hash_alg: HashlibHash) -> None:
323325
self.hash_alg = hash_alg
324326

327+
@staticmethod
328+
def _is_der_key(key_bytes: bytes) -> bool:
329+
if not has_crypto:
330+
return False
331+
332+
try:
333+
load_der_public_key(key_bytes)
334+
except (TypeError, ValueError, UnsupportedAlgorithm):
335+
pass
336+
else:
337+
return True
338+
339+
try:
340+
x509.load_der_x509_certificate(key_bytes)
341+
except (TypeError, ValueError, UnsupportedAlgorithm):
342+
return False
343+
else:
344+
return True
345+
325346
def prepare_key(self, key: str | bytes) -> bytes:
326347
key_bytes = force_bytes(key)
327348

328349
if len(key_bytes) == 0:
329350
raise InvalidKeyError("HMAC key must not be empty.")
330351

331-
if is_pem_format(key_bytes) or is_ssh_key(key_bytes):
352+
if (
353+
is_pem_format(key_bytes)
354+
or is_ssh_key(key_bytes)
355+
or self._is_der_key(key_bytes)
356+
):
332357
raise InvalidKeyError(
333358
"The specified key is an asymmetric key or x509 certificate and"
334359
" should not be used as an HMAC secret."

tests/test_algorithms.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .utils import crypto_required, key_path
1313

1414
if has_crypto:
15+
from cryptography import x509
1516
from cryptography.hazmat.primitives.asymmetric.ec import (
1617
EllipticCurvePrivateKey,
1718
EllipticCurvePublicKey,
@@ -28,6 +29,10 @@
2829
RSAPrivateKey,
2930
RSAPublicKey,
3031
)
32+
from cryptography.hazmat.primitives.serialization import (
33+
Encoding,
34+
PublicFormat,
35+
)
3136

3237
from jwt.algorithms import ECAlgorithm, OKPAlgorithm, RSAAlgorithm, RSAPSSAlgorithm
3338

@@ -259,6 +264,40 @@ def test_hmac_prepare_key_rejects_deep_jwk_with_surrogate(
259264
with pytest.raises(InvalidKeyError, match="looks like a JWK"):
260265
algo.prepare_key(key)
261266

267+
@crypto_required
268+
@pytest.mark.parametrize(
269+
"key_format_name",
270+
("SubjectPublicKeyInfo", "PKCS1"),
271+
)
272+
def test_hmac_prepare_key_rejects_der_public_key(
273+
self, key_format_name: str
274+
) -> None:
275+
algo = HMACAlgorithm(HMACAlgorithm.SHA256)
276+
public_key = cast(RSAPublicKey, load_rsa_pub_key())
277+
der_key = public_key.public_bytes(
278+
Encoding.DER, getattr(PublicFormat, key_format_name)
279+
)
280+
281+
with pytest.raises(InvalidKeyError, match="asymmetric key"):
282+
algo.prepare_key(der_key)
283+
284+
@crypto_required
285+
def test_hmac_prepare_key_rejects_der_certificate(self) -> None:
286+
algo = HMACAlgorithm(HMACAlgorithm.SHA256)
287+
with open(key_path("testkey_rsa.cer"), "rb") as certificate_file:
288+
certificate = x509.load_pem_x509_certificate(certificate_file.read())
289+
der_certificate = certificate.public_bytes(Encoding.DER)
290+
291+
with pytest.raises(InvalidKeyError, match="x509 certificate"):
292+
algo.prepare_key(der_certificate)
293+
294+
@crypto_required
295+
def test_hmac_prepare_key_accepts_non_key_binary_secret(self) -> None:
296+
algo = HMACAlgorithm(HMACAlgorithm.SHA256)
297+
secret = b"\x30\x82not-a-der-key"
298+
299+
assert algo.prepare_key(secret) == secret
300+
262301
@crypto_required
263302
def test_rsa_should_parse_pem_public_key(self) -> None:
264303
algo = RSAAlgorithm(RSAAlgorithm.SHA256)

0 commit comments

Comments
 (0)