use bitcoin::bech32::u5;
use bitcoin::hashes::{Hash, HashEngine};
-use bitcoin::hashes::sha256::HashEngine as Sha256State;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hash_types::WPubkeyHash;
use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness};
use crate::util::transaction_utils;
-use crate::util::crypto::{hkdf_extract_expand_twice, sign};
-use crate::util::ser::{Writeable, Writer, Readable};
-#[cfg(anchors)]
-use crate::util::events::HTLCDescriptor;
+use crate::util::crypto::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
+use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs};
use crate::chain::transaction::OutPoint;
+#[cfg(anchors)]
+use crate::events::bump_transaction::HTLCDescriptor;
use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
use crate::ln::{chan_utils, PaymentPreimage};
use crate::ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction};
use crate::prelude::*;
use core::convert::TryInto;
+use core::ops::Deref;
use core::sync::atomic::{AtomicUsize, Ordering};
use crate::io::{self, Error};
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
+use crate::util::atomic_counter::AtomicCounter;
+use crate::util::chacha20::ChaCha20;
use crate::util::invoice::construct_invoice_preimage;
/// Used as initial key material, to be expanded into multiple secret keys (but not to be used
/// directly). This is used within LDK to encrypt/decrypt inbound payment data.
///
-/// (C-not exported) as we just use `[u8; 32]` directly
+/// This is not exported to bindings users as we just use `[u8; 32]` directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
pub struct KeyMaterial(pub [u8; 32]);
/// outpoint describing which `txid` and output `index` is available, the full output which exists
/// at that `txid`/`index`, and any keys or other information required to sign.
///
-/// [`SpendableOutputs`]: crate::util::events::Event::SpendableOutputs
+/// [`SpendableOutputs`]: crate::events::Event::SpendableOutputs
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SpendableOutputDescriptor {
/// An output to a script which was provided via [`SignerProvider`] directly, either from
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript;
}
-#[derive(Clone)]
/// A simple implementation of [`WriteableEcdsaChannelSigner`] that just keeps the private keys in memory.
///
/// This implementation performs no policy checks and is insufficient by itself as
channel_value_satoshis: u64,
/// Key derivation parameters.
channel_keys_id: [u8; 32],
+ /// Seed from which all randomness produced is derived from.
+ rand_bytes_unique_start: [u8; 32],
+ /// Tracks the number of times we've produced randomness to ensure we don't return the same
+ /// bytes twice.
+ rand_bytes_index: AtomicCounter,
+}
+
+impl Clone for InMemorySigner {
+ fn clone(&self) -> Self {
+ Self {
+ funding_key: self.funding_key.clone(),
+ revocation_base_key: self.revocation_base_key.clone(),
+ payment_key: self.payment_key.clone(),
+ delayed_payment_base_key: self.delayed_payment_base_key.clone(),
+ htlc_base_key: self.htlc_base_key.clone(),
+ commitment_seed: self.commitment_seed.clone(),
+ holder_channel_pubkeys: self.holder_channel_pubkeys.clone(),
+ channel_parameters: self.channel_parameters.clone(),
+ channel_value_satoshis: self.channel_value_satoshis,
+ channel_keys_id: self.channel_keys_id,
+ rand_bytes_unique_start: self.get_secure_random_bytes(),
+ rand_bytes_index: AtomicCounter::new(),
+ }
+ }
}
impl InMemorySigner {
commitment_seed: [u8; 32],
channel_value_satoshis: u64,
channel_keys_id: [u8; 32],
+ rand_bytes_unique_start: [u8; 32],
) -> InMemorySigner {
let holder_channel_pubkeys =
InMemorySigner::make_holder_keys(secp_ctx, &funding_key, &revocation_base_key,
holder_channel_pubkeys,
channel_parameters: None,
channel_keys_id,
+ rand_bytes_unique_start,
+ rand_bytes_index: AtomicCounter::new(),
}
}
let remotepubkey = self.pubkeys().payment_point;
let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Testnet).script_pubkey();
let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
- let remotesig = sign(secp_ctx, &sighash, &self.payment_key);
+ let remotesig = sign_with_aux_rand(secp_ctx, &sighash, &self.payment_key, &self);
let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey();
if payment_script != descriptor.output.script_pubkey { return Err(()); }
let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
- let local_delayedsig = sign(secp_ctx, &sighash, &delayed_payment_key);
+ let local_delayedsig = sign_with_aux_rand(secp_ctx, &sighash, &delayed_payment_key, &self);
let payment_script = bitcoin::Address::p2wsh(&witness_script, Network::Bitcoin).script_pubkey();
if descriptor.output.script_pubkey != payment_script { return Err(()); }
}
}
+impl EntropySource for InMemorySigner {
+ fn get_secure_random_bytes(&self) -> [u8; 32] {
+ let index = self.rand_bytes_index.get_increment();
+ let mut nonce = [0u8; 16];
+ nonce[..8].copy_from_slice(&index.to_be_bytes());
+ ChaCha20::get_single_block(&self.rand_bytes_unique_start, &nonce)
+ }
+}
+
impl ChannelSigner for InMemorySigner {
fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1<secp256k1::All>) -> PublicKey {
let commitment_secret = SecretKey::from_slice(&chan_utils::build_commitment_secret(&self.commitment_seed, idx)).unwrap();
let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
let built_tx = trusted_tx.built_transaction();
- let commitment_sig = built_tx.sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx);
+ let commitment_sig = built_tx.sign_counterparty_commitment(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx);
let commitment_txid = built_tx.txid;
let mut htlc_sigs = Vec::with_capacity(commitment_tx.htlcs().len());
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
let trusted_tx = commitment_tx.trust();
- let sig = trusted_tx.built_transaction().sign(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, secp_ctx);
+ let sig = trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx);
let channel_parameters = self.get_channel_parameters();
- let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), secp_ctx)?;
+ let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), &self, secp_ctx)?;
Ok((sig, htlc_sigs))
}
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
let trusted_tx = commitment_tx.trust();
- let sig = trusted_tx.built_transaction().sign(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, secp_ctx);
+ let sig = trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx);
let channel_parameters = self.get_channel_parameters();
- let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), secp_ctx)?;
+ let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), &self, secp_ctx)?;
Ok((sig, htlc_sigs))
}
};
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
- return Ok(sign(secp_ctx, &sighash, &revocation_key))
+ return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self))
}
fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
};
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
- return Ok(sign(secp_ctx, &sighash, &revocation_key))
+ return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self))
}
#[cfg(anchors)]
let our_htlc_private_key = chan_utils::derive_private_key(
&secp_ctx, &per_commitment_point, &self.htlc_base_key
);
- Ok(sign(&secp_ctx, &hash_to_message!(sighash), &our_htlc_private_key))
+ Ok(sign_with_aux_rand(&secp_ctx, &hash_to_message!(sighash), &our_htlc_private_key, &self))
}
fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey);
let mut sighash_parts = sighash::SighashCache::new(htlc_tx);
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
- Ok(sign(secp_ctx, &sighash, &htlc_key))
+ Ok(sign_with_aux_rand(secp_ctx, &sighash, &htlc_key, &self))
}
fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
let sighash = sighash::SighashCache::new(&*anchor_tx).segwit_signature_hash(
input, &witness_script, ANCHOR_OUTPUT_VALUE_SATOSHI, EcdsaSighashType::All,
).unwrap();
- Ok(sign(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key))
+ Ok(sign_with_aux_rand(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key, &self))
}
fn sign_channel_announcement_with_funding_key(
&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>
) -> Result<Signature, ()> {
let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
- Ok(sign(secp_ctx, &msghash, &self.funding_key))
+ Ok(secp_ctx.sign_ecdsa(&msghash, &self.funding_key))
}
}
}
}
-impl Readable for InMemorySigner {
- fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+impl<ES: Deref> ReadableArgs<ES> for InMemorySigner where ES::Target: EntropySource {
+ fn read<R: io::Read>(reader: &mut R, entropy_source: ES) -> Result<Self, DecodeError> {
let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
let funding_key = Readable::read(reader)?;
holder_channel_pubkeys,
channel_parameters: counterparty_channel_data,
channel_keys_id: keys_id,
+ rand_bytes_unique_start: entropy_source.get_secure_random_bytes(),
+ rand_bytes_index: AtomicCounter::new(),
})
}
}
channel_master_key: ExtendedPrivKey,
channel_child_index: AtomicUsize,
- rand_bytes_master_key: ExtendedPrivKey,
- rand_bytes_child_index: AtomicUsize,
- rand_bytes_unique_start: Sha256State,
+ rand_bytes_unique_start: [u8; 32],
+ rand_bytes_index: AtomicCounter,
seed: [u8; 32],
starting_time_secs: u64,
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 rand_bytes_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(4).unwrap()).expect("Your RNG is busted");
let inbound_payment_key: SecretKey = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(5).unwrap()).expect("Your RNG is busted").private_key;
let mut inbound_pmt_key_bytes = [0; 32];
inbound_pmt_key_bytes.copy_from_slice(&inbound_payment_key[..]);
- let mut rand_bytes_unique_start = Sha256::engine();
- rand_bytes_unique_start.input(&starting_time_secs.to_be_bytes());
- rand_bytes_unique_start.input(&starting_time_nanos.to_be_bytes());
- rand_bytes_unique_start.input(seed);
+ let mut rand_bytes_engine = Sha256::engine();
+ rand_bytes_engine.input(&starting_time_secs.to_be_bytes());
+ rand_bytes_engine.input(&starting_time_nanos.to_be_bytes());
+ rand_bytes_engine.input(seed);
+ rand_bytes_engine.input(b"LDK PRNG Seed");
+ let rand_bytes_unique_start = Sha256::from_engine(rand_bytes_engine).into_inner();
let mut res = KeysManager {
secp_ctx,
channel_master_key,
channel_child_index: AtomicUsize::new(0),
- rand_bytes_master_key,
- rand_bytes_child_index: AtomicUsize::new(0),
rand_bytes_unique_start,
+ rand_bytes_index: AtomicCounter::new(),
seed: *seed,
starting_time_secs,
Err(_) => panic!("Your rng is busted"),
}
}
+
+ /// Gets the "node_id" secret key used to sign gossip announcements, decode onion data, etc.
+ pub fn get_node_secret_key(&self) -> SecretKey {
+ self.node_secret
+ }
+
/// Derive an old [`WriteableEcdsaChannelSigner`] containing per-channel secrets based on a key derivation parameters.
pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
let chan_id = u64::from_be_bytes(params[0..8].try_into().unwrap());
let payment_key = key_step!(b"payment key", revocation_base_key);
let delayed_payment_base_key = key_step!(b"delayed payment base key", payment_key);
let htlc_base_key = key_step!(b"HTLC base key", delayed_payment_base_key);
+ let prng_seed = self.get_secure_random_bytes();
InMemorySigner::new(
&self.secp_ctx,
commitment_seed,
channel_value_satoshis,
params.clone(),
+ prng_seed,
)
}
if payment_script != output.script_pubkey { return Err(()); };
let sighash = hash_to_message!(&sighash::SighashCache::new(&spend_tx).segwit_signature_hash(input_idx, &witness_script, output.value, EcdsaSighashType::All).unwrap()[..]);
- let sig = sign(secp_ctx, &sighash, &secret.private_key);
+ let sig = sign_with_aux_rand(secp_ctx, &sighash, &secret.private_key, &self);
let mut sig_ser = sig.serialize_der().to_vec();
sig_ser.push(EcdsaSighashType::All as u8);
spend_tx.input[input_idx].witness.push(sig_ser);
impl EntropySource for KeysManager {
fn get_secure_random_bytes(&self) -> [u8; 32] {
- let mut sha = self.rand_bytes_unique_start.clone();
-
- 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");
- sha.input(&child_privkey.private_key[..]);
-
- sha.input(b"Unique Secure Random Bytes Salt");
- Sha256::from_engine(sha).into_inner()
+ let index = self.rand_bytes_index.get_increment();
+ let mut nonce = [0u8; 16];
+ nonce[..8].copy_from_slice(&index.to_be_bytes());
+ ChaCha20::get_single_block(&self.rand_bytes_unique_start, &nonce)
}
}
fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
- Ok(sign(&self.secp_ctx, &msg_hash, &self.node_secret))
+ Ok(self.secp_ctx.sign_ecdsa(&msg_hash, &self.node_secret))
}
}
}
fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
- InMemorySigner::read(&mut io::Cursor::new(reader))
+ InMemorySigner::read(&mut io::Cursor::new(reader), self)
}
fn get_destination_script(&self) -> Script {
pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
self.inner.derive_channel_keys(channel_value_satoshis, params)
}
+
+ /// Gets the "node_id" secret key used to sign gossip announcements, decode onion data, etc.
+ pub fn get_node_secret_key(&self) -> SecretKey {
+ self.inner.get_node_secret_key()
+ }
+
+ /// Gets the "node_id" secret key of the phantom node used to sign invoices, decode the
+ /// last-hop onion data, etc.
+ pub fn get_phantom_node_secret_key(&self) -> SecretKey {
+ self.phantom_secret
+ }
}
// Ensure that EcdsaChannelSigner can have a vtable
pub fn dyn_sign() {
let _signer: Box<dyn EcdsaChannelSigner>;
}
+
+#[cfg(all(test, feature = "_bench_unstable", not(feature = "no-std")))]
+mod benches {
+ use std::sync::{Arc, mpsc};
+ use std::sync::mpsc::TryRecvError;
+ use std::thread;
+ use std::time::Duration;
+ use bitcoin::blockdata::constants::genesis_block;
+ use bitcoin::Network;
+ use crate::chain::keysinterface::{EntropySource, KeysManager};
+
+ use test::Bencher;
+
+ #[bench]
+ fn bench_get_secure_random_bytes(bench: &mut Bencher) {
+ let seed = [0u8; 32];
+ let now = Duration::from_secs(genesis_block(Network::Testnet).header.time as u64);
+ let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_micros()));
+
+ let mut handles = Vec::new();
+ let mut stops = Vec::new();
+ for _ in 1..5 {
+ let keys_manager_clone = Arc::clone(&keys_manager);
+ let (stop_sender, stop_receiver) = mpsc::channel();
+ let handle = thread::spawn(move || {
+ loop {
+ keys_manager_clone.get_secure_random_bytes();
+ match stop_receiver.try_recv() {
+ Ok(_) | Err(TryRecvError::Disconnected) => {
+ println!("Terminating.");
+ break;
+ }
+ Err(TryRecvError::Empty) => {}
+ }
+ }
+ });
+ handles.push(handle);
+ stops.push(stop_sender);
+ }
+
+ bench.iter(|| {
+ for _ in 1..100 {
+ keys_manager.get_secure_random_bytes();
+ }
+ });
+
+ for stop in stops {
+ let _ = stop.send(());
+ }
+ for handle in handles {
+ handle.join().unwrap();
+ }
+ }
+
+}