@@ -106,6 +106,10 @@ type DecryptedError struct {
106
106
107
107
// Message is the decrypted error message.
108
108
Message []byte
109
+
110
+ // HoldTimes is an array of hold times reported by each node on the error
111
+ // path.
112
+ HoldTimes []uint32
109
113
}
110
114
111
115
// zeroHMAC is the special HMAC value that allows the final node to determine
@@ -333,16 +337,20 @@ const minOnionErrorLength = minPaddedOnionErrorLength + sha256.Size
333
337
// onion failure is encrypted in backward manner, starting from the node where
334
338
// error have occurred. As a result, in order to decrypt the error we need get
335
339
// all shared secret and apply decryption in the reverse order. A structure is
336
- // returned that contains the decrypted error message and information on the
337
- // sender.
338
- func (o * OnionErrorDecrypter ) DecryptError (encryptedData []byte ) (
339
- * DecryptedError , error ) {
340
-
341
- // Ensure the error message length is as expected.
342
- if len (encryptedData ) < minOnionErrorLength {
343
- return nil , fmt .Errorf ("invalid error length: " +
344
- "expected at least %v got %v" , minOnionErrorLength ,
345
- len (encryptedData ))
340
+ // returned that contains the decrypted error message and information of the
341
+ // error sender. We also report the hold times in ms for each hop on the error
342
+ // path.
343
+ func (o * OnionErrorDecrypter ) DecryptError (encryptedData []byte ,
344
+ attrData []byte ) (* DecryptedError , error ) {
345
+
346
+ // Ensure the error message and attribution data length is as expected.
347
+ if len (encryptedData ) < minOnionErrorLength ||
348
+ len (attrData ) < o .hmacsAndPayloadsLen () {
349
+
350
+ return & DecryptedError {
351
+ Sender : o .circuit .PaymentPath [0 ],
352
+ SenderIdx : 1 ,
353
+ }, nil
346
354
}
347
355
348
356
sharedSecrets , err := generateSharedSecrets (
@@ -361,10 +369,16 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
361
369
)
362
370
copy (dummySecret [:], bytes .Repeat ([]byte {1 }, 32 ))
363
371
372
+ // Copy the failure message data in a new variable.
373
+ failData := make ([]byte , len (encryptedData ))
374
+ copy (failData , encryptedData )
375
+
376
+ hopPayloads := make ([]uint32 , 0 )
377
+
364
378
// We'll iterate a constant amount of hops to ensure that we don't give
365
379
// away an timing information pertaining to the position in the route
366
380
// that the error emanated from.
367
- for i := 0 ; i < NumMaxHops ; i ++ {
381
+ for i := 0 ; i < o . hopCount ; i ++ {
368
382
var sharedSecret Hash256
369
383
370
384
// If we've already found the sender, then we'll use our dummy
@@ -378,15 +392,54 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
378
392
}
379
393
380
394
// With the shared secret, we'll now strip off a layer of
381
- // encryption from the encrypted error payload.
382
- encryptedData = onionEncrypt (
383
- AMMAG , & sharedSecret , encryptedData ,
395
+ // encryption from the encrypted failure and attribution
396
+ // data.
397
+ failData = onionEncrypt (AMMAG , & sharedSecret , failData )
398
+ attrData = onionEncrypt (AMMAG_EXT , & sharedSecret , attrData )
399
+
400
+ payloads := o .payloads (attrData )
401
+ hmacs := o .hmacs (attrData )
402
+
403
+ // Let's calculate the HMAC we expect for the corresponding
404
+ // payloads.
405
+ position := o .hopCount - i - 1
406
+ expectedAttrHmac := o .calculateHmac (
407
+ sharedSecret , position , failData , payloads , hmacs ,
384
408
)
385
409
386
- // Next, we'll need to separate the data, from the MAC itself
387
- // so we can reconstruct and verify it.
388
- expectedMac := encryptedData [:sha256 .Size ]
389
- data := encryptedData [sha256 .Size :]
410
+ // Let's retrieve the actual HMAC from the correct position in
411
+ // the HMACs array.
412
+ actualAttrHmac := hmacs [i * o .hmacSize : (i + 1 )* o .hmacSize ]
413
+
414
+ // If the hmac does not match up, exit with a nil message. This
415
+ // is not done for the dummy iterations.
416
+ if ! bytes .Equal (actualAttrHmac , expectedAttrHmac ) &&
417
+ sender == 0 && i < len (o .circuit .PaymentPath ) {
418
+
419
+ sender = i + 1
420
+ msg = nil
421
+ }
422
+
423
+ // Extract the payload and exit with a nil message if it is
424
+ // invalid.
425
+ holdTime := o .extractPayload (payloads )
426
+ if sender == 0 {
427
+ // Store hold time reported by this node.
428
+ hopPayloads = append (hopPayloads , holdTime )
429
+
430
+ // Update the message.
431
+ msg = failData [sha256 .Size :]
432
+ }
433
+
434
+ // Shift payloads and hmacs to the left to prepare for the next
435
+ // iteration.
436
+ o .shiftPayloadsLeft (payloads )
437
+ o .shiftHmacsLeft (hmacs )
438
+
439
+ // Next, we'll need to separate the failure data, from the MAC
440
+ // itself so we can reconstruct and verify it.
441
+ expectedMac := failData [:sha256 .Size ]
442
+ data := failData [sha256 .Size :]
390
443
391
444
// With the data split, we'll now re-generate the MAC using its
392
445
// specified key.
@@ -410,12 +463,55 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
410
463
}
411
464
412
465
return & DecryptedError {
413
- SenderIdx : sender ,
414
466
Sender : o .circuit .PaymentPath [sender - 1 ],
467
+ SenderIdx : sender ,
415
468
Message : msg ,
469
+ HoldTimes : hopPayloads ,
416
470
}, nil
417
471
}
418
472
473
+ // extractPayload extracts the payload and payload origin information from the
474
+ // given byte slice.
475
+ func (o * OnionErrorDecrypter ) extractPayload (payloadBytes []byte ) uint32 {
476
+ // Extract payload.
477
+ holdTime := binary .BigEndian .Uint32 (payloadBytes [0 :o .payloadLen ()])
478
+
479
+ return holdTime
480
+ }
481
+
482
+ func (o * OnionErrorDecrypter ) shiftPayloadsLeft (payloads []byte ) {
483
+ copy (payloads , payloads [o .payloadLen ():o .hopCount * o .payloadLen ()])
484
+ }
485
+
486
+ func (o * OnionErrorDecrypter ) shiftHmacsLeft (hmacs []byte ) {
487
+ // Work from left to right to avoid overwriting data that is still
488
+ // needed later on in the shift operation.
489
+ srcIdx := o .hopCount
490
+ destIdx := 0
491
+ copyLen := o .hopCount - 1
492
+ for i := 0 ; i < o .hopCount - 1 ; i ++ {
493
+ // Clear first hmac slot. This slot is for the position farthest
494
+ // away from the error source. Because we are shifting, this
495
+ // cannot be relevant.
496
+ copy (hmacs [destIdx * o .hmacSize :], o .zeroHmac )
497
+
498
+ // The hmacs of the downstream hop become the remaining hmacs
499
+ // for the current hop.
500
+ copy (
501
+ hmacs [(destIdx + 1 )* o .hmacSize :],
502
+ hmacs [srcIdx * o .hmacSize :(srcIdx + copyLen )* o .hmacSize ],
503
+ )
504
+
505
+ srcIdx += copyLen
506
+ destIdx += copyLen + 1
507
+ copyLen --
508
+ }
509
+
510
+ // Clear the very last hmac slot. Because we just shifted, the most
511
+ // downstream hop can never be the error source.
512
+ copy (hmacs [destIdx * o .hmacSize :], o .zeroHmac )
513
+ }
514
+
419
515
// EncryptError is used to make data obfuscation using the generated shared
420
516
// secret.
421
517
//
0 commit comments