From 8f4e095e03f3b956da17b678b6a9b4805a012128 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 18 Jul 2019 19:51:12 -0400 Subject: [PATCH] Drop rng and SystemTime calls from KeysManager 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 | 63 +++++++++++++++++++++----------------- src/ln/functional_tests.rs | 7 ++--- src/util/test_utils.rs | 4 ++- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/chain/keysinterface.rs b/src/chain/keysinterface.rs index 013a3388..73d320b0 100644 --- a/src/chain/keysinterface.rs +++ b/src/chain/keysinterface.rs @@ -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, } 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) -> 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, 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"); diff --git a/src/ln/functional_tests.rs b/src/ln/functional_tests.rs index 892464fd..956995c9 100644 --- a/src/ln/functional_tests.rs +++ b/src/ln/functional_tests.rs @@ -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, diff --git a/src/util/test_utils.rs b/src/util/test_utils.rs index f88ded97..cc6fe969 100644 --- a/src/util/test_utils.rs +++ b/src/util/test_utils.rs @@ -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) -> 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), } -- 2.30.2