From 57caf641f3c4c7600da2b5c3abf447821728e43b Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Mon, 3 Oct 2022 10:25:50 -0400 Subject: [PATCH] generate cross node entropy and seed for PhantomKeysManager --- senseicore/src/database.rs | 68 ++++++++++++++++++++++++++++++++ senseicore/src/event_handler.rs | 5 ++- senseicore/src/node.rs | 63 ++++++++++++++++++++++------- senseicore/src/services/admin.rs | 32 ++++++++++++--- 4 files changed, 145 insertions(+), 23 deletions(-) diff --git a/senseicore/src/database.rs b/senseicore/src/database.rs index 10e9961..2c65391 100644 --- a/senseicore/src/database.rs +++ b/senseicore/src/database.rs @@ -680,6 +680,74 @@ impl SenseiDatabase { } } + pub async fn get_cross_node_entropy(&self, node_id: String) -> Result>, Error> { + self.get_value(node_id, String::from("cross_node_entropy")) + .await + .map(|model| model.map(|model| model.v)) + } + + pub fn get_cross_node_entropy_sync(&self, node_id: String) -> Result>, Error> { + tokio::task::block_in_place(move || { + self.runtime_handle + .block_on(async move { self.get_cross_node_entropy(node_id).await }) + }) + } + + pub async fn set_cross_node_entropy( + &self, + node_id: String, + cross_node_entropy: Vec, + ) -> Result { + self.set_value( + node_id, + String::from("cross_node_entropy"), + cross_node_entropy, + ) + .await + } + + pub fn set_cross_node_entropy_sync( + &self, + node_id: String, + cross_node_entropy: Vec, + ) -> Result { + tokio::task::block_in_place(move || { + self.runtime_handle.block_on(async move { + self.set_cross_node_entropy(node_id, cross_node_entropy) + .await + }) + }) + } + + pub async fn create_cross_node_entropy( + &self, + node_id: String, + cross_node_entropy: Vec, + ) -> Result { + self.create_value( + node_id, + String::from("cross_node_entropy"), + cross_node_entropy, + ) + .await + } + + pub fn get_cross_node_entropy_active_model( + &self, + node_id: String, + cross_node_entropy: Vec, + ) -> kv_store::ActiveModel { + let now = seconds_since_epoch(); + kv_store::ActiveModel { + node_id: ActiveValue::Set(node_id), + k: ActiveValue::Set(String::from("cross_node_entropy")), + v: ActiveValue::Set(cross_node_entropy), + created_at: ActiveValue::Set(now), + updated_at: ActiveValue::Set(now), + ..Default::default() + } + } + pub async fn insert_kv_store( &self, entity: kv_store::ActiveModel, diff --git a/senseicore/src/event_handler.rs b/senseicore/src/event_handler.rs index 2fb64e9..1f86f73 100644 --- a/senseicore/src/event_handler.rs +++ b/senseicore/src/event_handler.rs @@ -21,8 +21,9 @@ use bitcoin::{secp256k1::Secp256k1, Network}; use bitcoin_bech32::WitnessProgram; use entity::sea_orm::ActiveValue; use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; +use lightning::chain::keysinterface::PhantomKeysManager; use lightning::{ - chain::{chaininterface::ConfirmationTarget, keysinterface::KeysManager}, + chain::chaininterface::ConfirmationTarget, util::events::{Event, EventHandler, PaymentPurpose}, }; use rand::{thread_rng, Rng}; @@ -36,7 +37,7 @@ pub struct LightningNodeEventHandler { pub config: Arc, pub wallet: Arc>>, pub channel_manager: Arc, - pub keys_manager: Arc, + pub keys_manager: Arc, pub database: Arc, pub chain_manager: Arc, pub tokio_handle: Handle, diff --git a/senseicore/src/node.rs b/senseicore/src/node.rs index 5dd7be4..10dd949 100644 --- a/senseicore/src/node.rs +++ b/senseicore/src/node.rs @@ -51,7 +51,9 @@ use bitcoin::secp256k1::{PublicKey, Secp256k1}; use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey}; use bitcoin::BlockHash; use lightning::chain::chainmonitor; -use lightning::chain::keysinterface::{InMemorySigner, KeysInterface, KeysManager, Recipient}; +use lightning::chain::keysinterface::{ + InMemorySigner, KeysInterface, KeysManager, PhantomKeysManager, Recipient, +}; use lightning::chain::Watch; use lightning::chain::{self, Filter}; use lightning::ln::channelmanager::{self, ChannelDetails, ChannelManager as LdkChannelManager}; @@ -307,7 +309,7 @@ pub type ChainMonitor = chainmonitor::ChainMonitor< trait MustSized: Sized {} pub type SimpleArcChannelManager = - LdkChannelManager, Arc, Arc, Arc, Arc>; + LdkChannelManager, Arc, Arc, Arc, Arc>; pub type SimpleArcPeerManager = LdkPeerManager< SD, @@ -419,7 +421,7 @@ pub struct LightningNode { pub chain_manager: Arc, pub peer_manager: Arc, pub p2p: Arc, - pub keys_manager: Arc, + pub keys_manager: Arc, pub logger: Arc, pub invoice_payer: Arc, pub stop_listen: Arc, @@ -462,6 +464,28 @@ impl LightningNode { } } + async fn get_cross_node_entropy_for_node( + node_id: String, + passphrase: String, + database: Arc, + ) -> Result<[u8; 32], Error> { + let cryptor = RingCryptor::new(); + let mut entropy: [u8; 32] = [0; 32]; + match database.get_cross_node_entropy(node_id.clone()).await? { + Some(encrypted_entropy) => { + let decrypted_entropy = + cryptor.open(passphrase.as_bytes(), encrypted_entropy.as_slice())?; + + if decrypted_entropy.len() != 32 { + return Err(Error::InvalidEntropyLength); + } + entropy.copy_from_slice(decrypted_entropy.as_slice()); + Ok(entropy) + } + None => Err(Error::EntropyNotFound), + } + } + pub fn generate_macaroon(seed: &[u8], pubkey: String) -> Result<(Macaroon, String), Error> { let id = uuid::Uuid::new_v4().to_string(); let macaroon_data = MacaroonSession { @@ -572,22 +596,18 @@ impl LightningNode { ) -> Result<(Self, Vec>, BackgroundProcessor), Error> { let network = config.network; - let seed = + let entropy = LightningNode::get_entropy_for_node(id.clone(), passphrase.clone(), database.clone()) .await?; - let xprivkey = ExtendedPrivKey::new_master(network, &seed).unwrap(); - - let ldk_seed: [u8; 32] = xprivkey.private_key.secret_bytes(); - let cur = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap(); - let keys_manager = Arc::new(KeysManager::new( - &ldk_seed, - cur.as_secs(), - cur.subsec_nanos(), - )); + let cross_node_entropy = LightningNode::get_cross_node_entropy_for_node( + id.clone(), + passphrase.clone(), + database.clone(), + ) + .await?; + let xprivkey = ExtendedPrivKey::new_master(network, &entropy).unwrap(); let xkey = ExtendedKey::from(xprivkey); let native_segwit_base_path = "m/84"; let account_number = 0; @@ -615,6 +635,19 @@ impl LightningNode { let logger = Arc::new(FilesystemLogger::new(data_dir.clone(), config.network)); + let seed = LightningNode::get_seed_from_entropy(network, &entropy); + let cross_node_seed = LightningNode::get_seed_from_entropy(network, &cross_node_entropy); + + let cur = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap(); + let keys_manager = Arc::new(PhantomKeysManager::new( + &seed, + cur.as_secs(), + cur.subsec_nanos(), + &cross_node_seed, + )); + let broadcaster = Arc::new(SenseiBroadcaster::new( id.clone(), chain_manager.broadcaster.clone(), diff --git a/senseicore/src/services/admin.rs b/senseicore/src/services/admin.rs index 72c2adb..2637b66 100644 --- a/senseicore/src/services/admin.rs +++ b/senseicore/src/services/admin.rs @@ -699,20 +699,27 @@ impl AdminService { let mut nodes_with_macaroons = Vec::with_capacity(built_nodes.len()); let mut db_nodes = Vec::with_capacity(built_nodes.len()); - let mut db_seeds = Vec::with_capacity(built_nodes.len()); + let mut db_entropys = Vec::with_capacity(built_nodes.len()); + let mut db_cross_node_entropys = Vec::with_capacity(built_nodes.len()); let mut db_macaroons = Vec::with_capacity(built_nodes.len()); - for (node, macaroon, db_node, db_seed, db_macaroon) in built_nodes.drain(..) { + for (node, macaroon, db_node, db_entropy, db_cross_node_entropy, db_macaroon) in + built_nodes.drain(..) + { nodes_with_macaroons.push((node, macaroon)); db_nodes.push(db_node); - db_seeds.push(db_seed); + db_entropys.push(db_entropy); + db_cross_node_entropys.push(db_cross_node_entropy); db_macaroons.push(db_macaroon); } entity::node::Entity::insert_many(db_nodes) .exec(self.database.get_connection()) .await?; - entity::kv_store::Entity::insert_many(db_seeds) + entity::kv_store::Entity::insert_many(db_entropys) + .exec(self.database.get_connection()) + .await?; + entity::kv_store::Entity::insert_many(db_cross_node_entropys) .exec(self.database.get_connection()) .await?; entity::macaroon::Entity::insert_many(db_macaroons) @@ -734,6 +741,7 @@ impl AdminService { Macaroon, entity::node::ActiveModel, entity::kv_store::ActiveModel, + entity::kv_store::ActiveModel, entity::macaroon::ActiveModel, ), crate::error::Error, @@ -757,12 +765,20 @@ impl AdminService { // NODE ENTROPY let entropy = LightningNode::generate_entropy(); + let cross_node_entropy = LightningNode::generate_entropy(); + let encrypted_entropy = LightningNode::encrypt_entropy(&entropy, passphrase.as_bytes())?; + let encrypted_cross_node_entropy = + LightningNode::encrypt_entropy(&cross_node_entropy, passphrase.as_bytes())?; let entropy_active_model = self .database .get_entropy_active_model(node_id.clone(), encrypted_entropy); + let cross_node_entropy_active_model = self + .database + .get_cross_node_entropy_active_model(node_id.clone(), encrypted_cross_node_entropy); + let seed = LightningNode::get_seed_from_entropy(self.config.network, &entropy); // NODE PUBKEY @@ -818,6 +834,7 @@ impl AdminService { macaroon, active_node, entropy_active_model, + cross_node_entropy_active_model, db_macaroon, )) } @@ -829,10 +846,13 @@ impl AdminService { passphrase: String, role: node::NodeRole, ) -> Result<(node::Model, Macaroon), crate::error::Error> { - let (node, macaroon, db_node, db_seed, db_macaroon) = + let (node, macaroon, db_node, db_entropy, db_cross_node_entropy, db_macaroon) = self.build_node(username, alias, passphrase, role).await?; - db_seed.insert(self.database.get_connection()).await?; + db_entropy.insert(self.database.get_connection()).await?; + db_cross_node_entropy + .insert(self.database.get_connection()) + .await?; db_macaroon.insert(self.database.get_connection()).await?; db_node.insert(self.database.get_connection()).await?;