4
4
"bytes"
5
5
"crypto/hmac"
6
6
"crypto/sha256"
7
- "errors"
8
7
"fmt"
9
8
10
9
"github.com/aead/chacha20"
@@ -249,10 +248,11 @@ const onionErrorLength = 2 + 2 + 256 + sha256.Size
249
248
func (o * OnionErrorDecrypter ) DecryptError (encryptedData []byte ) (
250
249
* DecryptedError , error ) {
251
250
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 {
254
254
return nil , fmt .Errorf ("invalid error length: " +
255
- "expected %v got %v" , onionErrorLength ,
255
+ "expected at least %v got %v" , hmacsAndPayloadsLen ,
256
256
len (encryptedData ))
257
257
}
258
258
@@ -292,30 +292,40 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
292
292
// encryption from the encrypted error payload.
293
293
encryptedData = onionEncrypt (& sharedSecret , encryptedData )
294
294
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 {
310
305
sender = i + 1
311
- msg = data
306
+ msg = nil
312
307
}
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 )
313
320
}
314
321
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.
317
326
if sender == 0 {
318
- return nil , errors .New ("unable to retrieve onion failure" )
327
+ sender = NumMaxHops
328
+ msg = nil
319
329
}
320
330
321
331
return & DecryptedError {
@@ -325,6 +335,132 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
325
335
}, nil
326
336
}
327
337
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
+
328
464
// EncryptError is used to make data obfuscation using the generated shared
329
465
// secret.
330
466
//
@@ -338,12 +474,40 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) (
338
474
// failure and its origin.
339
475
func (o * OnionErrorEncrypter ) EncryptError (initial bool , data []byte ) []byte {
340
476
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 )
346
480
}
347
481
482
+ // Update hmac block.
483
+ o .addHmacs (data )
484
+
485
+ // Obfuscate.
348
486
return onionEncrypt (& o .sharedSecret , data )
349
487
}
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