class Interface(Frame): def __init__(self, window, id_equipment): Frame.__init__(self, window, width=900, height=900) self.__id_equipment = id_equipment self.__e = Equipment(name=str(id_equipment)) self.graph = nx.MultiDiGraph() self.graph.add_node(id_equipment, cert=self.__e.cert()) # Button creation: self.but_new_equipment = Button(window, text='New equipment', command=self.click_new_equipment) but_insert = Button( window, text='Insert', command=lambda: self.click_insert_or_sync(mode='insert')) but_sync = Button( window, text='Sync', command=lambda: self.click_insert_or_sync(mode='sync')) but_info = Button(window, text='Graph\nInformation', command=self.click_info) but_CA = Button(window, text='CA', command=self.click_CA) but_DA = Button(window, text='All cert', command=self.click_DA) # New equipment self.txt_equipment_type = Label(window, text='Type of this equipment', wraplength=70) self.equipment_type = ttk.Combobox(window, values=EQUIPMENT_TYPES) self.equipment_type.current(0) # Connection port: self.txt_port = Label(window, text='Port', width=10) self.port = Entry(window) self.port.insert(0, '11111') # Creation of the TextBox and its ScrollBar scrollbar = Scrollbar(window) self.txt_box = Text(window, height=10, width=60) scrollbar.config(command=self.txt_box.yview) self.txt_box.config(yscrollcommand=scrollbar.set) # Layout : but_info.grid(row=2, column=1, sticky='nsew', padx=10, pady=10) but_CA.grid(row=3, column=1, sticky='nsew', padx=10, pady=10) but_DA.grid(row=4, column=1, sticky='nsew', padx=10, pady=10) but_insert.grid(row=7, column=3, sticky='nsew', padx=5, pady=10) but_sync.grid(row=7, column=4, sticky='nsew', padx=5, pady=10) self.but_new_equipment.grid(row=6, column=4, sticky='nsew', padx=5, pady=10) self.equipment_type.grid(row=6, column=2, sticky='ew', padx=10, pady=10) self.txt_equipment_type.grid(row=6, column=1, sticky='nsew') self.txt_port.grid(row=7, column=1, padx=10, pady=10) self.port.grid(row=7, column=2, sticky='nsew', padx=10, pady=10) scrollbar.grid(row=2, column=5, rowspan=3, sticky="nsew") self.txt_box.grid(row=2, column=2, rowspan=3, columnspan=3, sticky="nsew") # Minimum size of the row for row in range(window.grid_size()[1]): window.grid_rowconfigure(row, minsize=20) def click_info(self): cert = self.__e.cert() text = self.display_cert(cert) self.txt_box.delete("1.0", END) self.txt_box.insert(END, text) nx.draw_networkx(self.graph, with_labels=True, node_size=500, edge_color='r') plt.title('Network of equipment {}'.format(self.__id_equipment)) plt.show() def click_CA(self): self.txt_box.delete("1.0", END) list_neighbors = [] neighbors = nx.all_neighbors(self.graph, self.__id_equipment) for neigh in neighbors: # neighbors contains successor and predecessor so we take into account only one of them if neigh not in list_neighbors: list_neighbors.append(neigh) text = self.display_cert( self.graph.get_edge_data(self.__id_equipment, neigh, 0)['cert']) self.txt_box.insert(END, text) number_of_ca = 'Number of CA : {} \n\n'.format(len(list_neighbors)) self.txt_box.insert("1.0", number_of_ca) def click_DA(self): self.txt_box.delete("1.0", END) cpt = 0 # add self cert in da for node in self.graph.nodes(): cpt += 1 text = self.display_cert(self.graph.nodes[node]['cert']) self.txt_box.insert(END, text) # add cert in da (including those already in ca) for edge in self.graph.edges(): cpt += 1 text = self.display_cert( self.graph.get_edge_data(edge[0], edge[1], 0)['cert']) self.txt_box.insert(END, text) number_of_da = 'Number of DA : {} \n\n'.format(cpt) self.txt_box.insert("1.0", number_of_da) def display_cert(self, cert): text = '\n'.join([ 'Version : {}'.format( str(cert.version)[str(cert.version).find(".") + 1:]), 'Serial number : {}'.format(cert.serial_number), 'Not valid before : {}'.format(cert.not_valid_before), 'Not valid after : {}'.format(cert.not_valid_after), 'Subject name : {}'.format( str(cert.subject)[str(cert.subject).find("=") + 1:str(cert.subject).find(")")]), 'Issuer name : {}'.format( str(cert.issuer)[str(cert.issuer).find("=") + 1:str(cert.issuer).find(")")]), 'Signature algorithm : {}'.format( 'SHA256' if cert.signature_algorithm_oid.dotted_string == "1.2.840.113549.1.1.11" else 'Unknown algorithm' ), # utilisation d'un dico de correspondance '\n\n' ]) return text def click_new_equipment(self): self.but_new_equipment.destroy() f = Tk() f.title('Equipment {}'.format(self.__id_equipment + 1)) Interface(window=f, id_equipment=self.__id_equipment + 1).mainloop() def click_insert_or_sync(self, mode): # preparation de la liste de messages port_number = self.port.get() if mode == 'insert': msg = ['self_cert', self.__e.byte_cert(), 'cert', 'da'] elif mode == 'sync': msg = ['self_cert', self.__e.byte_cert()] for node in self.graph.nodes(): # pour ne pas renvoyer deux fois le self cert if self.graph.nodes()[node]['cert'].public_bytes( encoding=serialization.Encoding.PEM ) != self.__e.byte_cert(): msg.append(self.graph.nodes()[node]['cert'].public_bytes( encoding=serialization.Encoding.PEM)) msg += [b'end', 'cert', 'da'] # insertion des certs contenus dans ca U da for node in self.graph.nodes(): cert = self.graph.nodes[node]['cert'] msg.append(cert.public_bytes(encoding=serialization.Encoding.PEM)) for edge in self.graph.edges(): cert = self.graph.get_edge_data(edge[0], edge[1], 0)['cert'] msg.append(cert.public_bytes(encoding=serialization.Encoding.PEM)) msg.append(b'end') # creation des threads client et serveur if self.equipment_type.get() == 'client': t = Thread(name='client_thread', target=self.client, args=( int(port_number), msg, mode, )) t.start() elif self.equipment_type.get() == 'server': t = Thread(name='server_thread', target=self.server, args=( int(port_number), msg, mode, )) t.start() def insertion_equipment(self, self_cert_received, mode): result = 'no' issuer = int(self_cert_received['id']) if mode == 'insert': if len(self.graph.nodes()) > 2: question = "Authorize Equipment {} to join our network ?".format( issuer) else: question = "Accept to join Equipment {}'s network ?".format( issuer) result = askquestion( 'Equipment {} - Connection'.format(self.__id_equipment), question) elif mode == 'sync': result = 'yes' if result == 'yes': cert = self.__e.generate_certificate( str(issuer), self_cert_received['cert'].public_key(), 10) byte_cert = cert.public_bytes(encoding=serialization.Encoding.PEM) if (self.__id_equipment, issuer) in self.graph.edges(): self.graph.remove_edge(self.__id_equipment, issuer) self.graph.add_edge(self.__id_equipment, issuer, cert=cert) return byte_cert else: txt = "No connection established" self.txt_box.delete("1.0", END) self.txt_box.insert(END, txt) return b'end' def do_we_know_id(self, byte_cert): cert_chain = [] try: byte_cert = byte_cert.encode('utf-8') except: pass cert_to_identify = x509.load_pem_x509_certificate( byte_cert, backend=default_backend()) # on fait verifier les certificat autosigne que l'on recoit self.__e.verify_certif(cert_to_identify, cert_to_identify.public_key()) issuer_name = normalize_issuer_name(cert_to_identify) if issuer_name in self.graph.nodes(): print('Id {} found in graph of id {}'.format( issuer_name, self.__id_equipment)) shortest_path = nx.shortest_path(self.graph, source=issuer_name, target=self.__id_equipment, method='dijkstra') for i in range(0, len(shortest_path) - 1): cert = self.graph.get_edge_data(shortest_path[i], shortest_path[i + 1], 0)['cert'] byte_cert = cert.public_bytes( encoding=serialization.Encoding.PEM) cert_chain.append(byte_cert) else: print('Id {} NOT found in graph of id {}'.format( issuer_name, self.__id_equipment)) return cert_chain def server(self, num_port, msg, mode): host = '' tmp_self_cert_received = None main_connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) main_connection.bind((host, num_port)) main_connection.listen(1) print("Server listen on port {}".format(num_port)) connected_clients = [] i = 0 state = '' self_cert_received = {} tmp_da = [] cert_chain_other_equipment = [] equipment_known = False client_finished = False server_finished = False while not client_finished or not server_finished: connexions_demandees, wlist, xlist = select.select( [main_connection], [], [], 0.05) for connexion in connexions_demandees: connection_with_client, connection_information = connexion.accept( ) connected_clients.append(connection_with_client) try: clients_a_lire, wlist, xlist = select.select( connected_clients, [], [], 0.05) except select.error: pass else: for client in clients_a_lire: # reception message received_msg = client.recv(2048) try: received_msg = received_msg.decode() except: pass # handle if mesage = string -> change state if received_msg == 'stop': print('Id {} received STOP'.format( self.__id_equipment)) self.graph.remove_node(self_cert_received['id']) server_finished = True client_finished = True client.send('stop'.encode()) state = 'end' if received_msg == 'self_cert': state = 'self_cert' elif received_msg == 'proof': state = 'proof' if not equipment_known: cert_to_use = msg[i - 1] self_cert_received = self.verify_self_cert_received( received_msg=tmp_self_cert_received) cert_chain = self.do_we_know_id( byte_cert=cert_to_use) if cert_chain: equipment_known = True msg = [msg[k] for k in range(i)] + [ 'proof' ] + msg[msg.index(b'end') + 1:] for y in range(len(cert_chain)): msg.insert(i + 1 + y, cert_chain[y]) elif received_msg == 'cert': if mode == 'insert': state = 'cert' elif mode == 'sync': if self.verify_proof(cert_chain_other_equipment, self_cert_received, cert_to_use): state = 'cert' else: print('Client not known or unverified') msg.insert(i, 'stop') state = 'end' elif received_msg == 'da': state = 'da' elif received_msg == 'end': client_finished = True state = 'end' # if receive = byte object else: if state == 'self_cert' and mode == 'insert': self_cert_received = self.verify_self_cert_received( received_msg=received_msg) elif state == 'self_cert' and mode == 'sync': if tmp_self_cert_received is None: tmp_self_cert_received = received_msg elif not equipment_known: cert_chain = self.do_we_know_id( byte_cert=received_msg) if cert_chain: cert_to_use = received_msg equipment_known = True msg = [msg[k] for k in range(0, i)] + [ 'proof' ] + msg[msg.index(b'end') + 1:] for y in range(len(cert_chain)): msg.insert(i + 1 + y, cert_chain[y]) self_cert_received = self.verify_self_cert_received( received_msg=tmp_self_cert_received) elif state == 'proof': if not cert_chain: msg.insert(i, 'stop') else: cert_chain_other_equipment.append(received_msg) elif state == 'cert': self.state_cert( received_msg=received_msg, self_cert_received=self_cert_received) elif state == 'da': tmp_da.append(received_msg) # send message if server_finished: msg_to_send = 'No more message to send' else: if msg[i] == b'end': server_finished = True msg_to_send = msg[i] if msg_to_send == 'cert': msg.insert( i + 1, self.insertion_equipment( self_cert_received, mode)) if msg[i + 1] == b'end': msg_to_send = b'stop' state = 'end' try: msg_to_send = msg_to_send.encode() except: pass client.send(msg_to_send) i += 1 if tmp_da: self.state_da(received_msg=tmp_da) print("Closing connections") for client in connected_clients: client.close() main_connection.close() def client(self, num_port, msg, mode): host = 'localhost' tmp_self_cert_received = None connection_with_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) connection_with_server.connect((host, num_port)) print("Connexion established with server on port {}".format(num_port)) self_cert_received = {} tmp_da = [] cert_chain_other_equipment = [] state = '' client_finished = False server_finished = False i = 0 equipment_known = False while not client_finished or not server_finished: # Sending messages if client_finished: msg_to_send = 'No more message to send' else: if msg[i] == b'end': client_finished = True msg_to_send = msg[i] if msg_to_send == 'cert': msg.insert( i + 1, self.insertion_equipment(self_cert_received, mode)) if msg[i + 1] == b'end': msg_to_send = b'stop' state = 'end' try: msg_to_send = msg_to_send.encode() except: pass connection_with_server.send(msg_to_send) # handle messages reception received_msg = connection_with_server.recv(2048) try: received_msg = received_msg.decode() except: pass # change state if receive str if received_msg == 'stop': print('Id {} received STOP'.format(self.__id_equipment)) self.graph.remove_node(self_cert_received['id']) server_finished = True client_finished = True connection_with_server.send('stop'.encode()) state = 'end' if received_msg == 'self_cert': state = 'self_cert' elif received_msg == 'proof': state = 'proof' if not equipment_known: cert_to_use = msg[i] self_cert_received = self.verify_self_cert_received( received_msg=tmp_self_cert_received) cert_chain = self.do_we_know_id(byte_cert=cert_to_use) if cert_chain: msg = [msg[k] for k in range(i + 1) ] + ['proof'] + msg[msg.index(b'end') + 1:] equipment_known = True for y in range(len(cert_chain)): msg.insert(i + 2 + y, cert_chain[y]) elif received_msg == 'cert': if mode == 'insert': state = 'cert' elif mode == 'sync': if self.verify_proof(cert_chain_other_equipment, self_cert_received, cert_to_use): state = 'cert' else: print('Server not known or unverified') msg.insert(i + 1, 'stop') state = 'end' elif received_msg == 'da': state = 'da' elif received_msg == 'end': server_finished = True state = 'end' # receive data (depends on state) else: if state == 'self_cert' and mode == 'insert': self_cert_received = self.verify_self_cert_received( received_msg=received_msg) elif state == 'self_cert' and mode == 'sync': if tmp_self_cert_received is None: tmp_self_cert_received = received_msg # elif because if the equipment knows the new equipment (and not the other way around), # the new equipment couldn't find a path to equipment elif not equipment_known: cert_chain = self.do_we_know_id(byte_cert=received_msg) if cert_chain: cert_to_use = received_msg equipment_known = True msg = [msg[k] for k in range(i + 1) ] + ['proof'] + msg[msg.index(b'end') + 1:] for y in range(len(cert_chain)): msg.insert(i + 2 + y, cert_chain[y]) self_cert_received = self.verify_self_cert_received( received_msg=tmp_self_cert_received) elif state == 'proof': if not cert_chain: msg.insert(i + 1, 'stop') else: cert_chain_other_equipment.append(received_msg) elif state == 'cert': self.state_cert(received_msg=received_msg, self_cert_received=self_cert_received) elif state == 'da': tmp_da.append(received_msg) i += 1 time.sleep(0.1) # To give time to server to receive messages if tmp_da: self.state_da(received_msg=tmp_da) print('Closing client') connection_with_server.close() def verify_proof(self, cert_chain, self_cert_received, byte_cert_of_the_common_id): chain_verified = True try: byte_cert_of_the_common_id = byte_cert_of_the_common_id.encode( 'utf-8') except: pass # recuperation de la clef publique de l'autorite racine pubkey_of_the_common_id = x509.load_pem_x509_certificate( byte_cert_of_the_common_id, backend=default_backend()).public_key() previous_pubkey = pubkey_of_the_common_id # verification de la chaine de certification en extrayant la clef publique du precedent certificat for byte_cert in cert_chain: cert = x509.load_pem_x509_certificate(byte_cert.encode('utf-8'), backend=default_backend()) try: self.__e.verify_certif(cert, previous_pubkey) previous_pubkey = cert.public_key() except InvalidSignature: print('Certificate invalid {}'.format(cert)) chain_verified = False # verification que la derniere clef publique qui porte sur l'element que l'on veut synchroniser try: self.__e.verify_certif(self_cert_received['cert'], previous_pubkey) except InvalidSignature: print('Not the good self pubkey') chain_verified = False if chain_verified: print('Cert chain verified in id {}'.format(self.__id_equipment)) return chain_verified def verify_self_cert_received(self, received_msg): # appelle uniquement apres avoir verifier la chaine de certification pour le mode sync # pour ne pas ajouter un node en trop """ :param received_msg: receive a self_certificate from another equipment - verify it - add a node in the graph :return: {'id': subject, 'pubkey': pubkey, 'cert': cert} """ cert = x509.load_pem_x509_certificate(received_msg.encode('utf-8'), backend=default_backend()) byte_pubkey = cert.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) self_cert_received = None try: cert.public_key().verify(signature=cert.signature, data=cert.tbs_certificate_bytes, padding=padding.PKCS1v15(), algorithm=cert.signature_hash_algorithm) subject = normalize_subject_name(cert) if subject not in self.graph.nodes(): self.graph.add_node(subject, cert=cert) else: self.graph.nodes()[subject]['cert'] = cert self_cert_received = { 'id': subject, 'pubkey': byte_pubkey, 'cert': cert } except InvalidSignature: print('ERROR : Self certificate unverified') return self_cert_received def state_cert(self, received_msg, self_cert_received): """ :param received_msg: receive a certificate on our public key :param self_cert_received: the self cert received from the other equipment - verify it - add a edge in the graph :return: """ cert = x509.load_pem_x509_certificate(received_msg.encode('utf-8'), backend=default_backend()) subject_pubkey = load_pem_public_key(self_cert_received['pubkey'], backend=default_backend()) issuer = normalize_issuer_name(cert) subject = normalize_subject_name(cert) self.__e.verify_certif(cert, subject_pubkey) # remove puis add pour remettre un nouveau certif avec nouvelle date de peremption if (subject, issuer) in self.graph.edges(): self.graph.remove_edge(subject, issuer) self.graph.add_edge(subject, issuer, cert=cert) def state_da(self, received_msg): """ add all graph of the other equipment while verifying every cert we receive with a public key we already know in our graph :param received_msg: :return: """ while received_msg: for rcv_msg in received_msg: cert_to_check = x509.load_pem_x509_certificate( rcv_msg.encode('utf-8'), backend=default_backend()) issuer = normalize_issuer_name(cert_to_check) subject = normalize_subject_name(cert_to_check) # on ajoute noeuds et cert if issuer == subject: # si le node n'existe pas dans le graph if subject not in self.graph.nodes(): self.__e.verify_certif(cert_to_check, cert_to_check.public_key()) self.graph.add_node(subject, cert=cert_to_check) # ou si le noeud existe déjà mais qu'il n'y a pas le certificat associe elif self.graph.nodes()[subject] == {}: self.__e.verify_certif(cert_to_check, cert_to_check.public_key()) self.graph.add_node(subject, cert=cert_to_check) received_msg.remove(rcv_msg) else: # on travaille sur les edges if (subject, issuer) in self.graph.edges(): received_msg.remove(rcv_msg) else: try: # dans le cas où self.graph.nodes[subject]['cert'] est vide cert = self.graph.nodes[subject]['cert'] self.__e.verify_certif(cert_to_check, cert.public_key()) self.graph.add_edge(subject, issuer, cert=cert_to_check) received_msg.remove(rcv_msg) except: pass