Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions jose/jwe.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,19 @@ def decrypt(jwe_str, key):
# recipient or not.
cek_valid = False

# To mitigate the attacks described in RFC 3218 [RFC3218], the
# recipient MUST NOT distinguish between format, padding, and length
# errors of encrypted keys. It is strongly recommended, in the event
# of receiving an improperly formatted key, that the recipient
# substitute a randomly generated CEK and proceed to the next step, to
# mitigate timing attacks.
cek_bytes = _get_random_cek_bytes_for_enc(enc)
# To mitigate the attacks described in RFC 3218 [RFC3218], the
# recipient MUST NOT distinguish between format, padding, and length
# errors of encrypted keys. It is strongly recommended, in the event
# of receiving an improperly formatted key, that the recipient
# substitute a randomly generated CEK and proceed to the next step, to
# mitigate timing attacks.
try:
random_cek_bytes = _get_random_cek_bytes_for_enc(enc)
except NotImplementedError:
raise JWEError(f"enc {enc} is not implemented")
if not cek_valid or len(cek_bytes) != len(random_cek_bytes):
cek_valid = False
cek_bytes = random_cek_bytes

# Compute the Encoded Protected Header value BASE64URL(UTF8(JWE
# Protected Header)). If the JWE Protected Header is not present
Expand Down
19 changes: 18 additions & 1 deletion tests/test_jwe.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from jose.constants import ALGORITHMS, ZIPS
from jose.exceptions import JWEError, JWEParseError
from jose.jwk import AESKey, RSAKey
from jose.utils import base64url_decode
from jose.utils import base64url_decode, base64url_encode

backends = []
try:
Expand Down Expand Up @@ -336,6 +336,23 @@ def test_decrypt_oct_256_key_wrap(self, jwe_package):
actual = jwe.decrypt(jwe_package, key)
assert actual == b"Live long and prosper."

def test_wrong_length_cek_is_not_distinguishable(self):
# A CEK that unwraps fine but has the wrong length for enc must fail
# the same way as a bad padding (RFC 7516 section 11.5).
encrypted_key = RSAKey(PUBLIC_KEY_PEM, ALGORITHMS.RSA1_5).wrap_key(b"\x00" * 16)
header = base64url_encode(json.dumps({"alg": "RSA1_5", "enc": "A256GCM"}).encode("utf-8"))
jwe_str = b".".join(
[
header,
base64url_encode(encrypted_key),
base64url_encode(b"\x00" * 12),
b"AAAA",
base64url_encode(b"\x00" * 16),
]
)
with pytest.raises(JWEError, match="Invalid JWE Auth Tag"):
jwe.decrypt(jwe_str, PRIVATE_KEY_PEM)

def test_invalid_jwe_is_parse_error(self):
with pytest.raises(JWEParseError):
jwe.decrypt("invalid", "key")
Expand Down