Skip to content

Commit 7997ef9

Browse files
authored
Merge pull request #800 from TheBlueMatt/2026-02-no-rand
Replace `rand` with `getrandom` and `KeysManager` entropy in non-test…
2 parents 9e0cfc5 + 93a88cc commit 7997ef9

File tree

7 files changed

+67
-39
lines changed

7 files changed

+67
-39
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ bip39 = { version = "2.0.0", features = ["rand"] }
6464
bip21 = { version = "0.5", features = ["std"], default-features = false }
6565

6666
base64 = { version = "0.22.1", default-features = false, features = ["std"] }
67-
rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] }
67+
getrandom = { version = "0.3", default-features = false }
6868
chrono = { version = "0.4", default-features = false, features = ["clock"] }
6969
tokio = { version = "1.37", default-features = false, features = [ "rt-multi-thread", "time", "sync", "macros" ] }
7070
esplora-client = { version = "0.12", default-features = false, features = ["tokio", "async-https-rustls"] }
@@ -85,6 +85,7 @@ winapi = { version = "0.3", features = ["winbase"] }
8585

8686
[dev-dependencies]
8787
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0", features = ["std", "_test_utils"] }
88+
rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] }
8889
proptest = "1.0.0"
8990
regex = "1.5.6"
9091
criterion = { version = "0.7.0", features = ["async_tokio"] }

src/event.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use lightning::impl_writeable_tlv_based_enum;
2222
use lightning::ln::channelmanager::PaymentId;
2323
use lightning::ln::types::ChannelId;
2424
use lightning::routing::gossip::NodeId;
25+
use lightning::sign::EntropySource;
2526
use lightning::util::config::{
2627
ChannelConfigOverrides, ChannelConfigUpdate, ChannelHandshakeConfigUpdate,
2728
};
@@ -30,7 +31,6 @@ use lightning::util::persist::KVStore;
3031
use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer};
3132
use lightning_liquidity::lsps2::utils::compute_opening_fee;
3233
use lightning_types::payment::{PaymentHash, PaymentPreimage};
33-
use rand::{rng, Rng};
3434

3535
use crate::config::{may_announce_channel, Config};
3636
use crate::connection::ConnectionManager;
@@ -48,6 +48,7 @@ use crate::payment::store::{
4848
PaymentDetails, PaymentDetailsUpdate, PaymentDirection, PaymentKind, PaymentStatus,
4949
};
5050
use crate::runtime::Runtime;
51+
use crate::types::KeysManager;
5152
use crate::types::{CustomTlvRecord, DynStore, OnionMessenger, PaymentStore, Sweeper, Wallet};
5253
use crate::{
5354
hex_utils, BumpTransactionEventHandler, ChannelManager, Error, Graph, PeerInfo, PeerStore,
@@ -488,6 +489,7 @@ where
488489
liquidity_source: Option<Arc<LiquiditySource<Arc<Logger>>>>,
489490
payment_store: Arc<PaymentStore>,
490491
peer_store: Arc<PeerStore<L>>,
492+
keys_manager: Arc<KeysManager>,
491493
runtime: Arc<Runtime>,
492494
logger: L,
493495
config: Arc<Config>,
@@ -507,9 +509,9 @@ where
507509
output_sweeper: Arc<Sweeper>, network_graph: Arc<Graph>,
508510
liquidity_source: Option<Arc<LiquiditySource<Arc<Logger>>>>,
509511
payment_store: Arc<PaymentStore>, peer_store: Arc<PeerStore<L>>,
510-
static_invoice_store: Option<StaticInvoiceStore>, onion_messenger: Arc<OnionMessenger>,
511-
om_mailbox: Option<Arc<OnionMessageMailbox>>, runtime: Arc<Runtime>, logger: L,
512-
config: Arc<Config>,
512+
keys_manager: Arc<KeysManager>, static_invoice_store: Option<StaticInvoiceStore>,
513+
onion_messenger: Arc<OnionMessenger>, om_mailbox: Option<Arc<OnionMessageMailbox>>,
514+
runtime: Arc<Runtime>, logger: L, config: Arc<Config>,
513515
) -> Self {
514516
Self {
515517
event_queue,
@@ -522,6 +524,7 @@ where
522524
liquidity_source,
523525
payment_store,
524526
peer_store,
527+
keys_manager,
525528
logger,
526529
runtime,
527530
config,
@@ -1218,7 +1221,9 @@ where
12181221
}
12191222
}
12201223

1221-
let user_channel_id: u128 = rng().random();
1224+
let user_channel_id: u128 = u128::from_ne_bytes(
1225+
self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(),
1226+
);
12221227
let allow_0conf = self.config.trusted_peers_0conf.contains(&counterparty_node_id);
12231228
let mut channel_override_config = None;
12241229
if let Some((lsp_node_id, _)) = self

src/io/utils.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ use lightning::util::persist::{
3535
};
3636
use lightning::util::ser::{Readable, ReadableArgs, Writeable};
3737
use lightning_types::string::PrintableString;
38-
use rand::rngs::OsRng;
39-
use rand::TryRngCore;
4038

4139
use super::*;
4240
use crate::chain::ChainSource;
@@ -72,7 +70,7 @@ pub(crate) fn read_or_generate_seed_file(
7270
Ok(key)
7371
} else {
7472
let mut key = [0; WALLET_KEYS_SEED_LEN];
75-
OsRng.try_fill_bytes(&mut key).map_err(|_| {
73+
getrandom::fill(&mut key).map_err(|_| {
7674
std::io::Error::new(std::io::ErrorKind::Other, "Failed to generate seed bytes")
7775
})?;
7876

src/io/vss_store.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ use bitcoin::key::Secp256k1;
2323
use bitcoin::Network;
2424
use lightning::impl_writeable_tlv_based_enum;
2525
use lightning::io::{self, Error, ErrorKind};
26+
use lightning::sign::{EntropySource as LdkEntropySource, RandomBytes};
2627
use lightning::util::persist::{KVStore, KVStoreSync};
2728
use lightning::util::ser::{Readable, Writeable};
2829
use prost::Message;
29-
use rand::RngCore;
3030
use vss_client::client::VssClient;
3131
use vss_client::error::VssError;
3232
use vss_client::headers::{FixedHeaders, LnurlAuthToJwtProvider, VssHeaderProvider};
@@ -114,6 +114,10 @@ impl VssStore {
114114
derive_data_encryption_and_obfuscation_keys(&vss_seed);
115115
let key_obfuscator = KeyObfuscator::new(obfuscation_master_key);
116116

117+
let mut entropy_seed = [0u8; 32];
118+
getrandom::fill(&mut entropy_seed).expect("Failed to generate random bytes");
119+
let entropy_source = RandomBytes::new(entropy_seed);
120+
117121
let sync_retry_policy = retry_policy();
118122
let blocking_client = VssClient::new_with_headers(
119123
base_url.clone(),
@@ -129,6 +133,7 @@ impl VssStore {
129133
&store_id,
130134
data_encryption_key,
131135
&key_obfuscator,
136+
&entropy_source,
132137
)
133138
.await
134139
})
@@ -145,6 +150,7 @@ impl VssStore {
145150
store_id,
146151
data_encryption_key,
147152
key_obfuscator,
153+
entropy_source,
148154
));
149155

150156
Ok(Self { inner, next_version, internal_runtime: Some(internal_runtime) })
@@ -385,6 +391,7 @@ struct VssStoreInner {
385391
store_id: String,
386392
data_encryption_key: [u8; 32],
387393
key_obfuscator: KeyObfuscator,
394+
entropy_source: RandomBytes,
388395
// Per-key locks that ensures that we don't have concurrent writes to the same namespace/key.
389396
// The lock also encapsulates the latest written version per key.
390397
locks: Mutex<HashMap<String, Arc<tokio::sync::Mutex<u64>>>>,
@@ -394,7 +401,7 @@ impl VssStoreInner {
394401
pub(crate) fn new(
395402
schema_version: VssSchemaVersion, blocking_client: VssClient<CustomRetryPolicy>,
396403
async_client: VssClient<CustomRetryPolicy>, store_id: String,
397-
data_encryption_key: [u8; 32], key_obfuscator: KeyObfuscator,
404+
data_encryption_key: [u8; 32], key_obfuscator: KeyObfuscator, entropy_source: RandomBytes,
398405
) -> Self {
399406
let locks = Mutex::new(HashMap::new());
400407
Self {
@@ -404,6 +411,7 @@ impl VssStoreInner {
404411
store_id,
405412
data_encryption_key,
406413
key_obfuscator,
414+
entropy_source,
407415
locks,
408416
}
409417
}
@@ -524,7 +532,7 @@ impl VssStoreInner {
524532
Error::new(ErrorKind::Other, msg)
525533
})?;
526534

527-
let storable_builder = StorableBuilder::new(RandEntropySource);
535+
let storable_builder = StorableBuilder::new(VssEntropySource(&self.entropy_source));
528536
let aad =
529537
if self.schema_version == VssSchemaVersion::V1 { store_key.as_bytes() } else { &[] };
530538
let decrypted = storable_builder.deconstruct(storable, &self.data_encryption_key, aad)?.0;
@@ -545,7 +553,7 @@ impl VssStoreInner {
545553

546554
let store_key = self.build_obfuscated_key(&primary_namespace, &secondary_namespace, &key);
547555
let vss_version = -1;
548-
let storable_builder = StorableBuilder::new(RandEntropySource);
556+
let storable_builder = StorableBuilder::new(VssEntropySource(&self.entropy_source));
549557
let aad =
550558
if self.schema_version == VssSchemaVersion::V1 { store_key.as_bytes() } else { &[] };
551559
let storable =
@@ -703,7 +711,7 @@ fn retry_policy() -> CustomRetryPolicy {
703711

704712
async fn determine_and_write_schema_version(
705713
client: &VssClient<CustomRetryPolicy>, store_id: &String, data_encryption_key: [u8; 32],
706-
key_obfuscator: &KeyObfuscator,
714+
key_obfuscator: &KeyObfuscator, entropy_source: &RandomBytes,
707715
) -> io::Result<VssSchemaVersion> {
708716
// Build the obfuscated `vss_schema_version` key.
709717
let obfuscated_prefix = key_obfuscator.obfuscate(&format! {"{}#{}", "", ""});
@@ -734,7 +742,7 @@ async fn determine_and_write_schema_version(
734742
Error::new(ErrorKind::Other, msg)
735743
})?;
736744

737-
let storable_builder = StorableBuilder::new(RandEntropySource);
745+
let storable_builder = StorableBuilder::new(VssEntropySource(entropy_source));
738746
// Schema version was added starting with V1, so if set at all, we use the key as `aad`
739747
let aad = store_key.as_bytes();
740748
let decrypted = storable_builder
@@ -778,7 +786,7 @@ async fn determine_and_write_schema_version(
778786
let schema_version = VssSchemaVersion::V1;
779787
let encoded_version = schema_version.encode();
780788

781-
let storable_builder = StorableBuilder::new(RandEntropySource);
789+
let storable_builder = StorableBuilder::new(VssEntropySource(entropy_source));
782790
let vss_version = -1;
783791
let aad = store_key.as_bytes();
784792
let storable =
@@ -805,12 +813,18 @@ async fn determine_and_write_schema_version(
805813
}
806814
}
807815

808-
/// A source for generating entropy/randomness using [`rand`].
809-
pub(crate) struct RandEntropySource;
816+
/// A thin wrapper bridging LDK's [`RandomBytes`] to the vss-client [`EntropySource`] trait.
817+
struct VssEntropySource<'a>(&'a RandomBytes);
810818

811-
impl EntropySource for RandEntropySource {
819+
impl EntropySource for VssEntropySource<'_> {
812820
fn fill_bytes(&self, buffer: &mut [u8]) {
813-
rand::rng().fill_bytes(buffer);
821+
let mut offset = 0;
822+
while offset < buffer.len() {
823+
let random = self.0.get_secure_random_bytes();
824+
let to_copy = (buffer.len() - offset).min(32);
825+
buffer[offset..offset + to_copy].copy_from_slice(&random[..to_copy]);
826+
offset += to_copy;
827+
}
814828
}
815829
}
816830

src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ use lightning::ln::channelmanager::PaymentId;
146146
use lightning::ln::funding::SpliceContribution;
147147
use lightning::ln::msgs::SocketAddress;
148148
use lightning::routing::gossip::NodeAlias;
149+
use lightning::sign::EntropySource;
149150
use lightning::util::persist::KVStoreSync;
150151
use lightning_background_processor::process_events_async;
151152
use liquidity::{LSPS1Liquidity, LiquiditySource};
@@ -157,7 +158,6 @@ use payment::{
157158
UnifiedPayment,
158159
};
159160
use peer_store::{PeerInfo, PeerStore};
160-
use rand::Rng;
161161
use runtime::Runtime;
162162
use types::{
163163
Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, DynStore, Graph,
@@ -574,6 +574,7 @@ impl Node {
574574
self.liquidity_source.clone(),
575575
Arc::clone(&self.payment_store),
576576
Arc::clone(&self.peer_store),
577+
Arc::clone(&self.keys_manager),
577578
static_invoice_store,
578579
Arc::clone(&self.onion_messenger),
579580
self.om_mailbox.clone(),
@@ -889,6 +890,7 @@ impl Node {
889890
pub fn bolt12_payment(&self) -> Bolt12Payment {
890891
Bolt12Payment::new(
891892
Arc::clone(&self.channel_manager),
893+
Arc::clone(&self.keys_manager),
892894
Arc::clone(&self.payment_store),
893895
Arc::clone(&self.config),
894896
Arc::clone(&self.is_running),
@@ -904,6 +906,7 @@ impl Node {
904906
pub fn bolt12_payment(&self) -> Arc<Bolt12Payment> {
905907
Arc::new(Bolt12Payment::new(
906908
Arc::clone(&self.channel_manager),
909+
Arc::clone(&self.keys_manager),
907910
Arc::clone(&self.payment_store),
908911
Arc::clone(&self.config),
909912
Arc::clone(&self.is_running),
@@ -1127,7 +1130,9 @@ impl Node {
11271130
}
11281131

11291132
let push_msat = push_to_counterparty_msat.unwrap_or(0);
1130-
let user_channel_id: u128 = rand::rng().random();
1133+
let user_channel_id: u128 = u128::from_ne_bytes(
1134+
self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(),
1135+
);
11311136

11321137
match self.channel_manager.create_channel(
11331138
peer_info.node_id,

src/liquidity.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use lightning::ln::channelmanager::{InterceptId, MIN_FINAL_CLTV_EXPIRY_DELTA};
2020
use lightning::ln::msgs::SocketAddress;
2121
use lightning::ln::types::ChannelId;
2222
use lightning::routing::router::{RouteHint, RouteHintHop};
23+
use lightning::sign::EntropySource;
2324
use lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription, InvoiceBuilder, RoutingFees};
2425
use lightning_liquidity::events::LiquidityEvent;
2526
use lightning_liquidity::lsps0::ser::{LSPSDateTime, LSPSRequestId};
@@ -35,7 +36,6 @@ use lightning_liquidity::lsps2::service::LSPS2ServiceConfig as LdkLSPS2ServiceCo
3536
use lightning_liquidity::lsps2::utils::compute_opening_fee;
3637
use lightning_liquidity::{LiquidityClientConfig, LiquidityServiceConfig};
3738
use lightning_types::payment::PaymentHash;
38-
use rand::Rng;
3939
use tokio::sync::oneshot;
4040

4141
use crate::builder::BuildError;
@@ -641,7 +641,9 @@ where
641641
return;
642642
};
643643

644-
let user_channel_id: u128 = rand::rng().random();
644+
let user_channel_id: u128 = u128::from_ne_bytes(
645+
self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(),
646+
);
645647
let intercept_scid = self.channel_manager.get_intercept_scid();
646648

647649
if let Some(payment_size_msat) = payment_size_msat {

src/payment/bolt12.rs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@ use lightning::ln::outbound_payment::Retry;
1919
use lightning::offers::offer::{Amount, Offer as LdkOffer, OfferFromHrn, Quantity};
2020
use lightning::offers::parse::Bolt12SemanticError;
2121
use lightning::routing::router::RouteParametersConfig;
22+
use lightning::sign::EntropySource;
2223
#[cfg(feature = "uniffi")]
2324
use lightning::util::ser::{Readable, Writeable};
2425
use lightning_types::string::UntrustedString;
25-
use rand::RngCore;
2626

2727
use crate::config::{AsyncPaymentsRole, Config, LDK_PAYMENT_RETRY_TIMEOUT};
2828
use crate::error::Error;
2929
use crate::ffi::{maybe_deref, maybe_wrap};
3030
use crate::logger::{log_error, log_info, LdkLogger, Logger};
3131
use crate::payment::store::{PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus};
32-
use crate::types::{ChannelManager, PaymentStore};
32+
use crate::types::{ChannelManager, KeysManager, PaymentStore};
3333

3434
#[cfg(not(feature = "uniffi"))]
3535
type Bolt12Invoice = lightning::offers::invoice::Bolt12Invoice;
@@ -59,6 +59,7 @@ type HumanReadableName = Arc<crate::ffi::HumanReadableName>;
5959
/// [`Node::bolt12_payment`]: crate::Node::bolt12_payment
6060
pub struct Bolt12Payment {
6161
channel_manager: Arc<ChannelManager>,
62+
keys_manager: Arc<KeysManager>,
6263
payment_store: Arc<PaymentStore>,
6364
config: Arc<Config>,
6465
is_running: Arc<RwLock<bool>>,
@@ -68,11 +69,19 @@ pub struct Bolt12Payment {
6869

6970
impl Bolt12Payment {
7071
pub(crate) fn new(
71-
channel_manager: Arc<ChannelManager>, payment_store: Arc<PaymentStore>,
72-
config: Arc<Config>, is_running: Arc<RwLock<bool>>, logger: Arc<Logger>,
73-
async_payments_role: Option<AsyncPaymentsRole>,
72+
channel_manager: Arc<ChannelManager>, keys_manager: Arc<KeysManager>,
73+
payment_store: Arc<PaymentStore>, config: Arc<Config>, is_running: Arc<RwLock<bool>>,
74+
logger: Arc<Logger>, async_payments_role: Option<AsyncPaymentsRole>,
7475
) -> Self {
75-
Self { channel_manager, payment_store, config, is_running, logger, async_payments_role }
76+
Self {
77+
channel_manager,
78+
keys_manager,
79+
payment_store,
80+
config,
81+
is_running,
82+
logger,
83+
async_payments_role,
84+
}
7685
}
7786

7887
/// Send a payment given an offer.
@@ -94,9 +103,7 @@ impl Bolt12Payment {
94103

95104
let offer = maybe_deref(offer);
96105

97-
let mut random_bytes = [0u8; 32];
98-
rand::rng().fill_bytes(&mut random_bytes);
99-
let payment_id = PaymentId(random_bytes);
106+
let payment_id = PaymentId(self.keys_manager.get_secure_random_bytes());
100107
let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT);
101108
let route_parameters =
102109
route_parameters.or(self.config.route_parameters).unwrap_or_default();
@@ -237,9 +244,7 @@ impl Bolt12Payment {
237244

238245
let offer = maybe_deref(offer);
239246

240-
let mut random_bytes = [0u8; 32];
241-
rand::rng().fill_bytes(&mut random_bytes);
242-
let payment_id = PaymentId(random_bytes);
247+
let payment_id = PaymentId(self.keys_manager.get_secure_random_bytes());
243248
let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT);
244249
let route_parameters =
245250
route_parameters.or(self.config.route_parameters).unwrap_or_default();
@@ -462,9 +467,7 @@ impl Bolt12Payment {
462467
&self, amount_msat: u64, expiry_secs: u32, quantity: Option<u64>,
463468
payer_note: Option<String>, route_parameters: Option<RouteParametersConfig>,
464469
) -> Result<Refund, Error> {
465-
let mut random_bytes = [0u8; 32];
466-
rand::rng().fill_bytes(&mut random_bytes);
467-
let payment_id = PaymentId(random_bytes);
470+
let payment_id = PaymentId(self.keys_manager.get_secure_random_bytes());
468471

469472
let absolute_expiry = (SystemTime::now() + Duration::from_secs(expiry_secs as u64))
470473
.duration_since(UNIX_EPOCH)

0 commit comments

Comments
 (0)