mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
routing: update Sphinx API to include r-hash and per-hop-payload
This commit modifies both the Sphinx packet generation and processing for recent updates to the API. With the version 1 Sphinx specification, the payment hash is now included in the MACs in order to thwart any potential replay attacks. As a result, any attempts to replay previous HTLC packets MUST re-use the same payment hash, meaning that the first-hop node can simply settle the HTLC immediately, thwarting the attacker. Additionally, within the Sphinx packet, each hop now gets a per-hop payload which contains the necessary details (CTLV value, fee, etc) for the node to successfully forward the payment. This per-hop payload is protected by a packet-wide MAC.
This commit is contained in:
parent
ac43de94f6
commit
f37956e38e
17
glide.lock
generated
17
glide.lock
generated
@ -1,5 +1,5 @@
|
||||
hash: 0bdf51a0e40c8cba475333645a769b711e699be36803b02f3b14430642ab79b2
|
||||
updated: 2016-10-17T19:44:40.047044856-07:00
|
||||
hash: 2106ce14ff53c14d3d0d3d8f34e1cf01c01a79eef409ffe871cd5783b77939c8
|
||||
updated: 2016-10-27T20:34:19.347013604-07:00
|
||||
imports:
|
||||
- name: github.com/aead/chacha20
|
||||
version: 7e1038a97ad08a9a16cb88ed7a6778b366ba4d99
|
||||
@ -61,6 +61,8 @@ imports:
|
||||
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/go-errors/errors
|
||||
version: a41850380601eeb43f4350f7d17c6bbd8944aaf8
|
||||
- name: github.com/golang/protobuf
|
||||
version: 98fa357170587e470c5f27d3c3ea0947b71eb455
|
||||
subpackages:
|
||||
@ -77,7 +79,7 @@ imports:
|
||||
- name: github.com/howeyc/gopass
|
||||
version: f5387c492211eb133053880d23dfae62aa14123d
|
||||
- name: github.com/lightningnetwork/lightning-onion
|
||||
version: 81647ffa2c5e17c0447d359e1963a54e18be85c4
|
||||
version: f38a054899049d1f5bdb6550c17724060384161b
|
||||
- name: github.com/roasbeef/btcd
|
||||
version: baea7691cc3c59480703fe1a3fb5595c838c963c
|
||||
subpackages:
|
||||
@ -120,7 +122,7 @@ imports:
|
||||
- name: github.com/urfave/cli
|
||||
version: a14d7d367bc02b1f57d88de97926727f2d936387
|
||||
- name: golang.org/x/crypto
|
||||
version: 5f31782cfb2b6373211f8f9fbf31283fa234b570
|
||||
version: ca7e7f10cb9fd9c1a6ff7f60436c086d73714180
|
||||
subpackages:
|
||||
- hkdf
|
||||
- nacl/secretbox
|
||||
@ -131,7 +133,7 @@ imports:
|
||||
- pbkdf2
|
||||
- ssh/terminal
|
||||
- name: golang.org/x/net
|
||||
version: 8b4af36cd21a1f85a7484b49feb7c79363106d8e
|
||||
version: b336a971b799939dd16ae9b1df8334cb8b977c4d
|
||||
subpackages:
|
||||
- context
|
||||
- http2
|
||||
@ -141,7 +143,7 @@ imports:
|
||||
- lex/httplex
|
||||
- internal/timeseries
|
||||
- name: golang.org/x/sys
|
||||
version: 9bb9f0998d48b31547d975974935ae9b48c7a03c
|
||||
version: c200b10b5d5e122be351b67af224adc6128af5bf
|
||||
subpackages:
|
||||
- unix
|
||||
- name: google.golang.org/grpc
|
||||
@ -155,7 +157,4 @@ imports:
|
||||
- naming
|
||||
- transport
|
||||
- peer
|
||||
- name: github.com/go-errors/errors
|
||||
version: a41850380601eeb43f4350f7d17c6bbd8944aaf8
|
||||
|
||||
testImports: []
|
||||
|
@ -57,6 +57,7 @@ import:
|
||||
- package: google.golang.org/grpc
|
||||
version: ^1.0.0
|
||||
- package: github.com/lightningnetwork/lightning-onion
|
||||
version: master
|
||||
- package: github.com/grpc-ecosystem/grpc-gateway
|
||||
version: ^1.1.0
|
||||
- package: github.com/aead/chacha20
|
||||
|
@ -57,6 +57,7 @@ type HTLCAddRequest struct {
|
||||
// and the shared secret is fresh, then the node should stip off a layer
|
||||
// of encryption, exposing the next hop to be used in the subsequent
|
||||
// HTLCAddRequest message.
|
||||
// TODO(roasbeef): can be fixed sized now that v1 Sphinx is "done".
|
||||
OnionBlob []byte
|
||||
}
|
||||
|
||||
|
16
peer.go
16
peer.go
@ -1109,13 +1109,24 @@ func (p *peer) handleUpstreamMsg(state *commitmentState, msg lnwire.Message) {
|
||||
p.Disconnect()
|
||||
return
|
||||
}
|
||||
sphinxPacket, err := state.sphinx.ProcessOnionPacket(onionPkt)
|
||||
|
||||
// Attempt to process the Sphinx packet. We include the payment
|
||||
// hash of the HTLC as it's authenticated within the Sphinx
|
||||
// packet itself as associated data in order to thwart attempts
|
||||
// a replay attacks. In the case of a replay, an attacker is
|
||||
// *forced* to use the same payment hash twice, thereby losing
|
||||
// their money entirely.
|
||||
rHash := htlcPkt.RedemptionHashes[0][:]
|
||||
sphinxPacket, err := state.sphinx.ProcessOnionPacket(onionPkt, rHash)
|
||||
if err != nil {
|
||||
peerLog.Errorf("unable to process onion pkt: %v", err)
|
||||
p.Disconnect()
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(roasbeef): perform sanity checks on per-hop payload
|
||||
// * time-lock is sane, fee, chain, etc
|
||||
|
||||
// We just received an add request from an upstream peer, so we
|
||||
// add it to our state machine, then add the HTLC to our
|
||||
// "settle" list in the event that we know the pre-image
|
||||
@ -1144,7 +1155,8 @@ func (p *peer) handleUpstreamMsg(state *commitmentState, msg lnwire.Message) {
|
||||
// switch, we'll attach the routing information so the switch
|
||||
// can finalize the circuit.
|
||||
case sphinx.MoreHops:
|
||||
// TODO(roasbeef): send cancel + error if not in rounting table
|
||||
// TODO(roasbeef): send cancel + error if not in
|
||||
// routing table
|
||||
state.pendingCircuits[index] = sphinxPacket
|
||||
default:
|
||||
peerLog.Errorf("mal formed onion packet")
|
||||
|
70
rpcserver.go
70
rpcserver.go
@ -564,16 +564,6 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
|
||||
}
|
||||
rpcsLog.Tracef("[sendpayment] selected route: %v", path)
|
||||
|
||||
// Generate the raw encoded sphinx packet to be
|
||||
// included along with the HTLC add message.
|
||||
// We snip off the first hop from the path as within
|
||||
// the routing table's star graph, we're always the
|
||||
// first hop.
|
||||
sphinxPacket, err := generateSphinxPacket(path[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we're in debug HTLC mode, then all outgoing
|
||||
// HTLC's will pay to the same debug rHash. Otherwise,
|
||||
// we pay to the rHash specified within the RPC
|
||||
@ -585,6 +575,16 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
|
||||
copy(rHash[:], nextPayment.PaymentHash)
|
||||
}
|
||||
|
||||
// Generate the raw encoded sphinx packet to be
|
||||
// included along with the HTLC add message. We snip
|
||||
// off the first hop from the path as within the
|
||||
// routing table's star graph, we're always the first
|
||||
// hop.
|
||||
sphinxPacket, err := generateSphinxPacket(path[1:], rHash[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Craft an HTLC packet to send to the routing
|
||||
// sub-system. The meta-data within this packet will be
|
||||
// used to route the payment through the network.
|
||||
@ -606,10 +606,11 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
|
||||
// TODO(roasbeef): semaphore to limit num outstanding
|
||||
// goroutines.
|
||||
go func() {
|
||||
// Finally, send this next packet to the routing layer in order
|
||||
// to complete the next payment.
|
||||
// TODO(roasbeef): this should go through the L3 router once
|
||||
// multi-hop is in place.
|
||||
// Finally, send this next packet to the
|
||||
// routing layer in order to complete the next
|
||||
// payment.
|
||||
// TODO(roasbeef): this should go through the
|
||||
// L3 router once multi-hop is in place.
|
||||
if err := r.server.htlcSwitch.SendHTLC(htlcPkt); err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
@ -632,10 +633,11 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
|
||||
// the onion route specified by the passed list of graph vertexes. The blob
|
||||
// returned from this function can immediately be included within an HTLC add
|
||||
// packet to be sent to the first hop within the route.
|
||||
func generateSphinxPacket(vertexes []graph.ID) ([]byte, error) {
|
||||
var dest sphinx.LightningAddress
|
||||
e2eMessage := []byte("test")
|
||||
|
||||
func generateSphinxPacket(vertexes []graph.ID, paymentHash []byte) ([]byte, error) {
|
||||
// First convert all the vertexs from the routing table to in-memory
|
||||
// public key objects. These objects are necessary in order to perform
|
||||
// the series of ECDH operations required to construct the Sphinx
|
||||
// packet below.
|
||||
route := make([]*btcec.PublicKey, len(vertexes))
|
||||
for i, vertex := range vertexes {
|
||||
vertexBytes, err := hex.DecodeString(vertex.String())
|
||||
@ -651,15 +653,35 @@ func generateSphinxPacket(vertexes []graph.ID) ([]byte, error) {
|
||||
route[i] = pub
|
||||
}
|
||||
|
||||
// Next generate the onion routing packet which allows
|
||||
// us to perform privacy preserving source routing
|
||||
// across the network.
|
||||
var onionBlob bytes.Buffer
|
||||
sphinxPacket, err := sphinx.NewOnionPacket(route, dest,
|
||||
e2eMessage)
|
||||
// Next we generate the per-hop payload which gives each node within
|
||||
// the route the necessary information (fees, CLTV value, etc) to
|
||||
// properly forward the payment.
|
||||
// TODO(roasbeef): properly set CLTV value, payment amount, and chain
|
||||
// within hop paylods.
|
||||
var hopPayloads [][]byte
|
||||
for i := 0; i < len(route); i++ {
|
||||
payload := bytes.Repeat([]byte{byte('A' + i)},
|
||||
sphinx.HopPayloadSize)
|
||||
hopPayloads = append(hopPayloads, payload)
|
||||
}
|
||||
|
||||
sessionKey, err := btcec.NewPrivateKey(btcec.S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Next generate the onion routing packet which allows
|
||||
// us to perform privacy preserving source routing
|
||||
// across the network.
|
||||
sphinxPacket, err := sphinx.NewOnionPacket(route, sessionKey,
|
||||
hopPayloads, paymentHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Finally, encode Sphinx packet using it's wire represenation to be
|
||||
// included within the HTLC add packet.
|
||||
var onionBlob bytes.Buffer
|
||||
if err := sphinxPacket.Encode(&onionBlob); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user