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:
Olaoluwa Osuntokun 2016-10-27 20:40:08 -07:00
parent ac43de94f6
commit f37956e38e
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
5 changed files with 70 additions and 35 deletions

17
glide.lock generated
View File

@ -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: []

View File

@ -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

View File

@ -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
View File

@ -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")

View File

@ -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
}