def makeFirstConnectCell(self): """add method def""" ECprivate_key = ec.generate_private_key( ec.SECP384R1(), default_backend()) # elliptic curve DHpublicKeyBytes = ECprivate_key.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) # send the initialising cell, by sending the DHpublicKeyBytes sendingCell = cell(DHpublicKeyBytes, Type="AddCon") return sendingCell, ECprivate_key
def exchange_keys(self, clientsocket, obtainedCell): """Exchange Key with someone, obtaining a shared secret. Also, generate salt and pass it back to them with your private key.""" private_key = ec.generate_private_key( ec.SECP384R1(), default_backend()) # elliptic curve public_key = private_key.public_key() # duh same. serialised_public_key = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) # serialise the public key that I'm going to send them theirkey = serialization.load_pem_public_key(obtainedCell.payload, backend=default_backend()) shared_key = private_key.exchange(ec.ECDH(), theirkey) salty = str.encode(str(randint(0, 99999999))) # randomised IV derived_key = HKDF(algorithm=hashes.SHA256(), length=32, salt=salty, info=None, backend=default_backend()).derive(shared_key) reply_cell = cell(serialised_public_key, salt=salty, Type="ConnectResp") signature = self.true_private_key.sign( salty, cryptography.hazmat.primitives.asymmetric.padding.PSS( mgf=cryptography.hazmat.primitives.asymmetric.padding.MGF1( hashes.SHA256()), salt_length=cryptography.hazmat.primitives.asymmetric.padding. PSS.MAX_LENGTH), hashes.SHA256()) reply_cell.signature = signature # assign the signature. print("reply cell") print(pickle.dumps(reply_cell)) # send them the serialised version. clientsocket.send(pickle.dumps(reply_cell)) return private_key, derived_key
def main(self): """main method""" client_class = None # initialise as none. readready, _, _ = select.select([self.server_socket] + self.CLIENTSOCKS, [], []) for i in readready: if (i == self.server_socket): # i've gotten a new connection print("client get") (clientsocket, _) = self.server_socket.accept() # clientsocket.setblocking(0) clientsocket.settimeout(0.3) try: obtainedCell = clientsocket.recv( 4096) # obtain their public key try: print("raw data obtained. (Cell)") print(obtainedCell) # decrypt the item. obtainedCell = self.decrypt(obtainedCell) # this is due to decryption failure. except ValueError: if client_class is not None: self.CLIENTS.remove(client_class) # just in case. # otherwise, it should continue print("rejected one connection") continue print("decrypted cell with actual keys.") print(obtainedCell) # i.e grab the cell that was passed forward. obtainedCell = pickle.loads(obtainedCell) print("after pickle load") print(obtainedCell) if obtainedCell.type != "AddCon": break # it was not a connection request. # obtain the generated public key, and the derived key. generatedPrivateKey, derivedkey = self.exchange_keys( clientsocket, obtainedCell) client_class = Client(clientsocket, derivedkey, generatedPrivateKey) self.CLIENTS.append(client_class) self.CLIENTSOCKS.append(clientsocket) print(client_class.socket.getpeername()) print("Connected to ONE client.\n\n\n") # error is socket error here. except (error, ConnectionResetError, timeout): print("socket ERROR! might have timed out.") if client_class is not None: self.CLIENTS.remove(client_class) # just in case. # otherwise, it should continue continue else: # came from an existing client. try: for k in self.CLIENTS: if k.socket == i: sending_client = k received = i.recv(4096) print("got a packet..") print(received) if not received: raise ConnectionResetError except (error, ConnectionResetError, ConnectionAbortedError, timeout): print("Client was closed or timed out.") sending_client.socket.close() if sending_client.bounce_socket is not None: sending_client.bounce_socket.close() self.CLIENTSOCKS.remove(i) self.CLIENTS.remove(sending_client) continue print("existing") # received_data = self.decrypt(received) gottencell = pickle.loads(received) derived_key = sending_client.key # take his derived key cipher = Cipher(algorithms.AES(derived_key), modes.CBC(gottencell.IV), backend=default_backend()) decryptor = cipher.decryptor() decrypted = decryptor.update(gottencell.payload) decrypted += decryptor.finalize() cell_to_next = pickle.loads(decrypted) print(cell_to_next.type) if cell_to_next.type == "relay connect": # is a request for a relay connect try: # your connection is TCP. sock = socket(AF_INET, SOCK_STREAM) sock.connect((cell_to_next.ip, cell_to_next.port)) print((cell_to_next.ip, cell_to_next.port)) print("cell to next") print(decrypted) print("payload") print(cell_to_next.payload) # send over the cell payload sock.send(cell_to_next.payload) theircell = sock.recv(4096) # await answer print("got values") print(theircell) IV = os.urandom(16) cipher = Cipher(algorithms.AES(derived_key), modes.CBC(IV), backend=default_backend()) encryptor = cipher.encryptor() if (theircell == b""): encrypted = encryptor.update( padder128(pickle.dumps(cell("", Type="failed")))) encrypted += encryptor.finalize() print("sent failed") i.send( pickle.dumps( cell(encrypted, IV=IV, Type="failed"))) else: encrypted = encryptor.update( padder128( pickle.dumps( cell(theircell, Type="ConnectResp")))) encrypted += encryptor.finalize() print("sent valid response") i.send( pickle.dumps( cell(encrypted, IV=IV, Type="AddCon"))) sending_client.bounce_ip = cell_to_next.ip sending_client.bounce_port = cell_to_next.port sending_client.bounce_socket = sock print("connection success.\n\n\n\n\n") except (ConnectionRefusedError, ConnectionResetError, ConnectionAbortedError, error, timeout) as e: print( "failed to connect to other server. sending back failure message, or timed out." ) IV = os.urandom(16) cipher = Cipher(algorithms.AES(derived_key), modes.CBC(IV), backend=default_backend()) encryptor = cipher.encryptor() encrypted = encryptor.update( padder128( pickle.dumps( cell(pickle.dumps( cell("CONNECTIONREFUSED", Type="failed")), Type="failed")))) encrypted += encryptor.finalize() i.send( pickle.dumps(cell(encrypted, IV=IV, Type="failed"))) print("sent back failure message.") # is an item to be relayed. elif cell_to_next.type == "relay": if sending_client.bounce_socket is None: # check if there is bounce socket return sock = sending_client.bounce_socket print("bouncing cell's decrypted..") print(decrypted) print("payload") print(cell_to_next.payload) print(cell_to_next.type) sock.send(cell_to_next.payload) # send over the cell try: theircell = sock.recv(32768) # await answer except timeout: theircell = "request timed out!" print("got answer back.. as a relay.") print(len(theircell)) print(theircell) IV = os.urandom(16) cipher = Cipher(algorithms.AES(derived_key), modes.CBC(IV), backend=default_backend()) encryptor = cipher.encryptor() encrypted = encryptor.update( padder128( pickle.dumps(cell(theircell, Type="ConnectResp")))) encrypted += encryptor.finalize() i.send(pickle.dumps(cell(encrypted, IV=IV, Type="AddCon"))) print("Relay success.\n\n\n\n\n") elif cell_to_next.type == "Req": print(cell_to_next.payload) if isinstance(cell_to_next.payload, type("")): request = cell_to_next.payload try: header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" } a = requests.get(request, headers=header) print("length of answer") print(len(a.content)) except requests.exceptions.ConnectionError: a = "ERROR" print( "failed to receive a response from the website." ) IV = os.urandom(16) cipher = Cipher(algorithms.AES(derived_key), modes.CBC(IV), backend=default_backend()) encryptor = cipher.encryptor() encrypted = encryptor.update( padder128( pickle.dumps( cell(pickle.dumps(a), Type="ConnectResp")))) encrypted += encryptor.finalize() i.send( pickle.dumps(cell(encrypted, IV=IV, Type="AddCon"))) print("VALID REQUEST REPLIED.") else: IV = os.urandom(16) cipher = Cipher(algorithms.AES(derived_key), modes.CBC(IV), backend=default_backend()) encryptor = cipher.encryptor() encrypted = encryptor.update( padder128( pickle.dumps( cell("INVALID REQUEST DUMDUM", Type="ConnectResp")))) encrypted += encryptor.finalize() i.send( pickle.dumps(cell(encrypted, IV=IV, Type="AddCon"))) print("INVALID REQUEST SENT BACK")
def mainloop(self): while (True): """if ((time.time() - self.lasttime) > 20.00): for i in self.registered_servers: i.time = time.time() i.socket.send(cell("",Type= "checkup")) else: """ #SOME INDENTATION. readready, _, _ = select.select([self.socket] + self.socketlist, [], []) print("obtained a server connection.") for i in readready: if (i == self.socket): #is receiving a new connection request. (serversocket, myport) = readready[0].accept obtained = serversocket.recv( 4096) # obtain the data sent over. try: receivedCell = pickle.loads(obtained) except (pickle.PickleError, pickle.PicklingError, pickle.UnpicklingError) as e: continue if (type(receivedCell) == type( cell(""))): #ensure it is indeed a cell. if (receivedCell.type == "giveDirect"): # signedbytearray = receivedCell.salt signature = receivedCell.signature identity = receivedCell.payload try: tempopen = open( "publics/publictest" + str(identity) + ".pem", "rb") theirpublickey = serialization.load_pem_private_key( tempopen.read(), password=None, backend=default_backend( )) # used for signing, etc. tempopen.close() except FileNotFoundError: continue #i.e the identity is not established. try: theirpublickey.verify( signature, signedbytearray, cryptography.hazmat.primitives.asymmetric. padding.PSS( mgf=cryptography.hazmat.primitives. asymmetric.padding.MGF1( hashes.SHA256()), salt_length=cryptography.hazmat. primitives.asymmetric.padding.PSS. MAX_LENGTH), hashes.SHA256()) except InvalidSignature: serversocket.close( ) #reject. signature validation failed. continue ip, port = serversocket.getpeername( ) # obtain the ip and port of that server. ## """ firstlatency = time.time() serversocket.send(cell("",Type="checkup")) serversocket.recv(4096) secondlatency = time.time() latency = secondlatency-firstlatency """ self.registered_servers.append( Serverreg(ip, port, serversocket, identity, latency)) else: serversocket.close() continue # reject connection as it does not contain a valid cell. else: print("got from existing.") received = i.recv(4096) for k in self.registered_servers: if (k.socket == i): # i.e it is part of the thing. reference = k if (len(received) == 0): #disconnect catch print("CLIENT WAS CLOSED! or timed out.") i.socket.close() self.registered_servers.remove(reference) continue """else: #am currently receiving an update
def req(self, request, intermediate_servers): """send out stuff in router.""" #print("REQUEST SENDING TEST") # must send IV and a cell that is encrypted with the next public key # public key list will have to be accessed in order with list of servers. # number between is to know when to stop i guess. # connection type. exit node always knows sendingCell = cell(request, Type="Req") IV = os.urandom(16) cipher = Cipher(algorithms.AES(intermediate_servers[2].key), modes.CBC(IV), backend=default_backend()) # 256 bit length cipher lel encryptor = cipher.encryptor() # encrypt the entire cell encrypted = encryptor.update(padder128(pickle.dumps(sendingCell))) encrypted += encryptor.finalize() # finalise encryption. sendingCell = cell(encrypted, IV=IV, Type="relay") sendingCell.ip = intermediate_servers[2].ip sendingCell.port = intermediate_servers[2].port sendingCell = cell(pickle.dumps(sendingCell), Type="relay") IV = os.urandom(16) cipher = Cipher(algorithms.AES(intermediate_servers[1].key), modes.CBC(IV), backend=default_backend()) # 256 bit length cipher lel encryptor = cipher.encryptor() # encrypt the entire cell encrypted = encryptor.update(padder128(pickle.dumps(sendingCell))) encrypted += encryptor.finalize() # finalise encryption. sendingCell = cell(encrypted, IV=IV, Type="relay") sendingCell.ip = intermediate_servers[1].ip sendingCell.port = intermediate_servers[1].port sendingCell = cell(pickle.dumps(sendingCell), Type="relay") IV = os.urandom(16) cipher = Cipher(algorithms.AES(intermediate_servers[0].key), modes.CBC(IV), backend=default_backend()) # 256 bit length cipher lel encryptor = cipher.encryptor() # encrypt the entire cell encrypted = encryptor.update(padder128(pickle.dumps(sendingCell))) encrypted += encryptor.finalize() # finalise encryption. sendingCell = cell(encrypted, IV=IV, Type="relay") sendingCell.ip = intermediate_servers[0].ip sendingCell.port = intermediate_servers[0].port try: sock = intermediate_servers[0].socket sock.send(pickle.dumps(sendingCell)) # send over the cell their_cell = sock.recv(32768) # await answer # you now receive a cell with encrypted payload. #print("received cell") # print(len(their_cell)) # print(their_cell) their_cell = pickle.loads(their_cell) #print("received cell payload") # print(their_cell.payload) counter = 0 while counter < len(intermediate_servers): cipher = Cipher(algorithms.AES( intermediate_servers[counter].key), modes.CBC(their_cell.IV), backend=default_backend()) decryptor = cipher.decryptor() decrypted = decryptor.update(their_cell.payload) decrypted += decryptor.finalize() # finalise decryption their_cell = pickle.loads(decrypted) counter += 1 if (counter < len(intermediate_servers)): their_cell = pickle.loads(their_cell.payload) if (their_cell.type == their_cell._Types[3]): #print("FAILED AT CONNECTION!") return response = pickle.loads(their_cell.payload) if not isinstance(response, type("")): print(response.content) print(response.status_code) return_dict = { "content": response.content.decode(response.encoding), "status code": response.status_code } print(json.dumps(return_dict)) else: # TODO - the error code should be specific to our implementation, not generic ones # e.g. node offline, decryption failure etc etc print(json.dumps({"content": "", "status": 404})) except error: print("socketerror")
def moreConnect2(self, gonnect, gonnectport, intermediate_servers, RSA_key): """must send IV and a cell that is encrypted with the next public key public key list will have to be accessed in order with list of servers. number between is to know when to stop i guess.""" sendingCell, ECprivate_key = self.makeFirstConnectCell() sendingCell = pickle.dumps(sendingCell) #print("Innermost cell with keys") # print(sendingCell) sendingCell = RSA_key.encrypt( sendingCell, cryptography.hazmat.primitives.asymmetric.padding.OAEP( mgf=cryptography.hazmat.primitives.asymmetric.padding.MGF1( algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)) #print("Innermost cell with keys (Encrypted)") # print(sendingCell) # connection type. exit node always knows sendingCell = cell(sendingCell, Type="relay connect") sendingCell.ip = gonnect # save the stuff i should be sending over. sendingCell.port = gonnectport IV = os.urandom(16) cipher = Cipher(algorithms.AES(intermediate_servers[1].key), modes.CBC(IV), backend=default_backend()) # 256 bit length cipher lel encryptor = cipher.encryptor() # encrypt the entire cell encrypted = encryptor.update(padder128(pickle.dumps(sendingCell))) encrypted += encryptor.finalize() # finalise encryption. sendingCell = cell(encrypted, IV=IV, Type="relay connect") sendingCell.ip = intermediate_servers[1].ip sendingCell.port = intermediate_servers[1].port sendingCell = cell(pickle.dumps(sendingCell), Type="relay") IV = os.urandom(16) cipher = Cipher(algorithms.AES(intermediate_servers[0].key), modes.CBC(IV), backend=default_backend()) # 256 bit length cipher lel encryptor = cipher.encryptor() # encrypt the entire cell encrypted = encryptor.update(padder128(pickle.dumps(sendingCell))) encrypted += encryptor.finalize() # finalise encryption. sendingCell = cell(encrypted, IV=IV, Type="relay") sendingCell.ip = intermediate_servers[0].ip sendingCell.port = intermediate_servers[0].port try: sock = intermediate_servers[0].socket sock.send(pickle.dumps(sendingCell)) # send over the cell their_cell = sock.recv(4096) # await answer # you now receive a cell with encrypted payload. # print(their_cell) their_cell = pickle.loads(their_cell) # print(their_cell.payload) counter = 0 while (counter < len(intermediate_servers)): cipher = Cipher(algorithms.AES( intermediate_servers[counter].key), modes.CBC(their_cell.IV), backend=default_backend()) decryptor = cipher.decryptor() decrypted = decryptor.update(their_cell.payload) decrypted += decryptor.finalize() # finalise decryption # print(decrypted) their_cell = pickle.loads(decrypted) counter += 1 their_cell = pickle.loads(their_cell.payload) if (their_cell.type == their_cell._Types[3]): #print("FAILED AT CONNECTION!") return # their_cell = pickle.loads(their_cell.payload) # this cell isn't encrypted. Extract the signature to verify signature = their_cell.signature their_cell.signature = None RSA_key.verify( signature, their_cell.salt, cryptography.hazmat.primitives.asymmetric.padding.PSS( mgf=cryptography.hazmat.primitives.asymmetric.padding.MGF1( hashes.SHA256()), salt_length=cryptography.hazmat.primitives.asymmetric. padding.PSS.MAX_LENGTH), hashes.SHA256()) # verify that the cell was signed using their key. # at this point, you have the cell that is the public key of your target server. Additionally, salt too.. theirKey = serialization.load_pem_public_key( their_cell.payload, backend=default_backend()) # load up their key. shared_key = ECprivate_key.exchange(ec.ECDH(), theirKey) derived_key = HKDF(algorithm=hashes.SHA256(), length=32, salt=their_cell.salt, info=None, backend=default_backend()).derive(shared_key) self.serverList.append( Server(gonnect, sock, derived_key, ECprivate_key, RSA_key, gonnectport)) #print("connected successfully to server @ " + gonnect + " Port: " + str(gonnectport)) except error: print("socket error occurred")