Skip to content

Commit

Permalink
generate ldk seed from bip32 master priv key
Browse files Browse the repository at this point in the history
  • Loading branch information
johncantrell97 committed Oct 3, 2022
1 parent 0fda2c7 commit 38f121a
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 56 deletions.
42 changes: 28 additions & 14 deletions senseicore/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,44 +622,58 @@ impl SenseiDatabase {
})
}

pub async fn get_seed(&self, node_id: String) -> Result<Option<Vec<u8>>, Error> {
self.get_value(node_id, String::from("seed"))
pub async fn get_entropy(&self, node_id: String) -> Result<Option<Vec<u8>>, 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<Option<Vec<u8>>, Error> {
pub fn get_entropy_sync(&self, node_id: String) -> Result<Option<Vec<u8>>, 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<u8>) -> Result<kv_store::Model, Error> {
self.set_value(node_id, String::from("seed"), seed).await
pub async fn set_entropy(
&self,
node_id: String,
entropy: Vec<u8>,
) -> Result<kv_store::Model, Error> {
self.set_value(node_id, String::from("entropy"), entropy)
.await
}

pub fn set_seed_sync(&self, node_id: String, seed: Vec<u8>) -> Result<kv_store::Model, Error> {
pub fn set_entropy_sync(
&self,
node_id: String,
entropy: Vec<u8>,
) -> Result<kv_store::Model, Error> {
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<u8>,
entropy: Vec<u8>,
) -> Result<kv_store::Model, Error> {
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<u8>) -> kv_store::ActiveModel {
pub fn get_entropy_active_model(
&self,
node_id: String,
entropy: Vec<u8>,
) -> 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()
Expand Down
10 changes: 6 additions & 4 deletions senseicore/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ pub enum Error {
LdkInvoiceSign(lightning_invoice::SignOrCreationError),
LdkInvoiceParse(lightning_invoice::ParseOrSemanticError),
InvalidSeedLength,
FailedToWriteSeed,
SeedNotFound,
InvalidEntropyLength,
FailedToWriteEntropy,
EntropyNotFound,
MacaroonNotFound,
Unauthenticated,
InvalidMacaroon,
Expand Down Expand Up @@ -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"),
Expand All @@ -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)
}
Expand Down
54 changes: 32 additions & 22 deletions senseicore/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>, Error> {
pub fn encrypt_entropy(entropy: &[u8; 32], passphrase: &[u8]) -> Result<Vec<u8>, 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<SenseiDatabase>,
) -> 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),
}
}

Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down
16 changes: 9 additions & 7 deletions senseicore/src/p2p/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};

Expand Down Expand Up @@ -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();
Expand Down
23 changes: 16 additions & 7 deletions senseicore/src/services/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())?;

Expand Down Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions senseicore/tests/smoke_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 38f121a

Please sign in to comment.