diff --git a/Cargo.toml b/Cargo.toml index eda73f04f..cb856fab1 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,7 @@ bip39 = { version = "2.0.0", features = ["rand"] } bip21 = { version = "0.5", features = ["std"], default-features = false } base64 = { version = "0.22.1", default-features = false, features = ["std"] } -rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] } +getrandom = { version = "0.3", default-features = false } chrono = { version = "0.4", default-features = false, features = ["clock"] } tokio = { version = "1.37", default-features = false, features = [ "rt-multi-thread", "time", "sync", "macros" ] } esplora-client = { version = "0.12", default-features = false, features = ["tokio", "async-https-rustls"] } @@ -85,6 +85,7 @@ winapi = { version = "0.3", features = ["winbase"] } [dev-dependencies] lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0", features = ["std", "_test_utils"] } +rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] } proptest = "1.0.0" regex = "1.5.6" criterion = { version = "0.7.0", features = ["async_tokio"] } diff --git a/src/event.rs b/src/event.rs index e24059ec7..5f02f9f87 100644 --- a/src/event.rs +++ b/src/event.rs @@ -22,6 +22,7 @@ use lightning::impl_writeable_tlv_based_enum; use lightning::ln::channelmanager::PaymentId; use lightning::ln::types::ChannelId; use lightning::routing::gossip::NodeId; +use lightning::sign::EntropySource; use lightning::util::config::{ ChannelConfigOverrides, ChannelConfigUpdate, ChannelHandshakeConfigUpdate, }; @@ -30,7 +31,6 @@ use lightning::util::persist::KVStore; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; use lightning_liquidity::lsps2::utils::compute_opening_fee; use lightning_types::payment::{PaymentHash, PaymentPreimage}; -use rand::{rng, Rng}; use crate::config::{may_announce_channel, Config}; use crate::connection::ConnectionManager; @@ -48,6 +48,7 @@ use crate::payment::store::{ PaymentDetails, PaymentDetailsUpdate, PaymentDirection, PaymentKind, PaymentStatus, }; use crate::runtime::Runtime; +use crate::types::KeysManager; use crate::types::{CustomTlvRecord, DynStore, OnionMessenger, PaymentStore, Sweeper, Wallet}; use crate::{ hex_utils, BumpTransactionEventHandler, ChannelManager, Error, Graph, PeerInfo, PeerStore, @@ -488,6 +489,7 @@ where liquidity_source: Option>>>, payment_store: Arc, peer_store: Arc>, + keys_manager: Arc, runtime: Arc, logger: L, config: Arc, @@ -507,9 +509,9 @@ where output_sweeper: Arc, network_graph: Arc, liquidity_source: Option>>>, payment_store: Arc, peer_store: Arc>, - static_invoice_store: Option, onion_messenger: Arc, - om_mailbox: Option>, runtime: Arc, logger: L, - config: Arc, + keys_manager: Arc, static_invoice_store: Option, + onion_messenger: Arc, om_mailbox: Option>, + runtime: Arc, logger: L, config: Arc, ) -> Self { Self { event_queue, @@ -522,6 +524,7 @@ where liquidity_source, payment_store, peer_store, + keys_manager, logger, runtime, config, @@ -1218,7 +1221,9 @@ where } } - let user_channel_id: u128 = rng().random(); + let user_channel_id: u128 = u128::from_ne_bytes( + self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(), + ); let allow_0conf = self.config.trusted_peers_0conf.contains(&counterparty_node_id); let mut channel_override_config = None; if let Some((lsp_node_id, _)) = self diff --git a/src/io/utils.rs b/src/io/utils.rs index d2f70377b..eef71ec0b 100644 --- a/src/io/utils.rs +++ b/src/io/utils.rs @@ -35,8 +35,6 @@ use lightning::util::persist::{ }; use lightning::util::ser::{Readable, ReadableArgs, Writeable}; use lightning_types::string::PrintableString; -use rand::rngs::OsRng; -use rand::TryRngCore; use super::*; use crate::chain::ChainSource; @@ -72,7 +70,7 @@ pub(crate) fn read_or_generate_seed_file( Ok(key) } else { let mut key = [0; WALLET_KEYS_SEED_LEN]; - OsRng.try_fill_bytes(&mut key).map_err(|_| { + getrandom::fill(&mut key).map_err(|_| { std::io::Error::new(std::io::ErrorKind::Other, "Failed to generate seed bytes") })?; diff --git a/src/io/vss_store.rs b/src/io/vss_store.rs index a7af8ecc2..becbbe5dd 100644 --- a/src/io/vss_store.rs +++ b/src/io/vss_store.rs @@ -23,10 +23,10 @@ use bitcoin::key::Secp256k1; use bitcoin::Network; use lightning::impl_writeable_tlv_based_enum; use lightning::io::{self, Error, ErrorKind}; +use lightning::sign::{EntropySource as LdkEntropySource, RandomBytes}; use lightning::util::persist::{KVStore, KVStoreSync}; use lightning::util::ser::{Readable, Writeable}; use prost::Message; -use rand::RngCore; use vss_client::client::VssClient; use vss_client::error::VssError; use vss_client::headers::{FixedHeaders, LnurlAuthToJwtProvider, VssHeaderProvider}; @@ -114,6 +114,10 @@ impl VssStore { derive_data_encryption_and_obfuscation_keys(&vss_seed); let key_obfuscator = KeyObfuscator::new(obfuscation_master_key); + let mut entropy_seed = [0u8; 32]; + getrandom::fill(&mut entropy_seed).expect("Failed to generate random bytes"); + let entropy_source = RandomBytes::new(entropy_seed); + let sync_retry_policy = retry_policy(); let blocking_client = VssClient::new_with_headers( base_url.clone(), @@ -129,6 +133,7 @@ impl VssStore { &store_id, data_encryption_key, &key_obfuscator, + &entropy_source, ) .await }) @@ -145,6 +150,7 @@ impl VssStore { store_id, data_encryption_key, key_obfuscator, + entropy_source, )); Ok(Self { inner, next_version, internal_runtime: Some(internal_runtime) }) @@ -385,6 +391,7 @@ struct VssStoreInner { store_id: String, data_encryption_key: [u8; 32], key_obfuscator: KeyObfuscator, + entropy_source: RandomBytes, // Per-key locks that ensures that we don't have concurrent writes to the same namespace/key. // The lock also encapsulates the latest written version per key. locks: Mutex>>>, @@ -394,7 +401,7 @@ impl VssStoreInner { pub(crate) fn new( schema_version: VssSchemaVersion, blocking_client: VssClient, async_client: VssClient, store_id: String, - data_encryption_key: [u8; 32], key_obfuscator: KeyObfuscator, + data_encryption_key: [u8; 32], key_obfuscator: KeyObfuscator, entropy_source: RandomBytes, ) -> Self { let locks = Mutex::new(HashMap::new()); Self { @@ -404,6 +411,7 @@ impl VssStoreInner { store_id, data_encryption_key, key_obfuscator, + entropy_source, locks, } } @@ -524,7 +532,7 @@ impl VssStoreInner { Error::new(ErrorKind::Other, msg) })?; - let storable_builder = StorableBuilder::new(RandEntropySource); + let storable_builder = StorableBuilder::new(VssEntropySource(&self.entropy_source)); let aad = if self.schema_version == VssSchemaVersion::V1 { store_key.as_bytes() } else { &[] }; let decrypted = storable_builder.deconstruct(storable, &self.data_encryption_key, aad)?.0; @@ -545,7 +553,7 @@ impl VssStoreInner { let store_key = self.build_obfuscated_key(&primary_namespace, &secondary_namespace, &key); let vss_version = -1; - let storable_builder = StorableBuilder::new(RandEntropySource); + let storable_builder = StorableBuilder::new(VssEntropySource(&self.entropy_source)); let aad = if self.schema_version == VssSchemaVersion::V1 { store_key.as_bytes() } else { &[] }; let storable = @@ -703,7 +711,7 @@ fn retry_policy() -> CustomRetryPolicy { async fn determine_and_write_schema_version( client: &VssClient, store_id: &String, data_encryption_key: [u8; 32], - key_obfuscator: &KeyObfuscator, + key_obfuscator: &KeyObfuscator, entropy_source: &RandomBytes, ) -> io::Result { // Build the obfuscated `vss_schema_version` key. let obfuscated_prefix = key_obfuscator.obfuscate(&format! {"{}#{}", "", ""}); @@ -734,7 +742,7 @@ async fn determine_and_write_schema_version( Error::new(ErrorKind::Other, msg) })?; - let storable_builder = StorableBuilder::new(RandEntropySource); + let storable_builder = StorableBuilder::new(VssEntropySource(entropy_source)); // Schema version was added starting with V1, so if set at all, we use the key as `aad` let aad = store_key.as_bytes(); let decrypted = storable_builder @@ -778,7 +786,7 @@ async fn determine_and_write_schema_version( let schema_version = VssSchemaVersion::V1; let encoded_version = schema_version.encode(); - let storable_builder = StorableBuilder::new(RandEntropySource); + let storable_builder = StorableBuilder::new(VssEntropySource(entropy_source)); let vss_version = -1; let aad = store_key.as_bytes(); let storable = @@ -805,12 +813,18 @@ async fn determine_and_write_schema_version( } } -/// A source for generating entropy/randomness using [`rand`]. -pub(crate) struct RandEntropySource; +/// A thin wrapper bridging LDK's [`RandomBytes`] to the vss-client [`EntropySource`] trait. +struct VssEntropySource<'a>(&'a RandomBytes); -impl EntropySource for RandEntropySource { +impl EntropySource for VssEntropySource<'_> { fn fill_bytes(&self, buffer: &mut [u8]) { - rand::rng().fill_bytes(buffer); + let mut offset = 0; + while offset < buffer.len() { + let random = self.0.get_secure_random_bytes(); + let to_copy = (buffer.len() - offset).min(32); + buffer[offset..offset + to_copy].copy_from_slice(&random[..to_copy]); + offset += to_copy; + } } } diff --git a/src/lib.rs b/src/lib.rs index 2b60307b0..b0fc1b2ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,6 +146,7 @@ use lightning::ln::channelmanager::PaymentId; use lightning::ln::funding::SpliceContribution; use lightning::ln::msgs::SocketAddress; use lightning::routing::gossip::NodeAlias; +use lightning::sign::EntropySource; use lightning::util::persist::KVStoreSync; use lightning_background_processor::process_events_async; use liquidity::{LSPS1Liquidity, LiquiditySource}; @@ -157,7 +158,6 @@ use payment::{ UnifiedPayment, }; use peer_store::{PeerInfo, PeerStore}; -use rand::Rng; use runtime::Runtime; use types::{ Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, DynStore, Graph, @@ -574,6 +574,7 @@ impl Node { self.liquidity_source.clone(), Arc::clone(&self.payment_store), Arc::clone(&self.peer_store), + Arc::clone(&self.keys_manager), static_invoice_store, Arc::clone(&self.onion_messenger), self.om_mailbox.clone(), @@ -889,6 +890,7 @@ impl Node { pub fn bolt12_payment(&self) -> Bolt12Payment { Bolt12Payment::new( Arc::clone(&self.channel_manager), + Arc::clone(&self.keys_manager), Arc::clone(&self.payment_store), Arc::clone(&self.config), Arc::clone(&self.is_running), @@ -904,6 +906,7 @@ impl Node { pub fn bolt12_payment(&self) -> Arc { Arc::new(Bolt12Payment::new( Arc::clone(&self.channel_manager), + Arc::clone(&self.keys_manager), Arc::clone(&self.payment_store), Arc::clone(&self.config), Arc::clone(&self.is_running), @@ -1127,7 +1130,9 @@ impl Node { } let push_msat = push_to_counterparty_msat.unwrap_or(0); - let user_channel_id: u128 = rand::rng().random(); + let user_channel_id: u128 = u128::from_ne_bytes( + self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(), + ); match self.channel_manager.create_channel( peer_info.node_id, diff --git a/src/liquidity.rs b/src/liquidity.rs index 30a8068ad..c22df0898 100644 --- a/src/liquidity.rs +++ b/src/liquidity.rs @@ -20,6 +20,7 @@ use lightning::ln::channelmanager::{InterceptId, MIN_FINAL_CLTV_EXPIRY_DELTA}; use lightning::ln::msgs::SocketAddress; use lightning::ln::types::ChannelId; use lightning::routing::router::{RouteHint, RouteHintHop}; +use lightning::sign::EntropySource; use lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription, InvoiceBuilder, RoutingFees}; use lightning_liquidity::events::LiquidityEvent; use lightning_liquidity::lsps0::ser::{LSPSDateTime, LSPSRequestId}; @@ -35,7 +36,6 @@ use lightning_liquidity::lsps2::service::LSPS2ServiceConfig as LdkLSPS2ServiceCo use lightning_liquidity::lsps2::utils::compute_opening_fee; use lightning_liquidity::{LiquidityClientConfig, LiquidityServiceConfig}; use lightning_types::payment::PaymentHash; -use rand::Rng; use tokio::sync::oneshot; use crate::builder::BuildError; @@ -641,7 +641,9 @@ where return; }; - let user_channel_id: u128 = rand::rng().random(); + let user_channel_id: u128 = u128::from_ne_bytes( + self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(), + ); let intercept_scid = self.channel_manager.get_intercept_scid(); if let Some(payment_size_msat) = payment_size_msat { diff --git a/src/payment/bolt12.rs b/src/payment/bolt12.rs index ada4cd7e2..d5ee30ba3 100644 --- a/src/payment/bolt12.rs +++ b/src/payment/bolt12.rs @@ -19,17 +19,17 @@ use lightning::ln::outbound_payment::Retry; use lightning::offers::offer::{Amount, Offer as LdkOffer, OfferFromHrn, Quantity}; use lightning::offers::parse::Bolt12SemanticError; use lightning::routing::router::RouteParametersConfig; +use lightning::sign::EntropySource; #[cfg(feature = "uniffi")] use lightning::util::ser::{Readable, Writeable}; use lightning_types::string::UntrustedString; -use rand::RngCore; use crate::config::{AsyncPaymentsRole, Config, LDK_PAYMENT_RETRY_TIMEOUT}; use crate::error::Error; use crate::ffi::{maybe_deref, maybe_wrap}; use crate::logger::{log_error, log_info, LdkLogger, Logger}; use crate::payment::store::{PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus}; -use crate::types::{ChannelManager, PaymentStore}; +use crate::types::{ChannelManager, KeysManager, PaymentStore}; #[cfg(not(feature = "uniffi"))] type Bolt12Invoice = lightning::offers::invoice::Bolt12Invoice; @@ -59,6 +59,7 @@ type HumanReadableName = Arc; /// [`Node::bolt12_payment`]: crate::Node::bolt12_payment pub struct Bolt12Payment { channel_manager: Arc, + keys_manager: Arc, payment_store: Arc, config: Arc, is_running: Arc>, @@ -68,11 +69,19 @@ pub struct Bolt12Payment { impl Bolt12Payment { pub(crate) fn new( - channel_manager: Arc, payment_store: Arc, - config: Arc, is_running: Arc>, logger: Arc, - async_payments_role: Option, + channel_manager: Arc, keys_manager: Arc, + payment_store: Arc, config: Arc, is_running: Arc>, + logger: Arc, async_payments_role: Option, ) -> Self { - Self { channel_manager, payment_store, config, is_running, logger, async_payments_role } + Self { + channel_manager, + keys_manager, + payment_store, + config, + is_running, + logger, + async_payments_role, + } } /// Send a payment given an offer. @@ -94,9 +103,7 @@ impl Bolt12Payment { let offer = maybe_deref(offer); - let mut random_bytes = [0u8; 32]; - rand::rng().fill_bytes(&mut random_bytes); - let payment_id = PaymentId(random_bytes); + let payment_id = PaymentId(self.keys_manager.get_secure_random_bytes()); let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT); let route_parameters = route_parameters.or(self.config.route_parameters).unwrap_or_default(); @@ -237,9 +244,7 @@ impl Bolt12Payment { let offer = maybe_deref(offer); - let mut random_bytes = [0u8; 32]; - rand::rng().fill_bytes(&mut random_bytes); - let payment_id = PaymentId(random_bytes); + let payment_id = PaymentId(self.keys_manager.get_secure_random_bytes()); let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT); let route_parameters = route_parameters.or(self.config.route_parameters).unwrap_or_default(); @@ -462,9 +467,7 @@ impl Bolt12Payment { &self, amount_msat: u64, expiry_secs: u32, quantity: Option, payer_note: Option, route_parameters: Option, ) -> Result { - let mut random_bytes = [0u8; 32]; - rand::rng().fill_bytes(&mut random_bytes); - let payment_id = PaymentId(random_bytes); + let payment_id = PaymentId(self.keys_manager.get_secure_random_bytes()); let absolute_expiry = (SystemTime::now() + Duration::from_secs(expiry_secs as u64)) .duration_since(UNIX_EPOCH)