summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Adams <mark@markadams.me>2015-05-18 23:00:23 -0500
committerMark Adams <mark@markadams.me>2015-05-18 23:13:02 -0500
commit06f461a21102712cfc2139568531c20dc302e79c (patch)
tree54b2189b2139457ccfa27a4598643218ced61169
parent4797f7f10ae78b4a9a2f04023ce45bfa73505735 (diff)
downloadpyjwt-06f461a21102712cfc2139568531c20dc302e79c.tar.gz
Added test vectors from the IETF JOSE Cookbook for HMAC, RSA, and EC.
-rw-r--r--tests/keys/__init__.py72
-rw-r--r--tests/keys/jwk_ec_key.json9
-rw-r--r--tests/keys/jwk_ec_pub.json8
-rw-r--r--tests/keys/jwk_hmac.json7
-rw-r--r--tests/keys/jwk_rsa_key.json13
-rw-r--r--tests/keys/jwk_rsa_pub.json7
-rw-r--r--tests/test_algorithms.py103
-rw-r--r--tests/utils.py21
8 files changed, 239 insertions, 1 deletions
diff --git a/tests/keys/__init__.py b/tests/keys/__init__.py
new file mode 100644
index 0000000..fad09f5
--- /dev/null
+++ b/tests/keys/__init__.py
@@ -0,0 +1,72 @@
+import json
+import os
+
+from jwt.utils import base64url_decode
+
+from tests.utils import ensure_bytes, int_from_bytes
+
+BASE_PATH = os.path.dirname(os.path.abspath(__file__))
+
+
+def decode_value(val):
+ decoded = base64url_decode(ensure_bytes(val))
+ return int_from_bytes(decoded, 'big')
+
+
+def load_hmac_key():
+ with open(os.path.join(BASE_PATH, 'jwk_hmac.json'), 'r') as infile:
+ keyobj = json.load(infile)
+
+ return base64url_decode(ensure_bytes(keyobj['k']))
+
+try:
+ from cryptography.hazmat.primitives.asymmetric import rsa
+ from cryptography.hazmat.primitives.asymmetric import ec
+ from cryptography.hazmat.backends import default_backend
+
+ has_crypto = True
+except ImportError:
+ has_crypto = False
+
+if has_crypto:
+ def load_rsa_key():
+ with open(os.path.join(BASE_PATH, 'jwk_rsa_key.json'), 'r') as infile:
+ keyobj = json.load(infile)
+
+ return rsa.RSAPrivateNumbers(
+ p=decode_value(keyobj['p']),
+ q=decode_value(keyobj['q']),
+ d=decode_value(keyobj['d']),
+ dmp1=decode_value(keyobj['dp']),
+ dmq1=decode_value(keyobj['dq']),
+ iqmp=decode_value(keyobj['qi']),
+ public_numbers=load_rsa_pub_key().public_numbers()
+ ).private_key(default_backend())
+
+ def load_rsa_pub_key():
+ with open(os.path.join(BASE_PATH, 'jwk_rsa_pub.json'), 'r') as infile:
+ keyobj = json.load(infile)
+
+ return rsa.RSAPublicNumbers(
+ n=decode_value(keyobj['n']),
+ e=decode_value(keyobj['e'])
+ ).public_key(default_backend())
+
+ def load_ec_key():
+ with open(os.path.join(BASE_PATH, 'jwk_ec_key.json'), 'r') as infile:
+ keyobj = json.load(infile)
+
+ return ec.EllipticCurvePrivateNumbers(
+ private_value=decode_value(keyobj['d']),
+ public_numbers=load_ec_pub_key().public_numbers()
+ )
+
+ def load_ec_pub_key():
+ with open(os.path.join(BASE_PATH, 'jwk_ec_pub.json'), 'r') as infile:
+ keyobj = json.load(infile)
+
+ return ec.EllipticCurvePublicNumbers(
+ x=decode_value(keyobj['x']),
+ y=decode_value(keyobj['y']),
+ curve=ec.SECP521R1()
+ ).public_key(default_backend())
diff --git a/tests/keys/jwk_ec_key.json b/tests/keys/jwk_ec_key.json
new file mode 100644
index 0000000..a7fa999
--- /dev/null
+++ b/tests/keys/jwk_ec_key.json
@@ -0,0 +1,9 @@
+{
+ "kty": "EC",
+ "kid": "bilbo.baggins@hobbiton.example",
+ "use": "sig",
+ "crv": "P-521",
+ "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt",
+ "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1",
+ "d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt"
+}
diff --git a/tests/keys/jwk_ec_pub.json b/tests/keys/jwk_ec_pub.json
new file mode 100644
index 0000000..5259ceb
--- /dev/null
+++ b/tests/keys/jwk_ec_pub.json
@@ -0,0 +1,8 @@
+{
+ "kty": "EC",
+ "kid": "bilbo.baggins@hobbiton.example",
+ "use": "sig",
+ "crv": "P-521",
+ "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt",
+ "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1"
+}
diff --git a/tests/keys/jwk_hmac.json b/tests/keys/jwk_hmac.json
new file mode 100644
index 0000000..17b0d8a
--- /dev/null
+++ b/tests/keys/jwk_hmac.json
@@ -0,0 +1,7 @@
+{
+ "kty": "oct",
+ "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037",
+ "use": "sig",
+ "alg": "HS256",
+ "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg"
+}
diff --git a/tests/keys/jwk_rsa_key.json b/tests/keys/jwk_rsa_key.json
new file mode 100644
index 0000000..2db90fc
--- /dev/null
+++ b/tests/keys/jwk_rsa_key.json
@@ -0,0 +1,13 @@
+{
+ "kty": "RSA",
+ "kid": "bilbo.baggins@hobbiton.example",
+ "use": "sig",
+ "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw",
+ "e": "AQAB",
+ "d": "bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ",
+ "p": "3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k",
+ "q": "uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc",
+ "dp": "B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik",
+ "dq": "CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8",
+ "qi": "3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4"
+}
diff --git a/tests/keys/jwk_rsa_pub.json b/tests/keys/jwk_rsa_pub.json
new file mode 100644
index 0000000..05fc712
--- /dev/null
+++ b/tests/keys/jwk_rsa_pub.json
@@ -0,0 +1,7 @@
+{
+ "kty": "RSA",
+ "kid": "bilbo.baggins@hobbiton.example",
+ "use": "sig",
+ "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw",
+ "e": "AQAB"
+}
diff --git a/tests/test_algorithms.py b/tests/test_algorithms.py
index b8240fa..710a82e 100644
--- a/tests/test_algorithms.py
+++ b/tests/test_algorithms.py
@@ -2,14 +2,16 @@ import base64
from jwt.algorithms import Algorithm, HMACAlgorithm, NoneAlgorithm
from jwt.exceptions import InvalidKeyError
+from jwt.utils import base64url_decode
import pytest
+from .keys import load_hmac_key
from .utils import ensure_bytes, ensure_unicode, key_path
try:
from jwt.algorithms import RSAAlgorithm, ECAlgorithm, RSAPSSAlgorithm
-
+ from .keys import load_rsa_pub_key, load_ec_pub_key
has_crypto = True
except ImportError:
has_crypto = False
@@ -257,3 +259,102 @@ class TestAlgorithms:
result = algo.verify(jwt_message, jwt_pub_key, jwt_sig)
assert result
+
+
+class TestAlgorithmsCookbook:
+ """
+ These test vectors were taken from IETF JOSE Cookbook Draft
+ (https://www.ietf.org/id/draft-ietf-jose-cookbook-08.txt)
+ """
+
+ def test_hmac_verify_should_return_true_for_test_vector(self):
+ signing_input = ensure_bytes(
+ 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZ'
+ 'jMxNGJjNzAzNyJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ'
+ '29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIG'
+ 'lmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmc'
+ 'gd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4'
+ )
+
+ signature = base64url_decode(ensure_bytes(
+ 's0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0'
+ ))
+
+ algo = HMACAlgorithm(HMACAlgorithm.SHA256)
+ key = algo.prepare_key(load_hmac_key())
+
+ result = algo.verify(signing_input, key, signature)
+ assert result
+
+ @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library')
+ def test_rsa_verify_should_return_true_for_test_vector(self):
+ signing_input = ensure_bytes(
+ 'eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb'
+ 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb'
+ '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS'
+ 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU'
+ 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4'
+ )
+
+ signature = base64url_decode(ensure_bytes(
+ 'MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZop'
+ 'dHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJ'
+ 'K3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4'
+ 'QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic'
+ '1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogor'
+ 'ee7vjbU5y18kDquDg'
+ ))
+
+ algo = RSAAlgorithm(RSAAlgorithm.SHA256)
+ key = algo.prepare_key(load_rsa_pub_key())
+
+ result = algo.verify(signing_input, key, signature)
+ assert result
+
+ @pytest.mark.skipif(True, "I'm not 100% sure if this test is correct")
+ @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library')
+ def test_rsapss_verify_should_return_true_for_test_vector(self):
+ signing_input = ensure_bytes(
+ 'eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb'
+ 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb'
+ '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS'
+ 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU'
+ 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4'
+ )
+
+ signature = base64url_decode(ensure_bytes(
+ 'cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2IpN'
+ '6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvW'
+ 'Xzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ij'
+ 'Q7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6ui'
+ 'P1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmW'
+ 'jwZ6oD4ifKo8DYM-X72Eaw'
+ ))
+
+ algo = RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384)
+ key = algo.prepare_key(load_rsa_pub_key())
+
+ result = algo.verify(signing_input, key, signature)
+ assert result
+
+ @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library')
+ def test_ec_verify_should_return_true_for_test_vector(self):
+ signing_input = ensure_bytes(
+ 'eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb'
+ 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb'
+ '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS'
+ 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU'
+ 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4'
+ )
+
+ signature = base64url_decode(ensure_bytes(
+ 'AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9P'
+ 'lon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890j'
+ 'l8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2'
+ ))
+
+ algo = ECAlgorithm(ECAlgorithm.SHA512)
+ key = algo.prepare_key(load_ec_pub_key())
+
+ result = algo.verify(signing_input, key, signature)
+ assert result
diff --git a/tests/utils.py b/tests/utils.py
index bf74e78..7722702 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -1,4 +1,5 @@
import os
+import struct
from calendar import timegm
from datetime import datetime
@@ -27,3 +28,23 @@ def utc_timestamp():
def key_path(key_name):
return os.path.join(os.path.dirname(os.path.realpath(__file__)),
'keys', key_name)
+
+# Borrowed from `cryptography`
+if hasattr(int, "from_bytes"):
+ int_from_bytes = int.from_bytes
+else:
+ def int_from_bytes(data, byteorder, signed=False):
+ assert byteorder == 'big'
+ assert not signed
+
+ if len(data) % 4 != 0:
+ data = (b'\x00' * (4 - (len(data) % 4))) + data
+
+ result = 0
+
+ while len(data) > 0:
+ digit, = struct.unpack('>I', data[:4])
+ result = (result << 32) + digit
+ data = data[4:]
+
+ return result