Skip to content

Commit c668132

Browse files
authored
v0.6 (#8)
* introduce BaseSignatureAlg.hashing_alg, and Ed2219Dsa and Ed448Dsa JWA classes * pre-commit autoupdate & poetry update * fix #7: correct key sizes in generate_for_alg() for HMAC signature algs and AES encryption algs * improvements and bugfixes to JWS format conversions * simplify OKPJwk.from_cryptography_key()
1 parent 7bc6226 commit c668132

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+850
-467
lines changed

.github/workflows/dev.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
- name: Install dependencies
3636
run: |
3737
python -m pip install --upgrade pip
38-
pip install poetry tox tox-gh-actions tox-poetry
38+
pip install poetry "tox<4" tox-gh-actions tox-poetry
3939
4040
- name: test with tox
4141
run:
@@ -57,7 +57,7 @@ jobs:
5757
- name: Install dependencies
5858
run: |
5959
python -m pip install --upgrade pip
60-
pip install poetry tox tox-gh-actions tox-poetry
60+
pip install poetry "tox<4" tox-gh-actions tox-poetry
6161
6262
- name: test with tox
6363
run:

.pre-commit-config.yaml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ repos:
77
- id: forbid-tabs
88
- id: remove-tabs
99
- repo: https://github.com/pre-commit/pre-commit-hooks
10-
rev: v4.3.0
10+
rev: v4.4.0
1111
hooks:
1212
- id: trailing-whitespace
1313
- id: end-of-file-fixer
1414
- id: check-merge-conflict
1515
- id: check-yaml
1616
args: [--unsafe]
1717
- repo: https://github.com/hadialqattan/pycln
18-
rev: v2.1.1
18+
rev: v2.1.2
1919
hooks:
2020
- id: pycln
2121
args: [--config=pyproject.toml]
@@ -24,28 +24,29 @@ repos:
2424
hooks:
2525
- id: isort
2626
- repo: https://github.com/psf/black
27-
rev: 22.10.0
27+
rev: 22.12.0
2828
hooks:
2929
- id: black
30-
- repo: https://gitlab.com/pycqa/flake8
31-
rev: 3.9.2
30+
- repo: https://github.com/pycqa/flake8
31+
rev: 6.0.0
3232
hooks:
3333
- id: flake8
34-
additional_dependencies: [flake8-typing-imports==1.10.0]
34+
additional_dependencies:
35+
- flake8-typing-imports==1.14.0
3536
- repo: https://github.com/asottile/blacken-docs
3637
rev: v1.12.1
3738
hooks:
3839
- id: blacken-docs
3940
- repo: https://github.com/pycqa/pydocstyle
40-
rev: 6.1.1
41+
rev: 6.2.3
4142
hooks:
4243
- id: pydocstyle
4344
additional_dependencies:
4445
- toml
4546
args:
4647
- --add-ignore=D107
4748
- repo: https://github.com/pre-commit/mirrors-mypy
48-
rev: v0.982
49+
rev: v0.991
4950
hooks:
5051
- id: mypy
5152
args: [--strict]

README.md

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
A Pythonic implementation of the JOSE set of IETF specifications: [Json Web Signature][rfc7515], [Keys][rfc7517],
77
[Algorithms][rfc7518], [Tokens][rfc7519] and [Encryption][rfc7516] (RFC7515 to 7519), and their extensions
8-
[ECDH Signatures][rfc8037] (RFC8037), [JWK Thumbprints][rfc7638] (RFC7638), and [JWK Thumbprint URI][rfc9278] (RFC9278).
8+
[ECDH Signatures][rfc8037] (RFC8037), [JWK Thumbprints][rfc7638] (RFC7638), and [JWK Thumbprint URI][rfc9278] (RFC9278),
9+
and with respect to [JWT Best Current Practices][rfc8725].
910

1011
- Free software: MIT
1112
- Documentation: <https://guillp.github.io/jwskate/>
@@ -140,25 +141,25 @@ The generated JWT claims will include the standardised claims:
140141

141142
`jwskate` supports the following signature algorithms:
142143

143-
| Signature Alg | Description | Key Type | Reference | Note |
144-
|---------------|------------------------------------------------|----------|------------------------------------|-----------------------|
145-
| HS256 | HMAC using SHA-256 | oct | [RFC7518, Section 3.2] | |
146-
| HS384 | HMAC using SHA-384 | oct | [RFC7518, Section 3.2] | |
147-
| HS512 | HMAC using SHA-512 | oct | [RFC7518, Section 3.2] | |
148-
| RS256 | RSASSA-PKCS1-v1_5 using SHA-256 | RSA | [RFC7518, Section 3.3] | |
149-
| RS384 | RSASSA-PKCS1-v1_5 using SHA-384 | RSA | [RFC7518, Section 3.3] | |
150-
| RS512 | RSASSA-PKCS1-v1_5 using SHA-512 | RSA | [RFC7518, Section 3.3] | |
151-
| ES256 | ECDSA using P-256 and SHA-256 | EC | [RFC7518, Section 3.4] | |
152-
| ES384 | ECDSA using P-384 and SHA-384 | EC | [RFC7518, Section 3.4] | |
153-
| ES512 | ECDSA using P-521 and SHA-512 | EC | [RFC7518, Section 3.4] | |
154-
| PS256 | RSASSA-PSS using SHA-256 and MGF1 with SHA-256 | RSA | [RFC7518, Section 3.5] | |
155-
| PS384 | RSASSA-PSS using SHA-384 and MGF1 with SHA-384 | RSA | [RFC7518, Section 3.5] | |
156-
| PS512 | RSASSA-PSS using SHA-512 and MGF1 with SHA-512 | RSA | [RFC7518, Section 3.5] | |
157-
| EdDSA | EdDSA signature algorithms | OKP | [RFC8037, Section 3.1] | |
158-
| ES256K | ECDSA using secp256k1 curve and SHA-256 | EC | [RFC8812, Section 3.2] | |
159-
| HS1 | HMAC using SHA-1 | oct | https://www.w3.org/TR/WebCryptoAPI | Validation Only |
160-
| RS1 | RSASSA-PKCS1-v1_5 with SHA-1 | oct | https://www.w3.org/TR/WebCryptoAPI | Validation Only |
161-
| none | No digital signature or MAC performed | | [RFC7518, Section 3.6] | Not usable by mistake |
144+
| Signature Alg | Description | Key Type | Reference | Note |
145+
|---------------|------------------------------------------------|----------|------------------------------------|--------------------------------|
146+
| HS256 | HMAC using SHA-256 | oct | [RFC7518, Section 3.2] | |
147+
| HS384 | HMAC using SHA-384 | oct | [RFC7518, Section 3.2] | |
148+
| HS512 | HMAC using SHA-512 | oct | [RFC7518, Section 3.2] | |
149+
| RS256 | RSASSA-PKCS1-v1_5 using SHA-256 | RSA | [RFC7518, Section 3.3] | |
150+
| RS384 | RSASSA-PKCS1-v1_5 using SHA-384 | RSA | [RFC7518, Section 3.3] | |
151+
| RS512 | RSASSA-PKCS1-v1_5 using SHA-512 | RSA | [RFC7518, Section 3.3] | |
152+
| ES256 | ECDSA using P-256 and SHA-256 | EC | [RFC7518, Section 3.4] | |
153+
| ES384 | ECDSA using P-384 and SHA-384 | EC | [RFC7518, Section 3.4] | |
154+
| ES512 | ECDSA using P-521 and SHA-512 | EC | [RFC7518, Section 3.4] | |
155+
| PS256 | RSASSA-PSS using SHA-256 and MGF1 with SHA-256 | RSA | [RFC7518, Section 3.5] | |
156+
| PS384 | RSASSA-PSS using SHA-384 and MGF1 with SHA-384 | RSA | [RFC7518, Section 3.5] | |
157+
| PS512 | RSASSA-PSS using SHA-512 and MGF1 with SHA-512 | RSA | [RFC7518, Section 3.5] | |
158+
| EdDSA | EdDSA signature algorithms | OKP | [RFC8037, Section 3.1] | Ed2219 and Ed448 are supported |
159+
| ES256K | ECDSA using secp256k1 curve and SHA-256 | EC | [RFC8812, Section 3.2] | |
160+
| HS1 | HMAC using SHA-1 | oct | https://www.w3.org/TR/WebCryptoAPI | Validation Only |
161+
| RS1 | RSASSA-PKCS1-v1_5 with SHA-1 | oct | https://www.w3.org/TR/WebCryptoAPI | Validation Only |
162+
| none | No digital signature or MAC performed | | [RFC7518, Section 3.6] | Not usable by mistake |
162163

163164
### Supported Key Management algorithms
164165

@@ -247,6 +248,8 @@ of mistakes.
247248

248249
### Safe Signature Verification
249250

251+
As advised in [JWT Best Practices][rfc8725] $3.1:
252+
250253
For every signature verification method in `jwskate`, the expected signature(s) algorithm(s) must be specified. That is
251254
to avoid a security flaw where your application accepts tokens with a weaker encryption scheme than what your security
252255
policy mandates; or even worse, where it accepts unsigned tokens, or tokens that are symmetrically signed with an
@@ -292,6 +295,7 @@ All cryptographic operations are handled by [cryptography](https://cryptography.
292295
[rfc8037]: https://www.rfc-editor.org/rfc/rfc8037.html
293296
[rfc8037, section 3.1]: https://www.rfc-editor.org/rfc/rfc8037.html#section-3.1
294297
[rfc8037, section 3.2]: https://www.rfc-editor.org/rfc/rfc8037.html#section-3.2
298+
[rfc8725]: https://www.rfc-editor.org/rfc/rfc8725
295299
[rfc8812, section 3.1]: https://www.rfc-editor.org/rfc/rfc8812.html#section-3.1
296300
[rfc8812, section 3.2]: https://www.rfc-editor.org/rfc/rfc8812.html#name-ecdsa-signature-with-secp25
297301
[rfc9278]: https://www.rfc-editor.org/rfc/rfc9278.html

jwskate/enums.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""This module contains enums for the various identifiers used in JWA and JWK.
22
33
See [IANA JOSE](https://www.iana.org/assignments/jose/jose.xhtml).
4+
45
"""
56

67

jwskate/jwa/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
`cryptography`.
66
77
[RFC7518]: https://www.rfc-editor.org/rfc/rfc7518
8+
89
"""
910

1011
from .base import (
@@ -71,6 +72,8 @@
7172
BaseECSignatureAlg,
7273
BaseHMACSigAlg,
7374
BaseRSASigAlg,
75+
Ed448Dsa,
76+
Ed25519Dsa,
7477
EdDsa,
7578
)
7679

@@ -111,7 +114,9 @@
111114
"EcdhEs_A192KW",
112115
"EcdhEs_A256KW",
113116
"Ed25519",
117+
"Ed25519Dsa",
114118
"Ed448",
119+
"Ed448Dsa",
115120
"EdDsa",
116121
"EllipticCurve",
117122
"HS256",

jwskate/jwa/base.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import cryptography.exceptions
99
from binapy import BinaPy
10+
from cryptography.hazmat.primitives import hashes
1011

1112

1213
class PrivateKeyRequired(AttributeError):
@@ -23,18 +24,19 @@ class BaseAlg:
2324
An algorithm has a `name` and a `description`, whose reference is found in [IANA JOSE registry][IANA].
2425
2526
[IANA]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
27+
2628
"""
2729

2830
use: str
2931
"""Alg use ('sig' or 'enc')"""
3032

3133
name: str
32-
"""Technical name of the algorithm"""
34+
"""Technical name of the algorithm."""
3335
description: str
3436
"""Description of the algorithm (human readable)"""
3537
read_only: bool = False
36-
"""For algs that are considered insecure, set to True to allow only signature verification
37-
or decryption of existing data, but don't allow new signatures or encryption."""
38+
"""For algs that are considered insecure, set to True to allow only signature verification or decryption of existing
39+
data, but don't allow new signatures or encryption."""
3840

3941
def __repr__(self) -> str:
4042
"""Use the name of the alg as repr."""
@@ -46,6 +48,7 @@ class BaseSymmetricAlg(BaseAlg):
4648
4749
Args:
4850
key: the key to use for cryptographic operations
51+
4952
"""
5053

5154
def __init__(self, key: bytes):
@@ -64,6 +67,7 @@ def check_key(cls, key: bytes) -> None:
6467
6568
Returns:
6669
Returns `None`. Raises an exception if the key is not suitable
70+
6771
"""
6872
pass
6973

@@ -78,6 +82,7 @@ def supports_key(cls, key: bytes) -> bool:
7882
7983
Returns:
8084
`True` if the key is suitable for this alg class, `False` otherwise
85+
8186
"""
8287
try:
8388
cls.check_key(key)
@@ -98,6 +103,7 @@ class BaseAsymmetricAlg(Generic[Kpriv, Kpub], BaseAlg):
98103
99104
Args:
100105
key: the key to use.
106+
101107
"""
102108

103109
private_key_class: Union[Type[Kpriv], Tuple[Type[Kpriv], ...]]
@@ -121,31 +127,34 @@ def check_key(cls, key: Union[Kpriv, Kpub]) -> None:
121127
122128
Raises:
123129
Exception: if the key is not suitable for use with this alg class
130+
124131
"""
125132

126133
@contextmanager
127134
def private_key_required(self) -> Iterator[Kpriv]:
128-
"""A context manager that checks if this alg is initialised with a private key.
135+
"""Check if this alg is initialised with a private key, as a context manager.
129136
130137
Yields:
131138
the private key
132139
133140
Raises:
134141
PrivateKeyRequired: if the configured key is not private
142+
135143
"""
136144
if not isinstance(self.key, self.private_key_class):
137145
raise PrivateKeyRequired()
138146
yield self.key # type: ignore
139147

140148
@contextmanager
141149
def public_key_required(self) -> Iterator[Kpub]:
142-
"""A context manager that checks if this alg is initialised with a public key.
150+
"""Check if this alg is initialised with a public key, as a context manager.
143151
144152
Yields:
145153
The public key
146154
147155
Raises:
148156
PublicKeyRequired: if the configured key is private
157+
149158
"""
150159
if not isinstance(self.key, self.public_key_class):
151160
raise PublicKeyRequired()
@@ -156,6 +165,7 @@ class BaseSignatureAlg(BaseAlg):
156165
"""Base class for signature algorithms."""
157166

158167
use = "sig"
168+
hashing_alg: hashes.HashAlgorithm
159169

160170
def sign(self, data: Union[bytes, SupportsBytes]) -> BinaPy:
161171
"""Sign arbitrary data, return the signature.
@@ -165,6 +175,7 @@ def sign(self, data: Union[bytes, SupportsBytes]) -> BinaPy:
165175
166176
Returns:
167177
the raw signature
178+
168179
"""
169180
raise NotImplementedError
170181

@@ -179,6 +190,7 @@ def verify(
179190
180191
Returns:
181192
`True` if the signature matches, `False` otherwise.
193+
182194
"""
183195
raise NotImplementedError
184196

@@ -201,6 +213,7 @@ def check_key(cls, key: bytes) -> None:
201213
202214
Raises:
203215
ValueError: if the key is not suitable
216+
204217
"""
205218
if len(key) * 8 != cls.key_size:
206219
raise ValueError(
@@ -213,6 +226,7 @@ def generate_key(cls) -> BinaPy:
213226
214227
Returns:
215228
a random AES key
229+
216230
"""
217231
return BinaPy.random_bits(cls.key_size)
218232

@@ -222,6 +236,7 @@ def generate_iv(cls) -> BinaPy:
222236
223237
Returns:
224238
a random IV
239+
225240
"""
226241
return BinaPy.random_bits(cls.iv_size)
227242

@@ -249,6 +264,7 @@ def encrypt(
249264
a tuple of ciphered data and authentication tag
250265
251266
[AEAD]: https://wikipedia.org/wiki/Authenticated_encryption
267+
252268
"""
253269
raise NotImplementedError
254270

@@ -275,6 +291,7 @@ def decrypt(
275291
276292
Returns:
277293
the deciphered data
294+
278295
"""
279296
raise NotImplementedError
280297

@@ -284,6 +301,7 @@ def init_random_key(cls) -> BaseAESEncryptionAlg:
284301
285302
Returns:
286303
a subclass of BaseAESEncryptionAlg initialized with a randomly generated key
304+
287305
"""
288306
return cls(cls.generate_key())
289307

0 commit comments

Comments
 (0)