Skip to content

Commit

Permalink
v0.8.0
Browse files Browse the repository at this point in the history
* BREAKING CHANGE: rename all `jwk` parameters to `key`, since they can accept any `cryptography` key instance
* add `Jwt.sign_arbitrary()`
* updated deps
* move SymmetricJwk specific code from `Jwk.generate()` to `SymmetricJwk.generate()`
* JwsCompact.from_parts() doesn't accept a str as signature anymore
  • Loading branch information
guillp committed Jun 23, 2023
1 parent f3795a6 commit ae36ce3
Show file tree
Hide file tree
Showing 47 changed files with 2,060 additions and 1,907 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install poetry "tox<4" tox-gh-actions tox-poetry
pip install poetry tox tox-gh-actions
- name: test with tox
run:
Expand All @@ -62,7 +62,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install poetry "tox<4" tox-gh-actions tox-poetry
pip install poetry tox tox-gh-actions
- name: test with tox
run:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
# This workflow contains a single job called "build"
release:
name: Create Release
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04

strategy:
matrix:
Expand Down
15 changes: 10 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ repos:
- id: python-use-type-annotations
- id: text-unicode-replacement-char
- repo: https://github.com/myint/docformatter
rev: v1.6.5
rev: v1.7.2
hooks:
- id: docformatter
args:
- --in-place
- --wrap-summaries=100
- --wrap-descriptions=100
- repo: https://github.com/hadialqattan/pycln
rev: v2.1.3
rev: v2.1.5
hooks:
- id: pycln
args: [--config=pyproject.toml]
Expand All @@ -50,7 +50,7 @@ repos:
additional_dependencies:
- flake8-typing-imports==1.14.0
- repo: https://github.com/asottile/blacken-docs
rev: 1.13.0
rev: 1.14.0
hooks:
- id: blacken-docs
- repo: https://github.com/pycqa/pydocstyle
Expand All @@ -62,10 +62,15 @@ repos:
args:
- --add-ignore=D107
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.3.0
rev: v1.4.0
hooks:
- id: mypy
args: [--strict]
args:
- --strict
- --implicit-reexport
- --show-error-codes
- --show-error-context
- --show-column-numbers
additional_dependencies:
- types-cryptography==3.3.23.2
- pytest-mypy==0.10.3
Expand Down
6 changes: 6 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# History
## 0.8.0 (2023-06-21)
- BREAKING CHANGE: all method parameters `jwk`, `sig_jwk`, `enc_jwk`, or `jwk_or_password`, accepting a `Jwk` instance
have been renamed to `key` or `sig_key`,`enc_key` or `key_or_password` respectively.
They now accept either a `Jwk` instance, or a dict containing a JWK, or a `cryptography` key instance directly.
- Added `Jwt.sign_arbitrary()` to sign JWT with arbitrary headers, for testing purposes only!
- Updated dev dependencies

## 0.1.0 (2021-11-15)

Expand Down
1 change: 1 addition & 0 deletions jwskate/enums.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""This module contains enums for the various identifiers used in JWA and JWK.
See [IANA JOSE](https://www.iana.org/assignments/jose/jose.xhtml).
"""


Expand Down
1 change: 1 addition & 0 deletions jwskate/jwa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
`cryptography`.
[RFC7518]: https://www.rfc-editor.org/rfc/rfc7518
"""

from .base import (
Expand Down
15 changes: 15 additions & 0 deletions jwskate/jwa/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class BaseAlg:
An algorithm has a `name` and a `description`, whose reference is found in [IANA JOSE registry][IANA].
[IANA]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
"""

use: str
Expand Down Expand Up @@ -53,6 +54,7 @@ class BaseSymmetricAlg(BaseAlg):
Args:
key: the key to use for cryptographic operations
"""

def __init__(self, key: bytes):
Expand All @@ -71,6 +73,7 @@ def check_key(cls, key: bytes) -> None:
Returns:
Returns `None`. Raises an exception if the key is not suitable
"""
pass

Expand Down Expand Up @@ -105,6 +108,7 @@ class BaseAsymmetricAlg(Generic[Kpriv, Kpub], BaseAlg):
Args:
key: the key to use.
"""

private_key_class: Union[Type[Kpriv], Tuple[Type[Kpriv], ...]]
Expand All @@ -128,6 +132,7 @@ def check_key(cls, key: Union[Kpriv, Kpub]) -> None:
Raises:
Exception: if the key is not suitable for use with this alg class
"""

@contextmanager
Expand All @@ -139,6 +144,7 @@ def private_key_required(self) -> Iterator[Kpriv]:
Raises:
PrivateKeyRequired: if the configured key is not private
"""
if not isinstance(self.key, self.private_key_class):
raise PrivateKeyRequired()
Expand All @@ -153,6 +159,7 @@ def public_key_required(self) -> Iterator[Kpub]:
Raises:
PublicKeyRequired: if the configured key is private
"""
if not isinstance(self.key, self.public_key_class):
raise PublicKeyRequired()
Expand Down Expand Up @@ -184,6 +191,7 @@ def sign(self, data: Union[bytes, SupportsBytes]) -> BinaPy:
Returns:
the raw signature
"""
raise NotImplementedError

Expand All @@ -198,6 +206,7 @@ def verify(
Returns:
`True` if the signature matches, `False` otherwise.
"""
raise NotImplementedError

Expand All @@ -220,6 +229,7 @@ def check_key(cls, key: bytes) -> None:
Raises:
ValueError: if the key is not suitable
"""
if len(key) * 8 != cls.key_size:
raise ValueError(
Expand All @@ -232,6 +242,7 @@ def generate_key(cls) -> BinaPy:
Returns:
a random AES key
"""
return BinaPy.random_bits(cls.key_size)

Expand All @@ -241,6 +252,7 @@ def generate_iv(cls) -> BinaPy:
Returns:
a random IV
"""
return BinaPy.random_bits(cls.iv_size)

Expand Down Expand Up @@ -268,6 +280,7 @@ def encrypt(
Returns:
a tuple of ciphered data and authentication tag
"""
raise NotImplementedError

Expand All @@ -294,6 +307,7 @@ def decrypt(
Returns:
the deciphered data
"""
raise NotImplementedError

Expand All @@ -304,6 +318,7 @@ def with_random_key(cls) -> Self:
Returns:
a subclass of `BaseAESEncryptionAlg` initialized with a randomly generated key
"""
return cls(cls.generate_key())

Expand Down
2 changes: 2 additions & 0 deletions jwskate/jwa/ec.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class EllipticCurve:
"""A descriptive class for Elliptic Curves.
Elliptic Curves have a name, a `cryptography.ec.EllipticCurve`, and a coordinate size.
"""

name: str
Expand Down Expand Up @@ -43,6 +44,7 @@ def generate(self) -> Tuple[int, int, int]:
Returns:
a tuple of 4 `int`s: `x` and `y` coordinates (public key) and `d` (private key)
"""
key = ec.generate_private_key(self.cryptography_curve)
pn = key.private_numbers() # type: ignore[attr-defined]
Expand Down
2 changes: 2 additions & 0 deletions jwskate/jwa/encryption/aescbchmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def __init__(self, key: bytes) -> None:
Args:
key: the key to use for encryption and decryption.
"""
super().__init__(key)
self.mac_key = self.key[: self.mac_key_size // 8]
Expand Down Expand Up @@ -129,6 +130,7 @@ def decrypt(
Returns:
the decrypted data
"""
if not isinstance(ciphertext, bytes):
ciphertext = bytes(ciphertext)
Expand Down
2 changes: 2 additions & 0 deletions jwskate/jwa/encryption/aesgcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def encrypt(
Raises:
ValueError: if the IV size is not appropriate
"""
if not isinstance(iv, bytes):
iv = bytes(iv)
Expand Down Expand Up @@ -70,6 +71,7 @@ def decrypt(
Raises:
ValueError: if the IV size is not appropriate
"""
if not isinstance(ciphertext, bytes):
ciphertext = bytes(ciphertext)
Expand Down
2 changes: 2 additions & 0 deletions jwskate/jwa/key_mgmt/aesgcmkw.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def wrap_key(
Returns:
a tuple (wrapped_key, authentication_tag)
"""
return self.encrypt(plainkey, iv=iv)

Expand All @@ -61,6 +62,7 @@ def unwrap_key(
Returns:
the unwrapped key.
"""
return self.decrypt(cipherkey, auth_tag=tag, iv=iv)

Expand Down
3 changes: 3 additions & 0 deletions jwskate/jwa/key_mgmt/aeskw.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def check_key(cls, key: bytes) -> None:
Raises:
ValueError: if the key is not appropriate
"""
if not isinstance(key, bytes) or len(key) * 8 != cls.key_size:
raise ValueError(f"Key must be {cls.key_size} bits.")
Expand All @@ -43,6 +44,7 @@ def wrap_key(self, plainkey: bytes) -> BinaPy:
Returns:
BinaPy: the wrapped key.
"""
return BinaPy(keywrap.aes_key_wrap(self.key, plainkey))

Expand All @@ -54,6 +56,7 @@ def unwrap_key(self, cipherkey: Union[bytes, SupportsBytes]) -> BinaPy:
Returns:
BinaPy: the unwrapped key.
"""
if not isinstance(cipherkey, bytes):
cipherkey = bytes(cipherkey)
Expand Down
1 change: 1 addition & 0 deletions jwskate/jwa/key_mgmt/dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def direct_key(self, aesalg: Type[BaseSymmetricAlg]) -> BinaPy:
Returns:
the current configured key, as-is
"""
aesalg.check_key(self.key)
return BinaPy(self.key)
8 changes: 8 additions & 0 deletions jwskate/jwa/key_mgmt/ecdh.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def otherinfo(cls, alg: str, apu: bytes, apv: bytes, key_size: int) -> BinaPy:
Returns:
the "otherinfo" value
"""
algorithm_id = BinaPy.from_int(len(alg), length=4) + BinaPy(alg)
partyuinfo = BinaPy.from_int(len(apu), length=4) + apu
Expand Down Expand Up @@ -81,6 +82,7 @@ def ecdh(
Returns:
a shared key
"""
if isinstance(private_key, ec.EllipticCurvePrivateKey) and isinstance(
public_key, ec.EllipticCurvePublicKey
Expand Down Expand Up @@ -125,6 +127,7 @@ def derive(
Returns:
the derived key
"""
shared_key = cls.ecdh(private_key, public_key)
ckdf = ConcatKDFHash(
Expand All @@ -141,6 +144,7 @@ def generate_ephemeral_key(
Returns:
a generated EllipticCurvePrivateKey, on the same curve as this algorithm key
"""
if isinstance(
self.key, (ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey)
Expand Down Expand Up @@ -171,6 +175,7 @@ def sender_key(
Returns:
the CEK for encryption by the sender
"""
with self.public_key_required() as key:
apu = BinaPy(headers.get("apu", b"")).decode_from("b64u")
Expand Down Expand Up @@ -204,6 +209,7 @@ def recipient_key(
Returns:
the CEK for decryption by the recipient
"""
with self.private_key_required() as key:
apu = BinaPy(headers.get("apu", b"")).decode_from("b64u")
Expand Down Expand Up @@ -240,6 +246,7 @@ def wrap_key_with_epk(
Returns:
the wrapped CEK
"""
aes_key = self.sender_key(
ephemeral_private_key, key_size=self.kwalg.key_size, **headers
Expand All @@ -263,6 +270,7 @@ def unwrap_key_with_epk(
Returns:
the unwrapped key
"""
aes_key = self.recipient_key(
ephemeral_public_key, key_size=self.kwalg.key_size, **headers
Expand Down
Loading

0 comments on commit ae36ce3

Please sign in to comment.