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