def __init__(self, database): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.bind((SERVER_IP_ADDR, SERVER_TCP_PORT)) # bind to port self.sock.listen(1) # listen with one pending connection # each connection maps to a hashmap, which keySet are {expect, dh_shared_key, addr, challenge, name} self.client_conn = {} # maps name to his/her ip address: name -> (ip, port) self.online_client = {} self.inputs = [self.sock] self.secret = os.urandom(16) # Loads user credential database into memory self.client_credentials = {} with open(database, 'rb') as datafile: for line in datafile: parts = line.split('\t') name = parts[0] self.client_credentials[name] = {} w1 = base64.b64decode(parts[1]) self.client_credentials[name]['w1_salt'] = w1[:16] self.client_credentials[name]['w1'] = w1[16:] self.client_credentials[name][ 'pub'] = utils.deserialize_public_key( base64.b64decode(parts[3])) self.client_credentials[name]['pri'] = base64.b64decode( parts[2])
def __handle_server_dhpub(self, rqst, rply): decrypted_key_bytes = b64decode_aes_cgm_decrypt( rply.public_key, self.w1) server_dh_public = utils.deserialize_public_key(decrypted_key_bytes) # compute shared key self.dh_shared = utils.generate_dh_key(self.dh_private, server_dh_public) # So we can compute y = W'{rsa private key} now y = b64decode_aes_cgm_decrypt(rply.private_key, self.dh_shared) # decrypt with W' and extract rsa private key self.w2_salt, iv1, tag, cipher_private = y[:16], y[16:32], y[32:48], y[ 48:] self.w2 = utils.password_to_w(self.w2_salt, self.password, utils.w2_iteration, utils.w2_length) private_key_bytes = utils.aes_cgm_decrypt(cipher_private, self.w2, iv1, tag) self.private_key = utils.deserialize_private_key(private_key_bytes) rqst.type = ClientToServer.USER_SIGN digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) digest.update(self.dh_shared) digest.update(base64.b64decode(rply.challenge)) hash_value = digest.finalize() # rqst.hash = base64.b64encode(hash_value) sign = self.private_key.sign( hash_value, padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), hashes.SHA256()) rqst.sign = base64.b64encode(sign) rqst.ip = Client_IP_ADDR rqst.port = str(self.listen_port) self.server_sock.send(rqst.SerializeToString()) logger.debug('__handle_server_dhpub: Processed SERVER_PUBKEY rqst')
def __handle_user_dh_pubkey(self, rqst, rply, conn): # server are expecting user's public key form next msg, all other kind of msg will be ignored or warned self.client_conn[conn]['expect'] = ClientToServer.USER_SIGN digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) digest.update(self.client_conn[conn]['addr'][0].encode()) digest.update(self.secret) msg_digest = base64.b64encode(digest.finalize()) # Checking for Dos attack if msg_digest != rqst.challenge: # log the DoS attack msg, and forget this connection logger.error( '__handle_user_dh_pubkey: Wrong hash, possible DoS: ' + self.client_conn[conn]['name']) # forget the connection self.inputs.remove(conn) self.client_conn.pop(conn, None) conn.close() else: try: # self.client_conn[conn]['client_pub'] = rqst.public_key # generate its own DH key server_dh_private_key = ec.generate_private_key( ec.SECP384R1(), default_backend()) # client's public DH key and shared DH key client_dh_public = utils.deserialize_public_key( base64.b64decode(rqst.public_key)) # using self.client_conn[conn]['dh_shared_key'] as shared key for further encrypting self.client_conn[conn][ 'dh_shared_key'] = utils.generate_dh_key( server_dh_private_key, client_dh_public) self.client_conn[conn]['challenge'] = os.urandom(16) name = self.client_conn[conn]['name'] w1 = self.client_credentials[name]['w1'] server_dh_public_serialized = server_dh_private_key \ .public_key() \ .public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo) rply.type = ServerToClient.SERVER_PUBKEY rply.public_key = b64encode_aes_cgm_encrypt( server_dh_public_serialized, w1) rply.challenge = base64.b64encode( self.client_conn[conn]['challenge']) rply.private_key = b64encode_aes_cgm_encrypt( self.client_credentials[name]['pri'], self.client_conn[conn]['dh_shared_key']) conn.send(rply.SerializeToString()) logger.debug( '__handle_user_dh_pubkey: Successful send dh_pub key: ' + self.client_conn[conn]['name']) except Exception as e: logger.error( "__handle_user_dh_pubkey: Fail to finish authentication" + str(e)) rply.type = ServerToClient.ERROR rply.info = "Fail to finish authentication" conn.send(rply.SerializeToString())
def __handle_recver_pub(self, rqst, s): rcver_dh_pub_bytes = base64.b64decode(rqst.public_key) recver_pub = utils.deserialize_public_key(rcver_dh_pub_bytes) # generate our own DH key self.conn_info[s]['shared_dh'] = utils.generate_dh_key( self.conn_info[s]['sender_dh_private'], recver_pub) self.conn_info[s]['expect'] = ClientToClient.RECVER_IDENTITY # cached sender_dh_pub_key_bytes # since we will have to verify signature of public key in the future protocol msg self.conn_info[s]['rcver_dh_pub_bytes'] = rcver_dh_pub_bytes # According to protocol, send back (g^ab mod p){"Alice", [g^a mod p]sign} self.__send_identity(self.conn_info[s]['sender_dh_private'], ClientToClient.SENDER_IDENTITY, s) logger.debug('__handle_recver_pub: Send identity as sender to ' + self.conn_info[s]['name'])
def __handle_sender_pub(self, rqst, s): sender_dh_pub_key_bytes = base64.b64decode(rqst.public_key) sender_dh_pub_key = utils.deserialize_public_key( sender_dh_pub_key_bytes) # generate our own DH key rcver_dh_private_key = ec.generate_private_key(ec.SECP384R1(), default_backend()) # compute shared key shared_key = utils.generate_dh_key(rcver_dh_private_key, sender_dh_pub_key) # cached sender_dh_pub_key_bytes # since we will have to verify signature of public key in the future protocol msg self.conn_info[s] = { 'shared_dh': shared_key, 'expect': ClientToClient.SENDER_IDENTITY, 'rcver_dh_private': rcver_dh_private_key, 'sender_dh_pub_bytes': sender_dh_pub_key_bytes } # send DH public key back self.__send_dh_pub(rcver_dh_private_key, ClientToClient.RECVER_PUB, s) logger.debug('__handle_sender_pub: Send dh pub as receiver ' + self.name)
def __handle_server_sock(self): while self.login: try: encrypted_data = self.server_sock.recv(BUFFER_SIZE) except: encrypted_data = '' if len(encrypted_data) == 0: # len(encrypted_data) == 0 means server is closing socket, so client can terminate program now self.server_sock.close() self.login = False logger.error('__handle_server_sock: Server is shutting down') return data = b64decode_aes_cgm_decrypt(encrypted_data, self.dh_shared) rply = ServerToClient() rply.ParseFromString(data) if abs(rply.time - time.time()) > TIME_TOLERANCE: # ignore the messages that are outdated logger.warn('__handle_server_sock: Message is too outdated') continue # server is ready to logout the user, prepare to close the socket if rply.type == ServerToClient.LOGOUT and self.server_expect[ 'Logout']: self.login = False self.server_sock.close() return # server replies with all login users if rply.type == ServerToClient.REPLY_LIST and self.server_expect[ 'List']: self.server_expect['List'] = False online_users = [] for online_user in rply.name_list: online_users.append(online_user) print ', '.join(online_users) # server replies with query elif rply.type == ServerToClient.REPLY_QUERY and rply.name in self.server_expect[ 'Query']: self.server_expect['Query'].remove(rply.name) # update meta data, in case of peer re-login or changing public key self.peer_info[ rply.name]['pub'] = utils.deserialize_public_key( base64.b64decode(rply.public_key)) self.peer_info[rply.name]['ip'] = rply.ip self.peer_info[rply.name]['port'] = rply.port # if we are the sender, then we haven't established connection with this peer if 'conn' not in self.peer_info[rply.name]: logger.debug( '__handle_server_sock: Receive pub key as sender to' + rply.name) self.__initiate_auth(rply.name) else: # if we are receiver, then we already establish the connection # we are in the step 3 of authentication protocol. After retrieving peer's public key # we can verify the signature, and sends back our own sign: g^ab mod p{"Bob", [g^b mod p]sign} conn = self.peer_info[rply.name]['conn'] self.peer_info[rply.name]['pub'].verify( base64.b64decode(self.conn_info[conn]['sign']), self.conn_info[conn]['sender_dh_pub_bytes'], padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), hashes.SHA256()) self.__send_identity( self.conn_info[conn]['rcver_dh_private'], ClientToClient.RECVER_IDENTITY, conn) logger.debug( '__handle_server_sock: Receive pub key as receiver to' + rply.name) logger.debug('__handle_server_sock: and send identity') if rply.type == ServerToClient.ERROR: print 'Error information: from server: ' + rply.info