def tlsn_msg_receiver(my_nick,counterparty_nick,ack_queue,recv_queue,message_headers,pk,seq_init=100000): '''Intended to be run as a thread; puts msgs sent to my_nick from counterparty_nick onto the Queue recv_queue, and sends acknowledgements onto ack_queue, filtering out messages whose headers/topics are not in message_headers, and using sequence numbering starting from seq_init (or 0 if seq_init is undef). Messages are received in chunks and decrypted using private key pk and base64 decoded, then reassembled according to line endings CRLF and EOL, as per tlsnotary's messaging protocol. ''' if not initialized: raise Exception("TLSN Messaging not yet instantiated") if not hasattr(tlsn_msg_receiver, 'last_seq_which_i_acked'): if not seq_init: seq_init=0 tlsn_msg_receiver.last_seq_which_i_acked = seq_init #static variable. Initialized only on first function's run chunks = [] while True: eemsg = mi.msg_receiver(my_nick,counterparty_nick) if not eemsg: continue #note that the timeout is in the implementation layer #acknowledgements are not our business here; put them on the queue if eemsg[0].startswith('ack'): #acks are not encrypted ack_queue.put(eemsg[0][len('ack:'):]) continue if len(eemsg) !=3: continue if not eemsg[0].startswith('seq'): continue #wrong format; old server hellos will do this msg_decrypted = dd(eemsg[1],pk) #print ("decrypted message is: ",msg_decrypted) if len(chunks) == 0: msg = [msg_decrypted.split(':')[0]] + [':'.join(msg_decrypted.split(':')[1:])]+[eemsg[2]] else: msg = [None,msg_decrypted,eemsg[2]] his_seq = int(eemsg[0][len('seq:'):]) if his_seq <= tlsn_msg_receiver.last_seq_which_i_acked: #the other side is out of sync, send an ack again mi.send_raw(' :' + counterparty_nick + ' ack:' + str(his_seq)) continue #we did not receive the next seq in order if not his_seq == tlsn_msg_receiver.last_seq_which_i_acked +1: continue #else we got a new seq if len(chunks)==0: #a new message is starting if not msg[0].startswith(message_headers) : continue hdr = msg[0] #'CRLF' is used at the end of the first chunk, 'EOL' is used to show that there are no more chunks chunks.append(msg[1]) mi.send_raw(' :' + counterparty_nick + ' ack:' + str(his_seq)) tlsn_msg_receiver.last_seq_which_i_acked = his_seq if msg[-1]=='EOL': assembled_message = ''.join(chunks) recv_queue.put(hdr+':'+assembled_message) chunks = []
def tlsn_receive_single_msg(header, pk, my_nick=None, ide=False): '''Non blocking receipt of a single message statelessly filtered on a message header, optionally prefixed by a username NB This is for handshake messages. All other messaging is handled by the tlsn_msg_receiver loop. 'header' is not currently used but could be to filter. Messages received are filtered by header 'my_nick' if defined, otherwise all messages are received. Messages are decrypted using private key pk and base64 decoded Sequence number, plaintext message, ending and (if relevant) nick of sending party are returned. If ide (ignore decryption errors) is true, we return False on a decryption error, treating the failure as receiving a handshake message from the wrong counterparty. ''' if not initialized: raise Exception("TLSN Messaging not yet instantiated") retval = mi.receive_single_msg(my_nick) if not retval: return False if len(retval) != 2: raise Exception("Invalid return from messaging implementation module") msg_array, ctrprty_nick = retval header = msg_array[1] if my_nick else msg_array[0] encrypted_encoded_msg = msg_array[2] if my_nick else msg_array[1] ending = msg_array[-1] try: msg = dd(encrypted_encoded_msg, pk) seq = msg[0] msg = ''.join(msg[1:]) except: if ide: return False #means we got a message from the wrong counterparty raise Exception("Failure in decryption or decoding of message: ", encrypted_encoded_msg) return ((header, int(seq), msg, ending), ctrprty_nick)
def tlsn_receive_single_msg(header, pk, my_nick=None,ide=False): '''Non blocking receipt of a single message statelessly filtered on a message header, optionally prefixed by a username NB This is for handshake messages. All other messaging is handled by the tlsn_msg_receiver loop. 'header' is not currently used but could be to filter. Messages received are filtered by header 'my_nick' if defined, otherwise all messages are received. Messages are decrypted using private key pk and base64 decoded Sequence number, plaintext message, ending and (if relevant) nick of sending party are returned. If ide (ignore decryption errors) is true, we return False on a decryption error, treating the failure as receiving a handshake message from the wrong counterparty. ''' if not initialized: raise Exception("TLSN Messaging not yet instantiated") retval = mi.receive_single_msg(my_nick) if not retval: return False if len(retval) != 2: raise Exception ("Invalid return from messaging implementation module") msg_array,ctrprty_nick = retval header = msg_array[1] if my_nick else msg_array[0] encrypted_encoded_msg = msg_array[2] if my_nick else msg_array[1] ending = msg_array[-1] try: msg = dd(encrypted_encoded_msg,pk) seq = msg[0] msg = ''.join(msg[1:]) except: if ide: return False #means we got a message from the wrong counterparty raise Exception ("Failure in decryption or decoding of message: ", encrypted_encoded_msg) return ((header,int(seq),msg,ending),ctrprty_nick)
def tlsn_msg_receiver(my_nick, counterparty_nick, ack_queue, recv_queue, message_headers, pk, seq_init=100000): '''Intended to be run as a thread; puts msgs sent to my_nick from counterparty_nick onto the Queue recv_queue, and sends acknowledgements onto ack_queue, filtering out messages whose headers/topics are not in message_headers, and using sequence numbering starting from seq_init (or 0 if seq_init is undef). Messages are received in chunks and decrypted using private key pk and base64 decoded, then reassembled according to line endings CRLF and EOL, as per tlsnotary's messaging protocol. ''' if not initialized: raise Exception("TLSN Messaging not yet instantiated") if not hasattr(tlsn_msg_receiver, 'last_seq_which_i_acked'): if not seq_init: seq_init = 0 tlsn_msg_receiver.last_seq_which_i_acked = seq_init #static variable. Initialized only on first function's run chunks = [] while True: eemsg = mi.msg_receiver(my_nick, counterparty_nick) if not eemsg: continue #note that the timeout is in the implementation layer #acknowledgements are not our business here; put them on the queue if eemsg[0].startswith('ack'): #acks are not encrypted ack_queue.put(eemsg[0][len('ack:'):]) continue if len(eemsg) != 3: continue if not eemsg[0].startswith('seq'): continue #wrong format; old server hellos will do this msg_decrypted = dd(eemsg[1], pk) #print ("decrypted message is: ",msg_decrypted) if len(chunks) == 0: msg = [msg_decrypted.split(':')[0] ] + [':'.join(msg_decrypted.split(':')[1:])] + [eemsg[2]] else: msg = [None, msg_decrypted, eemsg[2]] his_seq = int(eemsg[0][len('seq:'):]) if his_seq <= tlsn_msg_receiver.last_seq_which_i_acked: #the other side is out of sync, send an ack again mi.send_raw(' :' + counterparty_nick + ' ack:' + str(his_seq)) continue #we did not receive the next seq in order if not his_seq == tlsn_msg_receiver.last_seq_which_i_acked + 1: continue #else we got a new seq if len(chunks) == 0: #a new message is starting if not msg[0].startswith(message_headers): continue hdr = msg[0] #'CRLF' is used at the end of the first chunk, 'EOL' is used to show that there are no more chunks chunks.append(msg[1]) mi.send_raw(' :' + counterparty_nick + ' ack:' + str(his_seq)) tlsn_msg_receiver.last_seq_which_i_acked = his_seq if msg[-1] == 'EOL': assembled_message = ''.join(chunks) recv_queue.put(hdr + ':' + assembled_message) chunks = []