Browse Source

lnpeer: obfuscate error pakets of forwarded htlcs, that we

propageate back to the sender.

lnworker: in htlc_fulfilled and htlc_failed, return early if the
htlc was forwarded, so that we do not trigger invoice callbacks
master
ThomasV 2 years ago
parent
commit
4c42840c1c
  1. 9
      electrum/lnonion.py
  2. 6
      electrum/lnpeer.py
  3. 18
      electrum/lnworker.py

9
electrum/lnonion.py

@ -396,7 +396,7 @@ class OnionRoutingFailure(Exception):
def construct_onion_error(
reason: OnionRoutingFailure,
onion_packet: OnionPacket,
their_public_key: bytes,
our_onion_private_key: bytes,
) -> bytes:
# create payload
@ -409,11 +409,14 @@ def construct_onion_error(
error_packet += pad_len.to_bytes(2, byteorder="big")
error_packet += bytes(pad_len)
# add hmac
shared_secret = get_ecdh(our_onion_private_key, onion_packet.public_key)
shared_secret = get_ecdh(our_onion_private_key, their_public_key)
um_key = get_bolt04_onion_key(b'um', shared_secret)
hmac_ = hmac_oneshot(um_key, msg=error_packet, digest=hashlib.sha256)
error_packet = hmac_ + error_packet
# obfuscate
return error_packet
def obfuscate_onion_error(error_packet, their_public_key, our_onion_private_key):
shared_secret = get_ecdh(our_onion_private_key, their_public_key)
ammag_key = get_bolt04_onion_key(b'ammag', shared_secret)
stream_bytes = generate_cipher_stream(ammag_key, len(error_packet))
error_packet = xor_bytes(error_packet, stream_bytes)

6
electrum/lnpeer.py

@ -28,7 +28,7 @@ from .bitcoin import make_op_return, DummyAddress
from .transaction import PartialTxOutput, match_script_against_template, Sighash
from .logging import Logger
from .lnonion import (new_onion_packet, OnionFailureCode, calc_hops_data_for_payment,
process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailure,
process_onion_packet, OnionPacket, construct_onion_error, obfuscate_onion_error, OnionRoutingFailure,
ProcessedOnionPacket, UnsupportedOnionPacketVersion, InvalidOnionMac, InvalidOnionPubkey,
OnionFailureCodeMetaFlag)
from .lnchannel import Channel, RevokeAndAck, RemoteCtnTooFarInFuture, ChannelState, PeerState, ChanCloseOption, CF_ANNOUNCE_CHANNEL
@ -2405,7 +2405,9 @@ class Peer(Logger):
onion_packet_bytes=onion_packet_bytes,
onion_packet=onion_packet)
except OnionRoutingFailure as e:
error_bytes = construct_onion_error(e, onion_packet, our_onion_private_key=self.privkey)
error_bytes = construct_onion_error(e, onion_packet.public_key, our_onion_private_key=self.privkey)
if error_bytes:
error_bytes = obfuscate_onion_error(error_bytes, onion_packet.public_key, our_onion_private_key=self.privkey)
if fw_info:
unfulfilled[htlc_id] = local_ctn, remote_ctn, onion_packet_hex, fw_info
self.lnworker.downstream_htlc_to_upstream_peer_map[fw_info] = self.pubkey

18
electrum/lnworker.py

@ -2281,23 +2281,24 @@ class LNWallet(LNWorker):
info = info._replace(status=status)
self.save_payment_info(info)
def _on_maybe_forwarded_htlc_resolved(self, chan: Channel, htlc_id: int) -> None:
def is_forwarded_htlc_notify(self, chan: Channel, htlc_id: int) -> None:
"""Called when an HTLC we offered on chan gets irrevocably fulfilled or failed.
If we find this was a forwarded HTLC, the upstream peer is notified.
"""
fw_info = chan.short_channel_id.hex(), htlc_id
upstream_peer_pubkey = self.downstream_htlc_to_upstream_peer_map.get(fw_info)
if not upstream_peer_pubkey:
return
return False
upstream_peer = self.peers.get(upstream_peer_pubkey)
if not upstream_peer:
return
upstream_peer.downstream_htlc_resolved_event.set()
upstream_peer.downstream_htlc_resolved_event.clear()
if upstream_peer:
upstream_peer.downstream_htlc_resolved_event.set()
upstream_peer.downstream_htlc_resolved_event.clear()
return True
def htlc_fulfilled(self, chan: Channel, payment_hash: bytes, htlc_id: int):
util.trigger_callback('htlc_fulfilled', payment_hash, chan, htlc_id)
self._on_maybe_forwarded_htlc_resolved(chan=chan, htlc_id=htlc_id)
if self.is_forwarded_htlc_notify(chan=chan, htlc_id=htlc_id):
return
q = None
if shi := self.sent_htlcs_info.get((payment_hash, chan.short_channel_id, htlc_id)):
chan.pop_onion_key(htlc_id)
@ -2328,7 +2329,8 @@ class LNWallet(LNWorker):
failure_message: Optional['OnionRoutingFailure']):
util.trigger_callback('htlc_failed', payment_hash, chan, htlc_id)
self._on_maybe_forwarded_htlc_resolved(chan=chan, htlc_id=htlc_id)
if self.is_forwarded_htlc_notify(chan=chan, htlc_id=htlc_id):
return
q = None
if shi := self.sent_htlcs_info.get((payment_hash, chan.short_channel_id, htlc_id)):
onion_key = chan.pop_onion_key(htlc_id)

Loading…
Cancel
Save