Skip to content

Commit

Permalink
Merge pull request #182 from blag/update-tests-aenglander
Browse files Browse the repository at this point in the history
Move HMACKey, add JWE, and update and expand tests
  • Loading branch information
blag committed Aug 23, 2020
2 parents 5ec9f48 + 581c6d7 commit 99ec142
Show file tree
Hide file tree
Showing 29 changed files with 2,479 additions and 143 deletions.
62 changes: 43 additions & 19 deletions .travis.yml
@@ -1,13 +1,16 @@
# Travis infra requires pinning dist:precise, at least as of 2017-09-01
# detail: https://blog.travis-ci.com/2017-06-21-trusty-updates-2017-Q2-launch
dist: precise
dist: bionic
language: python
install:
- pip install -U setuptools && pip install -U tox codecov tox-travis
script:
- tox
after_success:
- codecov
jobs:
allow_failures:
- python: 3.9-dev
matrix:
include:
# Linting
Expand Down Expand Up @@ -47,36 +50,57 @@ matrix:
- python: 3.6
env: TOXENV=py36-compatibility
# CPython 3.7
# xenial + sudo are currently needed to get 3.7
# https://github.com/travis-ci/travis-ci/issues/9815
- python: 3.7
env: TOXENV=py37-base
dist: xenial
sudo: true
- python: 3.7
env: TOXENV=py37-cryptography-only
dist: xenial
sudo: true
- python: 3.7
env: TOXENV=py37-pycryptodome-norsa
dist: xenial
sudo: true
- python: 3.7
env: TOXENV=py37-pycrypto-norsa
dist: xenial
sudo: true
- python: 3.7
env: TOXENV=py37-compatibility
dist: xenial
sudo: true
# PyPy 3.5 (5.10.1?)
- python: pypy3.5
# CPython 3.8
- python: 3.8
env: TOXENV=py38-base
- python: 3.8
env: TOXENV=py38-cryptography-only
- python: 3.8
env: TOXENV=py38-pycryptodome-norsa
- python: 3.8
env: TOXENV=py38-pycrypto-norsa
- python: 3.8
env: TOXENV=py38-compatibility
# CPython 3.9 - dev
- python: 3.9-dev
env: TOXENV=py39-base
- python: 3.9-dev
env: TOXENV=py39-cryptography-only
- python: 3.9-dev
env: TOXENV=py39-pycryptodome-norsa
- python: 3.9-dev
env: TOXENV=py39-pycrypto-norsa
- python: 3.9-dev
env: TOXENV=py39-compatibility
# PyPy 2.7
- python: pypy2
env: TOXENV=pypy-base
- python: pypy3.5
- python: pypy2
env: TOXENV=pypy-cryptography-only
- python: pypy3.5
- python: pypy2
env: TOXENV=pypy-pycryptodome-norsa
- python: pypy3.5
- python: pypy2
env: TOXENV=pypy-pycrypto-norsa
- python: pypy3.5
- python: pypy2
env: TOXENV=pypy-compatibility
# PyPy 3.x
- python: pypy3
env: TOXENV=pypy-base
- python: pypy3
env: TOXENV=pypy3-cryptography-only
- python: pypy3
env: TOXENV=pypy3-pycryptodome-norsa
- python: pypy3
env: TOXENV=pypy3-pycrypto-norsa
- python: pypy3
env: TOXENV=pypy3-compatibility
2 changes: 2 additions & 0 deletions docs/index.rst
Expand Up @@ -24,6 +24,7 @@ Contents
jws/index
jwt/index
jwk/index
jwe/index


APIs
Expand All @@ -35,6 +36,7 @@ APIs
jws/api
jwt/api
jwk/api
jwe/api


Principles
Expand Down
6 changes: 6 additions & 0 deletions docs/jwe/api.rst
@@ -0,0 +1,6 @@

JWE API
^^^^^^^

.. automodule:: jose.jwe
:members:
71 changes: 71 additions & 0 deletions docs/jwe/index.rst
@@ -0,0 +1,71 @@
JSON Web Encryption
===================

JSON Web Encryption (JWE) are used to encrypt a payload and represent it as a
compact URL-safe string.

Supported Content Encryption Algorithms
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The following algorithms are currently supported.

+------------------+------------------------------------------------+
| Encryption Value | Encryption Algorithm, Mode, and Auth Tag |
+==================+================================================+
| A128CBC_HS256 | AES w/128 bit key in CBC mode w/SHA256 HMAC |
+------------------+------------------------------------------------+
| A192CBC_HS384 | AES w/128 bit key in CBC mode w/SHA256 HMAC |
+------------------+------------------------------------------------+
| A256CBC_HS512 | AES w/128 bit key in CBC mode w/SHA256 HMAC |
+------------------+------------------------------------------------+
| A128GCM | AES w/128 bit key in GCM mode and GCM auth tag |
+------------------+------------------------------------------------+
| A192GCM | AES w/192 bit key in GCM mode and GCM auth tag |
+------------------+------------------------------------------------+
| A256GCM | AES w/256 bit key in GCM mode and GCM auth tag |
+------------------+------------------------------------------------+

Supported Key Management Algorithms
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The following algorithms are currently supported.

+-----------------+------------------------------------------------+
| Algorithm Value | Key Wrap Algorithm |
+=================+================================================+
| DIR | Direct (no key wrap) |
+-----------------+------------------------------------------------+
| RSA1_5 | RSAES with PKCS1 v1.5 |
+-----------------+------------------------------------------------+
| RSA_OAEP | RSAES OAEP using default parameters |
+-----------------+------------------------------------------------+
| RSA_OAEP_256 | RSAES OAEP using SHA-256 and MGF1 with SHA-256 |
+-----------------+------------------------------------------------+
| A128KW | AES Key Wrap with default IV using 128-bit key |
+-----------------+------------------------------------------------+
| A192KW m | AES Key Wrap with default IV using 192-bit key |
+-----------------+------------------------------------------------+
| A256KW | AES Key Wrap with default IV using 256-bit key |
+-----------------+------------------------------------------------+

Examples
^^^^^^^^

Encrypting Payloads
-------------------

.. code:: python
>>> from jose import jwe
>>> jwe.encrypt('Hello, World!', 'asecret128bitkey', algorithm='dir', encryption='A128GCM')
'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..McILMB3dYsNJSuhcDzQshA.OfX9H_mcUpHDeRM4IA.CcnTWqaqxNsjT4eCaUABSg'
Decrypting Payloads
--------------------------

.. code:: python
>>> from jose import jwe
>>> jwe.decrypt('eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..McILMB3dYsNJSuhcDzQshA.OfX9H_mcUpHDeRM4IA.CcnTWqaqxNsjT4eCaUABSg', 'asecret128bitkey')
'Hello, World!'
32 changes: 32 additions & 0 deletions jose/backends/__init__.py
@@ -1,13 +1,45 @@
try:
from jose.backends.cryptography_backend import get_random_bytes # noqa: F401
except ImportError:
try:
from jose.backends.pycrypto_backend import get_random_bytes # noqa: F401
except ImportError:
from jose.backends.native import get_random_bytes # noqa: F401

try:
from jose.backends.cryptography_backend import CryptographyRSAKey as RSAKey # noqa: F401
except ImportError:
try:
from jose.backends.pycrypto_backend import RSAKey # noqa: F401

# time.clock was deprecated in python 3.3 in favor of time.perf_counter
# and removed in python 3.8. pycrypto was never updated for this. If
# time has no clock attribute, let it use perf_counter instead to work
# in 3.8+
# noinspection PyUnresolvedReferences
import time
if not hasattr(time, "clock"):
time.clock = time.perf_counter

except ImportError:
from jose.backends.rsa_backend import RSAKey # noqa: F401

try:
from jose.backends.cryptography_backend import CryptographyECKey as ECKey # noqa: F401
except ImportError:
from jose.backends.ecdsa_backend import ECDSAECKey as ECKey # noqa: F401

try:
from jose.backends.cryptography_backend import CryptographyAESKey as AESKey # noqa: F401
except ImportError:
try:
from jose.backends.pycrypto_backend import AESKey # noqa: F401
except ImportError:
AESKey = None

try:
from jose.backends.cryptography_backend import CryptographyHMACKey as HMACKey # noqa: F401
except ImportError:
from jose.backends.native import HMACKey # noqa: F401

from .base import DIRKey # noqa: F401
69 changes: 69 additions & 0 deletions jose/backends/base.py
@@ -1,3 +1,8 @@
import six

from ..utils import base64url_encode


class Key(object):
"""
A simple interface for implementing JWK keys.
Expand All @@ -19,3 +24,67 @@ def to_pem(self):

def to_dict(self):
raise NotImplementedError()

def encrypt(self, plain_text, aad=None):
"""
Encrypt the plain text and generate an auth tag if appropriate
Args:
plain_text (bytes): Data to encrypt
aad (bytes, optional): Authenticated Additional Data if key's algorithm supports auth mode
Returns:
(bytes, bytes, bytes): IV, cipher text, and auth tag
"""
raise NotImplementedError()

def decrypt(self, cipher_text, iv=None, aad=None, tag=None):
"""
Decrypt the cipher text and validate the auth tag if present
Args:
cipher_text (bytes): Cipher text to decrypt
iv (bytes): IV if block mode
aad (bytes): Additional Authenticated Data to verify if auth mode
tag (bytes): Authentication tag if auth mode
Returns:
bytes: Decrypted value
"""
raise NotImplementedError()

def wrap_key(self, key_data):
"""
Wrap the the plain text key data
Args:
key_data (bytes): Key data to wrap
Returns:
bytes: Wrapped key
"""
raise NotImplementedError()

def unwrap_key(self, wrapped_key):
"""
Unwrap the the wrapped key data
Args:
wrapped_key (bytes): Wrapped key data to unwrap
Returns:
bytes: Unwrapped key
"""
raise NotImplementedError()


class DIRKey(Key):
def __init__(self, key_data, algorithm):
self._key = six.ensure_binary(key_data)
self._alg = algorithm

def to_dict(self):
return {
'alg': self._alg,
'kty': 'oct',
'k': base64url_encode(self._key),
}

0 comments on commit 99ec142

Please sign in to comment.