Skip to content

Commit

Permalink
better separation of duties between jwa and jwk submodules (#4)
Browse files Browse the repository at this point in the history
* rename `select_alg` to `select_alg_class` and `select_algs` to `select_alg_classes`, reorganize parameters
* rename `BaseHMACSigAlg.hash_alg` to `hashing_alg` for consistency with the other signature classes
* introduce Jwk methods to get JWA classes and wrappers
* add `OKPJwk.from_bytes()` method
* introduce Jwk.check() and `to_jwk()` methods
* Jwk.key_ops returns a Tuple[str, ...] instead of a List
* simplify jwks.verify()
* misc fixes, more tests and improved code coverage
  • Loading branch information
guillp authored Nov 14, 2022
1 parent 51d24ea commit ba94526
Show file tree
Hide file tree
Showing 54 changed files with 1,596 additions and 695 deletions.
3 changes: 3 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ exclude_lines =
if __name__ == .__main__.:
def main
\.\.\.
assert False
pytest.skip
pass
3 changes: 1 addition & 2 deletions .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

### Description

Describe what you were trying to get done.
Tell us what happened, what went wrong, and what you expected to happen.
Describe what you were trying to get done. Tell us what happened, what went wrong, and what you expected to happen.

### What I Did

Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ jobs:
# The type of runner that the job will run on
strategy:
matrix:
python-versions: [3.7, 3.8, 3.9, '3.10']
python-versions: [3.7, 3.8, 3.9, '3.10', '3.11']
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-versions }}

Expand All @@ -49,10 +49,10 @@ jobs:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.9
python-version: '3.10'

- name: Install dependencies
run: |
Expand All @@ -66,7 +66,7 @@ jobs:
- name: list files
run: ls -l .

- uses: codecov/codecov-action@v1
- uses: codecov/codecov-action@v3
with:
fail_ci_if_error: true
files: coverage.xml
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ jobs:

strategy:
matrix:
python-versions: [3.8]
python-versions: ['3.10']

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Generate Changelog
uses: heinrichreimer/github-changelog-generator-action@v2.1.1
uses: heinrichreimer/github-changelog-generator-action@v2.3
with:
token: ${{ secrets.GITHUB_TOKEN }}
issues: true
Expand All @@ -41,7 +41,7 @@ jobs:
addSections: '{"documentation":{"prefix":"**Documentation:**","labels":["documentation"]}}'
output: CHANGELOG.md

- uses: actions/setup-python@v2
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-versions }}

Expand Down
12 changes: 2 additions & 10 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,6 @@ repos:
- id: check-merge-conflict
- id: check-yaml
args: [--unsafe]
- repo: https://github.com/executablebooks/mdformat
rev: 0.7.16
hooks:
- id: mdformat
args:
- --number
additional_dependencies:
- mdformat-black
- repo: https://github.com/hadialqattan/pycln
rev: v2.1.1
hooks:
Expand All @@ -32,7 +24,7 @@ repos:
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 22.8.0
rev: 22.10.0
hooks:
- id: black
- repo: https://gitlab.com/pycqa/flake8
Expand All @@ -53,7 +45,7 @@ repos:
args:
- --add-ignore=D107
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.981
rev: v0.982
hooks:
- id: mypy
args: [--strict]
Expand Down
35 changes: 14 additions & 21 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Contributing

Contributions are welcome, and they are greatly appreciated! Every little bit
helps, and credit will always be given.
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.

You can contribute in many ways:

Expand All @@ -19,19 +18,18 @@ If you are reporting a bug, please include:

### Fix Bugs

Look through the GitHub issues for bugs. Anything tagged with "bug" and "help
wanted" is open to whoever wants to implement it.
Look through the GitHub issues for bugs. Anything tagged with "bug" and "help wanted" is open to whoever wants to
implement it.

### Implement Features

Look through the GitHub issues for features. Anything tagged with "enhancement"
and "help wanted" is open to whoever wants to implement it.
Look through the GitHub issues for features. Anything tagged with "enhancement" and "help wanted" is open to whoever
wants to implement it.

### Write Documentation

JsonWebSkate could always use more documentation, whether as part of the
official JsonWebSkate docs, in docstrings, or even on the web in blog posts,
articles, and such.
JsonWebSkate could always use more documentation, whether as part of the official JsonWebSkate docs, in docstrings, or
even on the web in blog posts, articles, and such.

### Submit Feedback

Expand All @@ -41,8 +39,7 @@ If you are proposing a feature:

- Explain in detail how it would work.
- Keep the scope as narrow as possible, to make it easier to implement.
- Remember that this is a volunteer-driven project, and that contributions
are welcome :)
- Remember that this is a volunteer-driven project, and that contributions are welcome :)

## Get Started!

Expand Down Expand Up @@ -70,8 +67,8 @@ Ready to contribute? Here's how to set up `jwskate` for local development.

Now you can make your changes locally.

6. When you're done making changes, check that your changes pass the
tests, including testing other Python versions, with tox:
6. When you're done making changes, check that your changes pass the tests, including testing other Python versions,
with tox:

```
$ tox
Expand All @@ -92,12 +89,10 @@ Now you can make your changes locally.
Before you submit a pull request, check that it meets these guidelines:

1. The pull request should include tests.
2. If the pull request adds functionality, the docs should be updated. Put
your new functionality into a function with a docstring, and add the
feature to the list in README.md.
2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a
docstring, and add the feature to the list in README.md.
3. The pull request should work for Python 3.6, 3.7, 3.8, 3.9 and for PyPy. Check
https://github.com/guillp/jwskate/actions
and make sure that the tests pass for all supported Python versions.
https://github.com/guillp/jwskate/actions and make sure that the tests pass for all supported Python versions.

## Tips\`\`\`

Expand All @@ -116,9 +111,7 @@ Then run:
```

$ poetry patch # possible: major / minor / patch
$ git push
$ git push --tags
$ poetry patch # possible: major / minor / patch $ git push $ git push --tags

```
Expand Down
42 changes: 23 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
[![PyPi](https://img.shields.io/pypi/v/jwskate.svg)](https://pypi.python.org/pypi/jwskate)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

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).
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).

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

## Why a new lib ?

There are already multiple modules implementing JOSE and Json Web Crypto related specifications in Python. However, I have
been dissatisfied by all of them so far, so I decided to come up with my own module.
There are already multiple modules implementing JOSE and Json Web Crypto related specifications in Python. However, I
have been dissatisfied by all of them so far, so I decided to come up with my own module.

- [PyJWT](https://pyjwt.readthedocs.io)
- [JWCrypto](https://jwcrypto.readthedocs.io/)
- [Python-JOSE](https://python-jose.readthedocs.io/)
- [AuthLib](https://docs.authlib.org/en/latest/jose/)

Not to say that those are _bad_ libs (I actually use `jwcrypto` myself for `jwskate` unit tests), but they either don't
support some important features, lack documentation, or generally have APIs that don't feel easy-enough, Pythonic-enough to use.
support some important features, lack documentation, or generally have APIs that don't feel easy-enough, Pythonic-enough
to use.

## Design

### JWK are dicts

JWK are specified as JSON objects, which are parsed as `dict` in Python. The `Jwk` class in `jwskate` is actually a
`dict` subclass, so you can use it exactly like you would use a dict: you can access its members, dump it back as JSON, etc.
The same is true for Signed or Encrypted Json Web tokens in JSON format.
`dict` subclass, so you can use it exactly like you would use a dict: you can access its members, dump it back as JSON,
etc. The same is true for Signed or Encrypted Json Web tokens in JSON format.

### JWA Wrappers

You can use `cryptography` to do the cryptographic operations that are described in [JWA](https://www.rfc-editor.org/info/rfc7518),
but since `cryptography` is a general purpose library, its usage is not straightforward and gives you plenty of options
to carefully select and combine, leaving room for errors.
To work around this, `jwskate` comes with a set of wrappers that implement the exact JWA specifications, with minimum
risk of mistakes.
You can use `cryptography` to do the cryptographic operations that are described in
[JWA](https://www.rfc-editor.org/info/rfc7518), but since `cryptography` is a general purpose library, its usage is not
straightforward and gives you plenty of options to carefully select and combine, leaving room for errors. It has also a
quite inconsistent API to handle the different type of keys and algorithms. To work around
this, `jwskate` comes with a set of consistent wrappers that implement the exact JWA specifications, with minimum risk
of mistakes.

### Safe Signature Verification

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 improperly used public key, leaving your application exposed to exploitation by attackers.
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
improperly used public key, leaving your application exposed to exploitation by attackers.

To specify which signature algorithms are accepted, each signature verification method accepts, in order of preference:

- an `alg` parameter which contains the expected algorithm, or an `algs` parameter which contains a list of acceptable algorithms
- the `alg` parameter from the signature verification `Jwk`, if present. This `alg` is the algorithm intended for use with that key.
- an `alg` parameter which contains the expected algorithm, or an `algs` parameter which contains a list of acceptable
algorithms
- the `alg` parameter from the signature verification `Jwk`, if present. This `alg` is the algorithm intended for use
with that key.

Note that you cannot use `alg` and `algs` at the same time. If your `Jwk` contains an `alg` parameter, and you provide
an `alg` or `algs` which does not match that value, a `Warning` will be emitted.
Expand Down
78 changes: 68 additions & 10 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,68 @@
::: jwskate
selection:
docstring_style: google
filters:
\- "!^\_"
\- "^__init__"
rendering:
members_order: source
show_root_heading: true
heading_level: 2
::: jwskate.jwk
options:
docstring_style: google
filters:
- "!^\_"
- "^__init__"
members_order: source
show_root_heading: true
heading_level: 1
show_submodules: false
separate_signature: true
show_signature_annotations: true


::: jwskate.jwt
options:
docstring_style: google
filters:
- "!^\_"
- "^__init__"
members_order: source
show_root_heading: true
heading_level: 1
show_submodules: false
separate_signature: true
show_signature_annotations: true


::: jwskate.jws
options:
docstring_style: google
filters:
- "!^\_"
- "^__init__"
members_order: source
show_root_heading: true
heading_level: 1
show_submodules: false
separate_signature: true
show_signature_annotations: true


::: jwskate.jwe
options:
docstring_style: google
filters:
- "!^\_"
- "^__init__"
members_order: source
show_root_heading: true
heading_level: 1
show_submodules: false
separate_signature: true
show_signature_annotations: true


::: jwskate.jwa
options:
docstring_style: google
filters:
- "!^\_"
- "^__init__"
members_order: source
show_root_heading: true
heading_level: 1
show_submodules: false
separate_signature: true
show_signature_annotations: true
4 changes: 1 addition & 3 deletions docs/authors.md
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
{%
include-markdown "../AUTHORS.md"
%}
{% include-markdown "../AUTHORS.md" %}
4 changes: 1 addition & 3 deletions docs/contributing.md
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
{%
include-markdown "../CONTRIBUTING.md"
%}
{% include-markdown "../CONTRIBUTING.md" %}
4 changes: 1 addition & 3 deletions docs/history.md
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
{%
include-markdown "../HISTORY.md"
%}
{% include-markdown "../HISTORY.md" %}
4 changes: 1 addition & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
{%
include-markdown "../README.md"
%}
{% include-markdown "../README.md" %}
3 changes: 1 addition & 2 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ $ pip install jwskate

This is the preferred method to install `jwskate`, as it will always install the most recent stable release.

If you don't have [pip] installed, this [Python installation guide]
can guide you through the process.
If you don't have [pip] installed, this [Python installation guide] can guide you through the process.

## From source

Expand Down
Loading

0 comments on commit ba94526

Please sign in to comment.