44	"bytes" 
55	"crypto/hmac" 
66	"crypto/sha256" 
7- 	"errors" 
87	"fmt" 
98
109	"github.com/aead/chacha20" 
@@ -249,10 +248,11 @@ const onionErrorLength = 2 + 2 + 256 + sha256.Size
249248func  (o  * OnionErrorDecrypter ) DecryptError (encryptedData  []byte ) (
250249	* DecryptedError , error ) {
251250
252- 	// Ensure the error message length is as expected. 
253- 	if  len (encryptedData ) !=  onionErrorLength  {
251+ 	// Ensure the error message length is enough to contain the payloads and 
252+ 	// hmacs blocks. 
253+ 	if  len (encryptedData ) <  hmacsAndPayloadsLen  {
254254		return  nil , fmt .Errorf ("invalid error length: " + 
255- 			"expected %v got %v" , onionErrorLength ,
255+ 			"expected at least  %v got %v" , hmacsAndPayloadsLen ,
256256			len (encryptedData ))
257257	}
258258
@@ -292,30 +292,40 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
292292		// encryption from the encrypted error payload. 
293293		encryptedData  =  onionEncrypt (& sharedSecret , encryptedData )
294294
295- 		// Next, we'll need to separate the data, from the MAC itself 
296- 		// so we can reconstruct and verify it. 
297- 		expectedMac  :=  encryptedData [:sha256 .Size ]
298- 		data  :=  encryptedData [sha256 .Size :]
299- 
300- 		// With the data split, we'll now re-generate the MAC using its 
301- 		// specified key. 
302- 		umKey  :=  generateKey ("um" , & sharedSecret )
303- 		h  :=  hmac .New (sha256 .New , umKey [:])
304- 		h .Write (data )
305- 
306- 		// If the MAC matches up, then we've found the sender of the 
307- 		// error and have also obtained the fully decrypted message. 
308- 		realMac  :=  h .Sum (nil )
309- 		if  hmac .Equal (realMac , expectedMac ) &&  sender  ==  0  {
295+ 		message , payloads , hmacs  :=  getMsgComponents (encryptedData )
296+ 
297+ 		final  :=  payloads [0 ] ==  payloadFinal 
298+ 		// TODO: Extract hold time from payload. 
299+ 
300+ 		expectedHmac  :=  calculateHmac (sharedSecret , i , message , payloads , hmacs )
301+ 		actualHmac  :=  hmacs [i * sha256 .Size  : (i + 1 )* sha256 .Size ]
302+ 
303+ 		// If the hmac does not match up, exit with a nil message. 
304+ 		if  ! bytes .Equal (actualHmac , expectedHmac [:]) &&  sender  ==  0  {
310305			sender  =  i  +  1 
311- 			msg  =  data 
306+ 			msg  =  nil 
312307		}
308+ 
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 
314+ 		}
315+ 
316+ 		// Shift payloads and hmacs to the left to prepare for the next 
317+ 		// iteration. 
318+ 		shiftPayloadsLeft (payloads )
319+ 		shiftHmacsLeft (hmacs )
313320	}
314321
315- 	// If the sender index is still zero, then we haven't found the sender, 
316- 	// meaning we've failed to decrypt. 
322+ 	// If the sender index is still zero, all hmacs checked out but none of the 
323+ 	// payloads was a final payload. In this case we must be dealing with a max 
324+ 	// length route and a final hop that returned an intermediate payload. Blame 
325+ 	// the final hop. 
317326	if  sender  ==  0  {
318- 		return  nil , errors .New ("unable to retrieve onion failure" )
327+ 		sender  =  NumMaxHops 
328+ 		msg  =  nil 
319329	}
320330
321331	return  & DecryptedError {
@@ -325,6 +335,132 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
325335	}, nil 
326336}
327337
338+ const  (
339+ 	totalHmacs           =  (NumMaxHops  *  (NumMaxHops  +  1 )) /  2 
340+ 	allHmacsLen          =  totalHmacs  *  sha256 .Size 
341+ 	hmacsAndPayloadsLen  =  allHmacsLen  +  allPayloadsLen 
342+ 
343+ 	// payloadLen is the size of the per-node payload. It consists of a 1-byte 
344+ 	// payload type and an 8-byte hold time. 
345+ 	payloadLen  =  1  +  8 
346+ 
347+ 	allPayloadsLen  =  payloadLen  *  NumMaxHops 
348+ 
349+ 	payloadFinal         =  1 
350+ 	payloadIntermediate  =  0 
351+ )
352+ 
353+ func  shiftHmacsRight (hmacs  []byte ) {
354+ 	if  len (hmacs ) !=  allHmacsLen  {
355+ 		panic ("invalid hmac block length" )
356+ 	}
357+ 
358+ 	srcIdx  :=  totalHmacs  -  2 
359+ 	destIdx  :=  totalHmacs  -  1 
360+ 	copyLen  :=  1 
361+ 	for  i  :=  0 ; i  <  NumMaxHops - 1 ; i ++  {
362+ 		copy (hmacs [destIdx * sha256 .Size :], hmacs [srcIdx * sha256 .Size :(srcIdx + copyLen )* sha256 .Size ])
363+ 
364+ 		copyLen ++ 
365+ 
366+ 		srcIdx  -=  copyLen  +  1 
367+ 		destIdx  -=  copyLen 
368+ 	}
369+ }
370+ 
371+ func  shiftHmacsLeft (hmacs  []byte ) {
372+ 	if  len (hmacs ) !=  allHmacsLen  {
373+ 		panic ("invalid hmac block length" )
374+ 	}
375+ 
376+ 	srcIdx  :=  NumMaxHops 
377+ 	destIdx  :=  1 
378+ 	copyLen  :=  NumMaxHops  -  1 
379+ 	for  i  :=  0 ; i  <  NumMaxHops - 1 ; i ++  {
380+ 		copy (hmacs [destIdx * sha256 .Size :], hmacs [srcIdx * sha256 .Size :(srcIdx + copyLen )* sha256 .Size ])
381+ 
382+ 		srcIdx  +=  copyLen 
383+ 		destIdx  +=  copyLen  +  1 
384+ 		copyLen -- 
385+ 	}
386+ }
387+ 
388+ func  shiftPayloadsRight (payloads  []byte ) {
389+ 	if  len (payloads ) !=  allPayloadsLen  {
390+ 		panic ("invalid payload block length" )
391+ 	}
392+ 
393+ 	copy (payloads [payloadLen :], payloads )
394+ }
395+ 
396+ func  shiftPayloadsLeft (payloads  []byte ) {
397+ 	if  len (payloads ) !=  allPayloadsLen  {
398+ 		panic ("invalid payload block length" )
399+ 	}
400+ 
401+ 	copy (payloads , payloads [payloadLen :NumMaxHops * payloadLen ])
402+ }
403+ 
404+ // getMsgComponents splits a complete failure message into its components 
405+ // without re-allocating memory. 
406+ func  getMsgComponents (data  []byte ) ([]byte , []byte , []byte ) {
407+ 	payloads  :=  data [len (data )- hmacsAndPayloadsLen  : len (data )- allHmacsLen ]
408+ 	hmacs  :=  data [len (data )- allHmacsLen :]
409+ 	message  :=  data [:len (data )- hmacsAndPayloadsLen ]
410+ 
411+ 	return  message , payloads , hmacs 
412+ }
413+ 
414+ // calculateHmac calculates an hmac given a shared secret and a presumed 
415+ // position in the path. Position is expressed as the distance to the error 
416+ // source. The error source itself is at position 0. 
417+ func  calculateHmac (sharedSecret  Hash256 , position  int ,
418+ 	message , payloads , hmacs  []byte ) []byte  {
419+ 
420+ 	var  dataToHmac  []byte 
421+ 
422+ 	// Include payloads including our own. 
423+ 	dataToHmac  =  append (dataToHmac , payloads [:(NumMaxHops - position )* payloadLen ]... )
424+ 
425+ 	// Include downstream hmacs. 
426+ 	var  downstreamHmacsIdx  =  position  +  NumMaxHops 
427+ 	for  j  :=  0 ; j  <  NumMaxHops - position - 1 ; j ++  {
428+ 		dataToHmac  =  append (dataToHmac , hmacs [downstreamHmacsIdx * sha256 .Size :(downstreamHmacsIdx + 1 )* sha256 .Size ]... )
429+ 
430+ 		downstreamHmacsIdx  +=  NumMaxHops  -  j  -  1 
431+ 	}
432+ 
433+ 	// Include message. 
434+ 	dataToHmac  =  append (dataToHmac , message ... )
435+ 
436+ 	// Calculate and return hmac. 
437+ 	umKey  :=  generateKey ("um" , & sharedSecret )
438+ 	hash  :=  hmac .New (sha256 .New , umKey [:])
439+ 	hash .Write (dataToHmac )
440+ 
441+ 	return  hash .Sum (nil )
442+ }
443+ 
444+ // calculateHmac calculates an hmac using the shared secret for this 
445+ // OnionErrorEncryptor instance. 
446+ func  (o  * OnionErrorEncrypter ) calculateHmac (position  int ,
447+ 	message , payloads , hmacs  []byte ) []byte  {
448+ 
449+ 	return  calculateHmac (o .sharedSecret , position , message , payloads , hmacs )
450+ }
451+ 
452+ // addHmacs updates the failure data with a series of hmacs corresponding to all 
453+ // possible positions in the path for the current node. 
454+ func  (o  * OnionErrorEncrypter ) addHmacs (data  []byte ) {
455+ 	message , payloads , hmacs  :=  getMsgComponents (data )
456+ 
457+ 	for  i  :=  0 ; i  <  NumMaxHops ; i ++  {
458+ 		hmac  :=  o .calculateHmac (i , message , payloads , hmacs )
459+ 
460+ 		copy (hmacs [i * sha256 .Size :], hmac )
461+ 	}
462+ }
463+ 
328464// EncryptError is used to make data obfuscation using the generated shared 
329465// secret. 
330466// 
@@ -338,12 +474,40 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
338474// failure and its origin. 
339475func  (o  * OnionErrorEncrypter ) EncryptError (initial  bool , data  []byte ) []byte  {
340476	if  initial  {
341- 		umKey  :=  generateKey ("um" , & o .sharedSecret )
342- 		hash  :=  hmac .New (sha256 .New , umKey [:])
343- 		hash .Write (data )
344- 		h  :=  hash .Sum (nil )
345- 		data  =  append (h , data ... )
477+ 		data  =  o .initializePayload (data )
478+ 	} else  {
479+ 		o .addIntermediatePayload (data )
346480	}
347481
482+ 	// Update hmac block. 
483+ 	o .addHmacs (data )
484+ 
485+ 	// Obfuscate. 
348486	return  onionEncrypt (& o .sharedSecret , data )
349487}
488+ 
489+ func  (o  * OnionErrorEncrypter ) initializePayload (message  []byte ) []byte  {
490+ 	// Add space for payloads and hmacs. 
491+ 	data  :=  make ([]byte , len (message )+ hmacsAndPayloadsLen )
492+ 	copy (data , message )
493+ 
494+ 	_ , payloads , _  :=  getMsgComponents (data )
495+ 
496+ 	// Signal final hops in the payload. 
497+ 	// TODO: Add hold time to payload. 
498+ 	payloads [0 ] =  payloadFinal 
499+ 
500+ 	return  data 
501+ }
502+ 
503+ func  (o  * OnionErrorEncrypter ) addIntermediatePayload (data  []byte ) {
504+ 	_ , payloads , hmacs  :=  getMsgComponents (data )
505+ 
506+ 	// Shift hmacs and payloads to create space for the payload. 
507+ 	shiftPayloadsRight (payloads )
508+ 	shiftHmacsRight (hmacs )
509+ 
510+ 	// Signal intermediate hop in the payload. 
511+ 	// TODO: Add hold time to payload. 
512+ 	payloads [0 ] =  payloadIntermediate 
513+ }
0 commit comments