def main(): emailUTF8, passwordUTF8, command = sys.argv[1:4] assert isinstance(emailUTF8, binary_type) printhex("email", emailUTF8) printhex("password", passwordUTF8) k1 = pbkdf2_bin(passwordUTF8, KWE("first-PBKDF", emailUTF8), 20*1000, keylen=1*32, hashfunc=sha256) time_k1 = time.time() printhex("K1", k1) k2 = scrypt.hash(k1, KW("scrypt"), N=64*1024, r=8, p=1, buflen=1*32) time_k2 = time.time() printhex("K2", k2) stretchedPW = pbkdf2_bin(k2+passwordUTF8, KWE("second-PBKDF", emailUTF8), 20*1000, keylen=1*32, hashfunc=sha256) printhex("stretchedPW", stretchedPW) GET("__heartbeat__") if command == "create": mainKDFSalt = makeRandom() srpSalt = makeRandom() else: r = POST("session/auth/start", {"email": #emailUTF8.encode("hex") emailUTF8.encode("utf-8") }) print "auth/start", r srpToken = r["srpToken"] B = r["srp"]["B"].decode("hex") srpSalt = r["srp"]["s"].decode("hex") mainKDFSalt = r["stretch"]["salt"].decode("hex") # ignore stretch.rounds, srp.N_bits, srp.alg printhex("mainKDFSalt", mainKDFSalt) printhex("srpSalt", srpSalt) (srpPW, unwrapBKey) = split(HKDF(SKM=stretchedPW, XTS=mainKDFSalt, CTXinfo=KW("mainKDF"), dkLen=2*32)) if command == "create": (srpVerifier, _, _, _, _) = mysrp.create_verifier(emailUTF8, srpPW, srpSalt) r = POST("account/create", {#"email": emailUTF8.encode("hex"), # TODO prefer hex "email": emailUTF8.encode("utf-8"), "verifier": srpVerifier.encode("hex"), "salt": srpSalt.encode("hex"), "params": {"srp": {"alg": "sha256", "N_bits": 2048}, "stretch": {"salt": mainKDFSalt.encode("hex"), "rounds": 20000} }, }) print r else: srpClient = mysrp.Client() A = srpClient.one() M1 = srpClient.two(B, srpSalt, emailUTF8, srpPW) r = POST("session/auth/finish", {"srpToken": srpToken, "A": A.encode("hex"), "M": M1.encode("hex")}) print "auth/finish:", r bundle = r["bundle"].decode("hex") print "bundlelen", len(bundle) # note: the server is not yet using the new protocol. The old one # returns keyFetchToken+sessionToken if 1: # old protocol x = HKDF(SKM=srpClient.get_key(), dkLen=3*32, XTS=None, CTXinfo=KW("session/auth")) respHMACkey = x[0:32] respXORkey = x[32:] ct,respMAC = bundle[:-32], bundle[-32:] respMAC2 = HMAC(respHMACkey, ct) printhex("respHMACkey", respHMACkey) printhex("respXORkey", respXORkey) printhex("ct", ct) assert respMAC2 == respMAC, (respMAC2.encode("hex"), respMAC.encode("hex")) keyFetchToken, sessionToken = split(xor(respXORkey,ct)) if 0: # new protocol authToken = getAuthToken(srpClient.get_key()) x = HKDF(SKM=srpClient.get_key(), dkLen=2*32, XTS=None, CTXinfo=KW("auth/finish")) respHMACkey = x[0:32] respXORkey = x[32:] ct,respMAC = bundle[:-32], bundle[-32:] respMAC2 = HMAC(respHMACkey, ct) assert respMAC2 == respMAC, (respMAC2.encode("hex"), respMAC.encode("hex")) authToken = xor(ct, respXORkey) printhex("authToken", authToken) keyFetchToken, sessionToken = createSession(authToken) printhex("keyFetchToken", keyFetchToken) printhex("sessionToken", sessionToken) kA,kB = getKeys(keyFetchToken, unwrapBKey) printhex("kA", kA) printhex("kB", kB)
def main(): emailUTF8, passwordUTF8, command = sys.argv[1:4] assert command in ("create", "login", "changepw", "destroy", "forgotpw1", "forgotpw2") assert isinstance(emailUTF8, binary_type) printhex("email", emailUTF8) printhex("password", passwordUTF8) GET("__heartbeat__", versioned="") if command == "forgotpw1": r = POST("password/forgot/send_code", {"email": emailUTF8.encode("hex")}) print r return if command == "create": mainKDFSalt = makeRandom() srpSalt = makeRandom() PBKDF2_rounds_1 = PBKDF2_rounds_2 = 20*1000 scrypt_N = 64*1024 scrypt_r = 8 scrypt_p = 1 elif command in ("login", "changepw", "destroy"): r = POST("auth/start", {"email": emailUTF8.encode("hex") }) print "auth/start", r st = r["passwordStretching"] assert st["type"] == "PBKDF2/scrypt/PBKDF2/v1" mainKDFSalt = st["salt"].decode("hex") PBKDF2_rounds_1 = st["PBKDF2_rounds_1"] PBKDF2_rounds_2 = st["PBKDF2_rounds_2"] scrypt_N = st["scrypt_N"] scrypt_r = st["scrypt_r"] scrypt_p = st["scrypt_p"] srpToken = r["srpToken"] srpSalt = r["srp"]["salt"].decode("hex") B = r["srp"]["B"].decode("hex") else: assert False printhex("mainKDFSalt", mainKDFSalt) printhex("srpSalt", srpSalt) # MANGLE mangled_email = emailUTF8.encode("hex") if MANGLE else emailUTF8 stretchedPW = stretch(mangled_email, passwordUTF8, PBKDF2_rounds_1, scrypt_N, scrypt_r, scrypt_p, PBKDF2_rounds_2) (srpPW, unwrapBKey) = mainKDF(stretchedPW, mainKDFSalt) mangled_srpPW = srpPW.encode("hex") if MANGLE else srpPW if command == "create": (srpVerifier, _, _, _, _) = mysrp.create_verifier(mangled_email, mangled_srpPW, srpSalt) r = POST("account/create", {"email": emailUTF8.encode("hex"), "srp": { "type": "SRP-6a/SHA256/2048/v1", "verifier": srpVerifier.encode("hex"), "salt": srpSalt.encode("hex"), }, "passwordStretching": { "type": "PBKDF2/scrypt/PBKDF2/v1", "PBKDF2_rounds_1": PBKDF2_rounds_1, "scrypt_N": scrypt_N, "scrypt_r": scrypt_r, "scrypt_p": scrypt_p, "PBKDF2_rounds_2": PBKDF2_rounds_2, "salt": mainKDFSalt.encode("hex"), }, }) print r elif command in ("login", "changepw", "destroy"): srpClient = mysrp.Client() A = srpClient.one() M1 = srpClient.two(B, srpSalt, mangled_email, mangled_srpPW) r = POST("auth/finish", {"srpToken": srpToken, "A": A.encode("hex"), "M": M1.encode("hex")}) print "auth/finish:", r bundle = r["bundle"].decode("hex") print "bundlelen", len(bundle) x = HKDF(SKM=srpClient.get_key(), dkLen=2*32, XTS=None, CTXinfo=KW("auth/finish")) respHMACkey = x[0:32] respXORkey = x[32:] ct,respMAC = bundle[:-32], bundle[-32:] respMAC2 = HMAC(respHMACkey, ct) assert respMAC2 == respMAC, (respMAC2.encode("hex"), respMAC.encode("hex")) authToken = xor(ct, respXORkey) printhex("authToken", authToken) else: assert False if command == "login": keyFetchToken, sessionToken = createSession(authToken) printhex("keyFetchToken", keyFetchToken) printhex("sessionToken", sessionToken) email_status = getEmailStatus(sessionToken) print "email status:", email_status kA,kB = getKeys(keyFetchToken, unwrapBKey) printhex("kA", kA) printhex("kB", kB) # TODO: exercise /certificate/sign if command == "changepw": keyFetchToken, accountResetToken = changePassword(authToken) printhex("keyFetchToken", keyFetchToken) printhex("accountResetToken", accountResetToken) kA,kB = getKeys(keyFetchToken, unwrapBKey) printhex("kA", kA) printhex("kB", kB) # stretch new password new_passwordUTF8 = sys.argv[4] new_stretchedPW = stretch(emailUTF8, new_passwordUTF8, PBKDF2_rounds_1, scrypt_N, scrypt_r, scrypt_p, PBKDF2_rounds_2) new_mainKDFSalt = makeRandom() new_srpSalt = makeRandom() (new_srpPW, new_unwrapBKey) = mainKDF(new_stretchedPW, new_mainKDFSalt) # build new srpVerifier (new_srpVerifier, _,_,_,_) = mysrp.create_verifier(emailUTF8, new_srpPW, new_srpSalt) assert len(new_srpVerifier) == 256, len(new_srpVerifier) printhex("new_srpVerifier", new_srpVerifier) # re-wrap kB new_wrap_kB = xor(kB, new_unwrapBKey) printhex("new_wrap_kB", new_wrap_kB) # submit /account/reset x = HKDF(SKM=accountResetToken, XTS=None, CTXinfo=KW("accountResetToken"), dkLen=3*32) tokenID, reqHMACkey1, requestKey = split(x) plaintext = new_wrap_kB+new_srpVerifier print "LEN PLAIN", len(plaintext) y = HKDF(SKM=requestKey, XTS=None, CTXinfo=KW("account/reset"), dkLen=32+len(plaintext)) reqHMACkey2 = y[:32] reqXORkey = y[32:] bundle = xor(reqXORkey, plaintext) bundle_mac = HMAC(reqHMACkey2, bundle) payload = {"bundle": (bundle+bundle_mac).encode("hex"), "srp": { "type": "SRP-6a/SHA256/2048/v1", "salt": new_srpSalt.encode("hex"), }, "passwordStretching": { "type": "PBKDF2/scrypt/PBKDF2/v1", "PBKDF2_rounds_1": PBKDF2_rounds_1, "scrypt_N": scrypt_N, "scrypt_r": scrypt_r, "scrypt_p": scrypt_p, "PBKDF2_rounds_2": PBKDF2_rounds_2, "salt": new_mainKDFSalt.encode("hex"), }, } r = HAWK_POST("account/reset", tokenID, reqHMACkey1, payload) assert r == {}, r print "password changed" if command == "destroy": tokenID, reqHMACkey, requestKey = processAuthToken(authToken) r = HAWK_POST("account/destroy", tokenID, reqHMACkey, {}) print r