-
Notifications
You must be signed in to change notification settings - Fork 411
Simplify + clarify random-bytes-fetching from KeysInterface #674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -341,12 +341,10 @@ pub trait KeysInterface: Send + Sync { | |
/// Get a new set of ChannelKeys for per-channel secrets. These MUST be unique even if you | ||
/// restarted with some stale data! | ||
fn get_channel_keys(&self, inbound: bool, channel_value_satoshis: u64) -> Self::ChanKeySigner; | ||
/// Get a secret and PRNG seed for constructing an onion packet | ||
fn get_onion_rand(&self) -> (SecretKey, [u8; 32]); | ||
/// Get a unique temporary channel id. Channels will be referred to by this until the funding | ||
/// transaction is created, at which point they will use the outpoint in the funding | ||
/// transaction. | ||
fn get_channel_id(&self) -> [u8; 32]; | ||
/// Gets a unique, cryptographically-secure, random 32 byte value. This is used for encrypting | ||
/// onion packets and for temporary channel IDs. There is no requirement that these be | ||
/// persisted anywhere, though they must be unique across restarts. | ||
fn get_secure_random_bytes(&self) -> [u8; 32]; | ||
} | ||
|
||
#[derive(Clone)] | ||
|
@@ -666,10 +664,8 @@ pub struct KeysManager { | |
shutdown_pubkey: PublicKey, | ||
channel_master_key: ExtendedPrivKey, | ||
channel_child_index: AtomicUsize, | ||
session_master_key: ExtendedPrivKey, | ||
session_child_index: AtomicUsize, | ||
channel_id_master_key: ExtendedPrivKey, | ||
channel_id_child_index: AtomicUsize, | ||
rand_bytes_master_key: ExtendedPrivKey, | ||
rand_bytes_child_index: AtomicUsize, | ||
|
||
seed: [u8; 32], | ||
starting_time_secs: u64, | ||
|
@@ -678,7 +674,7 @@ pub struct KeysManager { | |
|
||
impl KeysManager { | ||
/// Constructs a KeysManager from a 32-byte seed. If the seed is in some way biased (eg your | ||
/// RNG is busted) this may panic (but more importantly, you will possibly lose funds). | ||
/// CSRNG is busted) this may panic (but more importantly, you will possibly lose funds). | ||
/// starting_time isn't strictly required to actually be a time, but it must absolutely, | ||
/// without a doubt, be unique to this instance. ie if you start multiple times with the same | ||
/// seed, starting_time must be unique to each run. Thus, the easiest way to achieve this is to | ||
|
@@ -715,8 +711,7 @@ impl KeysManager { | |
Err(_) => panic!("Your RNG is busted"), | ||
}; | ||
let channel_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(3).unwrap()).expect("Your RNG is busted"); | ||
let session_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(4).unwrap()).expect("Your RNG is busted"); | ||
let channel_id_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(5).unwrap()).expect("Your RNG is busted"); | ||
let rand_bytes_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(4).unwrap()).expect("Your RNG is busted"); | ||
|
||
KeysManager { | ||
secp_ctx, | ||
|
@@ -725,10 +720,8 @@ impl KeysManager { | |
shutdown_pubkey, | ||
channel_master_key, | ||
channel_child_index: AtomicUsize::new(0), | ||
session_master_key, | ||
session_child_index: AtomicUsize::new(0), | ||
channel_id_master_key, | ||
channel_id_child_index: AtomicUsize::new(0), | ||
rand_bytes_master_key, | ||
rand_bytes_child_index: AtomicUsize::new(0), | ||
|
||
seed: *seed, | ||
starting_time_secs, | ||
|
@@ -821,29 +814,14 @@ impl KeysInterface for KeysManager { | |
self.derive_channel_keys(channel_value_satoshis, ix_and_nanos, self.starting_time_secs) | ||
} | ||
|
||
fn get_onion_rand(&self) -> (SecretKey, [u8; 32]) { | ||
fn get_secure_random_bytes(&self) -> [u8; 32] { | ||
let mut sha = self.derive_unique_start(); | ||
|
||
let child_ix = self.session_child_index.fetch_add(1, Ordering::AcqRel); | ||
let child_privkey = self.session_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(child_ix as u32).expect("key space exhausted")).expect("Your RNG is busted"); | ||
sha.input(&child_privkey.private_key.key[..]); | ||
|
||
let mut rng_seed = sha.clone(); | ||
// Not exactly the most ideal construction, but the second value will get fed into | ||
// ChaCha so it is another step harder to break. | ||
rng_seed.input(b"RNG Seed Salt"); | ||
sha.input(b"Session Key Salt"); | ||
(SecretKey::from_slice(&Sha256::from_engine(sha).into_inner()).expect("Your RNG is busted"), | ||
Sha256::from_engine(rng_seed).into_inner()) | ||
} | ||
|
||
fn get_channel_id(&self) -> [u8; 32] { | ||
let mut sha = self.derive_unique_start(); | ||
|
||
let child_ix = self.channel_id_child_index.fetch_add(1, Ordering::AcqRel); | ||
let child_privkey = self.channel_id_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(child_ix as u32).expect("key space exhausted")).expect("Your RNG is busted"); | ||
let child_ix = self.rand_bytes_child_index.fetch_add(1, Ordering::AcqRel); | ||
let child_privkey = self.rand_bytes_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(child_ix as u32).expect("key space exhausted")).expect("Your RNG is busted"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Your CSRNG is busted" ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have "RNG is busted" in a ton of places. I don't think its really all that critical to explicitly say CSRNG in a panic message (which even a biased RNG should suffice to not hit), as long as the docs (which hopefully is the only thing users ever see) are good. |
||
sha.input(&child_privkey.private_key.key[..]); | ||
|
||
sha.input(b"Unique Secure Random Bytes Salt"); | ||
Sha256::from_engine(sha).into_inner() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -350,7 +350,7 @@ impl Logger for TestLogger { | |
|
||
pub struct TestKeysInterface { | ||
backing: keysinterface::KeysManager, | ||
pub override_session_priv: Mutex<Option<SecretKey>>, | ||
pub override_session_priv: Mutex<Option<[u8; 32]>>, | ||
pub override_channel_id_priv: Mutex<Option<[u8; 32]>>, | ||
} | ||
|
||
|
@@ -364,18 +364,19 @@ impl keysinterface::KeysInterface for TestKeysInterface { | |
EnforcingChannelKeys::new(self.backing.get_channel_keys(inbound, channel_value_satoshis)) | ||
} | ||
|
||
fn get_onion_rand(&self) -> (SecretKey, [u8; 32]) { | ||
match *self.override_session_priv.lock().unwrap() { | ||
Some(key) => (key.clone(), [0; 32]), | ||
None => self.backing.get_onion_rand() | ||
fn get_secure_random_bytes(&self) -> [u8; 32] { | ||
let override_channel_id = self.override_channel_id_priv.lock().unwrap(); | ||
let override_session_key = self.override_session_priv.lock().unwrap(); | ||
if override_channel_id.is_some() && override_session_key.is_some() { | ||
panic!("We don't know which override key to use!"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By your commit message, you mean the reason to have both functions to the interface was to potentially override both keys in test framework, which we never need to do so actually ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pretty much, yes, but it was also just a historical quirk - we had different ways to get data into the KeyInterface and the easiest way to keep that the way it was was to have multiple override hooks. |
||
} | ||
} | ||
|
||
fn get_channel_id(&self) -> [u8; 32] { | ||
match *self.override_channel_id_priv.lock().unwrap() { | ||
Some(key) => key.clone(), | ||
None => self.backing.get_channel_id() | ||
if let Some(key) = &*override_channel_id { | ||
return *key; | ||
} | ||
if let Some(key) = &*override_session_key { | ||
return *key; | ||
} | ||
self.backing.get_secure_random_bytes() | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this an API requirement change to switch from using a PRNG to CSPRNG ? This is better, but if so rename
prng_seed
mentions.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only see one doc comment mentioning RNG (which now says CSRNG).