2018-10-25 19:34:31 +02:00
|
|
|
# Copyright (C) 2018 Adam Gibson (waxwing)
|
|
|
|
|
# Copyright (C) 2018 The Electrum developers
|
|
|
|
|
# Distributed under the MIT software license, see the accompanying
|
|
|
|
|
# file LICENCE or http://www.opensource.org/licenses/mit-license.php
|
|
|
|
|
|
|
|
|
|
# Derived from https://gist.github.com/AdamISZ/046d05c156aaeb56cc897f85eecb3eb8
|
|
|
|
|
|
2018-10-15 11:05:53 +02:00
|
|
|
import hashlib
|
2019-02-01 20:21:59 +01:00
|
|
|
import asyncio
|
2018-10-22 15:35:57 +02:00
|
|
|
from asyncio import StreamReader, StreamWriter
|
2020-04-15 21:41:33 +02:00
|
|
|
from typing import Optional
|
lntransport: change name used in logs to make collisions unlikely
In particular, in the regtests, with incoming peers, we can have multiple transports open with the same node simultaneously
(see e.g. lnworker._request_force_close_from_backup).
We now use the first few bytes of peer_pubkey, as that is potentially familiar to users,
and the first few bytes of sha256(id(self)) to mitigate collisions in case the peer_pubkeys collide.
log excerpt:
```
I/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | handshake done for 030f0bf260acdbd3edcad84d7588ec7c5df4711e87e6a23016f989b8d3a4147230@163.172.94.64:9735
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Sending INIT
I/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | handshake done for 03933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134@34.250.234.192:9735
D/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | Sending INIT
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Received INIT
I/P | lnpeer.Peer.[LNWallet, 02651acf4a-79696c42] | handshake done for 02651acf4a7096091bf42baad19b3643ea318d6979f6dcc16ebaec43d5b0f4baf2@82.119.233.36:19735
D/P | lnpeer.Peer.[LNWallet, 02651acf4a-79696c42] | Sending INIT
D/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | Received INIT
I/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | saved remote_update
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Received CHANNEL_REESTABLISH
```
2022-02-16 18:53:24 +01:00
|
|
|
from functools import cached_property
|
2019-11-26 00:15:33 +01:00
|
|
|
|
2020-03-04 16:15:22 +01:00
|
|
|
from .crypto import sha256, hmac_oneshot, chacha20_poly1305_encrypt, chacha20_poly1305_decrypt
|
2018-10-22 15:35:57 +02:00
|
|
|
from .lnutil import (get_ecdh, privkey_to_pubkey, LightningPeerConnectionClosed,
|
2019-11-26 00:15:33 +01:00
|
|
|
HandshakeFailed, LNPeerAddr)
|
2018-10-15 11:05:53 +02:00
|
|
|
from . import ecc
|
2023-02-17 11:35:03 +00:00
|
|
|
from .util import MySocksProxy
|
2020-04-15 21:41:33 +02:00
|
|
|
|
2018-10-15 11:05:53 +02:00
|
|
|
|
|
|
|
|
class HandshakeState(object):
|
|
|
|
|
prologue = b"lightning"
|
|
|
|
|
protocol_name = b"Noise_XK_secp256k1_ChaChaPoly_SHA256"
|
|
|
|
|
handshake_version = b"\x00"
|
|
|
|
|
|
|
|
|
|
def __init__(self, responder_pub):
|
|
|
|
|
self.responder_pub = responder_pub
|
|
|
|
|
self.h = sha256(self.protocol_name)
|
|
|
|
|
self.ck = self.h
|
|
|
|
|
self.update(self.prologue)
|
|
|
|
|
self.update(self.responder_pub)
|
|
|
|
|
|
|
|
|
|
def update(self, data):
|
|
|
|
|
self.h = sha256(self.h + data)
|
|
|
|
|
return self.h
|
|
|
|
|
|
|
|
|
|
def get_nonce_bytes(n):
|
|
|
|
|
"""BOLT 8 requires the nonce to be 12 bytes, 4 bytes leading
|
|
|
|
|
zeroes and 8 bytes little endian encoded 64 bit integer.
|
|
|
|
|
"""
|
|
|
|
|
return b"\x00"*4 + n.to_bytes(8, 'little')
|
|
|
|
|
|
2018-11-22 21:05:41 +01:00
|
|
|
def aead_encrypt(key: bytes, nonce: int, associated_data: bytes, data: bytes) -> bytes:
|
2018-10-15 11:05:53 +02:00
|
|
|
nonce_bytes = get_nonce_bytes(nonce)
|
2020-03-04 16:15:22 +01:00
|
|
|
return chacha20_poly1305_encrypt(key=key,
|
|
|
|
|
nonce=nonce_bytes,
|
|
|
|
|
associated_data=associated_data,
|
|
|
|
|
data=data)
|
2018-10-15 11:05:53 +02:00
|
|
|
|
2018-11-22 21:05:41 +01:00
|
|
|
def aead_decrypt(key: bytes, nonce: int, associated_data: bytes, data: bytes) -> bytes:
|
2018-10-15 11:05:53 +02:00
|
|
|
nonce_bytes = get_nonce_bytes(nonce)
|
2020-03-04 16:15:22 +01:00
|
|
|
return chacha20_poly1305_decrypt(key=key,
|
|
|
|
|
nonce=nonce_bytes,
|
|
|
|
|
associated_data=associated_data,
|
|
|
|
|
data=data)
|
2018-10-15 11:05:53 +02:00
|
|
|
|
|
|
|
|
def get_bolt8_hkdf(salt, ikm):
|
|
|
|
|
"""RFC5869 HKDF instantiated in the specific form
|
|
|
|
|
used in Lightning BOLT 8:
|
|
|
|
|
Extract and expand to 64 bytes using HMAC-SHA256,
|
|
|
|
|
with info field set to a zero length string as per BOLT8
|
|
|
|
|
Return as two 32 byte fields.
|
|
|
|
|
"""
|
|
|
|
|
#Extract
|
2018-10-22 15:35:57 +02:00
|
|
|
prk = hmac_oneshot(salt, msg=ikm, digest=hashlib.sha256)
|
2018-10-15 11:05:53 +02:00
|
|
|
assert len(prk) == 32
|
|
|
|
|
#Expand
|
|
|
|
|
info = b""
|
|
|
|
|
T0 = b""
|
2018-10-22 15:35:57 +02:00
|
|
|
T1 = hmac_oneshot(prk, T0 + info + b"\x01", digest=hashlib.sha256)
|
|
|
|
|
T2 = hmac_oneshot(prk, T1 + info + b"\x02", digest=hashlib.sha256)
|
2018-10-15 11:05:53 +02:00
|
|
|
assert len(T1 + T2) == 64
|
|
|
|
|
return T1, T2
|
|
|
|
|
|
|
|
|
|
def act1_initiator_message(hs, epriv, epub):
|
|
|
|
|
ss = get_ecdh(epriv, hs.responder_pub)
|
|
|
|
|
ck2, temp_k1 = get_bolt8_hkdf(hs.ck, ss)
|
|
|
|
|
hs.ck = ck2
|
2018-10-16 17:45:28 +02:00
|
|
|
c = aead_encrypt(temp_k1, 0, hs.update(epub), b"")
|
2018-10-15 11:05:53 +02:00
|
|
|
#for next step if we do it
|
|
|
|
|
hs.update(c)
|
|
|
|
|
msg = hs.handshake_version + epub + c
|
|
|
|
|
assert len(msg) == 50
|
2018-10-16 17:45:28 +02:00
|
|
|
return msg, temp_k1
|
2018-10-15 11:05:53 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_ephemeral_key() -> (bytes, bytes):
|
|
|
|
|
privkey = ecc.ECPrivkey.generate_random_key()
|
|
|
|
|
return privkey.get_secret_bytes(), privkey.get_public_key_bytes()
|
|
|
|
|
|
2019-11-26 00:15:33 +01:00
|
|
|
|
2018-10-16 17:45:28 +02:00
|
|
|
class LNTransportBase:
|
2019-11-26 00:15:33 +01:00
|
|
|
reader: StreamReader
|
|
|
|
|
writer: StreamWriter
|
2020-04-13 15:57:53 +02:00
|
|
|
privkey: bytes
|
lntransport: change name used in logs to make collisions unlikely
In particular, in the regtests, with incoming peers, we can have multiple transports open with the same node simultaneously
(see e.g. lnworker._request_force_close_from_backup).
We now use the first few bytes of peer_pubkey, as that is potentially familiar to users,
and the first few bytes of sha256(id(self)) to mitigate collisions in case the peer_pubkeys collide.
log excerpt:
```
I/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | handshake done for 030f0bf260acdbd3edcad84d7588ec7c5df4711e87e6a23016f989b8d3a4147230@163.172.94.64:9735
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Sending INIT
I/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | handshake done for 03933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134@34.250.234.192:9735
D/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | Sending INIT
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Received INIT
I/P | lnpeer.Peer.[LNWallet, 02651acf4a-79696c42] | handshake done for 02651acf4a7096091bf42baad19b3643ea318d6979f6dcc16ebaec43d5b0f4baf2@82.119.233.36:19735
D/P | lnpeer.Peer.[LNWallet, 02651acf4a-79696c42] | Sending INIT
D/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | Received INIT
I/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | saved remote_update
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Received CHANNEL_REESTABLISH
```
2022-02-16 18:53:24 +01:00
|
|
|
peer_addr: Optional[LNPeerAddr] = None
|
2019-11-26 00:15:33 +01:00
|
|
|
|
|
|
|
|
def name(self) -> str:
|
lntransport: change name used in logs to make collisions unlikely
In particular, in the regtests, with incoming peers, we can have multiple transports open with the same node simultaneously
(see e.g. lnworker._request_force_close_from_backup).
We now use the first few bytes of peer_pubkey, as that is potentially familiar to users,
and the first few bytes of sha256(id(self)) to mitigate collisions in case the peer_pubkeys collide.
log excerpt:
```
I/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | handshake done for 030f0bf260acdbd3edcad84d7588ec7c5df4711e87e6a23016f989b8d3a4147230@163.172.94.64:9735
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Sending INIT
I/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | handshake done for 03933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134@34.250.234.192:9735
D/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | Sending INIT
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Received INIT
I/P | lnpeer.Peer.[LNWallet, 02651acf4a-79696c42] | handshake done for 02651acf4a7096091bf42baad19b3643ea318d6979f6dcc16ebaec43d5b0f4baf2@82.119.233.36:19735
D/P | lnpeer.Peer.[LNWallet, 02651acf4a-79696c42] | Sending INIT
D/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | Received INIT
I/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | saved remote_update
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Received CHANNEL_REESTABLISH
```
2022-02-16 18:53:24 +01:00
|
|
|
pubkey = self.remote_pubkey()
|
|
|
|
|
pubkey_hex = pubkey.hex() if pubkey else pubkey
|
|
|
|
|
return f"{pubkey_hex[:10]}-{self._id_hash[:8]}"
|
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
|
def _id_hash(self) -> str:
|
|
|
|
|
id_int = id(self)
|
|
|
|
|
id_bytes = id_int.to_bytes((id_int.bit_length() + 7) // 8, byteorder='big')
|
|
|
|
|
return sha256(id_bytes).hex()
|
2018-10-22 15:35:57 +02:00
|
|
|
|
2019-08-12 18:37:13 +02:00
|
|
|
def send_bytes(self, msg: bytes) -> None:
|
2018-10-15 11:05:53 +02:00
|
|
|
l = len(msg).to_bytes(2, 'big')
|
|
|
|
|
lc = aead_encrypt(self.sk, self.sn(), b'', l)
|
|
|
|
|
c = aead_encrypt(self.sk, self.sn(), b'', msg)
|
|
|
|
|
assert len(lc) == 18
|
|
|
|
|
assert len(c) == len(msg) + 16
|
|
|
|
|
self.writer.write(lc+c)
|
|
|
|
|
|
|
|
|
|
async def read_messages(self):
|
2021-03-21 22:19:37 +01:00
|
|
|
buffer = bytearray()
|
2018-10-15 11:05:53 +02:00
|
|
|
while True:
|
|
|
|
|
rn_l, rk_l = self.rn()
|
|
|
|
|
rn_m, rk_m = self.rn()
|
|
|
|
|
while True:
|
2021-03-21 22:19:37 +01:00
|
|
|
if len(buffer) >= 18:
|
|
|
|
|
lc = bytes(buffer[:18])
|
2018-10-15 11:05:53 +02:00
|
|
|
l = aead_decrypt(rk_l, rn_l, b'', lc)
|
|
|
|
|
length = int.from_bytes(l, 'big')
|
|
|
|
|
offset = 18 + length + 16
|
2021-03-21 22:19:37 +01:00
|
|
|
if len(buffer) >= offset:
|
|
|
|
|
c = bytes(buffer[18:offset])
|
|
|
|
|
del buffer[:offset] # much faster than: buffer=buffer[offset:]
|
2018-10-15 11:05:53 +02:00
|
|
|
msg = aead_decrypt(rk_m, rn_m, b'', c)
|
|
|
|
|
yield msg
|
|
|
|
|
break
|
|
|
|
|
try:
|
|
|
|
|
s = await self.reader.read(2**10)
|
2021-02-10 19:40:10 +01:00
|
|
|
except Exception:
|
2018-10-15 11:05:53 +02:00
|
|
|
s = None
|
|
|
|
|
if not s:
|
|
|
|
|
raise LightningPeerConnectionClosed()
|
2021-03-21 22:19:37 +01:00
|
|
|
buffer += s
|
2018-10-15 11:05:53 +02:00
|
|
|
|
2018-10-16 17:45:28 +02:00
|
|
|
def rn(self):
|
|
|
|
|
o = self._rn, self.rk
|
|
|
|
|
self._rn += 1
|
|
|
|
|
if self._rn == 1000:
|
|
|
|
|
self.r_ck, self.rk = get_bolt8_hkdf(self.r_ck, self.rk)
|
|
|
|
|
self._rn = 0
|
|
|
|
|
return o
|
|
|
|
|
|
|
|
|
|
def sn(self):
|
|
|
|
|
o = self._sn
|
|
|
|
|
self._sn += 1
|
|
|
|
|
if self._sn == 1000:
|
|
|
|
|
self.s_ck, self.sk = get_bolt8_hkdf(self.s_ck, self.sk)
|
|
|
|
|
self._sn = 0
|
|
|
|
|
return o
|
|
|
|
|
|
|
|
|
|
def init_counters(self, ck):
|
|
|
|
|
# init counters
|
|
|
|
|
self._sn = 0
|
|
|
|
|
self._rn = 0
|
|
|
|
|
self.r_ck = ck
|
|
|
|
|
self.s_ck = ck
|
|
|
|
|
|
2018-10-22 15:35:57 +02:00
|
|
|
def close(self):
|
|
|
|
|
self.writer.close()
|
|
|
|
|
|
2021-03-29 21:29:51 +02:00
|
|
|
def remote_pubkey(self) -> Optional[bytes]:
|
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
2018-10-22 15:35:57 +02:00
|
|
|
|
2018-10-16 17:45:28 +02:00
|
|
|
class LNResponderTransport(LNTransportBase):
|
2020-04-15 16:40:16 +02:00
|
|
|
"""Transport initiated by remote party."""
|
|
|
|
|
|
2018-10-22 15:35:57 +02:00
|
|
|
def __init__(self, privkey: bytes, reader: StreamReader, writer: StreamWriter):
|
2019-02-01 20:21:59 +01:00
|
|
|
LNTransportBase.__init__(self)
|
|
|
|
|
self.reader = reader
|
|
|
|
|
self.writer = writer
|
2018-10-16 17:45:28 +02:00
|
|
|
self.privkey = privkey
|
2021-03-29 21:29:51 +02:00
|
|
|
self._pubkey = None # remote pubkey
|
2018-10-16 17:45:28 +02:00
|
|
|
|
lntransport: change name used in logs to make collisions unlikely
In particular, in the regtests, with incoming peers, we can have multiple transports open with the same node simultaneously
(see e.g. lnworker._request_force_close_from_backup).
We now use the first few bytes of peer_pubkey, as that is potentially familiar to users,
and the first few bytes of sha256(id(self)) to mitigate collisions in case the peer_pubkeys collide.
log excerpt:
```
I/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | handshake done for 030f0bf260acdbd3edcad84d7588ec7c5df4711e87e6a23016f989b8d3a4147230@163.172.94.64:9735
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Sending INIT
I/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | handshake done for 03933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134@34.250.234.192:9735
D/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | Sending INIT
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Received INIT
I/P | lnpeer.Peer.[LNWallet, 02651acf4a-79696c42] | handshake done for 02651acf4a7096091bf42baad19b3643ea318d6979f6dcc16ebaec43d5b0f4baf2@82.119.233.36:19735
D/P | lnpeer.Peer.[LNWallet, 02651acf4a-79696c42] | Sending INIT
D/P | lnpeer.Peer.[LNWallet, 03933884aa-5e5dce45] | Received INIT
I/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | saved remote_update
D/P | lnpeer.Peer.[LNWallet, 030f0bf260-e0b33756] | Received CHANNEL_REESTABLISH
```
2022-02-16 18:53:24 +01:00
|
|
|
def name(self) -> str:
|
|
|
|
|
return f"{super().name()}(in)"
|
2019-02-01 20:21:59 +01:00
|
|
|
|
2018-10-16 17:45:28 +02:00
|
|
|
async def handshake(self, **kwargs):
|
|
|
|
|
hs = HandshakeState(privkey_to_pubkey(self.privkey))
|
|
|
|
|
act1 = b''
|
|
|
|
|
while len(act1) < 50:
|
2020-09-24 01:52:35 +02:00
|
|
|
buf = await self.reader.read(50 - len(act1))
|
|
|
|
|
if not buf:
|
|
|
|
|
raise HandshakeFailed('responder disconnected')
|
|
|
|
|
act1 += buf
|
2018-10-16 17:45:28 +02:00
|
|
|
if len(act1) != 50:
|
|
|
|
|
raise HandshakeFailed('responder: short act 1 read, length is ' + str(len(act1)))
|
|
|
|
|
if bytes([act1[0]]) != HandshakeState.handshake_version:
|
|
|
|
|
raise HandshakeFailed('responder: bad handshake version in act 1')
|
|
|
|
|
c = act1[-16:]
|
|
|
|
|
re = act1[1:34]
|
|
|
|
|
h = hs.update(re)
|
|
|
|
|
ss = get_ecdh(self.privkey, re)
|
|
|
|
|
ck, temp_k1 = get_bolt8_hkdf(sha256(HandshakeState.protocol_name), ss)
|
|
|
|
|
_p = aead_decrypt(temp_k1, 0, h, c)
|
|
|
|
|
hs.update(c)
|
|
|
|
|
|
|
|
|
|
# act 2
|
|
|
|
|
if 'epriv' not in kwargs:
|
|
|
|
|
epriv, epub = create_ephemeral_key()
|
|
|
|
|
else:
|
|
|
|
|
epriv = kwargs['epriv']
|
|
|
|
|
epub = ecc.ECPrivkey(epriv).get_public_key_bytes()
|
|
|
|
|
hs.ck = ck
|
|
|
|
|
hs.responder_pub = re
|
|
|
|
|
|
|
|
|
|
msg, temp_k2 = act1_initiator_message(hs, epriv, epub)
|
|
|
|
|
self.writer.write(msg)
|
|
|
|
|
|
|
|
|
|
# act 3
|
|
|
|
|
act3 = b''
|
|
|
|
|
while len(act3) < 66:
|
2020-09-24 01:52:35 +02:00
|
|
|
buf = await self.reader.read(66 - len(act3))
|
|
|
|
|
if not buf:
|
|
|
|
|
raise HandshakeFailed('responder disconnected')
|
|
|
|
|
act3 += buf
|
2018-10-16 17:45:28 +02:00
|
|
|
if len(act3) != 66:
|
|
|
|
|
raise HandshakeFailed('responder: short act 3 read, length is ' + str(len(act3)))
|
|
|
|
|
if bytes([act3[0]]) != HandshakeState.handshake_version:
|
|
|
|
|
raise HandshakeFailed('responder: bad handshake version in act 3')
|
|
|
|
|
c = act3[1:50]
|
|
|
|
|
t = act3[-16:]
|
|
|
|
|
rs = aead_decrypt(temp_k2, 1, hs.h, c)
|
|
|
|
|
ss = get_ecdh(epriv, rs)
|
|
|
|
|
ck, temp_k3 = get_bolt8_hkdf(hs.ck, ss)
|
|
|
|
|
_p = aead_decrypt(temp_k3, 0, hs.update(c), t)
|
|
|
|
|
self.rk, self.sk = get_bolt8_hkdf(ck, b'')
|
|
|
|
|
self.init_counters(ck)
|
2021-03-29 21:29:51 +02:00
|
|
|
self._pubkey = rs
|
2018-10-16 17:45:28 +02:00
|
|
|
return rs
|
|
|
|
|
|
2021-03-29 21:29:51 +02:00
|
|
|
def remote_pubkey(self) -> Optional[bytes]:
|
|
|
|
|
return self._pubkey
|
|
|
|
|
|
2020-04-15 16:40:16 +02:00
|
|
|
|
2018-10-16 17:45:28 +02:00
|
|
|
class LNTransport(LNTransportBase):
|
2020-04-15 16:40:16 +02:00
|
|
|
"""Transport initiated by local party."""
|
2019-02-01 20:21:59 +01:00
|
|
|
|
2020-04-15 21:41:33 +02:00
|
|
|
def __init__(self, privkey: bytes, peer_addr: LNPeerAddr, *,
|
|
|
|
|
proxy: Optional[dict]):
|
2019-02-01 20:21:59 +01:00
|
|
|
LNTransportBase.__init__(self)
|
2018-10-16 17:45:28 +02:00
|
|
|
assert type(privkey) is bytes and len(privkey) == 32
|
|
|
|
|
self.privkey = privkey
|
2019-02-02 08:53:06 +01:00
|
|
|
self.peer_addr = peer_addr
|
2020-04-15 21:41:33 +02:00
|
|
|
self.proxy = MySocksProxy.from_proxy_dict(proxy)
|
2019-02-01 20:21:59 +01:00
|
|
|
|
2018-10-15 11:05:53 +02:00
|
|
|
async def handshake(self):
|
2020-04-15 21:41:33 +02:00
|
|
|
if not self.proxy:
|
|
|
|
|
self.reader, self.writer = await asyncio.open_connection(self.peer_addr.host, self.peer_addr.port)
|
|
|
|
|
else:
|
|
|
|
|
self.reader, self.writer = await self.proxy.open_connection(self.peer_addr.host, self.peer_addr.port)
|
2019-11-26 00:15:33 +01:00
|
|
|
hs = HandshakeState(self.peer_addr.pubkey)
|
2018-10-15 11:05:53 +02:00
|
|
|
# Get a new ephemeral key
|
|
|
|
|
epriv, epub = create_ephemeral_key()
|
|
|
|
|
|
2018-10-16 17:45:28 +02:00
|
|
|
msg, _temp_k1 = act1_initiator_message(hs, epriv, epub)
|
2018-10-15 11:05:53 +02:00
|
|
|
# act 1
|
|
|
|
|
self.writer.write(msg)
|
|
|
|
|
rspns = await self.reader.read(2**10)
|
|
|
|
|
if len(rspns) != 50:
|
2019-11-26 00:15:33 +01:00
|
|
|
raise HandshakeFailed(f"Lightning handshake act 1 response has bad length, "
|
|
|
|
|
f"are you sure this is the right pubkey? {self.peer_addr}")
|
2018-10-15 11:05:53 +02:00
|
|
|
hver, alice_epub, tag = rspns[0], rspns[1:34], rspns[34:]
|
|
|
|
|
if bytes([hver]) != hs.handshake_version:
|
|
|
|
|
raise HandshakeFailed("unexpected handshake version: {}".format(hver))
|
|
|
|
|
# act 2
|
|
|
|
|
hs.update(alice_epub)
|
|
|
|
|
ss = get_ecdh(epriv, alice_epub)
|
|
|
|
|
ck, temp_k2 = get_bolt8_hkdf(hs.ck, ss)
|
|
|
|
|
hs.ck = ck
|
|
|
|
|
p = aead_decrypt(temp_k2, 0, hs.h, tag)
|
|
|
|
|
hs.update(tag)
|
|
|
|
|
# act 3
|
|
|
|
|
my_pubkey = privkey_to_pubkey(self.privkey)
|
|
|
|
|
c = aead_encrypt(temp_k2, 1, hs.h, my_pubkey)
|
|
|
|
|
hs.update(c)
|
|
|
|
|
ss = get_ecdh(self.privkey[:32], alice_epub)
|
|
|
|
|
ck, temp_k3 = get_bolt8_hkdf(hs.ck, ss)
|
|
|
|
|
hs.ck = ck
|
|
|
|
|
t = aead_encrypt(temp_k3, 0, hs.h, b'')
|
|
|
|
|
msg = hs.handshake_version + c + t
|
|
|
|
|
self.writer.write(msg)
|
2018-10-16 17:45:28 +02:00
|
|
|
self.sk, self.rk = get_bolt8_hkdf(hs.ck, b'')
|
|
|
|
|
self.init_counters(ck)
|
2021-03-29 21:29:51 +02:00
|
|
|
|
|
|
|
|
def remote_pubkey(self) -> Optional[bytes]:
|
|
|
|
|
return self.peer_addr.pubkey
|