44	"bytes" 
55	"crypto/hmac" 
66	"crypto/sha256" 
7+ 	"encoding/binary" 
8+ 	"errors" 
79	"fmt" 
810
911	"github.com/aead/chacha20" 
@@ -18,6 +20,8 @@ const (
1820	HMACSize  =  32 
1921)
2022
23+ var  byteOrder  =  binary .BigEndian 
24+ 
2125// Hash256 is a statically sized, 32-byte array, typically containing 
2226// the output of a SHA256 hash. 
2327type  Hash256  [sha256 .Size ]byte 
@@ -91,6 +95,10 @@ type DecryptedError struct {
9195
9296	// Message is the decrypted error message. 
9397	Message  []byte 
98+ 
99+ 	// HoldTimesMs is an array of millisecond durations reported by each node on 
100+ 	// the (error) path. 
101+ 	HoldTimesMs  []uint64 
94102}
95103
96104// zeroHMAC is the special HMAC value that allows the final node to determine 
@@ -275,6 +283,7 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
275283	// We'll iterate a constant amount of hops to ensure that we don't give 
276284	// away an timing information pertaining to the position in the route 
277285	// that the error emanated from. 
286+ 	holdTimesMs  :=  make ([]uint64 , 0 )
278287	for  i  :=  0 ; i  <  NumMaxHops ; i ++  {
279288		var  sharedSecret  Hash256 
280289
@@ -294,9 +303,6 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
294303
295304		message , payloads , hmacs  :=  getMsgComponents (encryptedData )
296305
297- 		final  :=  payloads [0 ] ==  payloadFinal 
298- 		// TODO: Extract hold time from payload. 
299- 
300306		expectedHmac  :=  calculateHmac (sharedSecret , i , message , payloads , hmacs )
301307		actualHmac  :=  hmacs [i * sha256 .Size  : (i + 1 )* sha256 .Size ]
302308
@@ -306,11 +312,23 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
306312			msg  =  nil 
307313		}
308314
309- 		// If we are at the node that is the source of the error, we can now 
310- 		// save the message in our return variable. 
311- 		if  final  &&  sender  ==  0  {
312- 			sender  =  i  +  1 
313- 			msg  =  message 
315+ 		// Extract the payload and exit with a nil message if it is invalid. 
316+ 		payloadType , holdTimeMs , err  :=  extractPayload (payloads )
317+ 		if  sender  ==  0  {
318+ 			if  err  !=  nil  {
319+ 				sender  =  i  +  1 
320+ 				msg  =  nil 
321+ 			}
322+ 
323+ 			// Store hold time reported by this node. 
324+ 			holdTimesMs  =  append (holdTimesMs , holdTimeMs )
325+ 
326+ 			// If we are at the node that is the source of the error, we can now 
327+ 			// save the message in our return variable. 
328+ 			if  payloadType  ==  payloadFinal  {
329+ 				sender  =  i  +  1 
330+ 				msg  =  message 
331+ 			}
314332		}
315333
316334		// Shift payloads and hmacs to the left to prepare for the next 
@@ -329,9 +347,10 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
329347	}
330348
331349	return  & DecryptedError {
332- 		SenderIdx : sender ,
333- 		Sender :    o .circuit .PaymentPath [sender - 1 ],
334- 		Message :   msg ,
350+ 		SenderIdx :   sender ,
351+ 		Sender :      o .circuit .PaymentPath [sender - 1 ],
352+ 		Message :     msg ,
353+ 		HoldTimesMs : holdTimesMs ,
335354	}, nil 
336355}
337356
@@ -472,11 +491,13 @@ func (o *OnionErrorEncrypter) addHmacs(data []byte) {
472491// The reason for using onion obfuscation is to not give 
473492// away to the nodes in the payment path the information about the exact 
474493// failure and its origin. 
475- func  (o  * OnionErrorEncrypter ) EncryptError (initial  bool , data  []byte ) []byte  {
494+ func  (o  * OnionErrorEncrypter ) EncryptError (initial  bool , data  []byte ,
495+ 	holdTimeMs  uint64 ) []byte  {
496+ 
476497	if  initial  {
477- 		data  =  o .initializePayload (data )
498+ 		data  =  o .initializePayload (data ,  holdTimeMs )
478499	} else  {
479- 		o .addIntermediatePayload (data )
500+ 		o .addIntermediatePayload (data ,  holdTimeMs )
480501	}
481502
482503	// Update hmac block. 
@@ -486,28 +507,52 @@ func (o *OnionErrorEncrypter) EncryptError(initial bool, data []byte) []byte {
486507	return  onionEncrypt (& o .sharedSecret , data )
487508}
488509
489- func  (o  * OnionErrorEncrypter ) initializePayload (message  []byte ) []byte  {
510+ func  (o  * OnionErrorEncrypter ) initializePayload (message  []byte ,
511+ 	holdTimeMs  uint64 ) []byte  {
512+ 
490513	// Add space for payloads and hmacs. 
491514	data  :=  make ([]byte , len (message )+ hmacsAndPayloadsLen )
492515	copy (data , message )
493516
494517	_ , payloads , _  :=  getMsgComponents (data )
495518
496519	// Signal final hops in the payload. 
497- 	// TODO: Add hold time to payload. 
498- 	payloads [0 ] =  payloadFinal 
520+ 	addPayload (payloads , payloadFinal , holdTimeMs )
499521
500522	return  data 
501523}
502524
503- func  (o  * OnionErrorEncrypter ) addIntermediatePayload (data  []byte ) {
525+ func  addPayload (payloads  []byte , payloadType  PayloadType , holdTimeMs  uint64 ) {
526+ 	byteOrder .PutUint64 (payloads [1 :], uint64 (holdTimeMs ))
527+ 
528+ 	payloads [0 ] =  byte (payloadType )
529+ }
530+ 
531+ func  extractPayload (payloads  []byte ) (PayloadType , uint64 , error ) {
532+ 	var  payloadType  PayloadType 
533+ 
534+ 	switch  payloads [0 ] {
535+ 	case  payloadFinal , payloadIntermediate :
536+ 		payloadType  =  PayloadType (payloads [0 ])
537+ 
538+ 	default :
539+ 		return  0 , 0 , errors .New ("invalid payload type" )
540+ 	}
541+ 
542+ 	holdTimeMs  :=  byteOrder .Uint64 (payloads [1 :])
543+ 
544+ 	return  payloadType , holdTimeMs , nil 
545+ }
546+ 
547+ func  (o  * OnionErrorEncrypter ) addIntermediatePayload (data  []byte ,
548+ 	holdTimeMs  uint64 ) {
549+ 
504550	_ , payloads , hmacs  :=  getMsgComponents (data )
505551
506552	// Shift hmacs and payloads to create space for the payload. 
507553	shiftPayloadsRight (payloads )
508554	shiftHmacsRight (hmacs )
509555
510556	// Signal intermediate hop in the payload. 
511- 	// TODO: Add hold time to payload. 
512- 	payloads [0 ] =  payloadIntermediate 
557+ 	addPayload (payloads , payloadIntermediate , holdTimeMs )
513558}
0 commit comments