From 38f121a8ff7d53a711daf5a6ebb24f2f0c44372b Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Thu, 29 Sep 2022 15:48:29 -0400 Subject: [PATCH] generate ldk seed from bip32 master priv key --- senseicore/src/database.rs | 42 ++++++++++++++++--------- senseicore/src/error.rs | 10 +++--- senseicore/src/node.rs | 54 +++++++++++++++++++------------- senseicore/src/p2p/mod.rs | 16 +++++----- senseicore/src/services/admin.rs | 23 +++++++++----- senseicore/tests/smoke_test.rs | 4 +-- 6 files changed, 93 insertions(+), 56 deletions(-) diff --git a/senseicore/src/database.rs b/senseicore/src/database.rs index 758fa18..10e9961 100644 --- a/senseicore/src/database.rs +++ b/senseicore/src/database.rs @@ -622,44 +622,58 @@ impl SenseiDatabase { }) } - pub async fn get_seed(&self, node_id: String) -> Result>, Error> { - self.get_value(node_id, String::from("seed")) + pub async fn get_entropy(&self, node_id: String) -> Result>, Error> { + self.get_value(node_id, String::from("entropy")) .await .map(|model| model.map(|model| model.v)) } - pub fn get_seed_sync(&self, node_id: String) -> Result>, Error> { + pub fn get_entropy_sync(&self, node_id: String) -> Result>, Error> { tokio::task::block_in_place(move || { self.runtime_handle - .block_on(async move { self.get_seed(node_id).await }) + .block_on(async move { self.get_entropy(node_id).await }) }) } - pub async fn set_seed(&self, node_id: String, seed: Vec) -> Result { - self.set_value(node_id, String::from("seed"), seed).await + pub async fn set_entropy( + &self, + node_id: String, + entropy: Vec, + ) -> Result { + self.set_value(node_id, String::from("entropy"), entropy) + .await } - pub fn set_seed_sync(&self, node_id: String, seed: Vec) -> Result { + pub fn set_entropy_sync( + &self, + node_id: String, + entropy: Vec, + ) -> Result { tokio::task::block_in_place(move || { self.runtime_handle - .block_on(async move { self.set_seed(node_id, seed).await }) + .block_on(async move { self.set_entropy(node_id, entropy).await }) }) } - pub async fn create_seed( + pub async fn create_entropy( &self, node_id: String, - seed: Vec, + entropy: Vec, ) -> Result { - self.create_value(node_id, String::from("seed"), seed).await + self.create_value(node_id, String::from("entropy"), entropy) + .await } - pub fn get_seed_active_model(&self, node_id: String, seed: Vec) -> kv_store::ActiveModel { + pub fn get_entropy_active_model( + &self, + node_id: String, + entropy: Vec, + ) -> kv_store::ActiveModel { let now = seconds_since_epoch(); kv_store::ActiveModel { node_id: ActiveValue::Set(node_id), - k: ActiveValue::Set(String::from("seed")), - v: ActiveValue::Set(seed), + k: ActiveValue::Set(String::from("entropy")), + v: ActiveValue::Set(entropy), created_at: ActiveValue::Set(now), updated_at: ActiveValue::Set(now), ..Default::default() diff --git a/senseicore/src/error.rs b/senseicore/src/error.rs index 2360132..eaf6954 100644 --- a/senseicore/src/error.rs +++ b/senseicore/src/error.rs @@ -28,8 +28,9 @@ pub enum Error { LdkInvoiceSign(lightning_invoice::SignOrCreationError), LdkInvoiceParse(lightning_invoice::ParseOrSemanticError), InvalidSeedLength, - FailedToWriteSeed, - SeedNotFound, + InvalidEntropyLength, + FailedToWriteEntropy, + EntropyNotFound, MacaroonNotFound, Unauthenticated, InvalidMacaroon, @@ -57,9 +58,9 @@ impl Display for Error { Error::LdkInvoiceSign(e) => e.to_string(), Error::LdkInvoiceParse(e) => e.to_string(), Error::InvalidSeedLength => String::from("invalid seed length"), - Error::SeedNotFound => String::from("seed not found for node"), + Error::EntropyNotFound => String::from("entropy not found for node"), Error::MacaroonNotFound => String::from("macaroon not found for node"), - Error::FailedToWriteSeed => String::from("failed to write seed"), + Error::FailedToWriteEntropy => String::from("failed to write entropy"), Error::Unauthenticated => String::from("unauthenticated"), Error::InvalidMacaroon => String::from("invalid macaroon"), Error::AdminNodeNotCreated => String::from("admin node not created"), @@ -71,6 +72,7 @@ impl Display for Error { Error::ChannelOpenRejected(reason) => { format!("Channel open rejected by peer: {:?}", reason) } + Error::InvalidEntropyLength => String::from("invalid entropy length"), }; write!(f, "{}", str) } diff --git a/senseicore/src/node.rs b/senseicore/src/node.rs index d183777..5dd7be4 100644 --- a/senseicore/src/node.rs +++ b/senseicore/src/node.rs @@ -429,36 +429,36 @@ pub struct LightningNode { } impl LightningNode { - pub fn generate_seed() -> [u8; 32] { - let mut seed: [u8; 32] = [0; 32]; - thread_rng().fill_bytes(&mut seed); - seed + pub fn generate_entropy() -> [u8; 32] { + let mut entropy: [u8; 32] = [0; 32]; + thread_rng().fill_bytes(&mut entropy); + entropy } - pub fn encrypt_seed(seed: &[u8; 32], passphrase: &[u8]) -> Result, Error> { + pub fn encrypt_entropy(entropy: &[u8; 32], passphrase: &[u8]) -> Result, Error> { let cryptor = RingCryptor::new(); - Ok(cryptor.seal_with_passphrase(passphrase, seed)?) + Ok(cryptor.seal_with_passphrase(passphrase, entropy)?) } - async fn get_seed_for_node( + async fn get_entropy_for_node( node_id: String, passphrase: String, database: Arc, ) -> Result<[u8; 32], Error> { let cryptor = RingCryptor::new(); - let mut seed: [u8; 32] = [0; 32]; - match database.get_seed(node_id.clone()).await? { - Some(encrypted_seed) => { - let decrypted_seed = - cryptor.open(passphrase.as_bytes(), encrypted_seed.as_slice())?; - - if decrypted_seed.len() != 32 { - return Err(Error::InvalidSeedLength); + let mut entropy: [u8; 32] = [0; 32]; + match database.get_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); } - seed.copy_from_slice(decrypted_seed.as_slice()); - Ok(seed) + entropy.copy_from_slice(decrypted_entropy.as_slice()); + Ok(entropy) } - None => Err(Error::SeedNotFound), + None => Err(Error::EntropyNotFound), } } @@ -543,6 +543,11 @@ impl LightningNode { .map_err(|_e| Error::InvalidMacaroon) } + pub fn get_seed_from_entropy(network: Network, entropy: &[u8; 32]) -> [u8; 32] { + let xprivkey = ExtendedPrivKey::new_master(network, entropy).unwrap(); + xprivkey.private_key.secret_bytes() + } + pub fn get_node_pubkey_from_seed(seed: &[u8; 32]) -> String { let secp_ctx = Secp256k1::new(); let keys_manager = KeysManager::new(seed, 0, 0); @@ -568,16 +573,21 @@ impl LightningNode { let network = config.network; let seed = - LightningNode::get_seed_for_node(id.clone(), passphrase.clone(), database.clone()) + 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 keys_manager = Arc::new(KeysManager::new(&seed, cur.as_secs(), cur.subsec_nanos())); - - let xprivkey = ExtendedPrivKey::new_master(network, &seed).unwrap(); let xkey = ExtendedKey::from(xprivkey); let native_segwit_base_path = "m/84"; let account_number = 0; diff --git a/senseicore/src/p2p/mod.rs b/senseicore/src/p2p/mod.rs index 4aa97ac..f8ac34a 100644 --- a/senseicore/src/p2p/mod.rs +++ b/senseicore/src/p2p/mod.rs @@ -34,7 +34,7 @@ use crate::{ config::{P2PConfig, SenseiConfig}, database::SenseiDatabase, disk::FilesystemLogger, - node::{NetworkGraph, NetworkGraphMessageHandler, RoutingPeerManager}, + node::{LightningNode, NetworkGraph, NetworkGraphMessageHandler, RoutingPeerManager}, persist::{AnyKVStore, DatabaseStore, SenseiPersister}, }; @@ -125,18 +125,20 @@ impl SenseiP2P { route_handler: p2p_gossip.clone(), }; - let mut seed: [u8; 32] = [0; 32]; - rand::thread_rng().fill_bytes(&mut seed); + let mut entropy: [u8; 32] = [0; 32]; + rand::thread_rng().fill_bytes(&mut entropy); - match database.get_seed_sync(p2p_node_id.clone()).unwrap() { - Some(seed_vec) => { - seed.copy_from_slice(seed_vec.as_slice()); + match database.get_entropy_sync(p2p_node_id.clone()).unwrap() { + Some(entropy_vec) => { + entropy.copy_from_slice(entropy_vec.as_slice()); } None => { - let _res = database.set_seed_sync(p2p_node_id, seed.to_vec()); + let _res = database.set_entropy_sync(p2p_node_id, entropy.to_vec()); } } + let seed = LightningNode::get_seed_from_entropy(config.network, &entropy); + let cur = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap(); diff --git a/senseicore/src/services/admin.rs b/senseicore/src/services/admin.rs index a808ad4..72c2adb 100644 --- a/senseicore/src/services/admin.rs +++ b/senseicore/src/services/admin.rs @@ -755,19 +755,22 @@ impl AdminService { let node_directory = format!("{}/{}/{}", self.data_dir, self.config.network, node_id); fs::create_dir_all(node_directory)?; - // NODE SEED - let seed = LightningNode::generate_seed(); - let encrypted_seed = LightningNode::encrypt_seed(&seed, passphrase.as_bytes())?; + // NODE ENTROPY + let entropy = LightningNode::generate_entropy(); + let encrypted_entropy = LightningNode::encrypt_entropy(&entropy, passphrase.as_bytes())?; - let seed_active_model = self + let entropy_active_model = self .database - .get_seed_active_model(node_id.clone(), encrypted_seed); + .get_entropy_active_model(node_id.clone(), encrypted_entropy); + + let seed = LightningNode::get_seed_from_entropy(self.config.network, &entropy); // NODE PUBKEY let node_pubkey = LightningNode::get_node_pubkey_from_seed(&seed); // NODE MACAROON - let (macaroon, macaroon_id) = LightningNode::generate_macaroon(&seed, node_pubkey.clone())?; + let (macaroon, macaroon_id) = + LightningNode::generate_macaroon(&entropy, node_pubkey.clone())?; let encrypted_macaroon = LightningNode::encrypt_macaroon(&macaroon, passphrase.as_bytes())?; @@ -810,7 +813,13 @@ impl AdminService { status: node::NodeStatus::Stopped.into(), }; - Ok((node, macaroon, active_node, seed_active_model, db_macaroon)) + Ok(( + node, + macaroon, + active_node, + entropy_active_model, + db_macaroon, + )) } async fn create_node( diff --git a/senseicore/tests/smoke_test.rs b/senseicore/tests/smoke_test.rs index 8df89a3..e4ae21f 100644 --- a/senseicore/tests/smoke_test.rs +++ b/senseicore/tests/smoke_test.rs @@ -624,7 +624,7 @@ mod test { } async fn smoke_test(bitcoind: BitcoinD, admin_service: AdminService) { - let admin_token = create_admin_account(&admin_service, "admin", "admin").await; + let _admin_token = create_admin_account(&admin_service, "admin", "admin").await; let alice = create_node(&admin_service, "alice", "alice", true).await; let bob = create_node(&admin_service, "bob", "bob", true).await; let charlie = create_node(&admin_service, "charlie", "charlie", true).await; @@ -732,7 +732,7 @@ mod test { } async fn batch_open_channels_test(bitcoind: BitcoinD, admin_service: AdminService) { - let admin_token = create_admin_account(&admin_service, "admin", "admin").await; + let _admin_token = create_admin_account(&admin_service, "admin", "admin").await; let alice = create_node(&admin_service, "alice", "alice", true).await; let bob = create_node(&admin_service, "bob", "bob", true).await; let charlie = create_node(&admin_service, "charlie", "charlie", true).await;