@@ -942,7 +942,7 @@ pub(crate) struct DecodedOnionFailure {
942942#[ inline]
943943pub ( super ) fn process_onion_failure < T : secp256k1:: Signing , L : Deref > (
944944 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 > ,
946946) -> DecodedOnionFailure
947947where
948948 L :: Target : Logger ,
@@ -1004,8 +1004,10 @@ where
10041004
10051005 let outer_session_priv = path. has_trampoline_hops ( ) . then ( || {
10061006 // 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+ } )
10091011 } ) ;
10101012
10111013 let num_blinded_hops = path. blinded_tail . as_ref ( ) . map_or ( 0 , |bt| bt. hops . len ( ) ) ;
@@ -1471,7 +1473,7 @@ impl HTLCFailReason {
14711473 {
14721474 match self . 0 {
14731475 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 )
14751477 } ,
14761478 #[ allow( unused) ]
14771479 HTLCFailReasonRepr :: Reason { ref failure_code, ref data, .. } => {
@@ -2039,11 +2041,11 @@ mod tests {
20392041 use crate :: prelude:: * ;
20402042 use crate :: util:: test_utils:: TestLogger ;
20412043
2044+ use super :: * ;
20422045 use bitcoin:: hex:: FromHex ;
20432046 use bitcoin:: secp256k1:: Secp256k1 ;
20442047 use bitcoin:: secp256k1:: { PublicKey , SecretKey } ;
2045-
2046- use super :: * ;
2048+ use types:: features:: Features ;
20472049
20482050 fn get_test_session_key ( ) -> SecretKey {
20492051 let hex = "4141414141414141414141414141414141414141414141414141414141414141" ;
@@ -2399,11 +2401,223 @@ mod tests {
23992401
24002402 // Assert that the original failure can be retrieved and that all hmacs check out.
24012403 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 ) ;
24032405
24042406 assert_eq ! ( decrypted_failure. onion_error_code, Some ( 0x2002 ) ) ;
24052407 }
24062408
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+
24072621 #[ test]
24082622 fn test_non_attributable_failure_packet_onion ( ) {
24092623 // Create a failure packet with bogus data.
@@ -2489,7 +2703,8 @@ mod tests {
24892703 payment_id : PaymentId ( [ 1 ; 32 ] ) ,
24902704 } ;
24912705
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 ) ;
24932708
24942709 decrypted_failure
24952710 }
0 commit comments