Beispiel #1
0
    def test_encode_decode_with_rsa_sha512(self):
        try:
            from Crypto.PublicKey import RSA

            # RSA-formatted key
            with open('tests/testkey', 'r') as rsa_priv_file:
                priv_rsakey = RSA.importKey(rsa_priv_file.read())
                jwt_message = jwt.encode(self.payload,
                                         priv_rsakey,
                                         algorithm='RS512')

            with open('tests/testkey.pub', 'r') as rsa_pub_file:
                pub_rsakey = RSA.importKey(rsa_pub_file.read())
                assert jwt.decode(jwt_message, pub_rsakey)

                load_output = jwt.load(jwt_message)
                jwt.verify_signature(key=pub_rsakey, *load_output)

            # string-formatted key
            with open('tests/testkey', 'r') as rsa_priv_file:
                priv_rsakey = rsa_priv_file.read()
                jwt_message = jwt.encode(self.payload,
                                         priv_rsakey,
                                         algorithm='RS512')

            with open('tests/testkey.pub', 'r') as rsa_pub_file:
                pub_rsakey = rsa_pub_file.read()
                assert jwt.decode(jwt_message, pub_rsakey)

                load_output = jwt.load(jwt_message)
                jwt.verify_signature(key=pub_rsakey, *load_output)
        except ImportError:
            pass
Beispiel #2
0
    def test_encode_decode_with_ecdsa_sha512(self):
        # PEM-formatted EC key
        with open('tests/testkey_ec', 'r') as ec_priv_file:
            priv_eckey = load_pem_private_key(ensure_bytes(ec_priv_file.read()),
                                              password=None, backend=default_backend())
            jwt_message = jwt.encode(self.payload, priv_eckey,
                                     algorithm='ES512')

        with open('tests/testkey_ec.pub', 'r') as ec_pub_file:
            pub_eckey = load_pem_public_key(ensure_bytes(ec_pub_file.read()), backend=default_backend())
            assert jwt.decode(jwt_message, pub_eckey)

            load_output = jwt.load(jwt_message)
            jwt.verify_signature(key=pub_eckey, *load_output)

        # string-formatted key
        with open('tests/testkey_ec', 'r') as ec_priv_file:
            priv_eckey = ec_priv_file.read()
            jwt_message = jwt.encode(self.payload, priv_eckey,
                                     algorithm='ES512')

        with open('tests/testkey_ec.pub', 'r') as ec_pub_file:
            pub_eckey = ec_pub_file.read()
            assert jwt.decode(jwt_message, pub_eckey)

            load_output = jwt.load(jwt_message)
            jwt.verify_signature(key=pub_eckey, *load_output)
Beispiel #3
0
    def test_encode_decode_with_rsa_sha384(self):
        # PEM-formatted RSA key
        with open('tests/testkey_rsa', 'r') as rsa_priv_file:
            priv_rsakey = load_pem_private_key(ensure_bytes(rsa_priv_file.read()),
                                               password=None, backend=default_backend())
            jwt_message = jwt.encode(self.payload, priv_rsakey,
                                     algorithm='RS384')

        with open('tests/testkey_rsa.pub', 'r') as rsa_pub_file:
            pub_rsakey = load_ssh_public_key(ensure_bytes(rsa_pub_file.read()),
                                             backend=default_backend())
            assert jwt.decode(jwt_message, pub_rsakey)

        # string-formatted key
        with open('tests/testkey_rsa', 'r') as rsa_priv_file:
            priv_rsakey = rsa_priv_file.read()
            jwt_message = jwt.encode(self.payload, priv_rsakey,
                                     algorithm='RS384')

        with open('tests/testkey_rsa.pub', 'r') as rsa_pub_file:
            pub_rsakey = rsa_pub_file.read()
            assert jwt.decode(jwt_message, pub_rsakey)

            load_output = jwt.load(jwt_message)
            jwt.verify_signature(key=pub_rsakey, *load_output)
Beispiel #4
0
    def test_encode_decode_with_ecdsa_sha512(self):
        try:
            import ecdsa

            # PEM-formatted EC key
            with open('tests/testkey_ec', 'r') as ec_priv_file:
                priv_eckey = ecdsa.SigningKey.from_pem(ec_priv_file.read())
                jwt_message = jwt.encode(self.payload, priv_eckey,
                                         algorithm='ES512')

            with open('tests/testkey_ec.pub', 'r') as ec_pub_file:
                pub_eckey = ecdsa.VerifyingKey.from_pem(ec_pub_file.read())
                assert jwt.decode(jwt_message, pub_eckey)

                load_output = jwt.load(jwt_message)
                jwt.verify_signature(key=pub_eckey, *load_output)

            # string-formatted key
            with open('tests/testkey_ec', 'r') as ec_priv_file:
                priv_eckey = ec_priv_file.read()
                jwt_message = jwt.encode(self.payload, priv_eckey,
                                         algorithm='ES512')

            with open('tests/testkey_ec.pub', 'r') as ec_pub_file:
                pub_eckey = ec_pub_file.read()
                assert jwt.decode(jwt_message, pub_eckey)

                load_output = jwt.load(jwt_message)
                jwt.verify_signature(key=pub_eckey, *load_output)
        except ImportError:
            pass
Beispiel #5
0
    def test_encode_decode_with_rsa_sha512(self):
        try:
            from Crypto.PublicKey import RSA

            # PEM-formatted RSA key
            with open('tests/testkey_rsa', 'r') as rsa_priv_file:
                priv_rsakey = RSA.importKey(rsa_priv_file.read())
                jwt_message = jwt.encode(self.payload, priv_rsakey,
                                         algorithm='RS512')

            with open('tests/testkey_rsa.pub', 'r') as rsa_pub_file:
                pub_rsakey = RSA.importKey(rsa_pub_file.read())
                assert jwt.decode(jwt_message, pub_rsakey)

                load_output = jwt.load(jwt_message)
                jwt.verify_signature(key=pub_rsakey, *load_output)

            # string-formatted key
            with open('tests/testkey_rsa', 'r') as rsa_priv_file:
                priv_rsakey = rsa_priv_file.read()
                jwt_message = jwt.encode(self.payload, priv_rsakey,
                                         algorithm='RS512')

            with open('tests/testkey_rsa.pub', 'r') as rsa_pub_file:
                pub_rsakey = rsa_pub_file.read()
                assert jwt.decode(jwt_message, pub_rsakey)

                load_output = jwt.load(jwt_message)
                jwt.verify_signature(key=pub_rsakey, *load_output)
        except ImportError:
            pass
Beispiel #6
0
    def test_decode_skip_expiration_verification(self):
        self.payload['exp'] = time.time() - 1
        secret = 'secret'
        jwt_message = jwt.encode(self.payload, secret)

        jwt.decode(jwt_message, secret, verify_expiration=False)

        decoded_payload, signing, header, signature = jwt.load(jwt_message)
        jwt.verify_signature(decoded_payload, signing, header,
                             signature, secret, verify_expiration=False)
Beispiel #7
0
    def test_decode_skip_notbefore_verification(self):
        self.payload['nbf'] = time.time() + 10
        secret = 'secret'
        jwt_message = jwt.encode(self.payload, secret)

        jwt.decode(jwt_message, secret, verify_expiration=False)

        decoded_payload, signing, header, signature = jwt.load(jwt_message)
        jwt.verify_signature(decoded_payload, signing, header,
                             signature, secret, verify_expiration=False)
Beispiel #8
0
    def test_load_verify_valid_jwt(self):
        example_payload = {"hello": "world"}
        example_secret = "secret"
        example_jwt = (b"eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9"
                       b".eyJoZWxsbyI6ICJ3b3JsZCJ9"
                       b".tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8")

        decoded_payload, signing, header, signature = jwt.load(example_jwt)

        jwt.verify_signature(decoded_payload, signing, header, signature,
                             example_secret)

        self.assertEqual(decoded_payload, example_payload)
Beispiel #9
0
    def test_load_verify_valid_jwt(self):
        example_payload = {'hello': 'world'}
        example_secret = 'secret'
        example_jwt = (
            b'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9'
            b'.eyJoZWxsbyI6ICJ3b3JsZCJ9'
            b'.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8')

        decoded_payload, signing, header, signature = jwt.load(example_jwt)

        jwt.verify_signature(decoded_payload, signing, header,
                             signature, example_secret)

        self.assertEqual(decoded_payload, example_payload)
Beispiel #10
0
    def test_bytes_secret(self):
        secret = b'\xc2'  # char value that ascii codec cannot decode
        jwt_message = jwt.encode(self.payload, secret)

        decoded_payload = jwt.decode(jwt_message, secret)

        self.assertEqual(decoded_payload, self.payload)

        decoded_payload, signing, header, signature = jwt.load(jwt_message)

        jwt.verify_signature(decoded_payload, signing,
                             header, signature, secret)

        self.assertEqual(decoded_payload, self.payload)
Beispiel #11
0
    def test_bytes_secret(self):
        secret = b'\xc2'  # char value that ascii codec cannot decode
        jwt_message = jwt.encode(self.payload, secret)

        decoded_payload = jwt.decode(jwt_message, secret)

        self.assertEqual(decoded_payload, self.payload)

        decoded_payload, signing, header, signature = jwt.load(jwt_message)

        jwt.verify_signature(decoded_payload, signing, header, signature,
                             secret)

        self.assertEqual(decoded_payload, self.payload)
Beispiel #12
0
    def test_unicode_secret(self):
        secret = '\xc2'
        jwt_message = jwt.encode(self.payload, secret)

        decoded_payload = jwt.decode(jwt_message, secret)

        self.assertEqual(decoded_payload, self.payload)

        decoded_payload, signing, header, signature = jwt.load(jwt_message)

        jwt.verify_signature(decoded_payload, signing, header, signature,
                             secret)

        self.assertEqual(decoded_payload, self.payload)
Beispiel #13
0
    def test_unicode_secret(self):
        secret = '\xc2'
        jwt_message = jwt.encode(self.payload, secret)

        decoded_payload = jwt.decode(jwt_message, secret)

        self.assertEqual(decoded_payload, self.payload)

        decoded_payload, signing, header, signature = jwt.load(jwt_message)

        jwt.verify_signature(decoded_payload, signing, header,
                             signature, secret)

        self.assertEqual(decoded_payload, self.payload)
Beispiel #14
0
def remote_user_jwt(request):
    # type: (HttpRequest) -> HttpResponse
    try:
        json_web_token = request.POST["json_web_token"]
        payload, signing_input, header, signature = jwt.load(json_web_token)
    except KeyError:
        raise JsonableError(_("No JSON web token passed in request"))
    except jwt.DecodeError:
        raise JsonableError(_("Bad JSON web token"))

    remote_user = payload.get("user", None)
    if remote_user is None:
        raise JsonableError(_("No user specified in JSON web token claims"))
    domain = payload.get('realm', None)
    if domain is None:
        raise JsonableError(_("No domain specified in JSON web token claims"))

    email = "%s@%s" % (remote_user, domain)

    try:
        jwt.verify_signature(payload, signing_input, header, signature,
                             settings.JWT_AUTH_KEYS[domain])
        # We do all the authentication we need here (otherwise we'd have to
        # duplicate work), but we need to call authenticate with some backend so
        # that the request.backend attribute gets set.
        return_data = {}  # type: Dict[str, bool]
        user_profile = authenticate(username=email,
                                    realm_subdomain=get_subdomain(request),
                                    return_data=return_data,
                                    use_dummy_backend=True)
        if return_data.get('invalid_subdomain'):
            logging.warning(
                "User attempted to JWT login to wrong subdomain %s: %s" % (
                    get_subdomain(request),
                    email,
                ))
            raise JsonableError(_("Wrong subdomain"))
    except (jwt.DecodeError, jwt.ExpiredSignature):
        raise JsonableError(_("Bad JSON web token signature"))
    except KeyError:
        raise JsonableError(_("Realm not authorized for JWT login"))
    except UserProfile.DoesNotExist:
        user_profile = None

    return login_or_register_remote_user(request, email, user_profile,
                                         remote_user)
Beispiel #15
0
    def test_verify_signature_no_secret(self):
        right_secret = 'foo'
        jwt_message = jwt.encode(self.payload, right_secret)
        decoded_payload, signing, header, signature = jwt.load(jwt_message)

        self.assertRaises(
            jwt.DecodeError, lambda: jwt.verify_signature(
                decoded_payload, signing, header, signature))
Beispiel #16
0
    def test_encode_decode_with_rsa_sha384(self):
        try:
            from Crypto.PublicKey import RSA

            with open('tests/testkey', 'r') as rsa_priv_file:
                priv_rsakey = RSA.importKey(rsa_priv_file.read())
                jwt_message = jwt.encode(self.payload, priv_rsakey,
                                         algorithm='RS384')

            with open('tests/testkey.pub', 'r') as rsa_pub_file:
                pub_rsakey = RSA.importKey(rsa_pub_file.read())
                assert jwt.decode(jwt_message, pub_rsakey)

                load_output = jwt.load(jwt_message)
                jwt.verify_signature(key=pub_rsakey, *load_output)
        except ImportError:
            pass
Beispiel #17
0
    def test_verify_signature_no_secret(self):
        right_secret = 'foo'
        jwt_message = jwt.encode(self.payload, right_secret)
        decoded_payload, signing, header, signature = jwt.load(jwt_message)

        self.assertRaises(
            jwt.DecodeError,
            lambda: jwt.verify_signature(decoded_payload, signing,
                                         header, signature))
Beispiel #18
0
def remote_user_jwt(request):
    # type: (HttpRequest) -> HttpResponse
    try:
        json_web_token = request.POST["json_web_token"]
        payload, signing_input, header, signature = jwt.load(json_web_token)
    except KeyError:
        raise JsonableError(_("No JSON web token passed in request"))
    except jwt.DecodeError:
        raise JsonableError(_("Bad JSON web token"))

    remote_user = payload.get("user", None)
    if remote_user is None:
        raise JsonableError(_("No user specified in JSON web token claims"))
    domain = payload.get('realm', None)
    if domain is None:
        raise JsonableError(_("No domain specified in JSON web token claims"))

    email = "%s@%s" % (remote_user, domain)

    try:
        jwt.verify_signature(payload, signing_input, header, signature,
                             settings.JWT_AUTH_KEYS[domain])
        # We do all the authentication we need here (otherwise we'd have to
        # duplicate work), but we need to call authenticate with some backend so
        # that the request.backend attribute gets set.
        return_data = {} # type: Dict[str, bool]
        user_profile = authenticate(username=email,
                                    realm_subdomain=get_subdomain(request),
                                    return_data=return_data,
                                    use_dummy_backend=True)
        if return_data.get('invalid_subdomain'):
            logging.warning("User attempted to JWT login to wrong subdomain %s: %s" % (get_subdomain(request), email,))
            raise JsonableError(_("Wrong subdomain"))
    except (jwt.DecodeError, jwt.ExpiredSignature):
        raise JsonableError(_("Bad JSON web token signature"))
    except KeyError:
        raise JsonableError(_("Realm not authorized for JWT login"))
    except UserProfile.DoesNotExist:
        user_profile = None

    return login_or_register_remote_user(request, email, user_profile, remote_user)
Beispiel #19
0
    def test_decode_with_expiration_with_leeway(self):
        self.payload['exp'] = utc_timestamp() - 2
        secret = 'secret'
        jwt_message = jwt.encode(self.payload, secret)

        decoded_payload, signing, header, signature = jwt.load(jwt_message)

        # With 3 seconds leeway, should be ok
        jwt.decode(jwt_message, secret, leeway=3)

        jwt.verify_signature(decoded_payload, signing, header,
                             signature, secret, leeway=3)

        # With 1 seconds, should fail
        self.assertRaises(
            jwt.ExpiredSignature,
            lambda: jwt.decode(jwt_message, secret, leeway=1))

        self.assertRaises(
            jwt.ExpiredSignature,
            lambda: jwt.verify_signature(decoded_payload, signing,
                                         header, signature, secret, leeway=1))
Beispiel #20
0
    def test_decode_with_notbefore_with_leeway(self):
        self.payload['nbf'] = utc_timestamp() + 10
        secret = 'secret'
        jwt_message = jwt.encode(self.payload, secret)

        decoded_payload, signing, header, signature = jwt.load(jwt_message)

        # With 13 seconds leeway, should be ok
        jwt.decode(jwt_message, secret, leeway=13)

        jwt.verify_signature(decoded_payload, signing, header,
                             signature, secret, leeway=13)

        # With 1 seconds, should fail
        self.assertRaises(
            jwt.ExpiredSignature,
            lambda: jwt.decode(jwt_message, secret, leeway=1))

        self.assertRaises(
            jwt.ExpiredSignature,
            lambda: jwt.verify_signature(decoded_payload, signing,
                                         header, signature, secret, leeway=1))
Beispiel #21
0
    def test_decode_with_expiration(self):
        self.payload['exp'] = utc_timestamp() - 1
        secret = 'secret'
        jwt_message = jwt.encode(self.payload, secret)

        self.assertRaises(jwt.ExpiredSignature,
                          lambda: jwt.decode(jwt_message, secret))

        decoded_payload, signing, header, signature = jwt.load(jwt_message)

        self.assertRaises(
            jwt.ExpiredSignature, lambda: jwt.verify_signature(
                decoded_payload, signing, header, signature, secret))
Beispiel #22
0
    def test_decode_with_notbefore(self):
        self.payload['nbf'] = utc_timestamp() + 10
        secret = 'secret'
        jwt_message = jwt.encode(self.payload, secret)

        self.assertRaises(
            jwt.ExpiredSignature,
            lambda: jwt.decode(jwt_message, secret))

        decoded_payload, signing, header, signature = jwt.load(jwt_message)

        self.assertRaises(
            jwt.ExpiredSignature,
            lambda: jwt.verify_signature(
                decoded_payload, signing, header, signature, secret))
Beispiel #23
0
def identify(mw_uri, consumer_token, access_token, leeway=10.0):
    """
    Gather's identifying information about a user via an authorized token.

    :Parameters:
        mw_uri : `str`
            The base URI of the MediaWiki installation.  Note that the URI
            should end in ``"index.php"``.
        consumer_token : :class:`~mwoauth.ConsumerToken`
            A token representing you, the consumer.
        access_token : :class:`~mwoauth.AccessToken`
            A token representing an authorized user.  Obtained from `complete()`
        leeway : `int` | `float`
            The number of seconds of leeway to account for when examining a
            tokens "issued at" timestamp.

    :Returns:
        A dictionary containing identity information.
    """

    # Construct an OAuth auth
    auth = OAuth1(consumer_token.key,
                  client_secret=consumer_token.secret,
                  resource_owner_key=access_token.key,
                  resource_owner_secret=access_token.secret)

    # Request the identity using auth
    r = requests.post(url=mw_uri,
                      params={'title': "Special:OAuth/identify"},
                      auth=auth)

    # Decode json & stuff
    try:
        identity, signing_input, header, signature = jwt.load(r.content)
    except jwt.DecodeError as e:
        raise Exception("An error occurred while trying to read json " +
                        "content: {0}".format(e))

    # Ensure no downgrade in authentication
    if not header['alg'] == "HS256":
        raise Exception("Unexpected algorithm used for authentication " +
                        "{0}, expected {1}".format("HS256", header['alg']))

    # Check signature
    try:
        jwt.verify_signature(identity, signing_input, header, signature,
                             consumer_token.secret, False)
    except jwt.DecodeError as e:
        raise Exception("Could not verify the jwt signature: {0}".format(e))

    # Verify the issuer is who we expect (server sends $wgCanonicalServer)
    issuer = urlparse(identity['iss']).netloc
    expected_domain = urlparse(mw_uri).netloc
    if not issuer == expected_domain:
        raise Exception("Unexpected issuer " +
                        "{0}, expected {1}".format(issuer, expected_domain))

    # Verify we are the intended audience of this response
    audience = identity['aud']
    if not audience == consumer_token.key:
        raise Exception("Unexpected audience " +
                        "{0}, expected {1}".format(audience, expected_domain))

    now = time.time()

    # Check that the identity was issued in the past.
    issued_at = float(identity['iat'])
    if not now >= (issued_at - leeway):
        raise Exception("Identity issued {0} ".format(issued_at - now) +
                        "seconds in the future!")

    # Check that the identity has not yet expired
    expiration = float(identity['exp'])
    if not now <= expiration:
        raise Exception("Identity expired {0} ".format(expiration - now) +
                        "seconds ago!")

    # Verify that the nonce matches our request one,
    # to avoid a replay attack
    authorization_header = force_unicode(r.request.headers['Authorization'])
    request_nonce = re.search(r'oauth_nonce="(.*?)"',
                              authorization_header).group(1)
    if identity['nonce'] != request_nonce:
        raise Exception('Replay attack detected: {0} != {1}'.format(
            identity['nonce'], request_nonce))

    return identity
Beispiel #24
0
def identify(mw_uri, consumer_token, access_token, leeway=10.0):
    """
    Gather's identifying information about a user via an authorized token.

    :Parameters:
        mw_uri : `str`
            The base URI of the MediaWiki installation.  Note that the URI
            should end in ``"index.php"``.
        consumer_token : :class:`~mwoauth.ConsumerToken`
            A token representing you, the consumer.
        access_token : :class:`~mwoauth.AccessToken`
            A token representing an authorized user.  Obtained from `complete()`
        leeway : `int` | `float`
            The number of seconds of leeway to account for when examining a
            tokens "issued at" timestamp.

    :Returns:
        A dictionary containing identity information.
    """

    # Construct an OAuth auth
    auth = OAuth1(consumer_token.key,
                  client_secret=consumer_token.secret,
                  resource_owner_key=access_token.key,
                  resource_owner_secret=access_token.secret)

    # Request the identity using auth
    r = requests.post(url=mw_uri,
                      params={'title': "Special:OAuth/identify"},
                      auth=auth)

    # Decode json & stuff
    try:
        identity, signing_input, header, signature = jwt.load(r.content)
    except jwt.DecodeError as e:
        raise Exception("An error occurred while trying to read json " +
                        "content: {0}".format(e))

    # Ensure no downgrade in authentication
    if not header['alg'] == "HS256":
        raise Exception("Unexpected algorithm used for authentication " +
                        "{0}, expected {1}".format("HS256", header['alg']))

    # Check signature
    try:
        jwt.verify_signature(identity, signing_input, header, signature,
                             consumer_token.secret, False)
    except jwt.DecodeError as e:
        raise Exception("Could not verify the jwt signature: {0}".format(e))

    # Verify the issuer is who we expect (server sends $wgCanonicalServer)
    issuer = urlparse(identity['iss']).netloc
    expected_domain = urlparse(mw_uri).netloc
    if not issuer == expected_domain:
        raise Exception("Unexpected issuer " +
                        "{0}, expected {1}".format(issuer, expected_domain))

    # Verify we are the intended audience of this response
    audience = identity['aud']
    if not audience == consumer_token.key:
        raise Exception("Unexpected audience " +
                        "{0}, expected {1}".format(audience, expected_domain))

    now = time.time()

    # Check that the identity was issued in the past.
    issued_at = float(identity['iat'])
    if not now >= (issued_at - leeway):
        raise Exception("Identity issued {0} ".format(issued_at - now) +
                        "seconds in the future!")

    # Check that the identity has not yet expired
    expiration = float(identity['exp'])
    if not now <= expiration:
        raise Exception("Identity expired {0} ".format(expiration - now) +
                        "seconds ago!")

    # Verify that the nonce matches our request one,
    # to avoid a replay attack
    authorization_header = force_unicode(r.request.headers['Authorization'])
    request_nonce = re.search(r'oauth_nonce="(.*?)"',
                              authorization_header).group(1)
    if identity['nonce'] != request_nonce:
        raise Exception('Replay attack detected: {0} != {1}'.format(
            identity['nonce'], request_nonce))

    return identity