def do_init(email, password_b64, db_server, do_network, scrypt_server): UK_b64 = b64encode(os.urandom(2*KEYLEN)) print "UK created:", UK_b64 keys = build_PWK(email, password_b64, scrypt_server, do_network) PWK_b64, MAC_b64, SRPpw_b64 = keys SRPv_b64 = do_SRP_setup(SRPpw_b64, email) MAGIC_SEND_SAFELY(db_server, [email, SRPv_b64], do_network) WUK_b64 = encrypt_and_mac(PWK_b64, MAC_b64, UK_b64) SRPsession = do_SRP(db_server, email, SRPpw_b64, do_network) resp = do_request(SRPsession, ["set", WUK_b64], do_network, db_server) if resp[0] != "ok": raise Oops("server reject") return UK_b64
def do_change(email, old_password_b64, new_password_b64, db_server, do_network, scrypt_server): # read the old password, compute the new secrets, send a change request UK_b64, old_SRPpw_b64 = read(email, old_password_b64, db_server, do_network, scrypt_server) keys = build_PWK(email, new_password_b64, scrypt_server, do_network) (new_PWK_b64, new_MAC_b64, new_SRPpw_b64) = keys new_SRPv_b64 = do_SRP_setup(new_SRPpw_b64, email) new_WUK_b64 = encrypt_and_mac(new_PWK_b64, new_MAC_b64, UK_b64) SRPsession = do_SRP(db_server, email, old_SRPpw_b64, do_network) resp = do_request(SRPsession, ["change", new_SRPv_b64, new_WUK_b64], do_network, db_server) if resp[0] != "ok": raise Oops("server reject")
def receive_request(self, tx): # HTTP body is utf8(json(PIECES)) pieces = json.loads(tx.decode("utf-8")) if pieces[0] == "magic-init": # PIECES = ["magic-init", email, b64(SRPv)] # reponse: "ok" print "MAGIC" email, SRPverifier_b64 = pieces[1:3] self.SRPverifier_b64[email] = SRPverifier_b64 return "ok" if pieces[0] == "srp-1": # PIECES = ["srp-1", b64(sessionid), email, b64(A)] # response: utf8(json(["ok", b64(s), b64(B)])) sid_b64, email, A_b64 = pieces[1:4] if sid_b64 in self.verifiers or sid_b64 in self.sessions: raise Oops("sessionid already claimed") salt = "" vkey = b64decode(self.SRPverifier_b64[email]) v = srp.Verifier(email.encode("utf-8"), salt, vkey, b64decode(A_b64), hash_alg=srp.SHA256) self.verifiers[sid_b64] = (v, email) s,B = v.get_challenge() if s is None or B is None: raise Oops("SRP rejected (A)") resp = ["ok", b64encode(s), b64encode(B)] return json.dumps(resp).encode("utf-8") if pieces[0] == "srp-2": # PIECES = ["srp-2", b64(sessionid), b64(M)] # response: utf8(json(["ok", b64(HAMK)])) sid_b64, M_b64 = pieces[1:3] if sid_b64 not in self.verifiers: raise Oops("no such session") if sid_b64 in self.sessions: raise Oops("sessionid already claimed") (v,email) = self.verifiers.pop(sid_b64) HAMK = v.verify_session(b64decode(M_b64)) if HAMK is None: raise Oops("SRP rejected (M)") if not v.authenticated(): raise Oops("SRP rejected") k_b64 = b64encode(v.get_session_key()) self.sessions[sid_b64] = (k_b64, email) resp = ["ok", b64encode(HAMK)] return json.dumps(resp).encode("utf-8") if pieces[0] == "encrypted-request": # PIECES = ["encrypted-request", b64(sessionid), b64(encreqdata)] # reqdata = utf8(json([REQ])) # response = b64(enc(utf8(json(RESP))) sid_b64, enc_req_b64 = (pieces[1], pieces[2]) if sid_b64 not in self.sessions: raise Oops("no such session") # We use very short-lived sessions for now: just one request. # TODO: need to time out old sessions, say after 5 minutes. k_b64,email = self.sessions.pop(sid_b64) (enc1_b64,mac1_b64,enc2_b64,mac2_b64) = make_session_keys(k_b64) rd_b64 = decrypt(enc1_b64, mac1_b64, enc_req_b64) req = json.loads(b64decode(rd_b64).decode("utf-8")) response = self.process_request(email, req) response_data_b64 = b64encode(json.dumps(response).encode("utf-8")) rx_b64 = encrypt_and_mac(enc2_b64, mac2_b64, response_data_b64) return rx_b64 print "bad request", pieces raise Oops("bad request")