Extend KeysInterface with derive_channel_keys
authorAntoine Riard <ariard@student.42.fr>
Wed, 6 May 2020 00:00:01 +0000 (20:00 -0400)
committerAntoine Riard <ariard@student.42.fr>
Tue, 12 May 2020 21:24:17 +0000 (17:24 -0400)
A dynamic-p2wsh-output like `to_local` on local commitment/HTLC txn
require a signature from delayed_payment_key to be spend. Instead of
sending private key in descriptor, we ask for spender to derive again
the corresponding ChannelKeys based on key state, uniquely identifying
a channel and encompassing its unique start data.

Descriptor modification is done in next commit.

fuzz/src/chanmon_consistency.rs
fuzz/src/full_stack.rs
lightning/src/chain/keysinterface.rs
lightning/src/ln/channel.rs
lightning/src/ln/channelmonitor.rs
lightning/src/util/enforcing_trait_impls.rs
lightning/src/util/test_utils.rs

index 135249f7414479c8405003f4fd57398f536c9035..6dccc23d7dffc2f1cacc6c9536d650f543a68ef2 100644 (file)
@@ -163,6 +163,7 @@ impl KeysInterface for KeyProvider {
                        SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, self.node_id]).unwrap(),
                        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, self.node_id],
                        channel_value_satoshis,
+                       (0, 0),
                ))
        }
 
index afdb5a83877b2a9669fbcadf6bb75be0fc073061..2c551e923afd3681552882658d37df0ddc652720 100644 (file)
@@ -261,6 +261,7 @@ impl KeysInterface for KeyProvider {
                                SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, ctr]).unwrap(),
                                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, ctr],
                                channel_value_satoshis,
+                               (0, 0),
                        )
                } else {
                        InMemoryChannelKeys::new(
@@ -272,6 +273,7 @@ impl KeysInterface for KeyProvider {
                                SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, ctr]).unwrap(),
                                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, ctr],
                                channel_value_satoshis,
+                               (0, 0),
                        )
                })
        }
index c6c66489541a00667ac58bf2289f5170f0ac7bbd..933e35faf1c8295e5a8e9c4b554e5e1deb128272 100644 (file)
@@ -209,6 +209,8 @@ pub trait ChannelKeys : Send+Clone {
        fn commitment_seed<'a>(&'a self) -> &'a [u8; 32];
        /// Gets the local channel public keys and basepoints
        fn pubkeys<'a>(&'a self) -> &'a ChannelPublicKeys;
+       /// Gets the key derivation parameters in case of new derivation.
+       fn key_derivation_params(&self) -> (u64, u64);
 
        /// Create a signature for a remote commitment transaction and associated HTLC transactions.
        ///
@@ -331,6 +333,8 @@ pub struct InMemoryChannelKeys {
        pub(crate) remote_channel_pubkeys: Option<ChannelPublicKeys>,
        /// The total value of this channel
        channel_value_satoshis: u64,
+       /// Key derivation parameters
+       key_derivation_params: (u64, u64),
 }
 
 impl InMemoryChannelKeys {
@@ -343,7 +347,8 @@ impl InMemoryChannelKeys {
                delayed_payment_base_key: SecretKey,
                htlc_base_key: SecretKey,
                commitment_seed: [u8; 32],
-               channel_value_satoshis: u64) -> InMemoryChannelKeys {
+               channel_value_satoshis: u64,
+               key_derivation_params: (u64, u64)) -> InMemoryChannelKeys {
                let local_channel_pubkeys =
                        InMemoryChannelKeys::make_local_keys(secp_ctx, &funding_key, &revocation_base_key,
                                                             &payment_key, &delayed_payment_base_key,
@@ -358,6 +363,7 @@ impl InMemoryChannelKeys {
                        channel_value_satoshis,
                        local_channel_pubkeys,
                        remote_channel_pubkeys: None,
+                       key_derivation_params,
                }
        }
 
@@ -386,6 +392,7 @@ impl ChannelKeys for InMemoryChannelKeys {
        fn htlc_base_key(&self) -> &SecretKey { &self.htlc_base_key }
        fn commitment_seed(&self) -> &[u8; 32] { &self.commitment_seed }
        fn pubkeys<'a>(&'a self) -> &'a ChannelPublicKeys { &self.local_channel_pubkeys }
+       fn key_derivation_params(&self) -> (u64, u64) { self.key_derivation_params }
 
        fn sign_remote_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, feerate_per_kw: u64, commitment_tx: &Transaction, keys: &TxCreationKeys, htlcs: &[&HTLCOutputInCommitment], to_self_delay: u16, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
                if commitment_tx.input.len() != 1 { return Err(()); }
@@ -490,6 +497,8 @@ impl Writeable for InMemoryChannelKeys {
                self.commitment_seed.write(writer)?;
                self.remote_channel_pubkeys.write(writer)?;
                self.channel_value_satoshis.write(writer)?;
+               self.key_derivation_params.0.write(writer)?;
+               self.key_derivation_params.1.write(writer)?;
 
                Ok(())
        }
@@ -510,6 +519,8 @@ impl Readable for InMemoryChannelKeys {
                        InMemoryChannelKeys::make_local_keys(&secp_ctx, &funding_key, &revocation_base_key,
                                                             &payment_key, &delayed_payment_base_key,
                                                             &htlc_base_key);
+               let user_id_1 = Readable::read(reader)?;
+               let user_id_2 = Readable::read(reader)?;
 
                Ok(InMemoryChannelKeys {
                        funding_key,
@@ -520,7 +531,8 @@ impl Readable for InMemoryChannelKeys {
                        commitment_seed,
                        channel_value_satoshis,
                        local_channel_pubkeys,
-                       remote_channel_pubkeys
+                       remote_channel_pubkeys,
+                       key_derivation_params: (user_id_1, user_id_2),
                })
        }
 }
@@ -620,31 +632,24 @@ impl KeysManager {
                unique_start.input(&self.seed);
                unique_start
        }
-}
-
-impl KeysInterface for KeysManager {
-       type ChanKeySigner = InMemoryChannelKeys;
-
-       fn get_node_secret(&self) -> SecretKey {
-               self.node_secret.clone()
-       }
-
-       fn get_destination_script(&self) -> Script {
-               self.destination_script.clone()
-       }
-
-       fn get_shutdown_pubkey(&self) -> PublicKey {
-               self.shutdown_pubkey.clone()
-       }
+       /// Derive an old set of ChannelKeys for per-channel secrets based on a key derivation
+       /// parameters.
+       /// Key derivation parameters are accessible through a per-channel secrets
+       /// ChannelKeys::key_derivation_params and is provided inside DynamicOuputP2WSH in case of
+       /// onchain output detection for which a corresponding delayed_payment_key must be derived.
+       pub fn derive_channel_keys(&self, channel_value_satoshis: u64, user_id_1: u64, user_id_2: u64) -> InMemoryChannelKeys {
+               let chan_id = ((user_id_1 & 0xFFFF0000) >> 32) as u32;
+               let mut unique_start = Sha256::engine();
+               unique_start.input(&byte_utils::be64_to_array(user_id_2));
+               unique_start.input(&byte_utils::be32_to_array(user_id_1 as u32));
+               unique_start.input(&self.seed);
 
-       fn get_channel_keys(&self, _inbound: bool, channel_value_satoshis: u64) -> InMemoryChannelKeys {
                // We only seriously intend to rely on the channel_master_key for true secure
                // entropy, everything else just ensures uniqueness. We rely on the unique_start (ie
                // starting_time provided in the constructor) to be unique.
                let mut sha = self.derive_unique_start();
 
-               let child_ix = self.channel_child_index.fetch_add(1, Ordering::AcqRel);
-               let child_privkey = self.channel_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(child_ix as u32).expect("key space exhausted")).expect("Your RNG is busted");
+               let child_privkey = self.channel_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(chan_id).expect("key space exhausted")).expect("Your RNG is busted");
                sha.input(&child_privkey.private_key.key[..]);
 
                let seed = Sha256::from_engine(sha).into_inner();
@@ -678,9 +683,32 @@ impl KeysInterface for KeysManager {
                        delayed_payment_base_key,
                        htlc_base_key,
                        commitment_seed,
-                       channel_value_satoshis
+                       channel_value_satoshis,
+                       (user_id_1, user_id_2),
                )
        }
+}
+
+impl KeysInterface for KeysManager {
+       type ChanKeySigner = InMemoryChannelKeys;
+
+       fn get_node_secret(&self) -> SecretKey {
+               self.node_secret.clone()
+       }
+
+       fn get_destination_script(&self) -> Script {
+               self.destination_script.clone()
+       }
+
+       fn get_shutdown_pubkey(&self) -> PublicKey {
+               self.shutdown_pubkey.clone()
+       }
+
+       fn get_channel_keys(&self, _inbound: bool, channel_value_satoshis: u64) -> InMemoryChannelKeys {
+               let child_ix = self.channel_child_index.fetch_add(1, Ordering::AcqRel);
+               let ix_and_nanos: u64 = (child_ix as u64) << 32 | (self.starting_time_nanos as u64);
+               self.derive_channel_keys(channel_value_satoshis, ix_and_nanos, self.starting_time_secs)
+       }
 
        fn get_onion_rand(&self) -> (SecretKey, [u8; 32]) {
                let mut sha = self.derive_unique_start();
index 92428a09d0e081629cda17e94f35ce01683b8b01..1ce6f2c6649d7d5f2beefd80198ecacb23f16dcc 100644 (file)
@@ -4447,6 +4447,7 @@ mod tests {
                        // These aren't set in the test vectors:
                        [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
                        10_000_000,
+                       (0, 0)
                );
 
                assert_eq!(PublicKey::from_secret_key(&secp_ctx, chan_keys.funding_key()).serialize()[..],
index 9324437dc7370ba45c0bfe25a90f72e35897ce31..5bde4792ea5e73e3bec4a5911ed7b416cf28cef0 100644 (file)
@@ -2529,6 +2529,7 @@ mod tests {
                        SecretKey::from_slice(&[41; 32]).unwrap(),
                        [41; 32],
                        0,
+                       (0, 0)
                );
 
                // Prune with one old state and a local commitment tx holding a few overlaps with the
index dacec1a936adcc9e33c711dc5977f2f96c1b5a4f..5c5b7044334a9a44322a75c588395726242eacc6 100644 (file)
@@ -60,6 +60,7 @@ impl ChannelKeys for EnforcingChannelKeys {
        fn htlc_base_key(&self) -> &SecretKey { self.inner.htlc_base_key() }
        fn commitment_seed(&self) -> &[u8; 32] { self.inner.commitment_seed() }
        fn pubkeys<'a>(&'a self) -> &'a ChannelPublicKeys { self.inner.pubkeys() }
+       fn key_derivation_params(&self) -> (u64, u64) { self.inner.key_derivation_params() }
 
        fn sign_remote_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, feerate_per_kw: u64, commitment_tx: &Transaction, keys: &TxCreationKeys, htlcs: &[&HTLCOutputInCommitment], to_self_delay: u16, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
                if commitment_tx.input.len() != 1 { panic!("lightning commitment transactions have a single input"); }
index 6a652aa6b39c0016c0ac3705cfcf9206733e04a8..28a398f5085ac2a8ac3869821bfb08cb3b9d0b9f 100644 (file)
@@ -283,6 +283,9 @@ impl TestKeysInterface {
                        override_channel_id_priv: Mutex::new(None),
                }
        }
+       pub fn derive_channel_keys(&self, channel_value_satoshis: u64, user_id_1: u64, user_id_2: u64) -> EnforcingChannelKeys {
+               EnforcingChannelKeys::new(self.backing.derive_channel_keys(channel_value_satoshis, user_id_1, user_id_2))
+       }
 }
 
 pub struct TestChainWatcher {