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
|
hash: 2106ce14ff53c14d3d0d3d8f34e1cf01c01a79eef409ffe871cd5783b77939c8
|
||||||
updated: 2016-10-17T19:44:40.047044856-07:00
|
updated: 2016-10-27T20:34:19.347013604-07:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/aead/chacha20
|
- name: github.com/aead/chacha20
|
||||||
version: 7e1038a97ad08a9a16cb88ed7a6778b366ba4d99
|
version: 7e1038a97ad08a9a16cb88ed7a6778b366ba4d99
|
||||||
@ -61,6 +61,8 @@ imports:
|
|||||||
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
|
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
|
||||||
subpackages:
|
subpackages:
|
||||||
- spew
|
- spew
|
||||||
|
- name: github.com/go-errors/errors
|
||||||
|
version: a41850380601eeb43f4350f7d17c6bbd8944aaf8
|
||||||
- name: github.com/golang/protobuf
|
- name: github.com/golang/protobuf
|
||||||
version: 98fa357170587e470c5f27d3c3ea0947b71eb455
|
version: 98fa357170587e470c5f27d3c3ea0947b71eb455
|
||||||
subpackages:
|
subpackages:
|
||||||
@ -77,7 +79,7 @@ imports:
|
|||||||
- name: github.com/howeyc/gopass
|
- name: github.com/howeyc/gopass
|
||||||
version: f5387c492211eb133053880d23dfae62aa14123d
|
version: f5387c492211eb133053880d23dfae62aa14123d
|
||||||
- name: github.com/lightningnetwork/lightning-onion
|
- name: github.com/lightningnetwork/lightning-onion
|
||||||
version: 81647ffa2c5e17c0447d359e1963a54e18be85c4
|
version: f38a054899049d1f5bdb6550c17724060384161b
|
||||||
- name: github.com/roasbeef/btcd
|
- name: github.com/roasbeef/btcd
|
||||||
version: baea7691cc3c59480703fe1a3fb5595c838c963c
|
version: baea7691cc3c59480703fe1a3fb5595c838c963c
|
||||||
subpackages:
|
subpackages:
|
||||||
@ -120,7 +122,7 @@ imports:
|
|||||||
- name: github.com/urfave/cli
|
- name: github.com/urfave/cli
|
||||||
version: a14d7d367bc02b1f57d88de97926727f2d936387
|
version: a14d7d367bc02b1f57d88de97926727f2d936387
|
||||||
- name: golang.org/x/crypto
|
- name: golang.org/x/crypto
|
||||||
version: 5f31782cfb2b6373211f8f9fbf31283fa234b570
|
version: ca7e7f10cb9fd9c1a6ff7f60436c086d73714180
|
||||||
subpackages:
|
subpackages:
|
||||||
- hkdf
|
- hkdf
|
||||||
- nacl/secretbox
|
- nacl/secretbox
|
||||||
@ -131,7 +133,7 @@ imports:
|
|||||||
- pbkdf2
|
- pbkdf2
|
||||||
- ssh/terminal
|
- ssh/terminal
|
||||||
- name: golang.org/x/net
|
- name: golang.org/x/net
|
||||||
version: 8b4af36cd21a1f85a7484b49feb7c79363106d8e
|
version: b336a971b799939dd16ae9b1df8334cb8b977c4d
|
||||||
subpackages:
|
subpackages:
|
||||||
- context
|
- context
|
||||||
- http2
|
- http2
|
||||||
@ -141,7 +143,7 @@ imports:
|
|||||||
- lex/httplex
|
- lex/httplex
|
||||||
- internal/timeseries
|
- internal/timeseries
|
||||||
- name: golang.org/x/sys
|
- name: golang.org/x/sys
|
||||||
version: 9bb9f0998d48b31547d975974935ae9b48c7a03c
|
version: c200b10b5d5e122be351b67af224adc6128af5bf
|
||||||
subpackages:
|
subpackages:
|
||||||
- unix
|
- unix
|
||||||
- name: google.golang.org/grpc
|
- name: google.golang.org/grpc
|
||||||
@ -155,7 +157,4 @@ imports:
|
|||||||
- naming
|
- naming
|
||||||
- transport
|
- transport
|
||||||
- peer
|
- peer
|
||||||
- name: github.com/go-errors/errors
|
|
||||||
version: a41850380601eeb43f4350f7d17c6bbd8944aaf8
|
|
||||||
|
|
||||||
testImports: []
|
testImports: []
|
||||||
|
@ -57,6 +57,7 @@ import:
|
|||||||
- package: google.golang.org/grpc
|
- package: google.golang.org/grpc
|
||||||
version: ^1.0.0
|
version: ^1.0.0
|
||||||
- package: github.com/lightningnetwork/lightning-onion
|
- package: github.com/lightningnetwork/lightning-onion
|
||||||
|
version: master
|
||||||
- package: github.com/grpc-ecosystem/grpc-gateway
|
- package: github.com/grpc-ecosystem/grpc-gateway
|
||||||
version: ^1.1.0
|
version: ^1.1.0
|
||||||
- package: github.com/aead/chacha20
|
- 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
|
// 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
|
// of encryption, exposing the next hop to be used in the subsequent
|
||||||
// HTLCAddRequest message.
|
// HTLCAddRequest message.
|
||||||
|
// TODO(roasbeef): can be fixed sized now that v1 Sphinx is "done".
|
||||||
OnionBlob []byte
|
OnionBlob []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
peer.go
16
peer.go
@ -1109,13 +1109,24 @@ func (p *peer) handleUpstreamMsg(state *commitmentState, msg lnwire.Message) {
|
|||||||
p.Disconnect()
|
p.Disconnect()
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
peerLog.Errorf("unable to process onion pkt: %v", err)
|
peerLog.Errorf("unable to process onion pkt: %v", err)
|
||||||
p.Disconnect()
|
p.Disconnect()
|
||||||
return
|
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
|
// We just received an add request from an upstream peer, so we
|
||||||
// add it to our state machine, then add the HTLC to our
|
// add it to our state machine, then add the HTLC to our
|
||||||
// "settle" list in the event that we know the pre-image
|
// "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
|
// switch, we'll attach the routing information so the switch
|
||||||
// can finalize the circuit.
|
// can finalize the circuit.
|
||||||
case sphinx.MoreHops:
|
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
|
state.pendingCircuits[index] = sphinxPacket
|
||||||
default:
|
default:
|
||||||
peerLog.Errorf("mal formed onion packet")
|
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)
|
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
|
// If we're in debug HTLC mode, then all outgoing
|
||||||
// HTLC's will pay to the same debug rHash. Otherwise,
|
// HTLC's will pay to the same debug rHash. Otherwise,
|
||||||
// we pay to the rHash specified within the RPC
|
// 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)
|
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
|
// Craft an HTLC packet to send to the routing
|
||||||
// sub-system. The meta-data within this packet will be
|
// sub-system. The meta-data within this packet will be
|
||||||
// used to route the payment through the network.
|
// 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
|
// TODO(roasbeef): semaphore to limit num outstanding
|
||||||
// goroutines.
|
// goroutines.
|
||||||
go func() {
|
go func() {
|
||||||
// Finally, send this next packet to the routing layer in order
|
// Finally, send this next packet to the
|
||||||
// to complete the next payment.
|
// routing layer in order to complete the next
|
||||||
// TODO(roasbeef): this should go through the L3 router once
|
// payment.
|
||||||
// multi-hop is in place.
|
// TODO(roasbeef): this should go through the
|
||||||
|
// L3 router once multi-hop is in place.
|
||||||
if err := r.server.htlcSwitch.SendHTLC(htlcPkt); err != nil {
|
if err := r.server.htlcSwitch.SendHTLC(htlcPkt); err != nil {
|
||||||
errChan <- err
|
errChan <- err
|
||||||
return
|
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
|
// 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
|
// returned from this function can immediately be included within an HTLC add
|
||||||
// packet to be sent to the first hop within the route.
|
// packet to be sent to the first hop within the route.
|
||||||
func generateSphinxPacket(vertexes []graph.ID) ([]byte, error) {
|
func generateSphinxPacket(vertexes []graph.ID, paymentHash []byte) ([]byte, error) {
|
||||||
var dest sphinx.LightningAddress
|
// First convert all the vertexs from the routing table to in-memory
|
||||||
e2eMessage := []byte("test")
|
// 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))
|
route := make([]*btcec.PublicKey, len(vertexes))
|
||||||
for i, vertex := range vertexes {
|
for i, vertex := range vertexes {
|
||||||
vertexBytes, err := hex.DecodeString(vertex.String())
|
vertexBytes, err := hex.DecodeString(vertex.String())
|
||||||
@ -651,15 +653,35 @@ func generateSphinxPacket(vertexes []graph.ID) ([]byte, error) {
|
|||||||
route[i] = pub
|
route[i] = pub
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next generate the onion routing packet which allows
|
// Next we generate the per-hop payload which gives each node within
|
||||||
// us to perform privacy preserving source routing
|
// the route the necessary information (fees, CLTV value, etc) to
|
||||||
// across the network.
|
// properly forward the payment.
|
||||||
var onionBlob bytes.Buffer
|
// TODO(roasbeef): properly set CLTV value, payment amount, and chain
|
||||||
sphinxPacket, err := sphinx.NewOnionPacket(route, dest,
|
// within hop paylods.
|
||||||
e2eMessage)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if err := sphinxPacket.Encode(&onionBlob); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user