@@ -340,17 +340,40 @@ const minOnionErrorLength = minPaddedOnionErrorLength + sha256.Size
340
340
// returned that contains the decrypted error message and information of the
341
341
// error sender. We also report the hold times in ms for each hop on the error
342
342
// 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.
343
353
func (o * OnionErrorDecrypter ) DecryptError (encryptedData []byte ,
344
- attrData []byte ) (* DecryptedError , error ) {
354
+ attrData []byte , strictAttribution bool ) (* DecryptedError , error ) {
345
355
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
+ }
349
362
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
+ }
354
377
}
355
378
356
379
sharedSecrets , err := generateSharedSecrets (
@@ -393,49 +416,69 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte,
393
416
394
417
// With the shared secret, we'll now strip off a layer of
395
418
// 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.
397
421
failData = onionEncrypt (AMMAG , & sharedSecret , failData )
398
- attrData = onionEncrypt (AMMAG_EXT , & sharedSecret , attrData )
399
-
400
- payloads := o .payloads (attrData )
401
- hmacs := o .hmacs (attrData )
402
422
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 {
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 )
421
480
}
422
481
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
482
// Next, we'll need to separate the failure data, from the MAC
440
483
// itself so we can reconstruct and verify it.
441
484
expectedMac := failData [:sha256 .Size ]
0 commit comments