summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/faq.rst8
-rw-r--r--docs/installation.rst34
-rw-r--r--jwt/algorithms.py79
-rw-r--r--jwt/contrib/__init__.py0
-rw-r--r--jwt/contrib/algorithms/__init__.py0
-rw-r--r--jwt/contrib/algorithms/py_ecdsa.py68
-rw-r--r--jwt/contrib/algorithms/py_ed25519.py79
-rw-r--r--jwt/contrib/algorithms/pycrypto.py46
-rw-r--r--jwt/help.py5
-rw-r--r--pyproject.toml2
-rw-r--r--setup.cfg4
-rw-r--r--tests/contrib/__init__.py0
-rw-r--r--tests/contrib/test_algorithms.py299
-rw-r--r--tests/test_algorithms.py85
14 files changed, 157 insertions, 552 deletions
diff --git a/docs/faq.rst b/docs/faq.rst
index a5eb139..e8fb177 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -28,11 +28,3 @@ extract the public or private keys from a x509 certificate in PEM format.
cert_obj = load_pem_x509_certificate(cert_str, default_backend())
public_key = cert_obj.public_key()
private_key = cert_obj.private_key()
-
-
-I'm using Google App Engine and can't install `cryptography`, what can I do?
-----------------------------------------------------------------------------
-
-Some platforms like Google App Engine don't allow you to install libraries
-that require C extensions to be built (like `cryptography`). If you're deploying
-to one of those environments, you should check out :ref:`legacy-deps`
diff --git a/docs/installation.rst b/docs/installation.rst
index e423cfb..6da2ccd 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -23,39 +23,5 @@ The ``pyjwt[crypto]`` format is recommended in requirements files in
projects using ``PyJWT``, as a separate ``cryptography`` requirement line
may later be mistaken for an unused requirement and removed.
-.. _legacy-deps:
-
-Legacy Dependencies
--------------------
-
-Some environments, most notably Google App Engine, do not allow the installation
-of Python packages that require compilation of C extensions and therefore
-cannot install ``cryptography``. If you can install ``cryptography``, you
-should disregard this section.
-
-If you are deploying an application to one of these environments, you may
-need to use the legacy implementations of the digital signature algorithms:
-
-.. code-block:: console
-
- $ pip install pycrypto ecdsa
-
-Once you have installed ``pycrypto`` and ``ecdcsa``, you can tell PyJWT to use
-the legacy implementations with ``jwt.register_algorithm()``. The following
-example code shows how to configure PyJWT to use the legacy implementations
-for RSA with SHA256 and EC with SHA256 signatures.
-
-.. code-block:: python
-
- import jwt
- from jwt.contrib.algorithms.pycrypto import RSAAlgorithm
- from jwt.contrib.algorithms.py_ecdsa import ECAlgorithm
-
- jwt.unregister_algorithm('RS256')
- jwt.unregister_algorithm('ES256')
-
- jwt.register_algorithm('RS256', RSAAlgorithm(RSAAlgorithm.SHA256))
- jwt.register_algorithm('ES256', ECAlgorithm(ECAlgorithm.SHA256))
-
.. _`cryptography`: https://cryptography.io
diff --git a/jwt/algorithms.py b/jwt/algorithms.py
index be41ff1..5723984 100644
--- a/jwt/algorithms.py
+++ b/jwt/algorithms.py
@@ -39,10 +39,15 @@ try:
from cryptography.hazmat.backends import default_backend
from cryptography.exceptions import InvalidSignature
+ import cryptography.exceptions
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import (
+ Ed25519PrivateKey,
+ Ed25519PublicKey,
+ )
+
has_crypto = True
except ImportError:
has_crypto = False
- has_ed25519 = False
requires_cryptography = {
"RS256",
@@ -85,16 +90,9 @@ def get_default_algorithms():
"PS256": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA256),
"PS384": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384),
"PS512": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA512),
+ "EdDSA": Ed25519Algorithm(),
}
)
- # Older versions of the `cryptography` libraries may not have Ed25519 available.
- # Needs a minimum of version 2.6
- try:
- from jwt.contrib.algorithms.py_ed25519 import Ed25519Algorithm
-
- default_algorithms.update({"EdDSA": Ed25519Algorithm()})
- except ImportError:
- pass
return default_algorithms
@@ -470,3 +468,66 @@ if has_crypto: # noqa: C901
return True
except InvalidSignature:
return False
+
+ class Ed25519Algorithm(Algorithm):
+ """
+ Performs signing and verification operations using Ed25519
+
+ This class requires ``cryptography>=2.6`` to be installed.
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ def prepare_key(self, key):
+
+ if isinstance(key, (Ed25519PrivateKey, Ed25519PublicKey)):
+ return key
+
+ if isinstance(key, (bytes, str)):
+ if isinstance(key, str):
+ key = key.encode("utf-8")
+ str_key = key.decode("utf-8")
+
+ if "-----BEGIN PUBLIC" in str_key:
+ return load_pem_public_key(key, backend=default_backend())
+ if "-----BEGIN PRIVATE" in str_key:
+ return load_pem_private_key(
+ key, password=None, backend=default_backend()
+ )
+ if str_key[0:4] == "ssh-":
+ return load_ssh_public_key(key, backend=default_backend())
+
+ raise TypeError("Expecting a PEM-formatted or OpenSSH key.")
+
+ def sign(self, msg, key):
+ """
+ Sign a message ``msg`` using the Ed25519 private key ``key``
+ :param str|bytes msg: Message to sign
+ :param Ed25519PrivateKey key: A :class:`.Ed25519PrivateKey` instance
+ :return bytes signature: The signature, as bytes
+ """
+ msg = bytes(msg, "utf-8") if type(msg) is not bytes else msg
+ return key.sign(msg)
+
+ def verify(self, msg, key, sig):
+ """
+ Verify a given ``msg`` against a signature ``sig`` using the Ed25519 key ``key``
+
+ :param str|bytes sig: Ed25519 signature to check ``msg`` against
+ :param str|bytes msg: Message to sign
+ :param Ed25519PrivateKey|Ed25519PublicKey key: A private or public Ed25519 key instance
+ :return bool verified: True if signature is valid, False if not.
+ """
+ try:
+ msg = bytes(msg, "utf-8") if type(msg) is not bytes else msg
+ sig = bytes(sig, "utf-8") if type(sig) is not bytes else sig
+
+ if isinstance(key, Ed25519PrivateKey):
+ key = key.public_key()
+ key.verify(sig, msg)
+ return (
+ True # If no exception was raised, the signature is valid.
+ )
+ except cryptography.exceptions.InvalidSignature:
+ return False
diff --git a/jwt/contrib/__init__.py b/jwt/contrib/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/jwt/contrib/__init__.py
+++ /dev/null
diff --git a/jwt/contrib/algorithms/__init__.py b/jwt/contrib/algorithms/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/jwt/contrib/algorithms/__init__.py
+++ /dev/null
diff --git a/jwt/contrib/algorithms/py_ecdsa.py b/jwt/contrib/algorithms/py_ecdsa.py
deleted file mode 100644
index 3f5fa76..0000000
--- a/jwt/contrib/algorithms/py_ecdsa.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Note: This file is named py_ecdsa.py because import behavior in Python 2
-# would cause ecdsa.py to squash the ecdsa library that it depends upon.
-
-import hashlib
-
-import ecdsa
-
-from jwt.algorithms import Algorithm
-
-
-class ECAlgorithm(Algorithm):
- """
- Performs signing and verification operations using
- ECDSA and the specified hash function
-
- This class requires the ecdsa package to be installed.
-
- This is based off of the implementation in PyJWT 0.3.2
- """
-
- SHA256 = hashlib.sha256
- SHA384 = hashlib.sha384
- SHA512 = hashlib.sha512
-
- def __init__(self, hash_alg):
- self.hash_alg = hash_alg
-
- def prepare_key(self, key):
-
- if isinstance(key, ecdsa.SigningKey) or isinstance(
- key, ecdsa.VerifyingKey
- ):
- return key
-
- if isinstance(key, (bytes, str)):
- if isinstance(key, str):
- key = key.encode("utf-8")
-
- # Attempt to load key. We don't know if it's
- # a Signing Key or a Verifying Key, so we try
- # the Verifying Key first.
- try:
- key = ecdsa.VerifyingKey.from_pem(key)
- except ecdsa.der.UnexpectedDER:
- key = ecdsa.SigningKey.from_pem(key)
-
- else:
- raise TypeError("Expecting a PEM-formatted key.")
-
- return key
-
- def sign(self, msg, key):
- return key.sign(
- msg, hashfunc=self.hash_alg, sigencode=ecdsa.util.sigencode_string
- )
-
- def verify(self, msg, key, sig):
- try:
- return key.verify(
- sig,
- msg,
- hashfunc=self.hash_alg,
- sigdecode=ecdsa.util.sigdecode_string,
- )
- # ecdsa <= 0.13.2 raises AssertionError on too long signatures,
- # ecdsa >= 0.13.3 raises BadSignatureError for verification errors.
- except (AssertionError, ecdsa.BadSignatureError):
- return False
diff --git a/jwt/contrib/algorithms/py_ed25519.py b/jwt/contrib/algorithms/py_ed25519.py
deleted file mode 100644
index a86000a..0000000
--- a/jwt/contrib/algorithms/py_ed25519.py
+++ /dev/null
@@ -1,79 +0,0 @@
-"""
-Implementation of Ed25519 using ``cryptography`` (as of Version 2.6 released in February 2019)
-"""
-
-import cryptography.exceptions
-from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives.asymmetric.ed25519 import (
- Ed25519PrivateKey,
- Ed25519PublicKey,
-)
-from cryptography.hazmat.primitives.serialization import (
- load_pem_private_key,
- load_pem_public_key,
- load_ssh_public_key,
-)
-
-from jwt.algorithms import Algorithm
-
-
-class Ed25519Algorithm(Algorithm):
- """
- Performs signing and verification operations using Ed25519
-
- This class requires ``cryptography>=2.6`` to be installed.
- """
-
- def __init__(self, **kwargs):
- pass
-
- def prepare_key(self, key):
-
- if isinstance(key, (Ed25519PrivateKey, Ed25519PublicKey)):
- return key
-
- if isinstance(key, (bytes, str)):
- if isinstance(key, str):
- key = key.encode("utf-8")
- str_key = key.decode("utf-8")
-
- if "-----BEGIN PUBLIC" in str_key:
- return load_pem_public_key(key, backend=default_backend())
- if "-----BEGIN PRIVATE" in str_key:
- return load_pem_private_key(
- key, password=None, backend=default_backend()
- )
- if str_key[0:4] == "ssh-":
- return load_ssh_public_key(key, backend=default_backend())
-
- raise TypeError("Expecting a PEM-formatted or OpenSSH key.")
-
- def sign(self, msg, key):
- """
- Sign a message ``msg`` using the Ed25519 private key ``key``
- :param str|bytes msg: Message to sign
- :param Ed25519PrivateKey key: A :class:`.Ed25519PrivateKey` instance
- :return bytes signature: The signature, as bytes
- """
- msg = bytes(msg, "utf-8") if type(msg) is not bytes else msg
- return key.sign(msg)
-
- def verify(self, msg, key, sig):
- """
- Verify a given ``msg`` against a signature ``sig`` using the Ed25519 key ``key``
-
- :param str|bytes sig: Ed25519 signature to check ``msg`` against
- :param str|bytes msg: Message to sign
- :param Ed25519PrivateKey|Ed25519PublicKey key: A private or public Ed25519 key instance
- :return bool verified: True if signature is valid, False if not.
- """
- try:
- msg = bytes(msg, "utf-8") if type(msg) is not bytes else msg
- sig = bytes(sig, "utf-8") if type(sig) is not bytes else sig
-
- if isinstance(key, Ed25519PrivateKey):
- key = key.public_key()
- key.verify(sig, msg)
- return True # If no exception was raised, the signature is valid.
- except cryptography.exceptions.InvalidSignature:
- return False
diff --git a/jwt/contrib/algorithms/pycrypto.py b/jwt/contrib/algorithms/pycrypto.py
deleted file mode 100644
index 0fb5871..0000000
--- a/jwt/contrib/algorithms/pycrypto.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import Crypto.Hash.SHA256
-import Crypto.Hash.SHA384
-import Crypto.Hash.SHA512
-from Crypto.PublicKey import RSA
-from Crypto.Signature import PKCS1_v1_5
-
-from jwt.algorithms import Algorithm
-
-
-class RSAAlgorithm(Algorithm):
- """
- Performs signing and verification operations using
- RSASSA-PKCS-v1_5 and the specified hash function.
-
- This class requires PyCrypto package to be installed.
-
- This is based off of the implementation in PyJWT 0.3.2
- """
-
- SHA256 = Crypto.Hash.SHA256
- SHA384 = Crypto.Hash.SHA384
- SHA512 = Crypto.Hash.SHA512
-
- def __init__(self, hash_alg):
- self.hash_alg = hash_alg
-
- def prepare_key(self, key):
-
- if isinstance(key, RSA._RSAobj):
- return key
-
- if isinstance(key, (bytes, str)):
- if isinstance(key, str):
- key = key.encode("utf-8")
-
- key = RSA.importKey(key)
- else:
- raise TypeError("Expecting a PEM- or RSA-formatted key.")
-
- return key
-
- def sign(self, msg, key):
- return PKCS1_v1_5.new(key).sign(self.hash_alg.new(msg))
-
- def verify(self, msg, key, sig):
- return PKCS1_v1_5.new(key).verify(self.hash_alg.new(msg), sig)
diff --git a/jwt/help.py b/jwt/help.py
index 9b42a4d..510be0a 100644
--- a/jwt/help.py
+++ b/jwt/help.py
@@ -9,11 +9,6 @@ try:
except ImportError:
cryptography = None # type: ignore
-try:
- import ecdsa # type: ignore
-except ImportError:
- ecdsa = None # type: ignore
-
def info():
"""
diff --git a/pyproject.toml b/pyproject.toml
index 0552d2b..1830eff 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -28,4 +28,4 @@ use_parentheses=true
combine_as_imports=true
known_first_party="jwt"
-known_third_party=["Crypto", "cryptography", "ecdsa", "pytest", "requests_mock", "setuptools", "sphinx_rtd_theme"]
+known_third_party=["pytest", "requests_mock", "setuptools", "sphinx_rtd_theme"]
diff --git a/setup.cfg b/setup.cfg
index 1a213c2..55e0edf 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -38,7 +38,7 @@ docs =
sphinx-rtd-theme
zope.interface
crypto =
- cryptography>=1.4
+ cryptography>=2.6,<3.0.0
tests =
pytest>=6.0.0,<7.0.0
coverage[toml]==5.0.4
@@ -47,7 +47,7 @@ dev =
sphinx
sphinx-rtd-theme
zope.interface
- cryptography>=1.4
+ cryptography>=2.6,<3.0.0
pytest>=6.0.0,<7.0.0
coverage[toml]==5.0.4
requests
diff --git a/tests/contrib/__init__.py b/tests/contrib/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/contrib/__init__.py
+++ /dev/null
diff --git a/tests/contrib/test_algorithms.py b/tests/contrib/test_algorithms.py
deleted file mode 100644
index f0cf394..0000000
--- a/tests/contrib/test_algorithms.py
+++ /dev/null
@@ -1,299 +0,0 @@
-import base64
-
-import pytest
-
-from jwt.utils import force_bytes, force_unicode
-
-from ..utils import key_path
-
-try:
- from jwt.contrib.algorithms.pycrypto import RSAAlgorithm
-
- has_pycrypto = True
-except ImportError:
- has_pycrypto = False
-
-try:
- from jwt.contrib.algorithms.py_ecdsa import ECAlgorithm
-
- has_ecdsa = True
-except ImportError:
- has_ecdsa = False
-
-try:
- from jwt.contrib.algorithms.py_ed25519 import Ed25519Algorithm
-
- has_ed25519 = True
-except ImportError:
- has_ed25519 = False
-
-
-@pytest.mark.skipif(
- not has_pycrypto, reason="Not supported without PyCrypto library"
-)
-class TestPycryptoAlgorithms:
- def test_rsa_should_parse_pem_public_key(self):
- algo = RSAAlgorithm(RSAAlgorithm.SHA256)
-
- with open(key_path("testkey2_rsa.pub.pem")) as pem_key:
- algo.prepare_key(pem_key.read())
-
- def test_rsa_should_accept_unicode_key(self):
- algo = RSAAlgorithm(RSAAlgorithm.SHA256)
-
- with open(key_path("testkey_rsa")) as rsa_key:
- algo.prepare_key(force_unicode(rsa_key.read()))
-
- def test_rsa_should_reject_non_string_key(self):
- algo = RSAAlgorithm(RSAAlgorithm.SHA256)
-
- with pytest.raises(TypeError):
- algo.prepare_key(None)
-
- def test_rsa_sign_should_generate_correct_signature_value(self):
- algo = RSAAlgorithm(RSAAlgorithm.SHA256)
-
- jwt_message = force_bytes("Hello World!")
-
- expected_sig = base64.b64decode(
- force_bytes(
- "yS6zk9DBkuGTtcBzLUzSpo9gGJxJFOGvUqN01iLhWHrzBQ9ZEz3+Ae38AXp"
- "10RWwscp42ySC85Z6zoN67yGkLNWnfmCZSEv+xqELGEvBJvciOKsrhiObUl"
- "2mveSc1oeO/2ujkGDkkkJ2epn0YliacVjZF5+/uDmImUfAAj8lzjnHlzYix"
- "sn5jGz1H07jYYbi9diixN8IUhXeTafwFg02IcONhum29V40Wu6O5tAKWlJX"
- "fHJnNUzAEUOXS0WahHVb57D30pcgIji9z923q90p5c7E2cU8V+E1qe8NdCA"
- "APCDzZZ9zQ/dgcMVaBrGrgimrcLbPjueOKFgSO+SSjIElKA=="
- )
- )
-
- with open(key_path("testkey_rsa")) as keyfile:
- jwt_key = algo.prepare_key(keyfile.read())
-
- with open(key_path("testkey_rsa.pub")) as keyfile:
- jwt_pub_key = algo.prepare_key(keyfile.read())
-
- algo.sign(jwt_message, jwt_key)
- result = algo.verify(jwt_message, jwt_pub_key, expected_sig)
- assert result
-
- def test_rsa_verify_should_return_false_if_signature_invalid(self):
- algo = RSAAlgorithm(RSAAlgorithm.SHA256)
-
- jwt_message = force_bytes("Hello World!")
-
- jwt_sig = base64.b64decode(
- force_bytes(
- "yS6zk9DBkuGTtcBzLUzSpo9gGJxJFOGvUqN01iLhWHrzBQ9ZEz3+Ae38AXp"
- "10RWwscp42ySC85Z6zoN67yGkLNWnfmCZSEv+xqELGEvBJvciOKsrhiObUl"
- "2mveSc1oeO/2ujkGDkkkJ2epn0YliacVjZF5+/uDmImUfAAj8lzjnHlzYix"
- "sn5jGz1H07jYYbi9diixN8IUhXeTafwFg02IcONhum29V40Wu6O5tAKWlJX"
- "fHJnNUzAEUOXS0WahHVb57D30pcgIji9z923q90p5c7E2cU8V+E1qe8NdCA"
- "APCDzZZ9zQ/dgcMVaBrGrgimrcLbPjueOKFgSO+SSjIElKA=="
- )
- )
-
- jwt_sig += force_bytes("123") # Signature is now invalid
-
- with open(key_path("testkey_rsa.pub")) as keyfile:
- jwt_pub_key = algo.prepare_key(keyfile.read())
-
- result = algo.verify(jwt_message, jwt_pub_key, jwt_sig)
- assert not result
-
- def test_rsa_verify_should_return_true_if_signature_valid(self):
- algo = RSAAlgorithm(RSAAlgorithm.SHA256)
-
- jwt_message = force_bytes("Hello World!")
-
- jwt_sig = base64.b64decode(
- force_bytes(
- "yS6zk9DBkuGTtcBzLUzSpo9gGJxJFOGvUqN01iLhWHrzBQ9ZEz3+Ae38AXp"
- "10RWwscp42ySC85Z6zoN67yGkLNWnfmCZSEv+xqELGEvBJvciOKsrhiObUl"
- "2mveSc1oeO/2ujkGDkkkJ2epn0YliacVjZF5+/uDmImUfAAj8lzjnHlzYix"
- "sn5jGz1H07jYYbi9diixN8IUhXeTafwFg02IcONhum29V40Wu6O5tAKWlJX"
- "fHJnNUzAEUOXS0WahHVb57D30pcgIji9z923q90p5c7E2cU8V+E1qe8NdCA"
- "APCDzZZ9zQ/dgcMVaBrGrgimrcLbPjueOKFgSO+SSjIElKA=="
- )
- )
-
- with open(key_path("testkey_rsa.pub")) as keyfile:
- jwt_pub_key = algo.prepare_key(keyfile.read())
-
- result = algo.verify(jwt_message, jwt_pub_key, jwt_sig)
- assert result
-
- def test_rsa_prepare_key_should_be_idempotent(self):
- algo = RSAAlgorithm(RSAAlgorithm.SHA256)
-
- with open(key_path("testkey_rsa.pub")) as keyfile:
- jwt_pub_key_first = algo.prepare_key(keyfile.read())
- jwt_pub_key_second = algo.prepare_key(jwt_pub_key_first)
-
- assert jwt_pub_key_first == jwt_pub_key_second
-
-
-@pytest.mark.skipif(
- not has_ecdsa, reason="Not supported without ecdsa library"
-)
-class TestEcdsaAlgorithms:
- def test_ec_should_reject_non_string_key(self):
- algo = ECAlgorithm(ECAlgorithm.SHA256)
-
- with pytest.raises(TypeError):
- algo.prepare_key(None)
-
- def test_ec_should_accept_unicode_key(self):
- algo = ECAlgorithm(ECAlgorithm.SHA256)
-
- with open(key_path("testkey_ec")) as ec_key:
- algo.prepare_key(force_unicode(ec_key.read()))
-
- def test_ec_sign_should_generate_correct_signature_value(self):
- algo = ECAlgorithm(ECAlgorithm.SHA256)
-
- jwt_message = force_bytes("Hello World!")
-
- expected_sig = base64.b64decode(
- force_bytes(
- "AC+m4Jf/xI3guAC6w0w37t5zRpSCF6F4udEz5LiMiTIjCS4vcVe6dDOxK+M"
- "mvkF8PxJuvqxP2CO3TR3okDPCl/NjATTO1jE+qBZ966CRQSSzcCM+tzcHzw"
- "LZS5kbvKu0Acd/K6Ol2/W3B1NeV5F/gjvZn/jOwaLgWEUYsg0o4XVrAg65"
- )
- )
-
- with open(key_path("testkey_ec")) as keyfile:
- jwt_key = algo.prepare_key(keyfile.read())
-
- with open(key_path("testkey_ec.pub")) as keyfile:
- jwt_pub_key = algo.prepare_key(keyfile.read())
-
- algo.sign(jwt_message, jwt_key)
- result = algo.verify(jwt_message, jwt_pub_key, expected_sig)
- assert result
-
- def test_ec_verify_should_return_false_if_signature_invalid(self):
- algo = ECAlgorithm(ECAlgorithm.SHA256)
-
- jwt_message = force_bytes("Hello World!")
-
- jwt_sig = base64.b64decode(
- force_bytes(
- "AC+m4Jf/xI3guAC6w0w37t5zRpSCF6F4udEz5LiMiTIjCS4vcVe6dDOxK+M"
- "mvkF8PxJuvqxP2CO3TR3okDPCl/NjATTO1jE+qBZ966CRQSSzcCM+tzcHzw"
- "LZS5kbvKu0Acd/K6Ol2/W3B1NeV5F/gjvZn/jOwaLgWEUYsg0o4XVrAg65"
- )
- )
-
- jwt_sig += force_bytes("123") # Signature is now invalid
-
- with open(key_path("testkey_ec.pub")) as keyfile:
- jwt_pub_key = algo.prepare_key(keyfile.read())
-
- result = algo.verify(jwt_message, jwt_pub_key, jwt_sig)
- assert not result
-
- def test_ec_verify_should_return_true_if_signature_valid(self):
- algo = ECAlgorithm(ECAlgorithm.SHA256)
-
- jwt_message = force_bytes("Hello World!")
-
- jwt_sig = base64.b64decode(
- force_bytes(
- "AC+m4Jf/xI3guAC6w0w37t5zRpSCF6F4udEz5LiMiTIjCS4vcVe6dDOxK+M"
- "mvkF8PxJuvqxP2CO3TR3okDPCl/NjATTO1jE+qBZ966CRQSSzcCM+tzcHzw"
- "LZS5kbvKu0Acd/K6Ol2/W3B1NeV5F/gjvZn/jOwaLgWEUYsg0o4XVrAg65"
- )
- )
-
- with open(key_path("testkey_ec.pub")) as keyfile:
- jwt_pub_key = algo.prepare_key(keyfile.read())
-
- result = algo.verify(jwt_message, jwt_pub_key, jwt_sig)
- assert result
-
- def test_ec_prepare_key_should_be_idempotent(self):
- algo = ECAlgorithm(ECAlgorithm.SHA256)
-
- with open(key_path("testkey_ec.pub")) as keyfile:
- jwt_pub_key_first = algo.prepare_key(keyfile.read())
- jwt_pub_key_second = algo.prepare_key(jwt_pub_key_first)
-
- assert jwt_pub_key_first == jwt_pub_key_second
-
-
-@pytest.mark.skipif(
- not has_ed25519, reason="Not supported without cryptography>=2.6 library"
-)
-class TestEd25519Algorithms:
- hello_world_sig = "Qxa47mk/azzUgmY2StAOguAd4P7YBLpyCfU3JdbaiWnXM4o4WibXwmIHvNYgN3frtE2fcyd8OYEaOiD/KiwkCg=="
- hello_world = force_bytes("Hello World!")
-
- def test_ed25519_should_reject_non_string_key(self):
- algo = Ed25519Algorithm()
-
- with pytest.raises(TypeError):
- algo.prepare_key(None)
-
- with open(key_path("testkey_ed25519")) as keyfile:
- algo.prepare_key(keyfile.read())
-
- with open(key_path("testkey_ed25519.pub")) as keyfile:
- algo.prepare_key(keyfile.read())
-
- def test_ed25519_should_accept_unicode_key(self):
- algo = Ed25519Algorithm()
-
- with open(key_path("testkey_ed25519")) as ec_key:
- algo.prepare_key(force_unicode(ec_key.read()))
-
- def test_ed25519_sign_should_generate_correct_signature_value(self):
- algo = Ed25519Algorithm()
-
- jwt_message = self.hello_world
-
- expected_sig = base64.b64decode(force_bytes(self.hello_world_sig))
-
- with open(key_path("testkey_ed25519")) as keyfile:
- jwt_key = algo.prepare_key(keyfile.read())
-
- with open(key_path("testkey_ed25519.pub")) as keyfile:
- jwt_pub_key = algo.prepare_key(keyfile.read())
-
- algo.sign(jwt_message, jwt_key)
- result = algo.verify(jwt_message, jwt_pub_key, expected_sig)
- assert result
-
- def test_ed25519_verify_should_return_false_if_signature_invalid(self):
- algo = Ed25519Algorithm()
-
- jwt_message = self.hello_world
- jwt_sig = base64.b64decode(force_bytes(self.hello_world_sig))
-
- jwt_sig += force_bytes("123") # Signature is now invalid
-
- with open(key_path("testkey_ed25519.pub")) as keyfile:
- jwt_pub_key = algo.prepare_key(keyfile.read())
-
- result = algo.verify(jwt_message, jwt_pub_key, jwt_sig)
- assert not result
-
- def test_ed25519_verify_should_return_true_if_signature_valid(self):
- algo = Ed25519Algorithm()
-
- jwt_message = self.hello_world
- jwt_sig = base64.b64decode(force_bytes(self.hello_world_sig))
-
- with open(key_path("testkey_ed25519.pub")) as keyfile:
- jwt_pub_key = algo.prepare_key(keyfile.read())
-
- result = algo.verify(jwt_message, jwt_pub_key, jwt_sig)
- assert result
-
- def test_ed25519_prepare_key_should_be_idempotent(self):
- algo = Ed25519Algorithm()
-
- with open(key_path("testkey_ed25519.pub")) as keyfile:
- jwt_pub_key_first = algo.prepare_key(keyfile.read())
- jwt_pub_key_second = algo.prepare_key(jwt_pub_key_first)
-
- assert jwt_pub_key_first == jwt_pub_key_second
diff --git a/tests/test_algorithms.py b/tests/test_algorithms.py
index 18c8e7b..4af4e6e 100644
--- a/tests/test_algorithms.py
+++ b/tests/test_algorithms.py
@@ -11,7 +11,12 @@ from .keys import load_hmac_key
from .utils import key_path
try:
- from jwt.algorithms import RSAAlgorithm, ECAlgorithm, RSAPSSAlgorithm
+ from jwt.algorithms import (
+ RSAAlgorithm,
+ ECAlgorithm,
+ RSAPSSAlgorithm,
+ Ed25519Algorithm,
+ )
from .keys import load_rsa_pub_key, load_ec_pub_key
has_crypto = True
@@ -669,3 +674,81 @@ class TestAlgorithmsRFC7520:
result = algo.verify(signing_input, key, signature)
assert result
+
+
+@pytest.mark.skipif(
+ not has_crypto, reason="Not supported without cryptography>=2.6 library"
+)
+class TestEd25519Algorithms:
+ hello_world_sig = "Qxa47mk/azzUgmY2StAOguAd4P7YBLpyCfU3JdbaiWnXM4o4WibXwmIHvNYgN3frtE2fcyd8OYEaOiD/KiwkCg=="
+ hello_world = force_bytes("Hello World!")
+
+ def test_ed25519_should_reject_non_string_key(self):
+ algo = Ed25519Algorithm()
+
+ with pytest.raises(TypeError):
+ algo.prepare_key(None)
+
+ with open(key_path("testkey_ed25519")) as keyfile:
+ algo.prepare_key(keyfile.read())
+
+ with open(key_path("testkey_ed25519.pub")) as keyfile:
+ algo.prepare_key(keyfile.read())
+
+ def test_ed25519_should_accept_unicode_key(self):
+ algo = Ed25519Algorithm()
+
+ with open(key_path("testkey_ed25519")) as ec_key:
+ algo.prepare_key(force_unicode(ec_key.read()))
+
+ def test_ed25519_sign_should_generate_correct_signature_value(self):
+ algo = Ed25519Algorithm()
+
+ jwt_message = self.hello_world
+
+ expected_sig = base64.b64decode(force_bytes(self.hello_world_sig))
+
+ with open(key_path("testkey_ed25519")) as keyfile:
+ jwt_key = algo.prepare_key(keyfile.read())
+
+ with open(key_path("testkey_ed25519.pub")) as keyfile:
+ jwt_pub_key = algo.prepare_key(keyfile.read())
+
+ algo.sign(jwt_message, jwt_key)
+ result = algo.verify(jwt_message, jwt_pub_key, expected_sig)
+ assert result
+
+ def test_ed25519_verify_should_return_false_if_signature_invalid(self):
+ algo = Ed25519Algorithm()
+
+ jwt_message = self.hello_world
+ jwt_sig = base64.b64decode(force_bytes(self.hello_world_sig))
+
+ jwt_sig += force_bytes("123") # Signature is now invalid
+
+ with open(key_path("testkey_ed25519.pub")) as keyfile:
+ jwt_pub_key = algo.prepare_key(keyfile.read())
+
+ result = algo.verify(jwt_message, jwt_pub_key, jwt_sig)
+ assert not result
+
+ def test_ed25519_verify_should_return_true_if_signature_valid(self):
+ algo = Ed25519Algorithm()
+
+ jwt_message = self.hello_world
+ jwt_sig = base64.b64decode(force_bytes(self.hello_world_sig))
+
+ with open(key_path("testkey_ed25519.pub")) as keyfile:
+ jwt_pub_key = algo.prepare_key(keyfile.read())
+
+ result = algo.verify(jwt_message, jwt_pub_key, jwt_sig)
+ assert result
+
+ def test_ed25519_prepare_key_should_be_idempotent(self):
+ algo = Ed25519Algorithm()
+
+ with open(key_path("testkey_ed25519.pub")) as keyfile:
+ jwt_pub_key_first = algo.prepare_key(keyfile.read())
+ jwt_pub_key_second = algo.prepare_key(jwt_pub_key_first)
+
+ assert jwt_pub_key_first == jwt_pub_key_second