Skip to content

Commit c998b7b

Browse files
committed
native: Improve asymmetric key check in CryptographyHMACKey
Implement same is_pem_format in native backend.
1 parent 6965967 commit c998b7b

File tree

4 files changed

+79
-81
lines changed

4 files changed

+79
-81
lines changed

jose/backends/cryptography_backend.py

+1-73
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import re
21
import math
32
import warnings
43

@@ -18,83 +17,12 @@
1817
from ..constants import ALGORITHMS
1918
from ..exceptions import JWEError, JWKError
2019
from ..utils import base64_to_long, base64url_decode, base64url_encode, ensure_binary, long_to_base64
20+
from ..utils import is_pem_format, is_ssh_key
2121
from .base import Key
2222

2323
_binding = None
2424

2525

26-
# Based on https://github.com/jpadilla/pyjwt/commit/9c528670c455b8d948aff95ed50e22940d1ad3fc
27-
# Based on https://github.com/hynek/pem/blob/7ad94db26b0bc21d10953f5dbad3acfdfacf57aa/src/pem/_core.py#L224-L252
28-
_PEMS = {
29-
b"CERTIFICATE",
30-
b"TRUSTED CERTIFICATE",
31-
b"PRIVATE KEY",
32-
b"PUBLIC KEY",
33-
b"ENCRYPTED PRIVATE KEY",
34-
b"OPENSSH PRIVATE KEY",
35-
b"DSA PRIVATE KEY",
36-
b"RSA PRIVATE KEY",
37-
b"RSA PUBLIC KEY",
38-
b"EC PRIVATE KEY",
39-
b"DH PARAMETERS",
40-
b"NEW CERTIFICATE REQUEST",
41-
b"CERTIFICATE REQUEST",
42-
b"SSH2 PUBLIC KEY",
43-
b"SSH2 ENCRYPTED PRIVATE KEY",
44-
b"X509 CRL",
45-
}
46-
47-
48-
_PEM_RE = re.compile(
49-
b"----[- ]BEGIN ("
50-
+ b"|".join(_PEMS)
51-
+ b""")[- ]----\r?
52-
.+?\r?
53-
----[- ]END \\1[- ]----\r?\n?""",
54-
re.DOTALL,
55-
)
56-
57-
58-
def is_pem_format(key):
59-
"""
60-
Return True if the key is PEM format
61-
This function uses the list of valid PEM headers defined in
62-
_PEMS dict.
63-
"""
64-
return bool(_PEM_RE.search(key))
65-
66-
67-
# Based on https://github.com/pyca/cryptography/blob/bcb70852d577b3f490f015378c75cba74986297b/src/cryptography/hazmat/primitives/serialization/ssh.py#L40-L46
68-
_CERT_SUFFIX = b"[email protected]"
69-
_SSH_PUBKEY_RC = re.compile(br"\A(\S+)[ \t]+(\S+)")
70-
_SSH_KEY_FORMATS = [
71-
b"ssh-ed25519",
72-
b"ssh-rsa",
73-
b"ssh-dss",
74-
b"ecdsa-sha2-nistp256",
75-
b"ecdsa-sha2-nistp384",
76-
b"ecdsa-sha2-nistp521",
77-
]
78-
79-
80-
def is_ssh_key(key):
81-
"""
82-
Return True if the key is a SSH key
83-
This function uses the list of valid SSH key format defined in
84-
_SSH_KEY_FORMATS dict.
85-
"""
86-
if any(string_value in key for string_value in _SSH_KEY_FORMATS):
87-
return True
88-
89-
ssh_pubkey_match = _SSH_PUBKEY_RC.match(key)
90-
if ssh_pubkey_match:
91-
key_type = ssh_pubkey_match.group(1)
92-
if _CERT_SUFFIX == key_type[-len(_CERT_SUFFIX) :]:
93-
return True
94-
95-
return False
96-
97-
9826
def get_random_bytes(num_bytes):
9927
"""
10028
Get random bytes

jose/backends/native.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from jose.constants import ALGORITHMS
77
from jose.exceptions import JWKError
88
from jose.utils import base64url_decode, base64url_encode
9+
from jose.utils import is_pem_format, is_ssh_key
910

1011

1112
def get_random_bytes(num_bytes):
@@ -36,14 +37,7 @@ def __init__(self, key, algorithm):
3637
if isinstance(key, str):
3738
key = key.encode("utf-8")
3839

39-
invalid_strings = [
40-
b"-----BEGIN PUBLIC KEY-----",
41-
b"-----BEGIN RSA PUBLIC KEY-----",
42-
b"-----BEGIN CERTIFICATE-----",
43-
b"ssh-rsa",
44-
]
45-
46-
if any(string_value in key for string_value in invalid_strings):
40+
if is_pem_format(key) or is_ssh_key(key):
4741
raise JWKError(
4842
"The specified key is an asymmetric key or x509 certificate and"
4943
" should not be used as an HMAC secret."

jose/utils.py

+73
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import re
12
import base64
23
import struct
34

@@ -105,3 +106,75 @@ def ensure_binary(s):
105106
if isinstance(s, str):
106107
return s.encode("utf-8", "strict")
107108
raise TypeError(f"not expecting type '{type(s)}'")
109+
110+
111+
# Based on https://github.com/jpadilla/pyjwt/commit/9c528670c455b8d948aff95ed50e22940d1ad3fc
112+
# Based on https://github.com/hynek/pem/blob/7ad94db26b0bc21d10953f5dbad3acfdfacf57aa/src/pem/_core.py#L224-L252
113+
_PEMS = {
114+
b"CERTIFICATE",
115+
b"TRUSTED CERTIFICATE",
116+
b"PRIVATE KEY",
117+
b"PUBLIC KEY",
118+
b"ENCRYPTED PRIVATE KEY",
119+
b"OPENSSH PRIVATE KEY",
120+
b"DSA PRIVATE KEY",
121+
b"RSA PRIVATE KEY",
122+
b"RSA PUBLIC KEY",
123+
b"EC PRIVATE KEY",
124+
b"DH PARAMETERS",
125+
b"NEW CERTIFICATE REQUEST",
126+
b"CERTIFICATE REQUEST",
127+
b"SSH2 PUBLIC KEY",
128+
b"SSH2 ENCRYPTED PRIVATE KEY",
129+
b"X509 CRL",
130+
}
131+
132+
133+
_PEM_RE = re.compile(
134+
b"----[- ]BEGIN ("
135+
+ b"|".join(_PEMS)
136+
+ b""")[- ]----\r?
137+
.+?\r?
138+
----[- ]END \\1[- ]----\r?\n?""",
139+
re.DOTALL,
140+
)
141+
142+
143+
def is_pem_format(key):
144+
"""
145+
Return True if the key is PEM format
146+
This function uses the list of valid PEM headers defined in
147+
_PEMS dict.
148+
"""
149+
return bool(_PEM_RE.search(key))
150+
151+
152+
# Based on https://github.com/pyca/cryptography/blob/bcb70852d577b3f490f015378c75cba74986297b/src/cryptography/hazmat/primitives/serialization/ssh.py#L40-L46
153+
_CERT_SUFFIX = b"[email protected]"
154+
_SSH_PUBKEY_RC = re.compile(br"\A(\S+)[ \t]+(\S+)")
155+
_SSH_KEY_FORMATS = [
156+
b"ssh-ed25519",
157+
b"ssh-rsa",
158+
b"ssh-dss",
159+
b"ecdsa-sha2-nistp256",
160+
b"ecdsa-sha2-nistp384",
161+
b"ecdsa-sha2-nistp521",
162+
]
163+
164+
165+
def is_ssh_key(key):
166+
"""
167+
Return True if the key is a SSH key
168+
This function uses the list of valid SSH key format defined in
169+
_SSH_KEY_FORMATS dict.
170+
"""
171+
if any(string_value in key for string_value in _SSH_KEY_FORMATS):
172+
return True
173+
174+
ssh_pubkey_match = _SSH_PUBKEY_RC.match(key)
175+
if ssh_pubkey_match:
176+
key_type = ssh_pubkey_match.group(1)
177+
if _CERT_SUFFIX == key_type[-len(_CERT_SUFFIX) :]:
178+
return True
179+
180+
return False

tests/algorithms/test_HMAC.py

+3
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ def test_non_string_key(self):
1414

1515
def test_RSA_key(self):
1616
key = "-----BEGIN PUBLIC KEY-----"
17+
key += "\n\n\n-----END PUBLIC KEY-----"
1718
with pytest.raises(JOSEError):
1819
HMACKey(key, ALGORITHMS.HS256)
1920

2021
key = "-----BEGIN RSA PUBLIC KEY-----"
22+
key += "\n\n\n-----END RSA PUBLIC KEY-----"
2123
with pytest.raises(JOSEError):
2224
HMACKey(key, ALGORITHMS.HS256)
2325

2426
key = "-----BEGIN CERTIFICATE-----"
27+
key += "\n\n\n-----END CERTIFICATE-----"
2528
with pytest.raises(JOSEError):
2629
HMACKey(key, ALGORITHMS.HS256)
2730

0 commit comments

Comments
 (0)