@@ -942,7 +942,7 @@ pub(crate) struct DecodedOnionFailure {
942
942
#[ inline]
943
943
pub ( super ) fn process_onion_failure < T : secp256k1:: Signing , L : Deref > (
944
944
secp_ctx : & Secp256k1 < T > , logger : & L , htlc_source : & HTLCSource ,
945
- mut encrypted_packet : OnionErrorPacket ,
945
+ mut encrypted_packet : OnionErrorPacket , secondary_session_priv : Option < SecretKey > ,
946
946
) -> DecodedOnionFailure
947
947
where
948
948
L :: Target : Logger ,
@@ -1004,8 +1004,10 @@ where
1004
1004
1005
1005
let outer_session_priv = path. has_trampoline_hops ( ) . then ( || {
1006
1006
// if we have Trampoline hops, the outer onion session_priv is a hash of the inner one
1007
- let session_priv_hash = Sha256 :: hash ( & session_priv. secret_bytes ( ) ) . to_byte_array ( ) ;
1008
- SecretKey :: from_slice ( & session_priv_hash[ ..] ) . expect ( "You broke SHA-256!" )
1007
+ secondary_session_priv. unwrap_or_else ( || {
1008
+ let session_priv_hash = Sha256 :: hash ( & session_priv. secret_bytes ( ) ) . to_byte_array ( ) ;
1009
+ SecretKey :: from_slice ( & session_priv_hash[ ..] ) . expect ( "You broke SHA-256!" )
1010
+ } )
1009
1011
} ) ;
1010
1012
1011
1013
let mut onion_keys = Vec :: with_capacity ( path. hops . len ( ) ) ;
@@ -1466,7 +1468,7 @@ impl HTLCFailReason {
1466
1468
{
1467
1469
match self . 0 {
1468
1470
HTLCFailReasonRepr :: LightningError { ref err } => {
1469
- process_onion_failure ( secp_ctx, logger, & htlc_source, err. clone ( ) )
1471
+ process_onion_failure ( secp_ctx, logger, & htlc_source, err. clone ( ) , None )
1470
1472
} ,
1471
1473
#[ allow( unused) ]
1472
1474
HTLCFailReasonRepr :: Reason { ref failure_code, ref data, .. } => {
@@ -2034,11 +2036,11 @@ mod tests {
2034
2036
use crate :: prelude:: * ;
2035
2037
use crate :: util:: test_utils:: TestLogger ;
2036
2038
2039
+ use super :: * ;
2037
2040
use bitcoin:: hex:: FromHex ;
2038
2041
use bitcoin:: secp256k1:: Secp256k1 ;
2039
2042
use bitcoin:: secp256k1:: { PublicKey , SecretKey } ;
2040
-
2041
- use super :: * ;
2043
+ use types:: features:: Features ;
2042
2044
2043
2045
fn get_test_session_key ( ) -> SecretKey {
2044
2046
let hex = "4141414141414141414141414141414141414141414141414141414141414141" ;
@@ -2394,11 +2396,284 @@ mod tests {
2394
2396
2395
2397
// Assert that the original failure can be retrieved and that all hmacs check out.
2396
2398
let decrypted_failure =
2397
- process_onion_failure ( & ctx_full, & logger, & htlc_source, onion_error) ;
2399
+ process_onion_failure ( & ctx_full, & logger, & htlc_source, onion_error, None ) ;
2398
2400
2399
2401
assert_eq ! ( decrypted_failure. onion_error_code, Some ( 0x2002 ) ) ;
2400
2402
}
2401
2403
2404
+ #[ test]
2405
+ fn test_trampoline_onion_error_vectors ( ) {
2406
+ // as per https://github.com/lightning/bolts/blob/079f761bf68caa48544bd6bf0a29591d43425b0b/bolt04/trampoline-onion-error-test.json
2407
+ // TODO(arik): check intermediate hops' perspectives once we have implemented forwarding
2408
+
2409
+ let dummy_amt_msat = 150_000_000 ;
2410
+ let path = Path {
2411
+ hops : vec ! [
2412
+ // Bob
2413
+ RouteHop {
2414
+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ) . unwrap( ) ) . unwrap( ) ,
2415
+ node_features: NodeFeatures :: empty( ) ,
2416
+ short_channel_id: 0 ,
2417
+ channel_features: ChannelFeatures :: empty( ) ,
2418
+ fee_msat: 3_000 ,
2419
+ cltv_expiry_delta: 24 ,
2420
+ maybe_announced_channel: false ,
2421
+ } ,
2422
+
2423
+ // Carol
2424
+ RouteHop {
2425
+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007" ) . unwrap( ) ) . unwrap( ) ,
2426
+ node_features: NodeFeatures :: empty( ) ,
2427
+ short_channel_id: ( 572330 << 40 ) + ( 42 << 16 ) + 2821 ,
2428
+ channel_features: ChannelFeatures :: empty( ) ,
2429
+ fee_msat: 153_000 ,
2430
+ cltv_expiry_delta: 0 ,
2431
+ maybe_announced_channel: false ,
2432
+ } ,
2433
+ ] ,
2434
+ blinded_tail : Some ( BlindedTail {
2435
+ trampoline_hops : vec ! [
2436
+ // Carol's pubkey
2437
+ TrampolineHop {
2438
+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007" ) . unwrap( ) ) . unwrap( ) ,
2439
+ node_features: Features :: empty( ) ,
2440
+ fee_msat: 2_500 ,
2441
+ cltv_expiry_delta: 24 ,
2442
+ } ,
2443
+
2444
+ // Eve's pubkey
2445
+ TrampolineHop {
2446
+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145" ) . unwrap( ) ) . unwrap( ) ,
2447
+ node_features: Features :: empty( ) ,
2448
+ fee_msat: 2_500 ,
2449
+ cltv_expiry_delta: 24 ,
2450
+ } ,
2451
+ ] ,
2452
+
2453
+ // Dummy blinded hop (because LDK doesn't allow unblinded Trampoline receives)
2454
+ hops : vec ! [
2455
+ // Eve's dummy blinded node id
2456
+ BlindedHop {
2457
+ blinded_node_id: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "0295d40514096a8be54859e7dfe947b376eaafea8afe5cb4eb2c13ff857ed0b4be" ) . unwrap( ) ) . unwrap( ) ,
2458
+ encrypted_payload: vec![ ] ,
2459
+ }
2460
+ ] ,
2461
+ blinding_point : PublicKey :: from_slice ( & <Vec < u8 > >:: from_hex ( "02988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e" ) . unwrap ( ) ) . unwrap ( ) ,
2462
+ excess_final_cltv_expiry_delta : 0 ,
2463
+ final_value_msat : dummy_amt_msat
2464
+ } ) ,
2465
+ } ;
2466
+
2467
+ // all dummy values
2468
+ let secp_ctx = Secp256k1 :: new ( ) ;
2469
+ let trampoline_session_priv = SecretKey :: from_slice ( & [ 3 ; 32 ] ) . unwrap ( ) ;
2470
+ let outer_session_priv = SecretKey :: from_slice ( & [ 4 ; 32 ] ) . unwrap ( ) ;
2471
+
2472
+ let logger: Arc < TestLogger > = Arc :: new ( TestLogger :: new ( ) ) ;
2473
+ let htlc_source = HTLCSource :: OutboundRoute {
2474
+ path,
2475
+ session_priv : trampoline_session_priv,
2476
+ first_hop_htlc_msat : dummy_amt_msat,
2477
+ payment_id : PaymentId ( [ 1 ; 32 ] ) ,
2478
+ } ;
2479
+
2480
+ let error_packet_hex = "f8941a320b8fde4ad7b9b920c69cbf334114737497d93059d77e591eaa78d6334d3e2aeefcb0cc83402eaaf91d07d695cd895d9cad1018abdaf7d2a49d7657b1612729db7f393f0bb62b25afaaaa326d72a9214666025385033f2ec4605dcf1507467b5726d806da180ea224a7d8631cd31b0bdd08eead8bfe14fc8c7475e17768b1321b54dd4294aecc96da391efe0ca5bd267a45ee085c85a60cf9a9ac152fa4795fff8700a3ea4f848817f5e6943e855ab2e86f6929c9e885d8b20c49b14d2512c59ed21f10bd38691110b0d82c00d9fa48a20f10c7550358724c6e8e2b966e56a0aadf458695b273768062fa7c6e60eb72d4cdc67bf525c194e4a17fdcaa0e9d80480b586bf113f14eea530b6728a1c53fe5cee092e24a90f21f4b764015e7ed5e23" ;
2481
+ let error_packet =
2482
+ OnionErrorPacket { data : <Vec < u8 > >:: from_hex ( error_packet_hex) . unwrap ( ) } ;
2483
+ let decrypted_failure = process_onion_failure (
2484
+ & secp_ctx,
2485
+ & logger,
2486
+ & htlc_source,
2487
+ error_packet,
2488
+ Some ( outer_session_priv) ,
2489
+ ) ;
2490
+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( 0x400f ) ) ;
2491
+ }
2492
+
2493
+ #[ test]
2494
+ fn test_trampoline_onion_decryption ( ) {
2495
+ // In this test, we construct a dummy path that uses Trampoline hops, and ensure that the
2496
+ // correct shared secrets are used to decrypt the error packets. The actual path configuration
2497
+ // is not particularly relevant.
2498
+
2499
+ let dummy_amt_msat = 150_000_000 ;
2500
+
2501
+ let path = Path {
2502
+ hops : vec ! [
2503
+ // Bob
2504
+ RouteHop {
2505
+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ) . unwrap( ) ) . unwrap( ) ,
2506
+ node_features: NodeFeatures :: empty( ) ,
2507
+ short_channel_id: 0 ,
2508
+ channel_features: ChannelFeatures :: empty( ) ,
2509
+ fee_msat: 3_000 ,
2510
+ cltv_expiry_delta: 24 ,
2511
+ maybe_announced_channel: false ,
2512
+ } ,
2513
+
2514
+ // Carol
2515
+ RouteHop {
2516
+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007" ) . unwrap( ) ) . unwrap( ) ,
2517
+ node_features: NodeFeatures :: empty( ) ,
2518
+ short_channel_id: ( 572330 << 40 ) + ( 42 << 16 ) + 2821 ,
2519
+ channel_features: ChannelFeatures :: empty( ) ,
2520
+ fee_msat: 153_000 ,
2521
+ cltv_expiry_delta: 0 ,
2522
+ maybe_announced_channel: false ,
2523
+ } ,
2524
+ ] ,
2525
+ blinded_tail : Some ( BlindedTail {
2526
+ trampoline_hops : vec ! [
2527
+ // Carol's pubkey
2528
+ TrampolineHop {
2529
+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007" ) . unwrap( ) ) . unwrap( ) ,
2530
+ node_features: Features :: empty( ) ,
2531
+ fee_msat: 2_500 ,
2532
+ cltv_expiry_delta: 24 ,
2533
+ } ,
2534
+ // Dave's pubkey
2535
+ TrampolineHop {
2536
+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "020e2dbadcc2005e859819ddebbe88a834ae8a6d2b049233c07335f15cd1dc5f22" ) . unwrap( ) ) . unwrap( ) ,
2537
+ node_features: Features :: empty( ) ,
2538
+ fee_msat: 2_500 ,
2539
+ cltv_expiry_delta: 24 ,
2540
+ } ,
2541
+ // Emily's pubkey (the intro node needs to be duplicated)
2542
+ TrampolineHop {
2543
+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991" ) . unwrap( ) ) . unwrap( ) ,
2544
+ node_features: Features :: empty( ) ,
2545
+ fee_msat: 150_500 ,
2546
+ cltv_expiry_delta: 36 ,
2547
+ }
2548
+ ] ,
2549
+ hops : vec ! [
2550
+ // Emily's blinded node id
2551
+ BlindedHop {
2552
+ blinded_node_id: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "0295d40514096a8be54859e7dfe947b376eaafea8afe5cb4eb2c13ff857ed0b4be" ) . unwrap( ) ) . unwrap( ) ,
2553
+ encrypted_payload: vec![ ] ,
2554
+ } ,
2555
+ // Forrest's blinded node id
2556
+ BlindedHop {
2557
+ blinded_node_id: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "020e2dbadcc2005e859819ddebbe88a834ae8a6d2b049233c07335f15cd1dc5f22" ) . unwrap( ) ) . unwrap( ) ,
2558
+ encrypted_payload: vec![ ] ,
2559
+ }
2560
+ ] ,
2561
+ blinding_point : PublicKey :: from_slice ( & <Vec < u8 > >:: from_hex ( "02988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e" ) . unwrap ( ) ) . unwrap ( ) ,
2562
+ excess_final_cltv_expiry_delta : 0 ,
2563
+ final_value_msat : dummy_amt_msat
2564
+ } ) ,
2565
+ } ;
2566
+
2567
+ // all dummy values
2568
+ let secp_ctx = Secp256k1 :: new ( ) ;
2569
+ let session_priv = get_test_session_key ( ) ;
2570
+
2571
+ let trampoline_onion_keys = construct_trampoline_onion_keys (
2572
+ & secp_ctx,
2573
+ & path. blinded_tail . as_ref ( ) . unwrap ( ) ,
2574
+ & session_priv,
2575
+ )
2576
+ . unwrap ( ) ;
2577
+
2578
+ let outer_onion_keys = {
2579
+ let session_priv_hash = Sha256 :: hash ( & session_priv. secret_bytes ( ) ) . to_byte_array ( ) ;
2580
+ let outer_session_priv = SecretKey :: from_slice ( & session_priv_hash[ ..] ) . unwrap ( ) ;
2581
+ construct_onion_keys ( & Secp256k1 :: new ( ) , & path, & outer_session_priv) . unwrap ( )
2582
+ } ;
2583
+
2584
+ let logger: Arc < TestLogger > = Arc :: new ( TestLogger :: new ( ) ) ;
2585
+ let htlc_source = HTLCSource :: OutboundRoute {
2586
+ path,
2587
+ session_priv,
2588
+ first_hop_htlc_msat : dummy_amt_msat,
2589
+ payment_id : PaymentId ( [ 1 ; 32 ] ) ,
2590
+ } ;
2591
+
2592
+ {
2593
+ let error_code = 0x2002 ;
2594
+ let mut first_hop_error_packet = build_unencrypted_failure_packet (
2595
+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2596
+ error_code,
2597
+ & [ 0 ; 0 ] ,
2598
+ ) ;
2599
+
2600
+ crypt_failure_packet (
2601
+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2602
+ & mut first_hop_error_packet,
2603
+ ) ;
2604
+
2605
+ let decrypted_failure = process_onion_failure (
2606
+ & secp_ctx,
2607
+ & logger,
2608
+ & htlc_source,
2609
+ first_hop_error_packet,
2610
+ None ,
2611
+ ) ;
2612
+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( error_code) ) ;
2613
+ } ;
2614
+
2615
+ {
2616
+ let error_code = 0x2003 ;
2617
+ let mut trampoline_outer_hop_error_packet = build_unencrypted_failure_packet (
2618
+ outer_onion_keys[ 1 ] . shared_secret . as_ref ( ) ,
2619
+ error_code,
2620
+ & [ 0 ; 0 ] ,
2621
+ ) ;
2622
+
2623
+ crypt_failure_packet (
2624
+ outer_onion_keys[ 1 ] . shared_secret . as_ref ( ) ,
2625
+ & mut trampoline_outer_hop_error_packet,
2626
+ ) ;
2627
+
2628
+ crypt_failure_packet (
2629
+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2630
+ & mut trampoline_outer_hop_error_packet,
2631
+ ) ;
2632
+
2633
+ let decrypted_failure = process_onion_failure (
2634
+ & secp_ctx,
2635
+ & logger,
2636
+ & htlc_source,
2637
+ trampoline_outer_hop_error_packet,
2638
+ None ,
2639
+ ) ;
2640
+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( error_code) ) ;
2641
+ } ;
2642
+
2643
+ {
2644
+ let error_code = 0x2004 ;
2645
+ let mut trampoline_inner_hop_error_packet = build_unencrypted_failure_packet (
2646
+ trampoline_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2647
+ error_code,
2648
+ & [ 0 ; 0 ] ,
2649
+ ) ;
2650
+
2651
+ crypt_failure_packet (
2652
+ trampoline_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2653
+ & mut trampoline_inner_hop_error_packet,
2654
+ ) ;
2655
+
2656
+ crypt_failure_packet (
2657
+ outer_onion_keys[ 1 ] . shared_secret . as_ref ( ) ,
2658
+ & mut trampoline_inner_hop_error_packet,
2659
+ ) ;
2660
+
2661
+ crypt_failure_packet (
2662
+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2663
+ & mut trampoline_inner_hop_error_packet,
2664
+ ) ;
2665
+
2666
+ let decrypted_failure = process_onion_failure (
2667
+ & secp_ctx,
2668
+ & logger,
2669
+ & htlc_source,
2670
+ trampoline_inner_hop_error_packet,
2671
+ None ,
2672
+ ) ;
2673
+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( error_code) ) ;
2674
+ }
2675
+ }
2676
+
2402
2677
#[ test]
2403
2678
fn test_non_attributable_failure_packet_onion ( ) {
2404
2679
// Create a failure packet with bogus data.
@@ -2484,7 +2759,8 @@ mod tests {
2484
2759
payment_id : PaymentId ( [ 1 ; 32 ] ) ,
2485
2760
} ;
2486
2761
2487
- let decrypted_failure = process_onion_failure ( & ctx_full, & logger, & htlc_source, packet) ;
2762
+ let decrypted_failure =
2763
+ process_onion_failure ( & ctx_full, & logger, & htlc_source, packet, None ) ;
2488
2764
2489
2765
decrypted_failure
2490
2766
}
0 commit comments