Skip to content

Commit 7d1f799

Browse files
committed
sphinx: return error source as integer
Returning as an integer allows a node to occur in the payment path multiple times.
1 parent 751fb4d commit 7d1f799

File tree

2 files changed

+54
-20
lines changed

2 files changed

+54
-20
lines changed

crypto.go

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ const (
2222
// the output of a SHA256 hash.
2323
type Hash256 [sha256.Size]byte
2424

25+
// DecryptedError contains the decrypted error message and its sender.
26+
type DecryptedError struct {
27+
// Sender is the node that sent the error. Note that a node may occur in
28+
// the path multiple times. If that is the case, the sender pubkey does
29+
// not tell the caller on which visit the error occurred.
30+
Sender *btcec.PublicKey
31+
32+
// SenderIdx is the position of the error sending node in the path.
33+
// Index zero is the self node. SenderIdx allows to distinguish between
34+
// errors from nodes that occur in the path multiple times.
35+
SenderIdx int
36+
37+
// Message is the decrypted error message.
38+
Message []byte
39+
}
40+
2541
// zeroHMAC is the special HMAC value that allows the final node to determine
2642
// if it is the payment destination or not.
2743
var zeroHMAC [HMACSize]byte
@@ -171,11 +187,15 @@ const onionErrorLength = 2 + 2 + 256 + sha256.Size
171187
// DecryptError attempts to decrypt the passed encrypted error response. The
172188
// onion failure is encrypted in backward manner, starting from the node where
173189
// error have occurred. As a result, in order to decrypt the error we need get
174-
// all shared secret and apply decryption in the reverse order.
175-
func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (*btcec.PublicKey, []byte, error) {
190+
// all shared secret and apply decryption in the reverse order. A structure is
191+
// returned that contains the decrypted error message and information on the
192+
// sender.
193+
func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
194+
*DecryptedError, error) {
195+
176196
// Ensure the error message length is as expected.
177197
if len(encryptedData) != onionErrorLength {
178-
return nil, nil, fmt.Errorf("invalid error length: "+
198+
return nil, fmt.Errorf("invalid error length: "+
179199
"expected %v got %v", onionErrorLength,
180200
len(encryptedData))
181201
}
@@ -186,7 +206,7 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (*btcec.PublicK
186206
)
187207

188208
var (
189-
sender *btcec.PublicKey
209+
sender int
190210
msg []byte
191211
dummySecret Hash256
192212
)
@@ -202,7 +222,7 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (*btcec.PublicK
202222
// secret to continue decryption attempts to fill out the rest
203223
// of the loop. Otherwise, we'll use the next shared secret in
204224
// line.
205-
if sender != nil || i > len(sharedSecrets)-1 {
225+
if sender != 0 || i > len(sharedSecrets)-1 {
206226
sharedSecret = dummySecret
207227
} else {
208228
sharedSecret = sharedSecrets[i]
@@ -226,19 +246,23 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (*btcec.PublicK
226246
// If the MAC matches up, then we've found the sender of the
227247
// error and have also obtained the fully decrypted message.
228248
realMac := h.Sum(nil)
229-
if hmac.Equal(realMac, expectedMac) && sender == nil {
230-
sender = o.circuit.PaymentPath[i]
249+
if hmac.Equal(realMac, expectedMac) && sender == 0 {
250+
sender = i + 1
231251
msg = data
232252
}
233253
}
234254

235-
// If the sender pointer is still nil, then we haven't found the
236-
// sender, meaning we've failed to decrypt.
237-
if sender == nil {
238-
return nil, nil, errors.New("unable to retrieve onion failure")
255+
// If the sender index is still zero, then we haven't found the sender,
256+
// meaning we've failed to decrypt.
257+
if sender == 0 {
258+
return nil, errors.New("unable to retrieve onion failure")
239259
}
240260

241-
return sender, msg, nil
261+
return &DecryptedError{
262+
SenderIdx: sender,
263+
Sender: o.circuit.PaymentPath[sender-1],
264+
Message: msg,
265+
}, nil
242266
}
243267

244268
// EncryptError is used to make data obfuscation using the generated shared

obfuscation_test.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,27 @@ func TestOnionFailure(t *testing.T) {
5959

6060
// Emulate that sender node receive the failure message and trying to
6161
// unwrap it, by applying obfuscation and checking the hmac.
62-
pubKey, deobfuscatedData, err := deobfuscator.DecryptError(obfuscatedData)
62+
decryptedError, err := deobfuscator.DecryptError(obfuscatedData)
6363
if err != nil {
6464
t.Fatalf("unable to de-obfuscate the onion failure: %v", err)
6565
}
6666

6767
// We should understand the node from which error have been received.
68-
if !bytes.Equal(pubKey.SerializeCompressed(),
68+
if !bytes.Equal(decryptedError.Sender.SerializeCompressed(),
6969
errorPath[len(errorPath)-1].SerializeCompressed()) {
7070
t.Fatalf("unable to properly conclude from which node in " +
7171
"the path we received an error")
7272
}
7373

74+
if decryptedError.SenderIdx != len(errorPath) {
75+
t.Fatalf("unable to properly conclude from which node in " +
76+
"the path we received an error")
77+
}
78+
7479
// Check that message have been properly de-obfuscated.
75-
if !bytes.Equal(deobfuscatedData, failureData) {
80+
if !bytes.Equal(decryptedError.Message, failureData) {
7681
t.Fatalf("data not equals, expected: \"%v\", real: \"%v\"",
77-
string(failureData), string(deobfuscatedData))
82+
string(failureData), string(decryptedError.Message))
7883
}
7984
}
8085

@@ -255,21 +260,26 @@ func TestOnionFailureSpecVector(t *testing.T) {
255260

256261
// Emulate that sender node receives the failure message and trying to
257262
// unwrap it, by applying obfuscation and checking the hmac.
258-
pubKey, deobfuscatedData, err := deobfuscator.DecryptError(obfuscatedData)
263+
decryptedError, err := deobfuscator.DecryptError(obfuscatedData)
259264
if err != nil {
260265
t.Fatalf("unable to de-obfuscate the onion failure: %v", err)
261266
}
262267

263268
// Check that message have been properly de-obfuscated.
264-
if !bytes.Equal(deobfuscatedData, failureData) {
269+
if !bytes.Equal(decryptedError.Message, failureData) {
265270
t.Fatalf("data not equals, expected: \"%v\", real: \"%v\"",
266-
string(failureData), string(deobfuscatedData))
271+
string(failureData), string(decryptedError.Message))
267272
}
268273

269274
// We should understand the node from which error have been received.
270-
if !bytes.Equal(pubKey.SerializeCompressed(),
275+
if !bytes.Equal(decryptedError.Sender.SerializeCompressed(),
271276
paymentPath[len(paymentPath)-1].SerializeCompressed()) {
272277
t.Fatalf("unable to properly conclude from which node in " +
273278
"the path we received an error")
274279
}
280+
281+
if decryptedError.SenderIdx != len(paymentPath) {
282+
t.Fatalf("unable to properly conclude from which node in " +
283+
"the path we received an error")
284+
}
275285
}

0 commit comments

Comments
 (0)