Skip to content

Commit 2be0f63

Browse files
authored
fix: update header validation raised errors (#45)
Use `UnsupportedHeaderError`, `MissingHeaderError` and `MissingCritHeaderError` for header validation
1 parent ae89af1 commit 2be0f63

File tree

8 files changed

+52
-12
lines changed

8 files changed

+52
-12
lines changed

Diff for: docs/changelog.rst

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Unreleased
2323
- Use ``ECKey.binding.register_curve`` to register new supported curves.
2424
- Use ``UnsupportedAlgorithmError`` instead of ``ValueError`` in JWS/JWE registry.
2525
- Use ``MissingKeyTypeError`` and ``InvalidKeyIdError`` for errors in JWK.
26+
- Use ``UnsupportedHeaderError``, ``MissingHeaderError``, and ``MissingCritHeaderError`` for header validation.
2627
- Respect RFC6749 character set in error descriptions.
2728

2829
1.0.4

Diff for: src/joserfc/errors.py

+28
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,30 @@ class UnsupportedAlgorithmError(JoseError):
7272
error = "unsupported_algorithm"
7373

7474

75+
class UnsupportedHeaderError(JoseError):
76+
error = "unsupported_header"
77+
78+
79+
class MissingHeaderError(JoseError):
80+
"""This error happens when the required header does not exist."""
81+
82+
error = "missing_header"
83+
84+
def __init__(self, key: str):
85+
description = f"Missing '{key}' value in header"
86+
super(MissingHeaderError, self).__init__(description=description)
87+
88+
89+
class MissingCritHeaderError(JoseError):
90+
"""This error happens when the critical header does not exist."""
91+
92+
error = "missing_crit_header"
93+
94+
def __init__(self, key: str):
95+
description = f"Missing critical '{key}' value in header"
96+
super(MissingCritHeaderError, self).__init__(description=description)
97+
98+
7599
class MissingEncryptionError(JoseError):
76100
error = "missing_encryption"
77101
description = "Missing 'enc' value in header"
@@ -104,6 +128,10 @@ class InvalidCEKLengthError(JoseError):
104128
error = "invalid_cek_length"
105129
description = "Invalid 'cek' length"
106130

131+
def __init__(self, cek_size: int):
132+
description = f"A key of size {cek_size} bits MUST be used"
133+
super(InvalidCEKLengthError, self).__init__(description=description)
134+
107135

108136
class InvalidClaimError(JoseError):
109137
error = "invalid_claim"

Diff for: src/joserfc/registry.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
from __future__ import annotations
22
from typing import Any, Dict, Callable, Union
3+
from .errors import (
4+
MissingHeaderError,
5+
MissingCritHeaderError,
6+
UnsupportedHeaderError,
7+
)
38

49
Header = Dict[str, Any]
510

@@ -174,13 +179,13 @@ def check_supported_header(registry: HeaderRegistryDict, header: Header) -> None
174179
allowed_keys = set(registry.keys())
175180
unsupported_keys = set(header.keys()) - allowed_keys
176181
if unsupported_keys:
177-
raise ValueError(f"Unsupported {unsupported_keys} in header")
182+
raise UnsupportedHeaderError(f"Unsupported {unsupported_keys} in header")
178183

179184

180185
def validate_registry_header(registry: HeaderRegistryDict, header: Header, check_required: bool = True) -> None:
181186
for key, reg in registry.items():
182187
if check_required and reg.required and key not in header:
183-
raise ValueError(f"Required '{key}' is missing in header")
188+
raise MissingHeaderError(key)
184189
if key in header:
185190
try:
186191
reg.validate(header[key])
@@ -193,4 +198,4 @@ def check_crit_header(header: Header) -> None:
193198
if "crit" in header:
194199
for k in header["crit"]:
195200
if k not in header:
196-
raise ValueError(f"'{k}' is a critical header")
201+
raise MissingCritHeaderError(k)

Diff for: src/joserfc/rfc7516/message.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def _perform_decrypt(obj: EncryptionData, registry: JWERegistry) -> None:
115115

116116
cek = cek_set.pop()
117117
if len(cek) * 8 != enc.cek_size: # pragma: no cover
118-
raise InvalidCEKLengthError(f"A key of size {enc.cek_size} bits MUST be used")
118+
raise InvalidCEKLengthError(enc.cek_size)
119119

120120
aad = json_b64encode(obj.protected)
121121
if isinstance(obj, BaseJSONEncryption) and obj.aad:
@@ -181,7 +181,7 @@ def __pre_encrypt_direct_mode(alg: JWEAlgModel, enc: JWEEncModel, recipient: Rec
181181
# let the CEK be the agreed upon key.
182182
cek = alg.encrypt_agreed_upon_key(enc, recipient)
183183
if len(cek) * 8 != enc.cek_size: # pragma: no cover
184-
raise InvalidCEKLengthError(f"A key of size {enc.cek_size} bits MUST be used")
184+
raise InvalidCEKLengthError(enc.cek_size)
185185
else:
186186
# 6. When Direct Encryption is employed, let the CEK be the shared
187187
# symmetric key.

Diff for: tests/jwe/test_errors.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
InvalidKeyLengthError,
88
DecodeError,
99
UnsupportedAlgorithmError,
10+
UnsupportedHeaderError,
1011
)
1112
from tests.base import load_key
1213

@@ -88,7 +89,7 @@ def test_invalid_key_length(self):
8889
def test_extra_header(self):
8990
key = OctKey.generate_key(256)
9091
protected = {"alg": "dir", "enc": "A128CBC-HS256", "custom": "hi"}
91-
self.assertRaises(ValueError, jwe.encrypt_compact, protected, b"i", key)
92+
self.assertRaises(UnsupportedHeaderError, jwe.encrypt_compact, protected, b"i", key)
9293

9394
registry = jwe.JWERegistry(strict_check_header=False)
9495
jwe.encrypt_compact(protected, b"i", key, registry=registry)
@@ -99,6 +100,6 @@ def test_extra_header(self):
99100
def test_strict_check_header_with_more_header_registry(self):
100101
key = load_key("ec-p256-private.pem")
101102
protected = {"alg": "ECDH-ES", "enc": "A128CBC-HS256", "custom": "hi"}
102-
self.assertRaises(ValueError, jwe.encrypt_compact, protected, b"i", key)
103+
self.assertRaises(UnsupportedHeaderError, jwe.encrypt_compact, protected, b"i", key)
103104
registry = jwe.JWERegistry(strict_check_header=False)
104105
jwe.encrypt_compact(protected, b"i", key, registry=registry)

Diff for: tests/jws/test_compact.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
DecodeError,
77
MissingAlgorithmError,
88
UnsupportedAlgorithmError,
9+
UnsupportedHeaderError,
910
)
1011

1112

@@ -68,7 +69,7 @@ def test_with_key_set(self):
6869
def test_strict_check_header(self):
6970
header = {"alg": "HS256", "custom": "hi"}
7071
key = OctKey.import_key("secret")
71-
self.assertRaises(ValueError, serialize_compact, header, b"hi", key)
72+
self.assertRaises(UnsupportedHeaderError, serialize_compact, header, b"hi", key)
7273

7374
registry = JWSRegistry(strict_check_header=False)
7475
serialize_compact(header, b"hi", key, registry=registry)

Diff for: tests/jws/test_errors.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
UnsupportedKeyAlgorithmError,
99
UnsupportedKeyOperationError,
1010
InvalidKeyTypeError,
11+
MissingHeaderError,
12+
MissingCritHeaderError,
13+
UnsupportedHeaderError,
1114
)
1215
from joserfc.util import urlsafe_b64encode
1316
from tests.base import load_key
@@ -17,7 +20,7 @@ class TestJWSErrors(TestCase):
1720
def test_without_alg(self):
1821
key = OctKey.import_key("secret")
1922
# missing alg
20-
self.assertRaises(ValueError, jws.serialize_compact, {"kid": "123"}, "i", key)
23+
self.assertRaises(MissingHeaderError, jws.serialize_compact, {"kid": "123"}, "i", key)
2124

2225
def test_none_alg(self):
2326
header = {"alg": "none"}
@@ -80,7 +83,7 @@ def test_crit_header(self):
8083
key = OctKey.import_key("secret")
8184
# missing kid header
8285
self.assertRaises(
83-
ValueError,
86+
MissingCritHeaderError,
8487
jws.serialize_compact,
8588
header,
8689
"i",
@@ -94,7 +97,7 @@ def test_extra_header(self):
9497
header = {"alg": "HS256", "extra": "hi"}
9598
key = OctKey.import_key("secret")
9699
self.assertRaises(
97-
ValueError,
100+
UnsupportedHeaderError,
98101
jws.serialize_compact,
99102
header,
100103
"i",

Diff for: tests/jwt/test_jwt.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from joserfc.errors import (
55
InvalidPayloadError,
66
MissingClaimError,
7+
UnsupportedHeaderError,
78
)
89

910

@@ -52,7 +53,7 @@ def test_using_registry(self):
5253
registry=jwe.JWERegistry(),
5354
)
5455
self.assertRaises(
55-
ValueError,
56+
UnsupportedHeaderError,
5657
jwt.encode,
5758
{"alg": "A128KW", "enc": "A128GCM"},
5859
{"sub": "a"},

0 commit comments

Comments
 (0)