Skip to content

Commit a90e2d9

Browse files
andrewshvvRoasbeef
authored andcommitted
sphinx: add variables for specification errors
In order to properly handle errors during decoding and parsing process the distinct variables of errors have been created.
1 parent 91a1fcd commit a90e2d9

File tree

4 files changed

+113
-75
lines changed

4 files changed

+113
-75
lines changed

error.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,17 @@ var (
77
// during processing due to being an attempted replay or probing
88
// attempt.
99
ErrReplayedPacket = fmt.Errorf("sphinx packet replay attempted")
10+
11+
// ErrInvalidOnionVersion is returned during decoding of the onion
12+
// packet, when the received packet has an unknown version byte.
13+
ErrInvalidOnionVersion = fmt.Errorf("invalid onion packet version")
14+
15+
// ErrInvalidOnionHMAC is returned during onion parsing process, when received
16+
// mac does not corresponds to the generated one.
17+
ErrInvalidOnionHMAC = fmt.Errorf("invalid mismathed mac")
18+
19+
// ErrInvalidOnionKey is returned during onion parsing process, when
20+
// onion key is invalid.
21+
ErrInvalidOnionKey = fmt.Errorf("invalid onion key: pubkey isn't on " +
22+
"secp256k1 curve")
1023
)

obfuscation.go

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package sphinx
22

33
import (
4-
"bytes"
54
"crypto/hmac"
65
"crypto/sha256"
76
"errors"
@@ -11,6 +10,7 @@ import (
1110
)
1211

1312
// onionObfuscation obfuscates the data with compliance with BOLT#4.
13+
//
1414
// In context of Lightning Network this function is used by sender to obfuscate
1515
// the onion failure and by receiver to unwrap the failure data.
1616
func onionObfuscation(sharedSecret [sha256.Size]byte,
@@ -23,7 +23,9 @@ func onionObfuscation(sharedSecret [sha256.Size]byte,
2323
}
2424

2525
// OnionObfuscator represent serializable object which is able to convert the
26-
// data to the obfuscated blob.
26+
// data to the obfuscated blob, by applying the stream of data generated by
27+
// the shared secret.
28+
//
2729
// In context of Lightning Network the obfuscated data is usually a failure
2830
// which will be propagated back to payment sender, and obfuscated by the
2931
// forwarding nodes.
@@ -45,7 +47,8 @@ func NewOnionObfuscator(router *Router, ephemeralKey *btcec.PublicKey) (*OnionOb
4547
}, nil
4648
}
4749

48-
// Obfuscate is used to make data obfuscation.
50+
// Obfuscate is used to make data obfuscation using the generated shared secret.
51+
//
4952
// In context of Lightning Network is either used by the nodes in order to
5053
// make initial obfuscation with the creation of the hmac or by the forwarding
5154
// nodes for backward failure obfuscation of the onion failure blob. By
@@ -78,54 +81,18 @@ func (o *OnionObfuscator) Encode(w io.Writer) error {
7881
return err
7982
}
8083

81-
// OnionDeobfuscator represents the serializable object which encapsulate the
82-
// all necessary data to properly de-obfuscate previously obfuscated data.
83-
// In context of Lightning Network the data which have to be deobfuscated
84-
// usually is onion failure.
85-
type OnionDeobfuscator struct {
86-
sessionKey *btcec.PrivateKey
87-
paymentPath []*btcec.PublicKey
88-
}
84+
// Circuit is used encapsulate the data which is needed for data deobfuscation.
85+
type Circuit struct {
86+
// SessionKey is the key which have been used during generation of the
87+
// shared secrets.
88+
SessionKey *btcec.PrivateKey
8989

90-
// NewOnionDeobfuscator creates new instance of onion deobfuscator.
91-
func NewOnionDeobfuscator(sessionKey *btcec.PrivateKey,
92-
paymentPath []*btcec.PublicKey) (*OnionDeobfuscator, error) {
93-
return &OnionDeobfuscator{
94-
sessionKey: sessionKey,
95-
paymentPath: paymentPath,
96-
}, nil
90+
// PaymentPath is the pub keys of the nodes in the payment path.
91+
PaymentPath []*btcec.PublicKey
9792
}
9893

99-
// Deobfuscate makes data deobfuscation. The onion failure is obfuscated in
100-
// backward manner, starting from the node where error have occurred, so in
101-
// order to deobfuscate the error we need get all shared secret and apply
102-
// obfuscation in reverse order.
103-
func (o *OnionDeobfuscator) Deobfuscate(obfuscatedData []byte) (*btcec.PublicKey,
104-
[]byte, error) {
105-
for i, sharedSecret := range generateSharedSecrets(o.paymentPath,
106-
o.sessionKey) {
107-
obfuscatedData = onionObfuscation(sharedSecret, obfuscatedData)
108-
umKey := generateKey("um", sharedSecret)
109-
110-
// Split the data and hmac.
111-
expectedMac := obfuscatedData[:sha256.Size]
112-
data := obfuscatedData[sha256.Size:]
113-
114-
// Calculate the real hmac.
115-
h := hmac.New(sha256.New, umKey[:])
116-
h.Write(data)
117-
realMac := h.Sum(nil)
118-
119-
if bytes.Equal(realMac, expectedMac) {
120-
return o.paymentPath[i], data, nil
121-
}
122-
}
123-
124-
return nil, nil, errors.New("unable to retrieve onion failure")
125-
}
126-
127-
// Decode initializes the deobfuscator from the byte stream.
128-
func (o *OnionDeobfuscator) Decode(r io.Reader) error {
94+
// Decode initializes the circuit from the byte stream.
95+
func (c *Circuit) Decode(r io.Reader) error {
12996
var keyLength [1]byte
13097
if _, err := r.Read(keyLength[:]); err != nil {
13198
return err
@@ -136,14 +103,14 @@ func (o *OnionDeobfuscator) Decode(r io.Reader) error {
136103
return err
137104
}
138105

139-
o.sessionKey, _ = btcec.PrivKeyFromBytes(btcec.S256(), sessionKeyData)
106+
c.SessionKey, _ = btcec.PrivKeyFromBytes(btcec.S256(), sessionKeyData)
140107
var pathLength [1]byte
141108
if _, err := r.Read(pathLength[:]); err != nil {
142109
return err
143110
}
144-
o.paymentPath = make([]*btcec.PublicKey, uint8(pathLength[0]))
111+
c.PaymentPath = make([]*btcec.PublicKey, uint8(pathLength[0]))
145112

146-
for i := 0; i < len(o.paymentPath); i++ {
113+
for i := 0; i < len(c.PaymentPath); i++ {
147114
var pubKeyData [btcec.PubKeyBytesLenCompressed]byte
148115
if _, err := r.Read(pubKeyData[:]); err != nil {
149116
return err
@@ -153,35 +120,88 @@ func (o *OnionDeobfuscator) Decode(r io.Reader) error {
153120
if err != nil {
154121
return err
155122
}
156-
o.paymentPath[i] = pubKey
123+
c.PaymentPath[i] = pubKey
157124
}
158125

159126
return nil
160127
}
161128

162-
// Encode writes converted deobfuscator in the byte stream.
163-
func (o *OnionDeobfuscator) Encode(w io.Writer) error {
129+
// Encode writes converted circuit in the byte stream.
130+
func (c *Circuit) Encode(w io.Writer) error {
164131
var keyLength [1]byte
165-
keyLength[0] = uint8(len(o.sessionKey.Serialize()))
132+
keyLength[0] = uint8(len(c.SessionKey.Serialize()))
166133
if _, err := w.Write(keyLength[:]); err != nil {
167134
return err
168135
}
169136

170-
if _, err := w.Write(o.sessionKey.Serialize()); err != nil {
137+
if _, err := w.Write(c.SessionKey.Serialize()); err != nil {
171138
return err
172139
}
173140

174141
var pathLength [1]byte
175-
pathLength[0] = uint8(len(o.paymentPath))
142+
pathLength[0] = uint8(len(c.PaymentPath))
176143
if _, err := w.Write(pathLength[:]); err != nil {
177144
return err
178145
}
179146

180-
for _, pubKey := range o.paymentPath {
147+
for _, pubKey := range c.PaymentPath {
181148
if _, err := w.Write(pubKey.SerializeCompressed()); err != nil {
182149
return err
183150
}
184151
}
185152

186153
return nil
187154
}
155+
156+
// OnionDeobfuscator represents the serializable object which encapsulate the
157+
// all necessary data to properly de-obfuscate previously obfuscated data.
158+
// In context of Lightning Network the data which have to be deobfuscated
159+
// usually is onion failure.
160+
type OnionDeobfuscator struct {
161+
circuit *Circuit
162+
}
163+
164+
// NewOnionDeobfuscator creates new instance of onion deobfuscator.
165+
func NewOnionDeobfuscator(circuit *Circuit) *OnionDeobfuscator {
166+
return &OnionDeobfuscator{
167+
circuit: circuit,
168+
}
169+
}
170+
171+
// Deobfuscate makes data deobfuscation. The onion failure is obfuscated in
172+
// backward manner, starting from the node where error have occurred, so in
173+
// order to deobfuscate the error we need get all shared secret and apply
174+
// obfuscation in reverse order.
175+
func (o *OnionDeobfuscator) Deobfuscate(obfuscatedData []byte) (*btcec.PublicKey,
176+
[]byte, error) {
177+
for i, sharedSecret := range generateSharedSecrets(o.circuit.PaymentPath,
178+
o.circuit.SessionKey) {
179+
obfuscatedData = onionObfuscation(sharedSecret, obfuscatedData)
180+
umKey := generateKey("um", sharedSecret)
181+
182+
// Split the data and hmac.
183+
expectedMac := obfuscatedData[:sha256.Size]
184+
data := obfuscatedData[sha256.Size:]
185+
186+
// Calculate the real hmac.
187+
h := hmac.New(sha256.New, umKey[:])
188+
h.Write(data)
189+
realMac := h.Sum(nil)
190+
191+
if hmac.Equal(realMac, expectedMac) {
192+
return o.circuit.PaymentPath[i], data, nil
193+
}
194+
}
195+
196+
return nil, nil, errors.New("unable to retrieve onion failure")
197+
}
198+
199+
// Decode writes converted deobfucator in the byte stream.
200+
func (o *OnionDeobfuscator) Decode(r io.Reader) error {
201+
return o.circuit.Decode(r)
202+
}
203+
204+
// Encode writes converted deobfucator in the byte stream.
205+
func (o *OnionDeobfuscator) Encode(w io.Writer) error {
206+
return o.circuit.Encode(w)
207+
}

obfuscation_test.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ func TestOnionFailure(t *testing.T) {
5050
}
5151

5252
// Emulate creation of the deobfuscator on the receiving onion error side.
53-
deobfuscator := &OnionDeobfuscator{
54-
sessionKey: sessionKey,
55-
paymentPath: paymentPath,
56-
}
53+
deobfuscator := NewOnionDeobfuscator(&Circuit{
54+
SessionKey: sessionKey,
55+
PaymentPath: paymentPath,
56+
})
5757

5858
// Emulate that sender node receive the failure message and trying to
5959
// unwrap it, by applying obfuscation and checking the hmac.
@@ -124,7 +124,6 @@ var onionErrorData = []struct {
124124
"1a62081df0ed46d4a3df295da0b0fe25c0115019f03f15ec86fa" +
125125
"bb4c852f83449e812f141a93",
126126
},
127-
128127
{
129128
sharedSecret: "3a6b412548762f0dbccce5c7ae7bb8147d1caf9b5471c3" +
130129
"4120b30bc9c04891cc",
@@ -145,7 +144,6 @@ var onionErrorData = []struct {
145144
"9b8e6faa83b81fbfa6133c0af5d6b07c017f7158fa94f0d206ba" +
146145
"f12dda6b68f785b773b360fd",
147146
},
148-
149147
{
150148
sharedSecret: "a6519e98832a0b179f62123b3567c106db99ee37bef036" +
151149
"e783263602f3488fae",
@@ -166,7 +164,6 @@ var onionErrorData = []struct {
166164
"de7084bb95b3ac2345201d624d31f4d52078aa0fa05a88b4e202" +
167165
"02bd2b86ac5b52919ea305a8",
168166
},
169-
170167
{
171168
sharedSecret: "53eb63ea8a3fec3b3cd433b85cd62a4b145e1dda09391b" +
172169
"348c4e1cd36a03ea66",
@@ -294,10 +291,10 @@ func TestOnionFailureSpecVector(t *testing.T) {
294291
}
295292
}
296293

297-
deobfuscator := &OnionDeobfuscator{
298-
sessionKey: sessionKey,
299-
paymentPath: paymentPath,
300-
}
294+
deobfuscator := NewOnionDeobfuscator(&Circuit{
295+
SessionKey: sessionKey,
296+
PaymentPath: paymentPath,
297+
})
301298

302299
// Emulate that sender node receives the failure message and trying to
303300
// unwrap it, by applying obfuscation and checking the hmac.

sphinx.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"crypto/hmac"
77
"crypto/sha256"
88
"encoding/binary"
9-
"fmt"
109
"io"
1110
"io/ioutil"
1211
"sync"
@@ -63,6 +62,9 @@ const (
6362
// encrypt payloads. Since we use SHA256 to generate the keys, the
6463
// maximum length currently is 32 bytes.
6564
keyLen = 32
65+
66+
// baseVersion represent the current supported version of onion packet.
67+
baseVersion = 0
6668
)
6769

6870
var (
@@ -199,6 +201,8 @@ func (hd *HopData) Decode(r io.Reader) error {
199201
return nil
200202
}
201203

204+
// generateSharedSecrets by the given nodes pubkeys, generates the shared
205+
// secrets.
202206
func generateSharedSecrets(paymentPath []*btcec.PublicKey,
203207
sessionKey *btcec.PrivateKey) [][sha256.Size]byte {
204208
// Each hop performs ECDH with our ephemeral key pair to arrive at a
@@ -319,7 +323,7 @@ func NewOnionPacket(paymentPath []*btcec.PublicKey, sessionKey *btcec.PrivateKey
319323
}
320324

321325
return &OnionPacket{
322-
Version: 0x01,
326+
Version: baseVersion,
323327
EphemeralKey: sessionKey.PubKey(),
324328
RoutingInfo: mixHeader,
325329
HeaderMAC: nextHmac,
@@ -401,6 +405,12 @@ func (f *OnionPacket) Decode(r io.Reader) error {
401405
}
402406
f.Version = buf[0]
403407

408+
// If version of the onion packet protocol unknown for us than in might
409+
// lead to improperly decoded data.
410+
if f.Version != baseVersion {
411+
return ErrInvalidOnionVersion
412+
}
413+
404414
var ephemeral [33]byte
405415
if _, err := io.ReadFull(r, ephemeral[:]); err != nil {
406416
return err
@@ -636,7 +646,6 @@ func NewRouter(nodeKey *btcec.PrivateKey, net *chaincfg.Params) *Router {
636646
// returned which houses the newly parsed packet, along with instructions on
637647
// what to do next.
638648
func (r *Router) ProcessOnionPacket(onionPkt *OnionPacket, assocData []byte) (*ProcessedPacket, error) {
639-
640649
dhKey := onionPkt.EphemeralKey
641650
routeInfo := onionPkt.RoutingInfo
642651
headerMac := onionPkt.HeaderMAC
@@ -662,8 +671,7 @@ func (r *Router) ProcessOnionPacket(onionPkt *OnionPacket, assocData []byte) (*P
662671
message := append(routeInfo[:], assocData...)
663672
calculatedMac := calcMac(generateKey("mu", sharedSecret), message)
664673
if !hmac.Equal(headerMac[:], calculatedMac[:]) {
665-
return nil, fmt.Errorf("MAC mismatch %x != %x, rejecting "+
666-
"forwarding message", headerMac, calculatedMac)
674+
return nil, ErrInvalidOnionHMAC
667675
}
668676

669677
// The MAC checks out, mark this current shared secret as processed in
@@ -732,7 +740,7 @@ func (r *Router) generateSharedSecret(dhKey *btcec.PublicKey) ([sha256.Size]byte
732740

733741
// Ensure that the public key is on our curve.
734742
if !r.onionKey.Curve.IsOnCurve(dhKey.X, dhKey.Y) {
735-
return sharedSecret, fmt.Errorf("pubkey isn't on secp256k1 curve")
743+
return sharedSecret, ErrInvalidOnionKey
736744
}
737745

738746
// Compute our shared secret.

0 commit comments

Comments
 (0)