def verify(self, assertion, audience=None): """Verify the given VEP assertion. This method posts the given VEP assertion to the remove verifier service. If it is successfully verified then a dict giving the email and audience is returned. If it is not valid then an error is raised. 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. """ # Read audience from assertion if not specified. if audience is None: try: token = decode_json_bytes(assertion)["assertion"] audience = decode_json_bytes(token.split(".")[1])["aud"] except (KeyError, IndexError): raise ValueError("Malformed JWT") # Encode the data into x-www-form-urlencoded. post_data = {"assertion": assertion, "audience": audience} post_data = "&".join("%s=%s" % item for item in post_data.items()) # Post it to the verifier. try: resp = self.urlopen(self.verifier_url, post_data) except ConnectionError, e: # BrowserID server sends "500 server error" for broken assertions. # For now, just translate that directly. Should check by hand. if "500" in str(e): raise ValueError("Malformed assertion") raise
def parse(cls, jwt): """Parse a JWT from a string.""" algorithm, payload, signature = jwt.split(".") signed_data = algorithm + "." + payload try: algorithm = decode_json_bytes(algorithm)["alg"] except KeyError: raise ValueError("badly formed JWT") payload = decode_json_bytes(payload) signature = decode_bytes(signature) return cls(algorithm, payload, signature, signed_data)
def test_error_handling_in_verify_certificate_chain(self): self.assertRaises(ValueError, self.verifier.verify_certificate_chain, []) certs = decode_json_bytes(EXPIRED_ASSERTION)["certificates"] certs = [jwt.parse(cert) for cert in certs] self.assertRaises(ExpiredSignatureError, self.verifier.verify_certificate_chain, certs)
def verify(self, assertion, audience=None): """Verify the given VEP assertion. This method posts the given VEP assertion to the remove verifier service. If it is successfully verified then a dict giving the email and audience is returned. If it is not valid then an error is raised. 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. """ # Read audience from assertion if not specified. if audience is None: try: _, token = unbundle_certs_and_assertion(assertion) audience = decode_json_bytes(token.split(".")[1])["aud"] except (KeyError, IndexError): raise ValueError("Malformed JWT") # Encode the data into x-www-form-urlencoded. post_data = {"assertion": assertion, "audience": audience} post_data = "&".join("%s=%s" % item for item in post_data.items()) # Post it to the verifier. try: resp = self.urlopen(self.verifier_url, post_data) except ConnectionError, e: # BrowserID server sends "500 server error" for broken assertions. # For now, just translate that directly. Should check by hand. if "500" in str(e): raise ValueError("Malformed assertion") raise
def test_encode_decode_json_bytes(self): obj = {"hello": "world"} self.assertEquals(obj, decode_json_bytes(encode_json_bytes(obj))) self.assertRaises(ValueError, decode_json_bytes, encode_bytes("NOJSON4U")) self.assertRaises(ValueError, decode_json_bytes, encode_bytes("42")) self.assertRaises(ValueError, decode_json_bytes, encode_bytes("[1, 2, 3]")) self.assertRaises(ValueError, encode_json_bytes, 42) self.assertRaises(ValueError, encode_json_bytes, [1, 3, 3])
def verify(self, assertion, audience=None, now=None): """Verify the given VEP assertion. This method parses a VEP 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) data = decode_json_bytes(assertion) # This catches KeyError and turns it into ValueError. try: # Check that the assertion is usable and valid. # No point doing all that crypto if we're going to fail out anyway. assertion = JWT.parse(data["assertion"]) if audience is not None: if assertion.payload["aud"] != audience: raise AudienceMismatchError(assertion.payload["aud"]) if assertion.payload["exp"] < now: raise ExpiredSignatureError(assertion.payload["exp"]) # Parse out the list of certificates. certificates = data["certificates"] certificates = [JWT.parse(c) for c in certificates] # Check that the root issuer is trusted. # No point doing all that crypto if we're going to fail out anyway. email = certificates[-1].payload["principal"]["email"] root_issuer = certificates[0].payload["iss"] if root_issuer not in self.trusted_secondaries: if not email.endswith("@" + 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 assertion.check_signature(cert.payload["public-key"]): raise InvalidSignatureError("invalid signature on assertion") except KeyError: raise ValueError("Malformed JWT") # Looks good! return { "status": "okay", "audience": assertion.payload["aud"], "email": email, "issuer": root_issuer, }
def verify(self, assertion, audience=None, now=None): """Verify the given VEP assertion. This method parses a VEP 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) data = decode_json_bytes(assertion) # This catches KeyError and turns it into ValueError. try: # Check that the assertion is usable and valid. # No point doing all that crypto if we're going to fail out anyway. assertion = JWT.parse(data["assertion"]) if audience is not None: if assertion.payload["aud"] != audience: raise AudienceMismatchError(assertion.payload["aud"]) if assertion.payload["exp"] < now: raise ExpiredSignatureError(assertion.payload["exp"]) # Parse out the list of certificates. certificates = data["certificates"] certificates = [JWT.parse(c) for c in certificates] # Check that the root issuer is trusted. # No point doing all that crypto if we're going to fail out anyway. email = certificates[-1].payload["principal"]["email"] root_issuer = certificates[0].payload["iss"] if root_issuer not in self.trusted_secondaries: if not email.endswith("@" + 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 assertion.check_signature(cert.payload["public-key"]): raise InvalidSignatureError("invalid signature on assertion") except KeyError: raise ValueError("Malformed JWT") # Looks good! return { "status": "okay", "audience": assertion.payload["aud"], "email": email, "issuer": root_issuer, }