def send_handshake(self, peer): self._sc.log.debug('send_handshake %s', peer._address) peer._mx.acquire() try: # TODO: Drain peer._recv_messages if not peer._recv_messages.empty(): self._sc.log.warning( 'send_handshake %s - Receive queue dumped.', peer._address) while not peer._recv_messages.empty(): peer._recv_messages.get(False) msg = MsgHandshake() msg._timestamp = int(time.time()) key_r = rfc6979_hmac_sha256_generate(self._csprng, 32) k = PrivateKey(key_r) msg._ephem_pk = PublicKey.from_secret(key_r).format() self.check_handshake_ephem_key(peer, msg._timestamp, msg._ephem_pk) ss = k.ecdh(peer._pubkey) hashed = hashlib.sha512( ss + struct.pack('>Q', msg._timestamp)).digest() peer._ke = hashed[:32] peer._km = hashed[32:] nonce = peer._km[24:] payload = self._sc._version nk = PrivateKey(self._network_key) sig = nk.sign_recoverable(peer._km) payload += sig aad = msg.encode_aad() aad += nonce cipher = ChaCha20_Poly1305.new(key=peer._ke, nonce=nonce) cipher.update(aad) msg._ct, msg._mac = cipher.encrypt_and_digest(payload) peer._sent_nonce = hashlib.sha256(nonce + msg._mac).digest() peer._recv_nonce = hashlib.sha256(peer._km).digest() # Init nonce peer._last_handshake_at = msg._timestamp peer._ready = False # Wait for peer to complete handshake self.send_msg(peer, msg) finally: peer._mx.release()
def test_ecdh(self): a = PrivateKey() b = PrivateKey() assert a.ecdh(b.public_key.format()) == b.ecdh(a.public_key.format())
def process_handshake(self, peer, msg_mv): self._sc.log.debug('process_handshake %s', peer._address) # TODO: Drain peer._recv_messages if not peer._recv_messages.empty(): self._sc.log.warning( 'process_handshake %s - Receive queue dumped.', peer._address) while not peer._recv_messages.empty(): peer._recv_messages.get(False) msg = MsgHandshake() msg.decode(msg_mv) try: now = int(time.time()) if now - peer._last_handshake_at < 30: raise ValueError('Too many handshakes from peer %s', peer._address) if abs(msg._timestamp - now) > TIMESTAMP_LEEWAY: raise ValueError('Bad handshake timestamp from peer %s', peer._address) self.check_handshake_ephem_key(peer, msg._timestamp, msg._ephem_pk, direction=2) nk = PrivateKey(self._network_key) ss = nk.ecdh(msg._ephem_pk) hashed = hashlib.sha512( ss + struct.pack('>Q', msg._timestamp)).digest() peer._ke = hashed[:32] peer._km = hashed[32:] nonce = peer._km[24:] aad = msg.encode_aad() aad += nonce cipher = ChaCha20_Poly1305.new(key=peer._ke, nonce=nonce) cipher.update(aad) plaintext = cipher.decrypt_and_verify( msg._ct, msg._mac) # Will raise error if mac doesn't match peer._version = plaintext[:6] sig = plaintext[6:] pk_peer = PublicKey.from_signature_and_message(sig, peer._km) # TODO: Should pk_peer be linked to public data? peer._pubkey = pk_peer.format() peer._recv_nonce = hashlib.sha256(nonce + msg._mac).digest() peer._sent_nonce = hashlib.sha256(peer._km).digest() # Init nonce peer._last_handshake_at = msg._timestamp peer._ready = True # Schedule a ping to complete the handshake, TODO: Send here? peer._last_ping_at = 0 except Exception as e: # TODO: misbehaving self._sc.log.debug('[rm] process_handshake %s', str(e))