Ejemplo n.º 1
0
    def parse_endpoint(self,
                       metrics,
                       token,
                       version="v1",
                       ckey_header=None,
                       auth_header=None):
        """Parse an endpoint into component elements of UAID, CHID and optional
        key hash if v2

        :param token: The obscured subscription data.
        :param version: This is the API version of the token.
        :param ckey_header: the Crypto-Key header bearing the public key
            (from Crypto-Key: p256ecdsa=)
        :param auth_header: The Authorization header bearing the VAPID info

        :raises ValueError: In the case of a malformed endpoint.

        :returns: a dict containing (uaid=UAID, chid=CHID, public_key=KEY)

        """
        token = self.fernet.decrypt(repad(token).encode('utf8'))
        public_key = None
        if ckey_header:
            try:
                crypto_key = CryptoKey(ckey_header)
            except CryptoKeyException:
                raise InvalidTokenException("Invalid key data")
            public_key = crypto_key.get_label('p256ecdsa')
        if auth_header:
            vapid_auth = parse_auth_header(auth_header)
            if not vapid_auth:
                raise VapidAuthException("Invalid Auth token")
            metrics.increment("updates.notification.auth.{}".format(
                vapid_auth['scheme']))
            # pull the public key from the VAPID auth header if needed
            try:
                if vapid_auth['version'] != 1:
                    public_key = vapid_auth['k']
            except KeyError:
                raise VapidAuthException("Missing Public Key")
        if version == 'v1' and len(token) != 32:
            raise InvalidTokenException("Corrupted push token")
        if version == 'v2':
            if not auth_header:
                raise VapidAuthException("Missing Authorization Header")
            if len(token) != 64:
                raise InvalidTokenException("Corrupted push token")
            if not public_key:
                raise VapidAuthException("Invalid key data")
            try:
                decoded_key = base64url_decode(public_key)
            except TypeError:
                raise VapidAuthException("Invalid key data")
            if not constant_time.bytes_eq(
                    sha256(decoded_key).digest(), token[32:]):
                raise VapidAuthException("Key mismatch")
        return dict(uaid=token[:16].encode('hex'),
                    chid=token[16:32].encode('hex'),
                    version=version,
                    public_key=public_key)
Ejemplo n.º 2
0
    def validate_auth(self, d):
        auth = d["headers"].get("authorization")
        needs_auth = d["token_info"]["api_ver"] == "v2"
        if not needs_auth and not auth:
            return
        try:
            vapid_auth = parse_auth_header(auth)
            token = vapid_auth['t']
            d["vapid_version"] = "draft{:0>2}".format(vapid_auth['version'])
            if vapid_auth['version'] == 2:
                public_key = vapid_auth['k']
            else:
                public_key = d["subscription"].get("public_key")
            jwt = extract_jwt(
                token,
                public_key,
                is_trusted=self.context['settings'].enable_tls_auth)
        except (KeyError, ValueError, InvalidSignature, TypeError,
                VapidAuthException):
            raise InvalidRequest("Invalid Authorization Header",
                                 status_code=401,
                                 errno=109,
                                 headers={"www-authenticate": PREF_SCHEME})
        if "exp" not in jwt:
            raise InvalidRequest("Invalid bearer token: No expiration",
                                 status_code=401,
                                 errno=109,
                                 headers={"www-authenticate": PREF_SCHEME})

        try:
            jwt_expires = int(jwt['exp'])
        except ValueError:
            raise InvalidRequest("Invalid bearer token: Invalid expiration",
                                 status_code=401,
                                 errno=109,
                                 headers={"www-authenticate": PREF_SCHEME})

        now = time.time()
        jwt_has_expired = now > jwt_expires
        if jwt_has_expired:
            raise InvalidRequest("Invalid bearer token: Auth expired",
                                 status_code=401,
                                 errno=109,
                                 headers={"www-authenticate": PREF_SCHEME})
        jwt_too_far_in_future = (jwt_expires - now) > (60 * 60 * 24)
        if jwt_too_far_in_future:
            raise InvalidRequest(
                "Invalid bearer token: Auth > 24 hours in "
                "the future",
                status_code=401,
                errno=109,
                headers={"www-authenticate": PREF_SCHEME})
        jwt_crypto_key = base64url_encode(public_key)
        d["jwt"] = dict(jwt_crypto_key=jwt_crypto_key, jwt_data=jwt)
Ejemplo n.º 3
0
    def validate_auth(self, d):
        crypto_exceptions = [
            KeyError, ValueError, TypeError, VapidAuthException
        ]

        if self.context['conf'].use_cryptography:
            crypto_exceptions.append(InvalidSignature)
        else:
            crypto_exceptions.extend([JOSEError, JWTError, AssertionError])

        auth = d["headers"].get("authorization")
        needs_auth = d["token_info"]["api_ver"] == "v2"
        if not needs_auth and not auth:
            return
        try:
            vapid_auth = parse_auth_header(auth)
            token = vapid_auth['t']
            d["vapid_version"] = "draft{:0>2}".format(vapid_auth['version'])
            if vapid_auth['version'] == 2:
                public_key = vapid_auth['k']
            else:
                public_key = d["subscription"].get("public_key")
            jwt = extract_jwt(token,
                              public_key,
                              is_trusted=self.context['conf'].enable_tls_auth,
                              use_crypto=self.context['conf'].use_cryptography)
            if not isinstance(jwt, Dict):
                raise InvalidRequest("Invalid Authorization Header",
                                     status_code=401,
                                     errno=109,
                                     headers={"www-authenticate": PREF_SCHEME})
        except tuple(crypto_exceptions):
            raise InvalidRequest("Invalid Authorization Header",
                                 status_code=401,
                                 errno=109,
                                 headers={"www-authenticate": PREF_SCHEME})
        if "aud" not in jwt:
            raise InvalidRequest("Invalid bearer token: No Audience specified",
                                 status_code=401,
                                 errno=109,
                                 headers={"www-authenticate": PREF_SCHEME})
        if jwt['aud'] != self.context["conf"].endpoint_url:
            raise InvalidRequest(
                "Invalid bearer token: Invalid Audience Specified",
                status_code=401,
                errno=109,
                headers={"www-authenticate": PREF_SCHEME})
        if "exp" not in jwt:
            raise InvalidRequest("Invalid bearer token: No expiration",
                                 status_code=401,
                                 errno=109,
                                 headers={"www-authenticate": PREF_SCHEME})

        try:
            jwt_expires = int(jwt['exp'])
        except (TypeError, ValueError):
            raise InvalidRequest("Invalid bearer token: Invalid expiration",
                                 status_code=401,
                                 errno=109,
                                 headers={"www-authenticate": PREF_SCHEME})

        now = time.time()
        jwt_has_expired = now > jwt_expires
        if jwt_has_expired:
            raise InvalidRequest("Invalid bearer token: Auth expired",
                                 status_code=401,
                                 errno=109,
                                 headers={"www-authenticate": PREF_SCHEME})
        jwt_too_far_in_future = (jwt_expires - now) > (60 * 60 * 24)
        if jwt_too_far_in_future:
            raise InvalidRequest(
                "Invalid bearer token: Auth > 24 hours in "
                "the future",
                status_code=401,
                errno=109,
                headers={"www-authenticate": PREF_SCHEME})
        jwt_crypto_key = base64url_encode(public_key)
        d["jwt"] = dict(jwt_crypto_key=jwt_crypto_key, jwt_data=jwt)