def verify(self, assertion, audience=None, now=None):
        """Verify the certificate chain for the receipt
        """
        if now is None:
            now = int(time.time())

        # This catches KeyError and turns it into ValueError.
        # It saves having to test for the existence of individual
        # items in the various payloads.
        try:
            # Grab the assertion, check that it has not expired.
            # No point doing all that crypto if we're going to fail out anyway.
            certificates, assertion = unbundle_certs_and_assertion(assertion)
            assertion = self.parse_jwt(assertion)
            if assertion.payload["exp"] < now:
                raise ExpiredSignatureError(assertion.payload["exp"])

            # Parse out the list of certificates.
            certificates = [self.parse_jwt(c) for c in certificates]

            # Verify the entire chain of certificates.
            cert = self.verify_certificate_chain(certificates, now=now)

            # Check the signature on the assertion.
            if not self.check_token_signature(assertion, cert):
                raise InvalidSignatureError("invalid signature on assertion")
        except KeyError:
            raise ValueError("Malformed JWT")
        # Looks good!
        return True
Beispiel #2
0
    def verify(self, assertion, audience=None, now=None):
        """Verify the certificate chain for the receipt
        """
        if now is None:
            now = int(time.time())

        # This catches KeyError and turns it into ValueError.
        # It saves having to test for the existence of individual
        # items in the various payloads.
        try:
            # Grab the assertion, check that it has not expired.
            # No point doing all that crypto if we're going to fail out anyway.
            certificates, assertion = unbundle_certs_and_assertion(assertion)
            assertion = self.parse_jwt(assertion)
            if assertion.payload["exp"] < now:
                raise ExpiredSignatureError(assertion.payload["exp"])

            # Parse out the list of certificates.
            certificates = [self.parse_jwt(c) for c in certificates]

            # Verify the entire chain of certificates.
            cert = self.verify_certificate_chain(certificates, now=now)

            # Check the signature on the assertion.
            if not self.check_token_signature(assertion, cert):
                raise InvalidSignatureError("invalid signature on assertion")
        except KeyError:
            raise ValueError("Malformed JWT")
        # Looks good!
        return True
Beispiel #3
0
def get_email_and_assertion(audience):
    global _email_assertion
    if not _email_assertion:
        import pickle
        try:
            import datetime
            from browserid.utils import unbundle_certs_and_assertion, decode_json_bytes
            # Get one from the cache, and check it's expiration
            email_assertion = pickle.load(open(ASSERTION_CACHE_FILE, 'rb'))
            _, assertion = unbundle_certs_and_assertion(email_assertion[1])
            exp = decode_json_bytes(assertion.split('.')[1])['exp']
            expiration_date = datetime.datetime.utcfromtimestamp(exp/1000)
            if expiration_date > datetime.datetime.utcnow():
                # If it hasn't expired, return
                _email_assertion = email_assertion
                return email_assertion
        except FileNotFoundError:
            pass
            # If no good one is available, get a new one
        import requests
        from urllib.parse import quote_plus
        r = requests.get('http://personatestuser.org/email_with_assertion/%s'%quote_plus(audience))
        _email_assertion = r.json()['email'], r.json()['assertion']
        pickle.dump(_email_assertion, open(ASSERTION_CACHE_FILE, 'wb'))
    return _email_assertion



    def _login(self):
        res = self.testapp.get('/', status=403)
    def check_audience(self, assertion, expected_audience=None):
        """Check that the assertion matches the expected audience.

        This method verifies that the audience for the given assertion is
        as expected - either matching the audience parameter if given, or
        or matching one of the audience patterns from the constructor if not.

        If the audience matches then it is returned as a string; if not then
        an AudienceMismatchError is raised.
        """
        try:
            _, token = unbundle_certs_and_assertion(assertion)
            audience = decode_json_bytes(token.split(".")[1])["aud"]
        except (KeyError, IndexError):
            raise ValueError("Malformed JWT")
        if expected_audience is None:
            audience_re = self._audience_re
        else:
            audience_re = self._compile_audience_patterns(expected_audience)
        if audience_re is None:
            raise AudienceMismatchError
        if not audience_re.match(audience):
            raise AudienceMismatchError
        return audience
Beispiel #5
0
    def verify(self, assertion, audience=None, now=None):
        """Verify the given BrowserID assertion.

        This method parses a BrowserID identity assertion, verifies the
        bundled chain of certificates and signatures, and returns the
        extracted email address and audience.

        If the 'audience' argument is given, it first verifies that the
        audience of the assertion matches the one given.  This can help
        avoid doing lots of crypto for assertions that can't be valid.
        If you don't specify an audience, you *MUST* validate the audience
        value returned by this method.

        If the 'now' argument is given, it is used as the current time in
        milliseconds.  This lets you verify expired assertions, e.g. for
        testing purposes.
        """
        if now is None:
            now = int(time.time() * 1000)

        # This catches KeyError and turns it into ValueError.
        # It saves having to test for the existence of individual
        # items in the various assertion payloads.
        try:
            # Check the audience against the given value, or the wildcards.
            self.check_audience(assertion, audience)

            # Grab the assertion, check that it has not expired.
            # No point doing all that crypto if we're going to fail out anyway.
            certificates, assertion = unbundle_certs_and_assertion(assertion)
            if len(certificates) > 1:
                raise UnsupportedCertChainError("too many certs")
            assertion = self.parse_jwt(assertion)
            if assertion.payload["exp"] < now:
                raise ExpiredSignatureError(assertion.payload["exp"])

            # Parse out the list of certificates.
            certificates = [self.parse_jwt(c) for c in certificates]

            # Extract the email, and the hostname of its provider.
            email = certificates[-1].payload["principal"]["email"]
            match = VALID_EMAIL.match(email)
            if match is None:
                raise ValueError("invalid email in assertion")
            provider = match.group(2)

            # Check that the root issuer is trusted.
            # No point doing all that crypto if we're going to fail out anyway.
            root_issuer = certificates[0].payload["iss"]
            if not self.is_trusted_issuer(provider, root_issuer):
                msg = "untrusted root issuer: %s" % (root_issuer,)
                raise InvalidSignatureError(msg)

            # Verify the entire chain of certificates.
            cert = self.verify_certificate_chain(certificates, now=now)

            # Check the signature on the assertion.
            if not self.check_token_signature(assertion, cert):
                raise InvalidSignatureError("invalid signature on assertion")
        except KeyError:
            raise ValueError("Malformed JWT")
        # Looks good!
        res = {
          "status": "okay",
          "audience": assertion.payload["aud"],
          "email": email,
          "issuer": root_issuer,
        }
        idpClaims = extract_extra_claims(certificates[-1])
        if idpClaims:
            res["idpClaims"] = idpClaims
        userClaims = extract_extra_claims(assertion)
        if userClaims:
            res["userClaims"] = userClaims
        return res
Beispiel #6
0
    def verify(self, assertion, audience=None, now=None):
        """Verify the given BrowserID assertion.

        This method parses a BrowserID identity assertion, verifies the
        bundled chain of certificates and signatures, and returns the
        extracted email address and audience.

        If the 'audience' argument is given, it first verifies that the
        audience of the assertion matches the one given.  This can help
        avoid doing lots of crypto for assertions that can't be valid.
        If you don't specify an audience, you *MUST* validate the audience
        value returned by this method.

        If the 'now' argument is given, it is used as the current time in
        milliseconds.  This lets you verify expired assertions, e.g. for
        testing purposes.
        """
        if now is None:
            now = int(time.time() * 1000)

        # This catches KeyError and turns it into ValueError.
        # It saves having to test for the existence of individual
        # items in the various assertion payloads.
        try:
            # Check the audience against the given value, or the wildcards.
            self.check_audience(assertion, audience)

            # Grab the assertion, check that it has not expired.
            # No point doing all that crypto if we're going to fail out anyway.
            certificates, assertion = unbundle_certs_and_assertion(assertion)
            if len(certificates) > 1:
                raise UnsupportedCertChainError("too many certs")
            assertion = self.parse_jwt(assertion)
            if assertion.payload["exp"] < now:
                raise ExpiredSignatureError(assertion.payload["exp"])

            # Parse out the list of certificates.
            certificates = [self.parse_jwt(c) for c in certificates]

            # Extract the email, and the hostname of its provider.
            email = certificates[-1].payload["principal"]["email"]
            match = VALID_EMAIL.match(email)
            if match is None:
                raise ValueError("invalid email in assertion")
            provider = match.group(2)

            # Check that the root issuer is trusted.
            # No point doing all that crypto if we're going to fail out anyway.
            root_issuer = certificates[0].payload["iss"]
            if not self.is_trusted_issuer(provider, root_issuer):
                msg = "untrusted root issuer: %s" % (root_issuer, )
                raise InvalidSignatureError(msg)

            # Verify the entire chain of certificates.
            cert = self.verify_certificate_chain(certificates, now=now)

            # Check the signature on the assertion.
            if not self.check_token_signature(assertion, cert):
                raise InvalidSignatureError("invalid signature on assertion")
        except KeyError:
            raise ValueError("Malformed JWT")
        # Looks good!
        res = {
            "status": "okay",
            "audience": assertion.payload["aud"],
            "email": email,
            "issuer": root_issuer,
        }
        idpClaims = extract_extra_claims(certificates[-1])
        if idpClaims:
            res["idpClaims"] = idpClaims
        userClaims = extract_extra_claims(assertion)
        if userClaims:
            res["userClaims"] = userClaims
        return res