From 40920a59090870be3d05e427642a0a2821a337d9 Mon Sep 17 00:00:00 2001 From: Gasper Zejn Date: Sun, 28 Apr 2019 22:01:33 +0200 Subject: [PATCH 1/7] Disallow verifying signatures with private RSA keys. Some backends are smart and know how to verify with private keys too. Disallow that on those backends. --- jose/backends/pycrypto_backend.py | 2 ++ jose/backends/rsa_backend.py | 2 ++ tests/test_jws.py | 10 ++++++++++ 3 files changed, 14 insertions(+) diff --git a/jose/backends/pycrypto_backend.py b/jose/backends/pycrypto_backend.py index a12e861c..42abe2a1 100644 --- a/jose/backends/pycrypto_backend.py +++ b/jose/backends/pycrypto_backend.py @@ -147,6 +147,8 @@ def sign(self, msg): raise JWKError(e) def verify(self, msg, sig): + if not self.is_public(): + return False try: return PKCS1_v1_5.new(self.prepared_key).verify(self.hash_alg.new(msg), sig) except Exception: diff --git a/jose/backends/rsa_backend.py b/jose/backends/rsa_backend.py index c1f5539d..82423a79 100644 --- a/jose/backends/rsa_backend.py +++ b/jose/backends/rsa_backend.py @@ -200,6 +200,8 @@ def sign(self, msg): return pyrsa.sign(msg, self._prepared_key, self.hash_alg) def verify(self, msg, sig): + if not self.is_public(): + return False try: pyrsa.verify(msg, sig, self._prepared_key) return True diff --git a/tests/test_jws.py b/tests/test_jws.py index f543a03a..c087e850 100644 --- a/tests/test_jws.py +++ b/tests/test_jws.py @@ -291,6 +291,16 @@ def test_wrong_key(self, payload): with pytest.raises(JWSError): jws.verify(token, rsa_public_key, ALGORITHMS.HS256) + def test_private_verify(self, payload): + token = jws.sign(payload, rsa_private_key, algorithm='RS256') + + # verify with public + dec = jws.verify(token, rsa_public_key, algorithms='RS256') + + with pytest.raises(JWSError): + # verify with private does not work + dec = jws.verify(token, rsa_private_key, algorithms='RS256') + ec_private_key = """-----BEGIN EC PRIVATE KEY----- MIHcAgEBBEIBzs13YUnYbLfYXTz4SG4DE4rPmsL3wBTdy34JcO+BDpI+NDZ0pqam From 27965026f0d016b3e0604c91382faf1da8e8a106 Mon Sep 17 00:00:00 2001 From: blag Date: Thu, 19 Dec 2019 02:16:45 -0800 Subject: [PATCH 2/7] Change to emit warnings when verifying with private keys --- jose/backends/pycrypto_backend.py | 4 +++- jose/backends/rsa_backend.py | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/jose/backends/pycrypto_backend.py b/jose/backends/pycrypto_backend.py index 5a44b58d..b8f7b678 100644 --- a/jose/backends/pycrypto_backend.py +++ b/jose/backends/pycrypto_backend.py @@ -1,6 +1,7 @@ from base64 import b64encode import six +import warnings import Crypto.Hash.SHA256 import Crypto.Hash.SHA384 @@ -148,7 +149,8 @@ def sign(self, msg): def verify(self, msg, sig): if not self.is_public(): - return False + warnings.warn("Attempting to verify a message with a private key. " + "This is not recommended.") try: return PKCS1_v1_5.new(self.prepared_key).verify(self.hash_alg.new(msg), sig) except Exception: diff --git a/jose/backends/rsa_backend.py b/jose/backends/rsa_backend.py index e91ddbb1..79862a3b 100644 --- a/jose/backends/rsa_backend.py +++ b/jose/backends/rsa_backend.py @@ -1,6 +1,8 @@ import binascii import six +import warnings + from pyasn1.error import PyAsn1Error import rsa as pyrsa @@ -201,7 +203,8 @@ def sign(self, msg): def verify(self, msg, sig): if not self.is_public(): - return False + warnings.warn("Attempting to verify a message with a private key. " + "This is not recommended.") try: pyrsa.verify(msg, sig, self._prepared_key) return True From efa828b48248fa0ee2c30c881ee52be85db14877 Mon Sep 17 00:00:00 2001 From: blag Date: Fri, 20 Dec 2019 01:04:39 -0800 Subject: [PATCH 3/7] Fix flake8 Travis tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a71e1965..736cb9cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ matrix: include: # Linting - python: 3.6 - env: TOX_ENV=flake8 + env: TOXENV=flake8 # CPython 2.7 - python: 2.7 env: TOXENV=py27-base From 8918de6864447fac0d830d16d8ef86bd4cbb8ea6 Mon Sep 17 00:00:00 2001 From: blag Date: Fri, 20 Dec 2019 01:16:27 -0800 Subject: [PATCH 4/7] Make flake8 happy: remove unused hmac import --- jose/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/jose/utils.py b/jose/utils.py index 39003ec9..e859f4c2 100644 --- a/jose/utils.py +++ b/jose/utils.py @@ -1,6 +1,5 @@ import base64 -import hmac import six import struct import sys From 2c10069800fc17acffdde9c185b9b2e5120aca0d Mon Sep 17 00:00:00 2001 From: blag Date: Fri, 20 Dec 2019 01:25:48 -0800 Subject: [PATCH 5/7] Make flake8 happy: ignore F401 in setup.py import jose --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ed196b84..984a036b 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ import os import platform -import jose +import jose # noqa: F401 from setuptools import setup From 74cdac58a2feb0bd63d39638d785492da812b857 Mon Sep 17 00:00:00 2001 From: blag Date: Thu, 19 Dec 2019 02:17:30 -0800 Subject: [PATCH 6/7] Register pytest marks to silence test warnings --- pytest.ini | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 pytest.ini diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..03589cf8 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,7 @@ +[pytest] +markers = + pycrypto: marks tests as applicable with PyCrypto backend + pycryptodome: marks tests as applicable with PyCryptodome backend + ecdsa: marks tests as applicable with ecdsa backend + cryptography: marks tests as applicable with cryptography backend + backend_compatibility: mark tests as testing compatibility between backends From 6a3865b3e8f8f02333ef4224c31b64e015987c75 Mon Sep 17 00:00:00 2001 From: blag Date: Thu, 19 Dec 2019 02:17:03 -0800 Subject: [PATCH 7/7] Test warning for backends that support verifying with private keys --- tests/test_jws.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/test_jws.py b/tests/test_jws.py index c087e850..b31e0b4f 100644 --- a/tests/test_jws.py +++ b/tests/test_jws.py @@ -1,11 +1,18 @@ import json +import warnings + +import pytest from jose import jwk from jose import jws +from jose.backends import RSAKey from jose.constants import ALGORITHMS from jose.exceptions import JWSError -import pytest +try: + from jose.backends.cryptography_backend import CryptographyRSAKey +except ImportError: + CryptographyRSAKey = None @pytest.fixture @@ -291,15 +298,19 @@ def test_wrong_key(self, payload): with pytest.raises(JWSError): jws.verify(token, rsa_public_key, ALGORITHMS.HS256) - def test_private_verify(self, payload): + @pytest.mark.skipif(RSAKey is CryptographyRSAKey, reason="Cryptography backend outright fails verification") + def test_private_verify_raises_warning(self, payload): token = jws.sign(payload, rsa_private_key, algorithm='RS256') # verify with public - dec = jws.verify(token, rsa_public_key, algorithms='RS256') + jws.verify(token, rsa_public_key, algorithms='RS256') - with pytest.raises(JWSError): - # verify with private does not work - dec = jws.verify(token, rsa_private_key, algorithms='RS256') + with warnings.catch_warnings(record=True) as w: + # verify with private raises warning + jws.verify(token, rsa_private_key, algorithms='RS256') + + assert ("Attempting to verify a message with a private key. " + "This is not recommended.") == str(w[-1].message) ec_private_key = """-----BEGIN EC PRIVATE KEY-----