@@ -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 , outer_session_priv_override : 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
+ outer_session_priv_override. 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 num_blinded_hops = path. blinded_tail . as_ref ( ) . map_or ( 0 , |bt| bt. hops . len ( ) ) ;
@@ -1471,7 +1473,7 @@ impl HTLCFailReason {
1471
1473
{
1472
1474
match self . 0 {
1473
1475
HTLCFailReasonRepr :: LightningError { ref err } => {
1474
- process_onion_failure ( secp_ctx, logger, & htlc_source, err. clone ( ) )
1476
+ process_onion_failure ( secp_ctx, logger, & htlc_source, err. clone ( ) , None )
1475
1477
} ,
1476
1478
#[ allow( unused) ]
1477
1479
HTLCFailReasonRepr :: Reason { ref failure_code, ref data, .. } => {
@@ -2039,11 +2041,11 @@ mod tests {
2039
2041
use crate :: prelude:: * ;
2040
2042
use crate :: util:: test_utils:: TestLogger ;
2041
2043
2044
+ use super :: * ;
2042
2045
use bitcoin:: hex:: FromHex ;
2043
2046
use bitcoin:: secp256k1:: Secp256k1 ;
2044
2047
use bitcoin:: secp256k1:: { PublicKey , SecretKey } ;
2045
-
2046
- use super :: * ;
2048
+ use types:: features:: Features ;
2047
2049
2048
2050
fn get_test_session_key ( ) -> SecretKey {
2049
2051
let hex = "4141414141414141414141414141414141414141414141414141414141414141" ;
@@ -2399,11 +2401,223 @@ mod tests {
2399
2401
2400
2402
// Assert that the original failure can be retrieved and that all hmacs check out.
2401
2403
let decrypted_failure =
2402
- process_onion_failure ( & ctx_full, & logger, & htlc_source, onion_error) ;
2404
+ process_onion_failure ( & ctx_full, & logger, & htlc_source, onion_error, None ) ;
2403
2405
2404
2406
assert_eq ! ( decrypted_failure. onion_error_code, Some ( 0x2002 ) ) ;
2405
2407
}
2406
2408
2409
+ fn build_trampoline_test_path ( ) -> Path {
2410
+ 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
+ // Dave'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
+ // Emily's pubkey
2453
+ TrampolineHop {
2454
+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991" ) . unwrap( ) ) . unwrap( ) ,
2455
+ node_features: Features :: empty( ) ,
2456
+ fee_msat: 150_500 ,
2457
+ cltv_expiry_delta: 36 ,
2458
+ } ,
2459
+ ] ,
2460
+
2461
+ // Dummy blinded hop (because LDK doesn't allow unblinded Trampoline receives)
2462
+ hops : vec ! [
2463
+ // Emily's dummy blinded node id
2464
+ BlindedHop {
2465
+ blinded_node_id: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "0295d40514096a8be54859e7dfe947b376eaafea8afe5cb4eb2c13ff857ed0b4be" ) . unwrap( ) ) . unwrap( ) ,
2466
+ encrypted_payload: vec![ ] ,
2467
+ }
2468
+ ] ,
2469
+ blinding_point : PublicKey :: from_slice ( & <Vec < u8 > >:: from_hex ( "02988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e" ) . unwrap ( ) ) . unwrap ( ) ,
2470
+ excess_final_cltv_expiry_delta : 0 ,
2471
+ final_value_msat : 150_000_000 ,
2472
+ } ) ,
2473
+ }
2474
+ }
2475
+
2476
+ #[ test]
2477
+ fn test_trampoline_onion_error_cryptography ( ) {
2478
+ // TODO(arik): check intermediate hops' perspectives once we have implemented forwarding
2479
+
2480
+ let secp_ctx = Secp256k1 :: new ( ) ;
2481
+ let logger: Arc < TestLogger > = Arc :: new ( TestLogger :: new ( ) ) ;
2482
+ let dummy_amt_msat = 150_000_000 ;
2483
+
2484
+ {
2485
+ // test vector per https://github.com/lightning/bolts/blob/079f761bf68caa48544bd6bf0a29591d43425b0b/bolt04/trampoline-onion-error-test.json
2486
+ // all dummy values
2487
+ let trampoline_session_priv = SecretKey :: from_slice ( & [ 3 ; 32 ] ) . unwrap ( ) ;
2488
+ let outer_session_priv = SecretKey :: from_slice ( & [ 4 ; 32 ] ) . unwrap ( ) ;
2489
+
2490
+ let htlc_source = HTLCSource :: OutboundRoute {
2491
+ path : build_trampoline_test_path ( ) ,
2492
+ session_priv : trampoline_session_priv,
2493
+ first_hop_htlc_msat : dummy_amt_msat,
2494
+ payment_id : PaymentId ( [ 1 ; 32 ] ) ,
2495
+ } ;
2496
+
2497
+ let error_packet_hex = "f8941a320b8fde4ad7b9b920c69cbf334114737497d93059d77e591eaa78d6334d3e2aeefcb0cc83402eaaf91d07d695cd895d9cad1018abdaf7d2a49d7657b1612729db7f393f0bb62b25afaaaa326d72a9214666025385033f2ec4605dcf1507467b5726d806da180ea224a7d8631cd31b0bdd08eead8bfe14fc8c7475e17768b1321b54dd4294aecc96da391efe0ca5bd267a45ee085c85a60cf9a9ac152fa4795fff8700a3ea4f848817f5e6943e855ab2e86f6929c9e885d8b20c49b14d2512c59ed21f10bd38691110b0d82c00d9fa48a20f10c7550358724c6e8e2b966e56a0aadf458695b273768062fa7c6e60eb72d4cdc67bf525c194e4a17fdcaa0e9d80480b586bf113f14eea530b6728a1c53fe5cee092e24a90f21f4b764015e7ed5e23" ;
2498
+ let error_packet =
2499
+ OnionErrorPacket { data : <Vec < u8 > >:: from_hex ( error_packet_hex) . unwrap ( ) } ;
2500
+ let decrypted_failure = process_onion_failure (
2501
+ & secp_ctx,
2502
+ & logger,
2503
+ & htlc_source,
2504
+ error_packet,
2505
+ Some ( outer_session_priv) ,
2506
+ ) ;
2507
+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( 0x400f ) ) ;
2508
+ }
2509
+
2510
+ {
2511
+ // shared secret cryptography sanity tests
2512
+ let session_priv = get_test_session_key ( ) ;
2513
+ let path = build_trampoline_test_path ( ) ;
2514
+
2515
+ let trampoline_onion_keys = construct_trampoline_onion_keys (
2516
+ & secp_ctx,
2517
+ & path. blinded_tail . as_ref ( ) . unwrap ( ) ,
2518
+ & session_priv,
2519
+ )
2520
+ . unwrap ( ) ;
2521
+
2522
+ let outer_onion_keys = {
2523
+ let session_priv_hash = Sha256 :: hash ( & session_priv. secret_bytes ( ) ) . to_byte_array ( ) ;
2524
+ let outer_session_priv = SecretKey :: from_slice ( & session_priv_hash[ ..] ) . unwrap ( ) ;
2525
+ construct_onion_keys ( & Secp256k1 :: new ( ) , & path, & outer_session_priv) . unwrap ( )
2526
+ } ;
2527
+
2528
+ let htlc_source = HTLCSource :: OutboundRoute {
2529
+ path,
2530
+ session_priv,
2531
+ first_hop_htlc_msat : dummy_amt_msat,
2532
+ payment_id : PaymentId ( [ 1 ; 32 ] ) ,
2533
+ } ;
2534
+
2535
+ {
2536
+ let error_code = 0x2002 ;
2537
+ let mut first_hop_error_packet = build_unencrypted_failure_packet (
2538
+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2539
+ error_code,
2540
+ & [ 0 ; 0 ] ,
2541
+ ) ;
2542
+
2543
+ crypt_failure_packet (
2544
+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2545
+ & mut first_hop_error_packet,
2546
+ ) ;
2547
+
2548
+ let decrypted_failure = process_onion_failure (
2549
+ & secp_ctx,
2550
+ & logger,
2551
+ & htlc_source,
2552
+ first_hop_error_packet,
2553
+ None ,
2554
+ ) ;
2555
+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( error_code) ) ;
2556
+ } ;
2557
+
2558
+ {
2559
+ let error_code = 0x2003 ;
2560
+ let mut trampoline_outer_hop_error_packet = build_unencrypted_failure_packet (
2561
+ outer_onion_keys[ 1 ] . shared_secret . as_ref ( ) ,
2562
+ error_code,
2563
+ & [ 0 ; 0 ] ,
2564
+ ) ;
2565
+
2566
+ crypt_failure_packet (
2567
+ outer_onion_keys[ 1 ] . shared_secret . as_ref ( ) ,
2568
+ & mut trampoline_outer_hop_error_packet,
2569
+ ) ;
2570
+
2571
+ crypt_failure_packet (
2572
+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2573
+ & mut trampoline_outer_hop_error_packet,
2574
+ ) ;
2575
+
2576
+ let decrypted_failure = process_onion_failure (
2577
+ & secp_ctx,
2578
+ & logger,
2579
+ & htlc_source,
2580
+ trampoline_outer_hop_error_packet,
2581
+ None ,
2582
+ ) ;
2583
+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( error_code) ) ;
2584
+ } ;
2585
+
2586
+ {
2587
+ let error_code = 0x2004 ;
2588
+ let mut trampoline_inner_hop_error_packet = build_unencrypted_failure_packet (
2589
+ trampoline_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2590
+ error_code,
2591
+ & [ 0 ; 0 ] ,
2592
+ ) ;
2593
+
2594
+ crypt_failure_packet (
2595
+ trampoline_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2596
+ & mut trampoline_inner_hop_error_packet,
2597
+ ) ;
2598
+
2599
+ crypt_failure_packet (
2600
+ outer_onion_keys[ 1 ] . shared_secret . as_ref ( ) ,
2601
+ & mut trampoline_inner_hop_error_packet,
2602
+ ) ;
2603
+
2604
+ crypt_failure_packet (
2605
+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2606
+ & mut trampoline_inner_hop_error_packet,
2607
+ ) ;
2608
+
2609
+ let decrypted_failure = process_onion_failure (
2610
+ & secp_ctx,
2611
+ & logger,
2612
+ & htlc_source,
2613
+ trampoline_inner_hop_error_packet,
2614
+ None ,
2615
+ ) ;
2616
+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( error_code) ) ;
2617
+ }
2618
+ }
2619
+ }
2620
+
2407
2621
#[ test]
2408
2622
fn test_non_attributable_failure_packet_onion ( ) {
2409
2623
// Create a failure packet with bogus data.
@@ -2489,7 +2703,8 @@ mod tests {
2489
2703
payment_id : PaymentId ( [ 1 ; 32 ] ) ,
2490
2704
} ;
2491
2705
2492
- let decrypted_failure = process_onion_failure ( & ctx_full, & logger, & htlc_source, packet) ;
2706
+ let decrypted_failure =
2707
+ process_onion_failure ( & ctx_full, & logger, & htlc_source, packet, None ) ;
2493
2708
2494
2709
decrypted_failure
2495
2710
}
0 commit comments