def make_assertion(email, audience, issuer=None, exp=None, assertion_sig=None, certificate_sig=None, new_style=True, email_keypair=None, issuer_keypair=None): """Generate a new dummy assertion for the given email address. This method lets you generate BrowserID assertions using dummy private keys. Called with just an email and audience it will generate an assertion from login.persona.org. By specifying the "exp", "assertion_sig" or "certificate_sig" arguments it is possible generate invalid assertions for testing purposes. """ if issuer is None: issuer = "login.persona.org" if exp is None: exp = int((time.time() + 60) * 1000) # Get private key for the email address itself. if email_keypair is None: email_keypair = get_keypair(email) email_pub, email_priv = email_keypair # Get private key for the hostname so we can sign it. if issuer_keypair is None: issuer_keypair = get_keypair(issuer) iss_pub, iss_priv = issuer_keypair # Generate the assertion, signed with email's public key. assertion = { "exp": exp, "aud": audience, } assertion = jwt.generate(assertion, email_priv) if assertion_sig is not None: assertion = ".".join( assertion.split(".")[:-1] + [encode_bytes(assertion_sig)]) # Generate the certificate signing the email's public key # with the issuer's public key. certificate = { "iss": issuer, "exp": exp, "principal": { "email": email }, "public-key": email_pub, } certificate = jwt.generate(certificate, iss_priv) if certificate_sig is not None: certificate = ".".join( certificate.split(".")[:-1] + [encode_bytes(certificate_sig)]) # Combine them into a BrowserID bundled assertion. return bundle_certs_and_assertion([certificate], assertion, new_style)
def make_assertion(email, audience, issuer=None, exp=None, assertion_sig=None, certificate_sig=None, new_style=True, email_keypair=None, issuer_keypair=None, idp_claims=None, user_claims=None): """Generate a new dummy assertion for the given email address. This method lets you generate BrowserID assertions using dummy private keys. Called with just an email and audience it will generate an assertion from login.persona.org. By specifying the "exp", "assertion_sig" or "certificate_sig" arguments it is possible generate invalid assertions for testing purposes. """ if issuer is None: issuer = "login.persona.org" if exp is None: exp = int((time.time() + 60) * 1000) # Get private key for the email address itself. if email_keypair is None: email_keypair = get_keypair(email) email_pub, email_priv = email_keypair # Get private key for the hostname so we can sign it. if issuer_keypair is None: issuer_keypair = get_keypair(issuer) iss_pub, iss_priv = issuer_keypair # Generate the assertion, signed with email's public key. assertion = { "exp": exp, "aud": audience, } if user_claims: assertion.update(user_claims) assertion = jwt.generate(assertion, email_priv) if assertion_sig is not None: assertion = ".".join(assertion.split(".")[:-1] + [encode_bytes(assertion_sig)]) # Generate the certificate signing the email's public key # with the issuer's public key. certificate = { "iss": issuer, "exp": exp, "principal": {"email": email}, "public-key": email_pub, } if idp_claims: certificate.update(idp_claims) certificate = jwt.generate(certificate, iss_priv) if certificate_sig is not None: certificate = ".".join(certificate.split(".")[:-1] + [encode_bytes(certificate_sig)]) # Combine them into a BrowserID bundled assertion. return bundle_certs_and_assertion([certificate], assertion, new_style)
def test_malformed_assertions(self): errors = (ValueError, TrustError) # This one doesn't actually contain an assertion assertion = encode_json_bytes({}) self.assertRaises(errors, self.verifier.verify, assertion) # This one has no certificates pub, priv = get_keypair("TEST") assertion = bundle_certs_and_assertion([], jwt.generate({"aud": "TEST"}, priv)) self.assertRaises(errors, self.verifier.verify, assertion) # This one has too many certificates in the chain. assertion = bundle_certs_and_assertion( [jwt.generate({}, priv), jwt.generate({}, priv)], jwt.generate({"aud": "TEST"}, priv) ) self.assertRaises(errors, self.verifier.verify, assertion)
def signcert(): email = request.json.get("email") duration = request.json.get("certDuration") public_key = json.loads(request.json.get("publicKey")) if not email or not duration or not public_key or email != current_user.email: return abort(400) now = time.time() expiry_time = int(now + float(duration)) * 1000 with open("key.json") as f: k = json.load(f) key = load_key("RS256", k) data = { "iss": app.config["DOMAIN"], "exp": expiry_time, "iat": int(now) * 1000, "public-key": public_key, "principal": {"email": current_user.email} } signed_data = generate(data, key) return jsonify(certificate=signed_data)
def build_assertion(keypair, certificate, audience, exp=None, new_style=True): """ Generate a new assertion for the given email address. This method lets you generate BrowserID assertions. Called with just an email and audience it will generate an assertion from login.persona.org. """ logging.debug("build_assertion()") #Default expiry to 5 minutes if exp is None: exp = int((time.time() + (5*60)) * 1000) pubkey, privkey = keypair #TODO - verify keypair matches certificate # Generate the assertion, signed with email's public key. assertion = { "exp": exp, "aud": audience, } assertion = jwt.generate(assertion, privkey.get_key()) #print "assertion:\n" + assertion # Combine them into a BrowserID bundled assertion. return bundle_certs_and_assertion([certificate], assertion, new_style)
def test_malformed_assertions(self): errors = (ValueError, TrustError) # This one doesn't actually contain an assertion assertion = encode_json_bytes({}) self.assertRaises(errors, self.verifier.verify, assertion) # This one has no certificates pub, priv = get_keypair("TEST") assertion = bundle_certs_and_assertion( [], jwt.generate({"aud": "TEST"}, priv), ) self.assertRaises(errors, self.verifier.verify, assertion) # This one has too many certificates in the chain. assertion = bundle_certs_and_assertion( [jwt.generate({}, priv), jwt.generate({}, priv)], jwt.generate({"aud": "TEST"}, priv), ) self.assertRaises(errors, self.verifier.verify, assertion)
def test_malformed_assertions(self): errors = (ValueError, TrustError) # This one doesn't actually contain an assertion assertion = encode_json_bytes({}) self.assertRaises(errors, self.verifier.verify, assertion) # This one has no certificates pub, priv = get_keypair("TEST") assertion = encode_json_bytes({ "assertion": jwt.generate({"aud": "TEST"}, priv), "certificates": [] }) self.assertRaises(errors, self.verifier.verify, assertion)
def make_certificate(email, email_public_key, issuer, issuer_private_key, iat=None, exp=None): # Generate the certificate signing the email's public key # with the issuer's private key. if issuer is None: issuer = "mockmyid.com" if iat is None: iat = int(time.time() * 1000) if exp is None: exp = int(iat + 60 * 1000) certificate = { "iss": issuer, "iat": iat, "exp": exp, "principal": {"email": email}, "public-key": email_public_key, } certificate = jwt.generate(certificate, issuer_private_key) return certificate
def test_error_jwt_with_mismatched_algorithm(self): pub, priv = get_keypair("TEST") token = jwt.generate({}, priv) token = jwt.parse(token) pub["algorithm"] = "RS" self.assertFalse(token.check_signature(pub))
def main(): arguments = docopt(HELP) host = arguments["--host"].rstrip('/') headers = {'content-type': 'application/json'} if arguments["--version"]: print("msisdn-cli %s" % __version__) sys.exit(0) verify = True if arguments["--insecure"]: verify = False # 1. Start the discover url = "%s/discover" % host discover_args = {"mcc": arguments["--mcc"], "roaming": False} if arguments["--mnc"] is not None: discover_args["mnc"] = arguments["--mnc"] if arguments["--msisdn"] is not None: discover_args["msisdn"] = arguments["--msisdn"] r = requests.post(url, json.dumps(discover_args), headers=headers, verify=verify) try: r.raise_for_status() except: print(url, r.content) raise discover = r.json() # 1.1 Register url = "%s/register" % host r = requests.post(url, headers=headers, verify=verify) try: r.raise_for_status() except: print(url, r.content) raise register = r.json() hawk_auth = HawkAuth(hawk_session=register["msisdnSessionToken"], server_url=host) hawkId = hawk_auth.credentials["id"] method = discover['verificationMethods'][0] try: mtSender = discover['verificationDetails'][method]["mtSender"] except KeyError: mtSender = '' # 2. If MT Flow if method == "sms/mt": # 2.1 If no MSISDN, ask the MSISDN if arguments["--msisdn"] is None: msisdn = input("Please enter your MSISDN number (ie +123456789): ") else: msisdn = arguments["--msisdn"] # 2.2 Start the registration print("MT Flow for %s" % msisdn) url = "%s/sms/mt/verify" % host verify_args = { "msisdn": msisdn, "mcc": discover_args["mcc"], "shortVerificationCode": True } r = requests.post(url, json.dumps(verify_args), auth=hawk_auth, headers=headers, verify=verify) try: r.raise_for_status() except: print(url, r.content) raise # 3. If MOMT Flow else: print("MOMT Flow") # 3.1 Give the Number and HawkId moVerifier = discover['verificationDetails']["sms/momt"]["moVerifier"] print("Please send the following message to %s:" % moVerifier) print("\n\tSMS %s\n" % hawkId.decode("ascii")) # 4. Ask for the code code = input("Please enter the code that you will get by SMS from %s: " % mtSender) # 5. Verify the code url = "%s/sms/verify_code" % host r = requests.post(url, json.dumps({"code": code.strip()}), auth=hawk_auth, headers=headers, verify=verify) try: r.raise_for_status() except: print(url, r.content) raise # 6. Print out the certificate publicKey, privateKey = get_keypair("msisdn") url = "%s/certificate/sign" % host sign_args = { "publicKey": json.dumps(publicKey), "duration": 86400 # One day } r = requests.post(url, json.dumps(sign_args), auth=hawk_auth, headers=headers, verify=verify) try: r.raise_for_status() except: print(url, r.content) raise sign = r.json() cert = sign["cert"] info = json.loads(decode_bytes(cert.split('.')[1]).decode("utf-8")) info["publicKey"] = "<stripped>" info["public-key"] = "<stripped>" info["pubkey"] = "<stripped>" print("Verified: %s" % json.dumps(info, indent=2, sort_keys=True)) # Build assertion if arguments["--audience"]: audience = arguments["--audience"] assertion = {"exp": int((time.time() + 60) * 1000), "aud": audience} assertion = bundle_certs_and_assertion([cert], jwt.generate( assertion, privateKey)) if arguments["--verbose"]: print("BID Assertion for %s:\n\n%s\n\n" % (audience, assertion)) if arguments["--dry-run"]: curl = """\ncurl -X POST -D - \\ -H 'Authorization: BROWSERID %s' \\""" % assertion if arguments["--data"]: curl += """ -d '%s' \\""" % arguments["--data"] if arguments["--json"]: curl += """ -H 'Content-Type: application/json' -H 'Accept: application/json' \\ -d '%s' \\""" % arguments["--json"] login_endpoint = arguments["--login-endpoint"] or "<login_URL>" curl += "\n %s\n" "" % login_endpoint print("\nTo validate the configuration of the service provider, " "you can run the curl command below.\n\n" "You should get a 200 OK status code with a " "Hawk-Session-Token header:\n\n") print(curl) print(ERROR_EXPLAINATION) else: headers = {"Authorization": "BROWSERID %s" % assertion} data = arguments["--data"] if arguments["--json"]: data = arguments["--json"] headers["Content-Type"] = "application/json" headers["Accept"] = "application/json" r = requests.post(arguments["--login-endpoint"], data=data, headers=headers, verify=verify) # Try to extract an Hawk sessionToken from the response. sessionToken = None if "Access-Control-Expose-Headers" in r.headers: tokenHeader = r.headers["Access-Control-Expose-Headers"] sessionHeaders = tokenHeader.split(",") sessionHeader = None for header in sessionHeaders: if "token" in header.lower(): sessionHeader = header.strip() break if sessionHeader: sessionToken = r.headers[sessionHeader] else: try: json_resp = r.json() for key in json_resp.keys(): if "token" in key.lower(): sessionToken = json_resp["key"] except ValueError: pass print("Status: %s" % r.status_code) if sessionToken: print("Hawk sessionToken: %s" % sessionToken) else: print("Headers: %s" % r.headers) print("Content: %s" % r.content)
def main(): arguments = docopt(HELP) host = arguments["--host"].rstrip('/') headers = {'content-type': 'application/json'} if arguments["--version"]: print("msisdn-cli %s" % __version__) sys.exit(0) verify = True if arguments["--insecure"]: verify = False # 1. Start the discover url = "%s/discover" % host discover_args = {"mcc": arguments["--mcc"], "roaming": False} if arguments["--mnc"] is not None: discover_args["mnc"] = arguments["--mnc"] if arguments["--msisdn"] is not None: discover_args["msisdn"] = arguments["--msisdn"] r = requests.post(url, json.dumps(discover_args), headers=headers, verify=verify) try: r.raise_for_status() except: print(url, r.content) raise discover = r.json() # 1.1 Register url = "%s/register" % host r = requests.post(url, headers=headers, verify=verify) try: r.raise_for_status() except: print(url, r.content) raise register = r.json() hawk_auth = HawkAuth(hawk_session=register["msisdnSessionToken"], server_url=host) hawkId = hawk_auth.credentials["id"] method = discover['verificationMethods'][0] try: mtSender = discover['verificationDetails'][method]["mtSender"] except KeyError: mtSender = '' # 2. If MT Flow if method == "sms/mt": # 2.1 If no MSISDN, ask the MSISDN if arguments["--msisdn"] is None: msisdn = input("Please enter your MSISDN number (ie +123456789): ") else: msisdn = arguments["--msisdn"] # 2.2 Start the registration print("MT Flow for %s" % msisdn) url = "%s/sms/mt/verify" % host verify_args = { "msisdn": msisdn, "mcc": discover_args["mcc"], "shortVerificationCode": True } r = requests.post(url, json.dumps(verify_args), auth=hawk_auth, headers=headers, verify=verify) try: r.raise_for_status() except: print(url, r.content) raise # 3. If MOMT Flow else: print("MOMT Flow") # 3.1 Give the Number and HawkId moVerifier = discover['verificationDetails']["sms/momt"]["moVerifier"] print("Please send the following message to %s:" % moVerifier) print("\n\tSMS %s\n" % hawkId.decode("ascii")) # 4. Ask for the code code = input( "Please enter the code that you will get by SMS from %s: " % mtSender ) # 5. Verify the code url = "%s/sms/verify_code" % host r = requests.post(url, json.dumps({"code": code.strip()}), auth=hawk_auth, headers=headers, verify=verify) try: r.raise_for_status() except: print(url, r.content) raise # 6. Print out the certificate publicKey, privateKey = get_keypair("msisdn") url = "%s/certificate/sign" % host sign_args = { "publicKey": json.dumps(publicKey), "duration": 86400 # One day } r = requests.post(url, json.dumps(sign_args), auth=hawk_auth, headers=headers, verify=verify) try: r.raise_for_status() except: print(url, r.content) raise sign = r.json() cert = sign["cert"] info = json.loads(decode_bytes(cert.split('.')[1]).decode("utf-8")) info["publicKey"] = "<stripped>" info["public-key"] = "<stripped>" info["pubkey"] = "<stripped>" print("Verified: %s" % json.dumps(info, indent=2, sort_keys=True)) # Build assertion if arguments["--audience"]: audience = arguments["--audience"] assertion = { "exp": int((time.time() + 60) * 1000), "aud": audience } assertion = bundle_certs_and_assertion( [cert], jwt.generate(assertion, privateKey) ) if arguments["--verbose"]: print("BID Assertion for %s:\n\n%s\n\n" % (audience, assertion)) if arguments["--dry-run"]: curl = """\ncurl -X POST -D - \\ -H 'Authorization: BROWSERID %s' \\""" % assertion if arguments["--data"]: curl += """ -d '%s' \\""" % arguments["--data"] if arguments["--json"]: curl += """ -H 'Content-Type: application/json' -H 'Accept: application/json' \\ -d '%s' \\""" % arguments["--json"] login_endpoint = arguments["--login-endpoint"] or "<login_URL>" curl += "\n %s\n""" % login_endpoint print("\nTo validate the configuration of the service provider, " "you can run the curl command below.\n\n" "You should get a 200 OK status code with a " "Hawk-Session-Token header:\n\n") print(curl) print(ERROR_EXPLAINATION) else: headers = {"Authorization": "BROWSERID %s" % assertion} data = arguments["--data"] if arguments["--json"]: data = arguments["--json"] headers["Content-Type"] = "application/json" headers["Accept"] = "application/json" r = requests.post(arguments["--login-endpoint"], data=data, headers=headers, verify=verify) # Try to extract an Hawk sessionToken from the response. sessionToken = None if "Access-Control-Expose-Headers" in r.headers: tokenHeader = r.headers["Access-Control-Expose-Headers"] sessionHeaders = tokenHeader.split(",") sessionHeader = None for header in sessionHeaders: if "token" in header.lower(): sessionHeader = header.strip() break if sessionHeader: sessionToken = r.headers[sessionHeader] else: try: json_resp = r.json() for key in json_resp.keys(): if "token" in key.lower(): sessionToken = json_resp["key"] except ValueError: pass print("Status: %s" % r.status_code) if sessionToken: print("Hawk sessionToken: %s" % sessionToken) else: print("Headers: %s" % r.headers) print("Content: %s" % r.content)