Blame SOURCES/jwcrypto.patch

1eb7b1
diff -r -u oauthlib-2.0.1/oauthlib/common.py oauthlib-2.0.1.patched/oauthlib/common.py
1eb7b1
--- oauthlib-2.0.1/oauthlib/common.py	2016-11-23 05:30:34.000000000 -0500
1eb7b1
+++ oauthlib-2.0.1.patched/oauthlib/common.py	2017-03-16 17:50:22.814033506 -0400
1eb7b1
@@ -15,6 +15,16 @@
1eb7b1
 import re
1eb7b1
 import sys
1eb7b1
 import time
1eb7b1
+from calendar import timegm
1eb7b1
+
1eb7b1
+from jwcrypto.jwk import JWK as _JWK
1eb7b1
+from jwcrypto.jwt import JWT
1eb7b1
+from jwcrypto.common import json_decode
1eb7b1
+from cryptography.hazmat.backends import default_backend
1eb7b1
+from cryptography.hazmat.primitives.serialization import (
1eb7b1
+    load_pem_private_key, load_pem_public_key)
1eb7b1
+from cryptography.hazmat.primitives.asymmetric.rsa import (
1eb7b1
+    RSAPrivateKey, RSAPublicKey)
1eb7b1
 
1eb7b1
 try:
1eb7b1
     from urllib import quote as _quote
1eb7b1
@@ -29,6 +39,82 @@
1eb7b1
 except ImportError:
1eb7b1
     import urllib.parse as urlparse
1eb7b1
 
1eb7b1
+# Copy code from jwcrypto 0.3.2, we need JWK.from_pyca()
1eb7b1
+from cryptography.hazmat.primitives.asymmetric import rsa
1eb7b1
+class JWK(_JWK):
1eb7b1
+    def __init__(self, **kwargs):
1eb7b1
+        self._params = dict()
1eb7b1
+        self._key = dict()
1eb7b1
+        self._unknown = dict()
1eb7b1
+
1eb7b1
+        if 'generate' in kwargs:
1eb7b1
+            self.generate_key(**kwargs)
1eb7b1
+        elif kwargs:
1eb7b1
+            self.import_key(**kwargs)
1eb7b1
+
1eb7b1
+    def _import_pyca_pri_rsa(self, key, **params):
1eb7b1
+        pn = key.private_numbers()
1eb7b1
+        params.update(
1eb7b1
+            kty='RSA',
1eb7b1
+            n=self._encode_int(pn.public_numbers.n),
1eb7b1
+            e=self._encode_int(pn.public_numbers.e),
1eb7b1
+            d=self._encode_int(pn.d),
1eb7b1
+            p=self._encode_int(pn.p),
1eb7b1
+            q=self._encode_int(pn.q),
1eb7b1
+            dp=self._encode_int(pn.dmp1),
1eb7b1
+            dq=self._encode_int(pn.dmq1),
1eb7b1
+            qi=self._encode_int(pn.iqmp)
1eb7b1
+        )
1eb7b1
+        self.import_key(**params)
1eb7b1
+
1eb7b1
+    def _import_pyca_pub_rsa(self, key, **params):
1eb7b1
+        pn = key.public_numbers()
1eb7b1
+        params.update(
1eb7b1
+            kty='RSA',
1eb7b1
+            n=self._encode_int(pn.n),
1eb7b1
+            e=self._encode_int(pn.e)
1eb7b1
+        )
1eb7b1
+        self.import_key(**params)
1eb7b1
+
1eb7b1
+    def _import_pyca_pri_ec(self, key, **params):
1eb7b1
+        pn = key.private_numbers()
1eb7b1
+        params.update(
1eb7b1
+            kty='EC',
1eb7b1
+            crv=JWKpycaCurveMap[key.curve.name],
1eb7b1
+            x=self._encode_int(pn.public_numbers.x),
1eb7b1
+            y=self._encode_int(pn.public_numbers.y),
1eb7b1
+            d=self._encode_int(pn.private_value)
1eb7b1
+        )
1eb7b1
+        self.import_key(**params)
1eb7b1
+
1eb7b1
+    def _import_pyca_pub_ec(self, key, **params):
1eb7b1
+        pn = key.public_numbers()
1eb7b1
+        params.update(
1eb7b1
+            kty='EC',
1eb7b1
+            crv=JWKpycaCurveMap[key.curve.name],
1eb7b1
+            x=self._encode_int(pn.x),
1eb7b1
+            y=self._encode_int(pn.y),
1eb7b1
+        )
1eb7b1
+        self.import_key(**params)
1eb7b1
+
1eb7b1
+    def import_from_pyca(self, key):
1eb7b1
+        if isinstance(key, rsa.RSAPrivateKey):
1eb7b1
+            self._import_pyca_pri_rsa(key)
1eb7b1
+        elif isinstance(key, rsa.RSAPublicKey):
1eb7b1
+            self._import_pyca_pub_rsa(key)
1eb7b1
+        elif isinstance(key, ec.EllipticCurvePrivateKey):
1eb7b1
+            self._import_pyca_pri_ec(key)
1eb7b1
+        elif isinstance(key, ec.EllipticCurvePublicKey):
1eb7b1
+            self._import_pyca_pub_ec(key)
1eb7b1
+        else:
1eb7b1
+            raise InvalidJWKValue('Unknown key object %r' % key)
1eb7b1
+
1eb7b1
+    @classmethod
1eb7b1
+    def from_pyca(cls, key):
1eb7b1
+        obj = cls()
1eb7b1
+        obj.import_from_pyca(key)
1eb7b1
+        return obj
1eb7b1
+
1eb7b1
 UNICODE_ASCII_CHARACTER_SET = ('abcdefghijklmnopqrstuvwxyz'
1eb7b1
                                'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
1eb7b1
                                '0123456789')
1eb7b1
@@ -229,28 +315,69 @@
1eb7b1
     return ''.join(rand.choice(chars) for x in range(length))
1eb7b1
 
1eb7b1
 
1eb7b1
-def generate_signed_token(private_pem, request):
1eb7b1
-    import jwt
1eb7b1
+def get_rsa_private_key(data):
1eb7b1
+    if isinstance(data, (bytes_type, unicode_type)):
1eb7b1
+        if isinstance(data, unicode_type):
1eb7b1
+            data = data.encode('ascii')
1eb7b1
+        # load_pem_private_key() requires the data to be str or bytes
1eb7b1
+        private_key = load_pem_private_key(data, None, default_backend())
1eb7b1
+    else:
1eb7b1
+        private_key = data
1eb7b1
 
1eb7b1
-    now = datetime.datetime.utcnow()
1eb7b1
+    if not isinstance(private_key, RSAPrivateKey):
1eb7b1
+        raise TypeError("Expected RSAPrivateKey, but got %s" %
1eb7b1
+                        private_key.__class__.__name__)
1eb7b1
+
1eb7b1
+    return private_key
1eb7b1
+
1eb7b1
+
1eb7b1
+def get_rsa_public_key(data):
1eb7b1
+    if isinstance(data, (bytes_type, unicode_type)):
1eb7b1
+        if isinstance(data, unicode_type):
1eb7b1
+            data = data.encode('ascii')
1eb7b1
+        # load_pem_public_key() requires the data to be str or bytes
1eb7b1
+        public_key = load_pem_public_key(data, default_backend())
1eb7b1
+    else:
1eb7b1
+        public_key = data
1eb7b1
+
1eb7b1
+    if not isinstance(public_key, RSAPublicKey):
1eb7b1
+        raise TypeError("Expected RSAPublicKey, but got %s" %
1eb7b1
+                        public_key.__class__.__name__)
1eb7b1
 
1eb7b1
+    return public_key
1eb7b1
+
1eb7b1
+
1eb7b1
+def normalize_claims(claims):
1eb7b1
+    for claim in ['exp', 'iat', 'nbf']:
1eb7b1
+        # Convert datetime to a intDate value in known time-format claims
1eb7b1
+        if isinstance(claims.get(claim), datetime.datetime):
1eb7b1
+            claims[claim] = timegm(claims[claim].utctimetuple())
1eb7b1
+    return claims
1eb7b1
+
1eb7b1
+
1eb7b1
+def generate_jwt_assertion(private_key, claims):
1eb7b1
+    rsa_private_key = get_rsa_private_key(private_key)
1eb7b1
+    jwkey = JWK.from_pyca(rsa_private_key)
1eb7b1
+    token = JWT(header={'alg': 'RS256'}, claims=normalize_claims(claims))
1eb7b1
+    token.make_signed_token(jwkey)
1eb7b1
+    return to_unicode(token.serialize(), "UTF-8")
1eb7b1
+
1eb7b1
+
1eb7b1
+def generate_signed_token(private_pem, request):
1eb7b1
+    now = datetime.datetime.utcnow()
1eb7b1
     claims = {
1eb7b1
         'scope': request.scope,
1eb7b1
         'exp': now + datetime.timedelta(seconds=request.expires_in)
1eb7b1
     }
1eb7b1
-
1eb7b1
     claims.update(request.claims)
1eb7b1
+    return generate_jwt_assertion(private_pem, claims)
1eb7b1
 
1eb7b1
-    token = jwt.encode(claims, private_pem, 'RS256')
1eb7b1
-    token = to_unicode(token, "UTF-8")
1eb7b1
-
1eb7b1
-    return token
1eb7b1
-
1eb7b1
-
1eb7b1
-def verify_signed_token(public_pem, token):
1eb7b1
-    import jwt
1eb7b1
 
1eb7b1
-    return jwt.decode(token, public_pem, algorithms=['RS256'])
1eb7b1
+def verify_signed_token(public_key, token):
1eb7b1
+    rsa_public_key = get_rsa_public_key(public_key)
1eb7b1
+    jwkey = JWK.from_pyca(rsa_public_key)
1eb7b1
+    signed_token = JWT(key=jwkey, jwt=token)
1eb7b1
+    return json_decode(signed_token.claims)
1eb7b1
 
1eb7b1
 
1eb7b1
 def generate_client_id(length=30, chars=CLIENT_ID_CHARACTER_SET):
1eb7b1
diff -r -u oauthlib-2.0.1/oauthlib/oauth1/rfc5849/signature.py oauthlib-2.0.1.patched/oauthlib/oauth1/rfc5849/signature.py
1eb7b1
--- oauthlib-2.0.1/oauthlib/oauth1/rfc5849/signature.py	2016-11-23 05:30:34.000000000 -0500
1eb7b1
+++ oauthlib-2.0.1.patched/oauthlib/oauth1/rfc5849/signature.py	2017-03-16 13:54:56.881287445 -0400
1eb7b1
@@ -31,8 +31,15 @@
1eb7b1
 except ImportError:
1eb7b1
     import urllib.parse as urlparse
1eb7b1
 from . import utils
1eb7b1
-from oauthlib.common import urldecode, extract_params, safe_string_equals
1eb7b1
-from oauthlib.common import bytes_type, unicode_type
1eb7b1
+
1eb7b1
+from cryptography.hazmat.primitives import hashes
1eb7b1
+from cryptography.hazmat.primitives.asymmetric import padding
1eb7b1
+from jwcrypto.jwk import JWK
1eb7b1
+from jwcrypto.jws import JWS
1eb7b1
+
1eb7b1
+from oauthlib.common import (urldecode, extract_params, safe_string_equals,
1eb7b1
+                             bytes_type, unicode_type,
1eb7b1
+                             get_rsa_private_key, get_rsa_public_key)
1eb7b1
 
1eb7b1
 
1eb7b1
 def construct_base_string(http_method, base_string_uri,
1eb7b1
@@ -464,17 +471,8 @@
1eb7b1
     # .. _`RFC2045, Section 6.8`: http://tools.ietf.org/html/rfc2045#section-6.8
1eb7b1
     return binascii.b2a_base64(signature.digest())[:-1].decode('utf-8')
1eb7b1
 
1eb7b1
-_jwtrs1 = None
1eb7b1
-
1eb7b1
-#jwt has some nice pycrypto/cryptography abstractions
1eb7b1
-def _jwt_rs1_signing_algorithm():
1eb7b1
-    global _jwtrs1
1eb7b1
-    if _jwtrs1 is None:
1eb7b1
-        import jwt.algorithms as jwtalgo
1eb7b1
-        _jwtrs1 = jwtalgo.RSAAlgorithm(jwtalgo.hashes.SHA1)
1eb7b1
-    return _jwtrs1
1eb7b1
 
1eb7b1
-def sign_rsa_sha1(base_string, rsa_private_key):
1eb7b1
+def sign_rsa_sha1(base_string, private_key):
1eb7b1
     """**RSA-SHA1**
1eb7b1
 
1eb7b1
     Per `section 3.4.3`_ of the spec.
1eb7b1
@@ -493,10 +491,11 @@
1eb7b1
     if isinstance(base_string, unicode_type):
1eb7b1
         base_string = base_string.encode('utf-8')
1eb7b1
     # TODO: finish RSA documentation
1eb7b1
-    alg = _jwt_rs1_signing_algorithm()
1eb7b1
-    key = _prepare_key_plus(alg, rsa_private_key)
1eb7b1
-    s=alg.sign(base_string, key)
1eb7b1
-    return binascii.b2a_base64(s)[:-1].decode('utf-8')
1eb7b1
+    key = get_rsa_private_key(private_key)
1eb7b1
+    signer = key.signer(padding.PKCS1v15(), hashes.SHA1())
1eb7b1
+    signer.update(base_string)
1eb7b1
+    signature = signer.finalize()
1eb7b1
+    return binascii.b2a_base64(signature)[:-1].decode('utf-8')
1eb7b1
 
1eb7b1
 
1eb7b1
 def sign_rsa_sha1_with_client(base_string, client):
1eb7b1
@@ -568,17 +567,13 @@
1eb7b1
                                resource_owner_secret)
1eb7b1
     return safe_string_equals(signature, request.signature)
1eb7b1
 
1eb7b1
-def _prepare_key_plus(alg, keystr):
1eb7b1
-    if isinstance(keystr, bytes_type):
1eb7b1
-        keystr = keystr.decode('utf-8')
1eb7b1
-    return alg.prepare_key(keystr)
1eb7b1
 
1eb7b1
 def verify_rsa_sha1(request, rsa_public_key):
1eb7b1
     """Verify a RSASSA-PKCS #1 v1.5 base64 encoded signature.
1eb7b1
 
1eb7b1
     Per `section 3.4.3`_ of the spec.
1eb7b1
 
1eb7b1
-    Note this method requires the jwt and cryptography libraries.
1eb7b1
+    Note this method requires the cryptography library.
1eb7b1
 
1eb7b1
     .. _`section 3.4.3`: http://tools.ietf.org/html/rfc5849#section-3.4.3
1eb7b1
 
1eb7b1
@@ -595,9 +590,14 @@
1eb7b1
     message = construct_base_string(request.http_method, uri, norm_params).encode('utf-8')
1eb7b1
     sig = binascii.a2b_base64(request.signature.encode('utf-8'))
1eb7b1
 
1eb7b1
-    alg = _jwt_rs1_signing_algorithm()
1eb7b1
-    key = _prepare_key_plus(alg, rsa_public_key)
1eb7b1
-    return alg.verify(message, key, sig)
1eb7b1
+    key = get_rsa_public_key(rsa_public_key)
1eb7b1
+    verifier = key.verifier(sig, padding.PKCS1v15(), hashes.SHA1())
1eb7b1
+    verifier.update(message)
1eb7b1
+    try:
1eb7b1
+        verifier.verify()
1eb7b1
+        return True
1eb7b1
+    except InvalidSignature:
1eb7b1
+        return False
1eb7b1
 
1eb7b1
 
1eb7b1
 def verify_plaintext(request, client_secret=None, resource_owner_secret=None):
1eb7b1
diff -r -u oauthlib-2.0.1/oauthlib/oauth2/rfc6749/clients/service_application.py oauthlib-2.0.1.patched/oauthlib/oauth2/rfc6749/clients/service_application.py
1eb7b1
--- oauthlib-2.0.1/oauthlib/oauth2/rfc6749/clients/service_application.py	2016-11-23 05:30:34.000000000 -0500
1eb7b1
+++ oauthlib-2.0.1.patched/oauthlib/oauth2/rfc6749/clients/service_application.py	2017-03-16 13:54:56.881287445 -0400
1eb7b1
@@ -10,7 +10,7 @@
1eb7b1
 
1eb7b1
 import time
1eb7b1
 
1eb7b1
-from oauthlib.common import to_unicode
1eb7b1
+from oauthlib.common import generate_jwt_assertion
1eb7b1
 
1eb7b1
 from .base import Client
1eb7b1
 from ..parameters import prepare_token_request
1eb7b1
@@ -139,7 +139,6 @@
1eb7b1
 
1eb7b1
         .. _`Section 3.2.1`: http://tools.ietf.org/html/rfc6749#section-3.2.1
1eb7b1
         """
1eb7b1
-        import jwt
1eb7b1
 
1eb7b1
         key = private_key or self.private_key
1eb7b1
         if not key:
1eb7b1
@@ -166,8 +165,7 @@
1eb7b1
 
1eb7b1
         claim.update(extra_claims or {})
1eb7b1
 
1eb7b1
-        assertion = jwt.encode(claim, key, 'RS256')
1eb7b1
-        assertion = to_unicode(assertion)
1eb7b1
+        assertion = generate_jwt_assertion(key, claim)
1eb7b1
 
1eb7b1
         return prepare_token_request(self.grant_type,
1eb7b1
                                      body=body,
1eb7b1
diff -r -u oauthlib-2.0.1/oauthlib.egg-info/requires.txt oauthlib-2.0.1.patched/oauthlib.egg-info/requires.txt
1eb7b1
--- oauthlib-2.0.1/oauthlib.egg-info/requires.txt	2016-11-23 05:31:05.000000000 -0500
1eb7b1
+++ oauthlib-2.0.1.patched/oauthlib.egg-info/requires.txt	2017-03-16 13:55:02.296191560 -0400
1eb7b1
@@ -7,10 +7,10 @@
1eb7b1
 
1eb7b1
 [signedtoken]
1eb7b1
 cryptography
1eb7b1
-pyjwt>=1.0.0
1eb7b1
+jwcrypto
1eb7b1
 
1eb7b1
 [test]
1eb7b1
 nose
1eb7b1
 cryptography
1eb7b1
-pyjwt>=1.0.0
1eb7b1
+jwcrypto
1eb7b1
 blinker
1eb7b1
diff -r -u oauthlib-2.0.1/oauthlib.egg-info/SOURCES.txt oauthlib-2.0.1.patched/oauthlib.egg-info/SOURCES.txt
1eb7b1
--- oauthlib-2.0.1/oauthlib.egg-info/SOURCES.txt	2016-11-23 05:31:05.000000000 -0500
1eb7b1
+++ oauthlib-2.0.1.patched/oauthlib.egg-info/SOURCES.txt	2017-03-16 13:55:02.320191135 -0400
1eb7b1
@@ -2,6 +2,7 @@
1eb7b1
 LICENSE
1eb7b1
 MANIFEST.in
1eb7b1
 README.rst
1eb7b1
+setup.cfg
1eb7b1
 setup.py
1eb7b1
 oauthlib/__init__.py
1eb7b1
 oauthlib/common.py
1eb7b1
diff -r -u oauthlib-2.0.1/setup.py oauthlib-2.0.1.patched/setup.py
1eb7b1
--- oauthlib-2.0.1/setup.py	2016-11-23 05:30:34.000000000 -0500
1eb7b1
+++ oauthlib-2.0.1.patched/setup.py	2017-03-16 13:54:56.883287410 -0400
1eb7b1
@@ -18,11 +18,11 @@
1eb7b1
         return f.read()
1eb7b1
 
1eb7b1
 if sys.version_info[0] == 3:
1eb7b1
-    tests_require = ['nose', 'cryptography', 'pyjwt>=1.0.0', 'blinker']
1eb7b1
+    tests_require = ['nose', 'cryptography', 'jwcrypto', 'blinker']
1eb7b1
 else:
1eb7b1
-    tests_require = ['nose', 'unittest2', 'cryptography', 'mock', 'pyjwt>=1.0.0', 'blinker']
1eb7b1
+    tests_require = ['nose', 'cryptography', 'mock', 'jwcrypto', 'blinker']
1eb7b1
 rsa_require = ['cryptography']
1eb7b1
-signedtoken_require = ['cryptography', 'pyjwt>=1.0.0']
1eb7b1
+signedtoken_require = ['cryptography', 'jwcrypto']
1eb7b1
 signals_require = ['blinker']
1eb7b1
 
1eb7b1
 requires = []
1eb7b1
diff -r -u oauthlib-2.0.1/tests/oauth2/rfc6749/clients/test_service_application.py oauthlib-2.0.1.patched/tests/oauth2/rfc6749/clients/test_service_application.py
1eb7b1
--- oauthlib-2.0.1/tests/oauth2/rfc6749/clients/test_service_application.py	2016-11-23 05:30:34.000000000 -0500
1eb7b1
+++ oauthlib-2.0.1.patched/tests/oauth2/rfc6749/clients/test_service_application.py	2017-03-16 13:54:56.882287427 -0400
1eb7b1
@@ -4,10 +4,9 @@
1eb7b1
 import os
1eb7b1
 from time import time
1eb7b1
 
1eb7b1
-import jwt
1eb7b1
 from mock import patch
1eb7b1
 
1eb7b1
-from oauthlib.common import Request
1eb7b1
+from oauthlib.common import Request, verify_signed_token
1eb7b1
 from oauthlib.oauth2 import ServiceApplicationClient
1eb7b1
 
1eb7b1
 from ....unittest import TestCase
1eb7b1
@@ -92,10 +91,10 @@
1eb7b1
         self.assertEqual(r.isnot, 'empty') 
1eb7b1
         self.assertEqual(r.grant_type, ServiceApplicationClient.grant_type) 
1eb7b1
 
1eb7b1
-        claim = jwt.decode(r.assertion, self.public_key, audience=self.audience, algorithms=['RS256'])
1eb7b1
+        claim = verify_signed_token(self.public_key, r.assertion)
1eb7b1
 
1eb7b1
         self.assertEqual(claim['iss'], self.issuer)
1eb7b1
-        # audience verification is handled during decode now
1eb7b1
+        self.assertEqual(claim['aud'], self.audience)
1eb7b1
         self.assertEqual(claim['sub'], self.subject)
1eb7b1
         self.assertEqual(claim['iat'], int(t.return_value))
1eb7b1
 
1eb7b1
diff -r -u oauthlib-2.0.1/tests/oauth2/rfc6749/test_server.py oauthlib-2.0.1.patched/tests/oauth2/rfc6749/test_server.py
1eb7b1
--- oauthlib-2.0.1/tests/oauth2/rfc6749/test_server.py	2016-11-23 05:30:34.000000000 -0500
1eb7b1
+++ oauthlib-2.0.1.patched/tests/oauth2/rfc6749/test_server.py	2017-03-16 13:54:56.882287427 -0400
1eb7b1
@@ -2,7 +2,6 @@
1eb7b1
 from __future__ import absolute_import, unicode_literals
1eb7b1
 from ...unittest import TestCase
1eb7b1
 import json
1eb7b1
-import jwt
1eb7b1
 import mock
1eb7b1
 
1eb7b1
 from oauthlib import common