Drop rng and SystemTime calls from KeysManager
authorMatt Corallo <git@bluematt.me>
Thu, 18 Jul 2019 23:51:12 +0000 (19:51 -0400)
committerMatt Corallo <git@bluematt.me>
Tue, 23 Jul 2019 18:29:53 +0000 (14:29 -0400)
They were only used for ensuring generated keys were globally
unique (ie in case the user opened the same seed at a different
time, we need generated keys to be globally unique).

Instead, we let the user specify a time in secs/nanos, and provide
a precise meaning for the user to understand.

src/chain/keysinterface.rs
src/ln/functional_tests.rs
src/util/test_utils.rs

index 013a3388f797a302304b2307637804f408f2570a..73d320b0f69a6e123deb20d1f6261960d57d2680 100644 (file)
@@ -9,6 +9,7 @@ use bitcoin::network::constants::Network;
 use bitcoin::util::bip32::{ExtendedPrivKey, ExtendedPubKey, ChildNumber};
 
 use bitcoin_hashes::{Hash, HashEngine};
+use bitcoin_hashes::sha256::HashEngine as Sha256State;
 use bitcoin_hashes::sha256::Hash as Sha256;
 use bitcoin_hashes::hash160::Hash as Hash160;
 
@@ -16,11 +17,9 @@ use secp256k1::key::{SecretKey, PublicKey};
 use secp256k1::Secp256k1;
 use secp256k1;
 
-use util::logger::Logger;
-use util::rng;
 use util::byte_utils;
+use util::logger::Logger;
 
-use std::time::{SystemTime, UNIX_EPOCH};
 use std::sync::Arc;
 use std::sync::atomic::{AtomicUsize, Ordering};
 
@@ -131,13 +130,31 @@ pub struct KeysManager {
        channel_id_master_key: ExtendedPrivKey,
        channel_id_child_index: AtomicUsize,
 
+       unique_start: Sha256State,
        logger: Arc<Logger>,
 }
 
 impl KeysManager {
        /// Constructs a KeysManager from a 32-byte seed. If the seed is in some way biased (eg your
-       /// RNG is busted) this may panic.
-       pub fn new(seed: &[u8; 32], network: Network, logger: Arc<Logger>) -> KeysManager {
+       /// RNG is busted) this may panic (but more importantly, you will possibly lose funds).
+       /// starting_time isn't strictly required to actually be a time, but it must absolutely,
+       /// without a doubt, be unique to this instance. ie if you start multiple times with the same
+       /// seed, starting_time must be unique to each run. Thus, the easiest way to achieve this is to
+       /// simply use the current time (with very high precision).
+       ///
+       /// The seed MUST be backed up safely prior to use so that the keys can be re-created, however,
+       /// obviously, starting_time should be unique every time you reload the library - it is only
+       /// used to generate new ephemeral key data (which will be stored by the individual channel if
+       /// necessary).
+       ///
+       /// Note that the seed is required to recover certain on-chain funds independent of
+       /// ChannelMonitor data, though a current copy of ChannelMonitor data is also required for any
+       /// channel, and some on-chain during-closing funds.
+       ///
+       /// Note that until the 0.1 release there is no guarantee of backward compatibility between
+       /// versions. Once the library is more fully supported, the docs will be updated to include a
+       /// detailed description of the guarantee.
+       pub fn new(seed: &[u8; 32], network: Network, logger: Arc<Logger>, starting_time_secs: u64, starting_time_nanos: u32) -> KeysManager {
                let secp_ctx = Secp256k1::signing_only();
                match ExtendedPrivKey::new_master(network.clone(), seed) {
                        Ok(master_key) => {
@@ -158,6 +175,12 @@ impl KeysManager {
                                let channel_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(3).unwrap()).expect("Your RNG is busted");
                                let session_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(4).unwrap()).expect("Your RNG is busted");
                                let channel_id_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(5).unwrap()).expect("Your RNG is busted");
+
+                               let mut unique_start = Sha256::engine();
+                               unique_start.input(&byte_utils::be64_to_array(starting_time_secs));
+                               unique_start.input(&byte_utils::be32_to_array(starting_time_nanos));
+                               unique_start.input(seed);
+
                                KeysManager {
                                        secp_ctx,
                                        node_secret,
@@ -170,6 +193,7 @@ impl KeysManager {
                                        channel_id_master_key,
                                        channel_id_child_index: AtomicUsize::new(0),
 
+                                       unique_start,
                                        logger,
                                }
                        },
@@ -193,24 +217,15 @@ impl KeysInterface for KeysManager {
 
        fn get_channel_keys(&self, _inbound: bool) -> ChannelKeys {
                // We only seriously intend to rely on the channel_master_key for true secure
-               // entropy, everything else just ensures uniqueness. We generally don't expect
-               // all clients to have non-broken RNGs here, so we also include the current
-               // time as a fallback to get uniqueness.
-               let mut sha = Sha256::engine();
-
-               let mut seed = [0u8; 32];
-               rng::fill_bytes(&mut seed[..]);
-               sha.input(&seed);
-
-               let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
-               sha.input(&byte_utils::be32_to_array(now.subsec_nanos()));
-               sha.input(&byte_utils::be64_to_array(now.as_secs()));
+               // 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.unique_start.clone();
 
                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");
                sha.input(&child_privkey.private_key.key[..]);
 
-               seed = Sha256::from_engine(sha).into_inner();
+               let seed = Sha256::from_engine(sha).into_inner();
 
                let commitment_seed = {
                        let mut sha = Sha256::engine();
@@ -244,11 +259,7 @@ impl KeysInterface for KeysManager {
        }
 
        fn get_session_key(&self) -> SecretKey {
-               let mut sha = Sha256::engine();
-
-               let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
-               sha.input(&byte_utils::be32_to_array(now.subsec_nanos()));
-               sha.input(&byte_utils::be64_to_array(now.as_secs()));
+               let mut sha = self.unique_start.clone();
 
                let child_ix = self.session_child_index.fetch_add(1, Ordering::AcqRel);
                let child_privkey = self.session_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(child_ix as u32).expect("key space exhausted")).expect("Your RNG is busted");
@@ -257,11 +268,7 @@ impl KeysInterface for KeysManager {
        }
 
        fn get_channel_id(&self) -> [u8; 32] {
-               let mut sha = Sha256::engine();
-
-               let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
-               sha.input(&byte_utils::be32_to_array(now.subsec_nanos()));
-               sha.input(&byte_utils::be64_to_array(now.as_secs()));
+               let mut sha = self.unique_start.clone();
 
                let child_ix = self.channel_id_child_index.fetch_add(1, Ordering::AcqRel);
                let child_privkey = self.channel_id_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(child_ix as u32).expect("key space exhausted")).expect("Your RNG is busted");
index 892464fd933486009784a9872cb278d37d6749b5..956995c9b97e436a7eaa037225ff8a28c890e6a4 100644 (file)
@@ -5,7 +5,6 @@
 use chain::transaction::OutPoint;
 use chain::chaininterface::{ChainListener, ChainWatchInterface};
 use chain::keysinterface::{KeysInterface, SpendableOutputDescriptor};
-use chain::keysinterface;
 use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC, BREAKDOWN_TIMEOUT};
 use ln::channelmanager::{ChannelManager,ChannelManagerReadArgs,HTLCForwardInfo,RAACommitmentOrder, PaymentPreimage, PaymentHash};
 use ln::channelmonitor::{ChannelMonitor, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ManyChannelMonitor, ANTI_REORG_DELAY};
@@ -3225,7 +3224,7 @@ fn test_no_txn_manager_serialize_deserialize() {
 
        let mut nodes_0_read = &nodes_0_serialized[..];
        let config = UserConfig::new();
-       let keys_manager = Arc::new(keysinterface::KeysManager::new(&nodes[0].node_seed, Network::Testnet, Arc::new(test_utils::TestLogger::new())));
+       let keys_manager = Arc::new(test_utils::TestKeysInterface::new(&nodes[0].node_seed, Network::Testnet, Arc::new(test_utils::TestLogger::new())));
        let (_, nodes_0_deserialized) = {
                let mut channel_monitors = HashMap::new();
                channel_monitors.insert(chan_0_monitor.get_funding_txo().unwrap(), &chan_0_monitor);
@@ -3290,7 +3289,7 @@ fn test_simple_manager_serialize_deserialize() {
        assert!(chan_0_monitor_read.is_empty());
 
        let mut nodes_0_read = &nodes_0_serialized[..];
-       let keys_manager = Arc::new(keysinterface::KeysManager::new(&nodes[0].node_seed, Network::Testnet, Arc::new(test_utils::TestLogger::new())));
+       let keys_manager = Arc::new(test_utils::TestKeysInterface::new(&nodes[0].node_seed, Network::Testnet, Arc::new(test_utils::TestLogger::new())));
        let (_, nodes_0_deserialized) = {
                let mut channel_monitors = HashMap::new();
                channel_monitors.insert(chan_0_monitor.get_funding_txo().unwrap(), &chan_0_monitor);
@@ -3354,7 +3353,7 @@ fn test_manager_serialize_deserialize_inconsistent_monitor() {
        }
 
        let mut nodes_0_read = &nodes_0_serialized[..];
-       let keys_manager = Arc::new(keysinterface::KeysManager::new(&nodes[0].node_seed, Network::Testnet, Arc::new(test_utils::TestLogger::new())));
+       let keys_manager = Arc::new(test_utils::TestKeysInterface::new(&nodes[0].node_seed, Network::Testnet, Arc::new(test_utils::TestLogger::new())));
        let (_, nodes_0_deserialized) = <(Sha256dHash, ChannelManager)>::read(&mut nodes_0_read, ChannelManagerReadArgs {
                default_config: UserConfig::new(),
                keys_manager,
index f88ded97e2d5ce67472d3f6b095518b3a105ca0c..cc6fe969df81f47fa6864df00905e9aa24c1c51d 100644 (file)
@@ -18,6 +18,7 @@ use bitcoin::network::constants::Network;
 
 use secp256k1::{SecretKey, PublicKey};
 
+use std::time::{SystemTime, UNIX_EPOCH};
 use std::sync::{Arc,Mutex};
 use std::{mem};
 
@@ -242,8 +243,9 @@ impl keysinterface::KeysInterface for TestKeysInterface {
 
 impl TestKeysInterface {
        pub fn new(seed: &[u8; 32], network: Network, logger: Arc<Logger>) -> Self {
+               let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
                Self {
-                       backing: keysinterface::KeysManager::new(seed, network, logger),
+                       backing: keysinterface::KeysManager::new(seed, network, logger, now.as_secs(), now.subsec_nanos()),
                        override_session_priv: Mutex::new(None),
                        override_channel_id_priv: Mutex::new(None),
                }