def send_authentication(self, remote_node): """ 1. initiator generates ecdhe-random and nonce and creates auth 2. initiator connects to remote and sends auth Handshake for connecting to Known Peer eciesEncrypt(remote-pubk, sign(privkey, token^nonce) || 0x80 || ecdhe-random || nonce ) Handshake for connecting to New Peer eciesEncrypt(remote-pubk, sign(privkey, nonce) || 0x80 || ecdhe-random || nonce ) The value 0x80 is a placeholder which maybe used in the future for versioning and/or protocol handshake. """ self.ephemeral_ecc = ECCx() # FIXME, add seed ecdhe_pubkey = self.ephemeral_ecc.get_pubkey() assert len(ecdhe_pubkey) == 512 / 8 token = remote_node.token nonce = ienc(random.randint(0, 2**256 - 1)) assert len(nonce) == 32 token_or_nonce = token or nonce signature = self.node.sign(ienc(token_or_nonce)) assert len(signature) == 65 payload = signature + '0x80' + ecdhe_pubkey + token_or_nonce auth_message = crypto.encrypt(payload, remote.pubkey) self.peer.send(auth_message) self._authentication_sent = True
def dump_Peers(self, peers): """ :param peers: a sequence of (ip, port, pid) :return: None if no peers """ data = [self.cmd_map_by_name["Peers"]] for ip, port, pid in peers: ip = list((ienc(int(x)) for x in ip.split("."))) data.append([ip, port, pid]) return self.dump_packet(data)
def list_ienc(lst): "recursively big endian encode all integers in a list" nlst = [] for i, e in enumerate(lst): if isinstance(e, list): nlst.append(list_ienc(e)) elif isinstance(e, int): nlst.append(ienc(e)) else: nlst.append(e) return nlst
def receive_authentication(self, other, ciphertext): """ Verification (function, upon receive of PresetAuthentication): 3. remote generates ecdhe-random and nonce and creates auth 4. remote receives auth and decrypts (ECIES performs authentication before decryption) - If address is known, lookup token and public key to be authenticated - derive signature-message = sha3(token || addr^addrRemote) - success -> AcknowledgeAuthentication 5. remote sends auth 6. remote derives shared-secret, aes-secret, mac-secret, ingress-mac, egress-mac """ # eciesEncrypt(remote-pubk, sign(privkey, token^nonce) || 0x80 || ecdhe-random || nonce ) data = self.node.decrypt(ciphertext) assert len(data) == 64 + 1 + 64 + 32 signature = data[:65] assert data[65] == '0x80' remote_ecdhe_pubkey = data[65:65 + 64] token_or_nonce = idec(data[-32:]) # verify signature if not self.node.verify(signature, token_or_nonce): return self.disconnect() # recover remote pubkey remote_pubkey = ecdsa_recover(token_or_nonce, signature) # lookup pubkey and related token token_database = dict() # FIXME token = token_database.get(remote_pubkey, None) if token and token != token_or_nonce: # something fishy # FIXME reset node reputation pass remote_nonce = token_or_nonce # send authentication if not yet if not self._authentication_sent: remote_node = RemoteNode(remote_pubkey) # FIXME LOOKUP self.send_authentication(remote_node) # - success -> AcknowledgeAuthentication self.acknowledge_authentication(other, remote_pubkey, remote_ecdhe_pubkey) # ecdhe_shared_secret = ecdh.agree(ecdhe-random, ecdhe-random-public) # Compute public key with the local private key and return a 512bits shared key ecdhe_shared_secret = self.ephemeral_ecc.get_ecdh_key(remote_pubkey) ecdhe_pubkey = ephemeral_ecc.get_pubkey() # shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || remote-nonce)) shared_secret = sha3(ecdhe_shared_secret + sha3(ienc(self.nonce) + ienc(remote_nonce))) self.aes_secret = sha3_256(ecdhe_shared_secret + shared_secret) self.mac_secret = sha3_256(ecdhe_shared_secret + self.aes_secret) # egress-mac = sha3(mac-secret^nonce || auth) self.egress_mac = sha3_256( sxor(self.mac_secret, self.nonce) + ciphertext) # ingress-mac = sha3(mac-secret^remote-nonce || auth) self.ingress_mac = sha3_256( sxor(self.mac_secret, remote_nonce) + ciphertext) self.token = sha3(shared_secret) iv = pyelliptic.Cipher.gen_IV('aes-256-ctr') self.aes_enc = pyelliptic.Cipher(self.aes_secret, iv, 1, ciphername='aes-256-ctr') self.aes_dec = pyelliptic.Cipher(self.aes_secret, iv, 0, ciphername='aes-256-ctr') self.is_ready = True