Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.6 #8

Merged
merged 17 commits into from
Jan 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 tox-gh-actions tox-poetry
pip install poetry "tox<4" tox-gh-actions tox-poetry

- name: test with tox
run:
Expand All @@ -57,7 +57,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install poetry tox tox-gh-actions tox-poetry
pip install poetry "tox<4" tox-gh-actions tox-poetry

- name: test with tox
run:
Expand Down
17 changes: 9 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ repos:
- id: forbid-tabs
- id: remove-tabs
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-merge-conflict
- id: check-yaml
args: [--unsafe]
- repo: https://github.com/hadialqattan/pycln
rev: v2.1.1
rev: v2.1.2
hooks:
- id: pycln
args: [--config=pyproject.toml]
Expand All @@ -24,28 +24,29 @@ repos:
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 22.12.0
hooks:
- id: black
- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
additional_dependencies: [flake8-typing-imports==1.10.0]
additional_dependencies:
- flake8-typing-imports==1.14.0
- repo: https://github.com/asottile/blacken-docs
rev: v1.12.1
hooks:
- id: blacken-docs
- repo: https://github.com/pycqa/pydocstyle
rev: 6.1.1
rev: 6.2.3
hooks:
- id: pydocstyle
additional_dependencies:
- toml
args:
- --add-ignore=D107
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.982
rev: v0.991
hooks:
- id: mypy
args: [--strict]
Expand Down
44 changes: 24 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

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

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

`jwskate` supports the following signature algorithms:

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

### Supported Key Management algorithms

Expand Down Expand Up @@ -247,6 +248,8 @@ of mistakes.

### Safe Signature Verification

As advised in [JWT Best Practices][rfc8725] $3.1:

For every signature verification method in `jwskate`, the expected signature(s) algorithm(s) must be specified. That is
to avoid a security flaw where your application accepts tokens with a weaker encryption scheme than what your security
policy mandates; or even worse, where it accepts unsigned tokens, or tokens that are symmetrically signed with an
Expand Down Expand Up @@ -292,6 +295,7 @@ All cryptographic operations are handled by [cryptography](https://cryptography.
[rfc8037]: https://www.rfc-editor.org/rfc/rfc8037.html
[rfc8037, section 3.1]: https://www.rfc-editor.org/rfc/rfc8037.html#section-3.1
[rfc8037, section 3.2]: https://www.rfc-editor.org/rfc/rfc8037.html#section-3.2
[rfc8725]: https://www.rfc-editor.org/rfc/rfc8725
[rfc8812, section 3.1]: https://www.rfc-editor.org/rfc/rfc8812.html#section-3.1
[rfc8812, section 3.2]: https://www.rfc-editor.org/rfc/rfc8812.html#name-ecdsa-signature-with-secp25
[rfc9278]: https://www.rfc-editor.org/rfc/rfc9278.html
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
5 changes: 5 additions & 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 Expand Up @@ -71,6 +72,8 @@
BaseECSignatureAlg,
BaseHMACSigAlg,
BaseRSASigAlg,
Ed448Dsa,
Ed25519Dsa,
EdDsa,
)

Expand Down Expand Up @@ -111,7 +114,9 @@
"EcdhEs_A192KW",
"EcdhEs_A256KW",
"Ed25519",
"Ed25519Dsa",
"Ed448",
"Ed448Dsa",
"EdDsa",
"EllipticCurve",
"HS256",
Expand Down
28 changes: 23 additions & 5 deletions jwskate/jwa/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import cryptography.exceptions
from binapy import BinaPy
from cryptography.hazmat.primitives import hashes


class PrivateKeyRequired(AttributeError):
Expand All @@ -23,18 +24,19 @@ 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
"""Alg use ('sig' or 'enc')"""

name: str
"""Technical name of the algorithm"""
"""Technical name of the algorithm."""
description: str
"""Description of the algorithm (human readable)"""
read_only: bool = False
"""For algs that are considered insecure, set to True to allow only signature verification
or decryption of existing data, but don't allow new signatures or encryption."""
"""For algs that are considered insecure, set to True to allow only signature verification or decryption of existing
data, but don't allow new signatures or encryption."""

def __repr__(self) -> str:
"""Use the name of the alg as repr."""
Expand All @@ -46,6 +48,7 @@ class BaseSymmetricAlg(BaseAlg):

Args:
key: the key to use for cryptographic operations

"""

def __init__(self, key: bytes):
Expand All @@ -64,6 +67,7 @@ def check_key(cls, key: bytes) -> None:

Returns:
Returns `None`. Raises an exception if the key is not suitable

"""
pass

Expand All @@ -78,6 +82,7 @@ def supports_key(cls, key: bytes) -> bool:

Returns:
`True` if the key is suitable for this alg class, `False` otherwise

"""
try:
cls.check_key(key)
Expand All @@ -98,6 +103,7 @@ class BaseAsymmetricAlg(Generic[Kpriv, Kpub], BaseAlg):

Args:
key: the key to use.

"""

private_key_class: Union[Type[Kpriv], Tuple[Type[Kpriv], ...]]
Expand All @@ -121,31 +127,34 @@ def check_key(cls, key: Union[Kpriv, Kpub]) -> None:

Raises:
Exception: if the key is not suitable for use with this alg class

"""

@contextmanager
def private_key_required(self) -> Iterator[Kpriv]:
"""A context manager that checks if this alg is initialised with a private key.
"""Check if this alg is initialised with a private key, as a context manager.

Yields:
the private key

Raises:
PrivateKeyRequired: if the configured key is not private

"""
if not isinstance(self.key, self.private_key_class):
raise PrivateKeyRequired()
yield self.key # type: ignore

@contextmanager
def public_key_required(self) -> Iterator[Kpub]:
"""A context manager that checks if this alg is initialised with a public key.
"""Check if this alg is initialised with a public key, as a context manager.

Yields:
The public key

Raises:
PublicKeyRequired: if the configured key is private

"""
if not isinstance(self.key, self.public_key_class):
raise PublicKeyRequired()
Expand All @@ -156,6 +165,7 @@ class BaseSignatureAlg(BaseAlg):
"""Base class for signature algorithms."""

use = "sig"
hashing_alg: hashes.HashAlgorithm

def sign(self, data: Union[bytes, SupportsBytes]) -> BinaPy:
"""Sign arbitrary data, return the signature.
Expand All @@ -165,6 +175,7 @@ def sign(self, data: Union[bytes, SupportsBytes]) -> BinaPy:

Returns:
the raw signature

"""
raise NotImplementedError

Expand All @@ -179,6 +190,7 @@ def verify(

Returns:
`True` if the signature matches, `False` otherwise.

"""
raise NotImplementedError

Expand All @@ -201,6 +213,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 @@ -213,6 +226,7 @@ def generate_key(cls) -> BinaPy:

Returns:
a random AES key

"""
return BinaPy.random_bits(cls.key_size)

Expand All @@ -222,6 +236,7 @@ def generate_iv(cls) -> BinaPy:

Returns:
a random IV

"""
return BinaPy.random_bits(cls.iv_size)

Expand Down Expand Up @@ -249,6 +264,7 @@ def encrypt(
a tuple of ciphered data and authentication tag

[AEAD]: https://wikipedia.org/wiki/Authenticated_encryption

"""
raise NotImplementedError

Expand All @@ -275,6 +291,7 @@ def decrypt(

Returns:
the deciphered data

"""
raise NotImplementedError

Expand All @@ -284,6 +301,7 @@ def init_random_key(cls) -> BaseAESEncryptionAlg:

Returns:
a subclass of BaseAESEncryptionAlg initialized with a randomly generated key

"""
return cls(cls.generate_key())

Expand Down