@@ -340,17 +340,40 @@ const minOnionErrorLength = minPaddedOnionErrorLength + sha256.Size
340340// returned that contains the decrypted error message and information of the 
341341// error sender. We also report the hold times in ms for each hop on the error 
342342// path. 
343+ // 
344+ // The strictAttribution flag controls the behavior of the decryption logic 
345+ // surrounding the presence of attribution data: 
346+ // 
347+ //   - If set, then the first node with bad attribution data will be blamed 
348+ //     immediately. 
349+ // 
350+ //   - If unset, decryption continues optimistically until a successful error 
351+ //     message decryption occurs, regardless of attribution data validity. Hold 
352+ //     times are still extracted for nodes that provided valid attribution data. 
343353func  (o  * OnionErrorDecrypter ) DecryptError (encryptedData  []byte ,
344- 	attrData  []byte ) (* DecryptedError , error ) {
354+ 	attrData  []byte ,  strictAttribution   bool ) (* DecryptedError , error ) {
345355
346- 	// Ensure the error message and attribution data length is as expected. 
347- 	if  len (encryptedData ) <  minOnionErrorLength  || 
348- 		len (attrData ) <  o .hmacsAndPayloadsLen () {
356+ 	// Ensure the error message field is present and has the correct length. 
357+ 	if  len (encryptedData ) <  minOnionErrorLength  {
358+ 		return  nil , fmt .Errorf ("invalid error length: " + 
359+ 			"expected at least %v got %v" , minOnionErrorLength ,
360+ 			len (encryptedData ))
361+ 	}
349362
350- 		return  & DecryptedError {
351- 			Sender :    o .circuit .PaymentPath [0 ],
352- 			SenderIdx : 1 ,
353- 		}, nil 
363+ 	validAttr  :=  true 
364+ 
365+ 	// If we're decrypting with strict attribution, we need to have the 
366+ 	// correct attribution data present too. If strictAttribution is set 
367+ 	// then we immediately blame the first hop. 
368+ 	if  len (attrData ) <  o .hmacsAndPayloadsLen () {
369+ 		if  strictAttribution  {
370+ 			return  & DecryptedError {
371+ 				Sender :    o .circuit .PaymentPath [0 ],
372+ 				SenderIdx : 1 ,
373+ 			}, nil 
374+ 		} else  {
375+ 			validAttr  =  false 
376+ 		}
354377	}
355378
356379	sharedSecrets , _ , err  :=  generateSharedSecrets (
@@ -393,49 +416,69 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte,
393416
394417		// With the shared secret, we'll now strip off a layer of 
395418		// encryption from the encrypted failure and attribution 
396- 		// data. 
419+ 		// data. This needs to be done before parsing the attribution 
420+ 		// data, as the attribution data HMACs commit to it. 
397421		failData  =  onionEncrypt (AMMAG , & sharedSecret , failData )
398- 		attrData  =  onionEncrypt (AMMAG_EXT , & sharedSecret , attrData )
399- 
400- 		payloads  :=  o .payloads (attrData )
401- 		hmacs  :=  o .hmacs (attrData )
402422
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 ,
408- 		)
409- 
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 
423+ 		// If the attribution data are valid then do another round of 
424+ 		// attribution data decryption. 
425+ 		if  validAttr  {
426+ 			attrData  =  onionEncrypt (
427+ 				AMMAG_EXT , & sharedSecret , attrData ,
428+ 			)
429+ 
430+ 			payloads  :=  o .payloads (attrData )
431+ 			hmacs  :=  o .hmacs (attrData )
432+ 
433+ 			// Let's calculate the HMAC we expect for the 
434+ 			// corresponding payloads. 
435+ 			position  :=  o .hopCount  -  i  -  1 
436+ 			expectedAttrHmac  :=  o .calculateHmac (
437+ 				sharedSecret , position , failData , payloads ,
438+ 				hmacs ,
439+ 			)
440+ 
441+ 			// Let's retrieve the actual HMAC from the correct 
442+ 			// position in the HMACs array. 
443+ 			actualAttrHmac  :=  hmacs [i * o .hmacSize  : (i + 1 )* o .hmacSize ]
444+ 
445+ 			// If the hmac does not match up, exit with a nil 
446+ 			// message. This is not done for the dummy iterations. 
447+ 			if  ! bytes .Equal (actualAttrHmac , expectedAttrHmac ) && 
448+ 				sender  ==  0  &&  i  <  len (o .circuit .PaymentPath ) {
449+ 
450+ 				switch  strictAttribution  {
451+ 				case  true :
452+ 					sender  =  i  +  1 
453+ 					msg  =  nil 
454+ 
455+ 				case  false :
456+ 					// Flag the attribution data as invalid 
457+ 					// from this point onwards. This will 
458+ 					// prevent the loop from trying to 
459+ 					// extract anything from the attribution 
460+ 					// data. 
461+ 					validAttr  =  false 
462+ 				}
463+ 			}
464+ 
465+ 			// Extract the payload and exit with a nil message if it 
466+ 			// is invalid. 
467+ 			holdTime  :=  o .extractPayload (payloads )
468+ 			if  sender  ==  0  &&  validAttr  {
469+ 				// Store hold time reported by this node. 
470+ 				hopPayloads  =  append (hopPayloads , holdTime )
471+ 
472+ 				// Update the message. 
473+ 				msg  =  failData [sha256 .Size :]
474+ 			}
475+ 
476+ 			// Shift payloads and hmacs to the left to prepare for 
477+ 			// the next iteration. 
478+ 			o .shiftPayloadsLeft (payloads )
479+ 			o .shiftHmacsLeft (hmacs )
421480		}
422481
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- 
439482		// Next, we'll need to separate the failure data, from the MAC 
440483		// itself so we can reconstruct and verify it. 
441484		expectedMac  :=  failData [:sha256 .Size ]
0 commit comments