def gen_user_pwrd_hash_and_salt(self, user_pass, hash_func=SHA256.new, rand_pool=Random.new()): def gen_user_salt(rand_pool, num_salt_bytes=USR_DB_SALT_SIZE): return (rand_pool.read(num_salt_bytes)) def gen_user_hash(user_pwrd, user_salt, hash_func=SHA256_HASH_FUNC): assert (type(user_pwrd) == str) assert (type(user_salt) == str) user_hash = hash_func(user_pwrd + user_salt) for i in xrange(PWRD_HASH_ROUNDS): user_hash = hash_func(user_hash.digest() + user_salt) return (user_hash.digest()) user_pass = DECODE_FUNC(user_pass.encode("utf-8")) user_salt = gen_user_salt(rand_pool) user_hash = gen_user_hash(user_pass, user_salt, hash_func) assert (type(user_salt) == str) assert (type(user_hash) == str) return (ENCODE_FUNC(user_hash), ENCODE_FUNC(user_salt))
def out_LOGIN(self): #print("[LOGIN][time=%d::iter=%d] sec_sess=%d" % (time.time(), self.iters, self.use_secure_session())) if (self.use_secure_session()): password = ENCODE_FUNC(self.password) else: password = ENCODE_FUNC(LEGACY_HASH_FUNC(self.password).digest()) self.Send("LOGIN %s %s 0 *\tstresstester client\t0\tsp cl p" % (self.username, password)) self.requested_authentication = True
def out_REGISTER(self): print("[REGISTER][time=%d::iter=%d] sec_sess=%d" % (time.time(), self.iters, self.use_secure_session())) if (self.use_secure_session()): self.Send("REGISTER %s %s" % (self.username, ENCODE_FUNC(self.password))) else: self.Send("REGISTER %s %s" % (self.username, ENCODE_FUNC(LEGACY_HASH_FUNC(self.password).digest()))) self.requested_registration = True
def in_SHAREDKEY(self, key_status, key_digest, extra_data = ""): assert(self.received_public_key) assert(self.sent_unacked_shared_key) assert(not self.server_valid_shared_key) assert(not self.client_acked_shared_key) print("[SHAREDKEY][time=%d::iter=%d] %s %s %s" % (time.time(), self.iters, key_status, key_digest, extra_data)) can_send_ack_shared_key = False if (key_status == "INITSESS"): ## special case during first session-key exchange assert(not self.use_secure_session()) assert(len(self.session_keys[self.session_key_id]) != 0) self.set_session_key(self.session_keys[self.session_key_id]) return elif (key_status == "ACCEPTED"): server_key_sig = SAFE_DECODE_FUNC(key_digest) client_key_sha = SECURE_HASH_FUNC(self.session_keys[self.session_key_id]) client_key_sig = client_key_sha.digest() print("\tserver_key_sig=%s\n\tclient_key_sig=%s (id=%d)" % (ENCODE_FUNC(server_key_sig), ENCODE_FUNC(client_key_sig), self.session_key_id)) ## server considers key valid and has accepted it ## now check for data manipulation or corruption ## before sending back our final acknowledgement self.server_valid_shared_key = True can_send_ack_shared_key = (server_key_sig == client_key_sig) elif (key_status == "DISABLED"): self.reset_session_state() self.set_session_key("") ## never sent, no longer supported by server assert(False) return if (not can_send_ack_shared_key): ## if this was triggered during an actual key RE-negotiation ## (much more unlikely) the client's only option would be to ## disconnect and initialize a new session assert(key_status != "ACCEPTED") self.sent_unacked_shared_key = False self.server_valid_shared_key = False self.client_acked_shared_key = False ## try again with a new session key ## ## this assumes we did NOT get an ACCEPTED, which ## would indicate data corruption or manipulation self.out_SETSHAREDKEY() else: ## let server know it can begin sending secure data self.out_ACKSHAREDKEY()
def out_GETSIGNEDMSG(self): assert(self.received_public_key) assert(not self.sent_unacked_shared_key) assert(not self.server_valid_shared_key) assert(not self.client_acked_shared_key) print("[GETSIGNEDMSG][time=%d::iter=%d]" % (time.time(), self.iters)) self.Send("GETSIGNEDMSG %s" % ENCODE_FUNC(MAGIC_WORDS))
def secure_test_user_pwrd(self, user_inst, user_pwrd, hash_func=SHA256.new): user_pwrd = DECODE_FUNC(user_pwrd.encode("utf-8")) user_salt = DECODE_FUNC(user_inst.randsalt.encode("utf-8")) user_hash = hash_func(user_pwrd + user_salt) for i in xrange(PWRD_HASH_ROUNDS): user_hash = hash_func(user_hash.digest() + user_salt) return (user_inst.password.encode("utf-8") == ENCODE_FUNC( user_hash.digest()))
def out_SETSHAREDKEY(self): assert (self.received_public_key) assert (not self.sent_unacked_shared_key) assert (not self.server_valid_shared_key) assert (not self.client_acked_shared_key) def generate_session_key(num_bytes=CryptoHandler.MIN_AES_KEY_SIZE * 2): ## encrypt converts its argument to a long, so the ## MSB should *always* be non-zero or D(E(K)) != K key_head = "\x00" key_tail = GLOBAL_RAND_POOL.read(num_bytes - 1) while (ord(key_head) == 0): key_head = GLOBAL_RAND_POOL.read(1) return (key_head + key_tail) ## note: server will use HASH(RAW) as key and echo back HASH(HASH(RAW)) ## hence we send ENCODE(ENCRYPT(RAW)) and use HASH(RAW) on our side too aes_key_raw = generate_session_key() aes_key_sig = SECURE_HASH_FUNC(aes_key_raw) aes_key_enc = self.rsa_cipher_obj.encrypt_encode_bytes(aes_key_raw) ## make a copy of the previous key (if any) since ## we still need it to decrypt SHAREDKEY response ## etc self.session_keys[(self.session_key_id + 0) % NUM_SESSION_KEYS] = self.aes_cipher_obj.get_key() self.session_keys[(self.session_key_id + 1) % NUM_SESSION_KEYS] = aes_key_sig.digest() ## wrap around when we reach the largest allowed id self.session_key_id += 1 self.session_key_id %= NUM_SESSION_KEYS print("[SETSHAREDKEY][time=%d::iter=%d] client_key_sig=%s" % (time.time(), self.iters, ENCODE_FUNC(SECURE_HASH_FUNC(aes_key_sig.digest()).digest()))) ## when re-negotiating a key during an established session, ## reset_session_state() makes this false but we need it to ## be true temporarily to get the message out self.client_acked_shared_key = self.use_secure_session() ## ENCODE(ENCRYPT_RSA(AES_KEY, RSA_PUB_KEY)) self.Send("SETSHAREDKEY %s" % aes_key_enc) self.client_acked_shared_key = False self.sent_unacked_shared_key = True
def convert_legacy_user_pwrd(self, session, db_user, password): assert (db_user != None) assert (len(db_user.randsalt) == 0) ## in a secure session, so password was only base64-encoded ## by client and we must apply B64ENCODE(MD5(B64DECODE(..))) ## to it first legacy_pwrd = password.encode("utf-8") legacy_pwrd = DECODE_FUNC(legacy_pwrd) legacy_pwrd = MD5.new(legacy_pwrd) legacy_pwrd = ENCODE_FUNC(legacy_pwrd.digest()) legacy_pwrd = legacy_pwrd.decode("utf-8") ## check if a legacy LOGIN would succeed with given password if (not self.legacy_test_user_pwrd(db_user, legacy_pwrd)): return False ## commit new-style password(-hash) and salt to DB db_user.set_pwrd_salt(self.gen_user_pwrd_hash_and_salt(password)) session.commit() assert (not db_user.has_legacy_password()) assert (self.secure_test_user_pwrd(db_user, password)) return True
def EncodePassword(self, password): return ENCODE_FUNC(md5(self.password.encode()).digest()).decode()
def encode_password(password): return ENCODE_FUNC(md5(password.encode()).digest()).decode()
def out_REGISTER(self): print("[REGISTER][time=%d::iter=%d]" % (time.time(), self.iters)) self.Send("REGISTER %s %s" % (self.username, ENCODE_FUNC(LEGACY_HASH_FUNC(self.password).digest()))) self.requested_registration = True
def out_LOGIN(self): print("[LOGIN][time=%d::iter=%d]" % (time.time(), self.iters)) self.Send("LOGIN %s %s" % (self.username, ENCODE_FUNC(LEGACY_HASH_FUNC(self.password.encode("utf-8")).digest()))) self.requested_authentication = True