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 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 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()