X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fkeysinterface.rs;h=a2611e7df87f79fefad954aa3b9133addaeb650e;hb=56a01de61d0eab8c3d343b51e1a500098baa5aea;hp=76781376f4ba1ea587fe12d7cecfec58ea373384;hpb=f7211fbf7907508a9ff2744ed56e60ed736e931d;p=rust-lightning diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index 76781376..a2611e7d 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -21,7 +21,6 @@ use bitcoin::util::sighash; 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; @@ -34,14 +33,14 @@ 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, ReadableArgs}; +use crate::util::ser::{Writeable, Writer, Readable}; #[cfg(anchors)] use crate::util::events::HTLCDescriptor; use crate::chain::transaction::OutPoint; 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::ln::msgs::UnsignedChannelAnnouncement; +use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage}; use crate::ln::script::ShutdownScript; use crate::prelude::*; @@ -49,6 +48,8 @@ use core::convert::TryInto; 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 @@ -75,7 +76,7 @@ pub struct DelayedPaymentOutputDescriptor { /// The revocation point specific to the commitment transaction which was broadcast. Used to /// derive the witnessScript for this output. pub revocation_pubkey: PublicKey, - /// Arbitrary identification information returned by a call to [`BaseSign::channel_keys_id`]. + /// Arbitrary identification information returned by a call to [`ChannelSigner::channel_keys_id`]. /// This may be useful in re-deriving keys used in the channel to spend the output. pub channel_keys_id: [u8; 32], /// The value of the channel which this output originated from, possibly indirectly. @@ -107,7 +108,7 @@ pub struct StaticPaymentOutputDescriptor { pub outpoint: OutPoint, /// The output which is referenced by the given outpoint. pub output: TxOut, - /// Arbitrary identification information returned by a call to [`BaseSign::channel_keys_id`]. + /// Arbitrary identification information returned by a call to [`ChannelSigner::channel_keys_id`]. /// This may be useful in re-deriving keys used in the channel to spend the output. pub channel_keys_id: [u8; 32], /// The value of the channel which this transactions spends. @@ -137,7 +138,7 @@ impl_writeable_tlv_based!(StaticPaymentOutputDescriptor, { /// [`SpendableOutputs`]: crate::util::events::Event::SpendableOutputs #[derive(Clone, Debug, PartialEq, Eq)] pub enum SpendableOutputDescriptor { - /// An output to a script which was provided via [`KeysInterface`] directly, either from + /// An output to a script which was provided via [`SignerProvider`] directly, either from /// [`get_destination_script`] or [`get_shutdown_scriptpubkey`], thus you should already /// know how to spend it. No secret keys are provided as LDK was never given any key. /// These may include outputs from a transaction punishing our counterparty or claiming an HTLC @@ -172,15 +173,15 @@ pub enum SpendableOutputDescriptor { /// /// To derive the delayed payment key which is used to sign this input, you must pass the /// holder [`InMemorySigner::delayed_payment_base_key`] (i.e., the private key which corresponds to the - /// [`ChannelPublicKeys::delayed_payment_basepoint`] in [`BaseSign::pubkeys`]) and the provided + /// [`ChannelPublicKeys::delayed_payment_basepoint`] in [`ChannelSigner::pubkeys`]) and the provided /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to [`chan_utils::derive_private_key`]. The public key can be /// generated without the secret key using [`chan_utils::derive_public_key`] and only the - /// [`ChannelPublicKeys::delayed_payment_basepoint`] which appears in [`BaseSign::pubkeys`]. + /// [`ChannelPublicKeys::delayed_payment_basepoint`] which appears in [`ChannelSigner::pubkeys`]. /// /// To derive the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] provided here (which is /// used in the witness script generation), you must pass the counterparty /// [`ChannelPublicKeys::revocation_basepoint`] (which appears in the call to - /// [`BaseSign::provide_channel_parameters`]) and the provided + /// [`ChannelSigner::provide_channel_parameters`]) and the provided /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to /// [`chan_utils::derive_public_revocation_key`]. /// @@ -191,7 +192,7 @@ pub enum SpendableOutputDescriptor { /// [`chan_utils::get_revokeable_redeemscript`]. DelayedPaymentOutput(DelayedPaymentOutputDescriptor), /// An output to a P2WPKH, spendable exclusively by our payment key (i.e., the private key - /// which corresponds to the `payment_point` in [`BaseSign::pubkeys`]). The witness + /// which corresponds to the `payment_point` in [`ChannelSigner::pubkeys`]). The witness /// in the spending input is, thus, simply: /// ```bitcoin /// @@ -212,18 +213,14 @@ impl_writeable_tlv_based_enum!(SpendableOutputDescriptor, (2, StaticPaymentOutput), ); -/// A trait to sign Lightning channel transactions as described in -/// [BOLT 3](https://github.com/lightning/bolts/blob/master/03-transactions.md). -/// -/// Signing services could be implemented on a hardware wallet and should implement signing -/// policies in order to be secure. Please refer to the [VLS Policy -/// Controls](https://gitlab.com/lightning-signer/validating-lightning-signer/-/blob/main/docs/policy-controls.md) -/// for an example of such policies. -pub trait BaseSign { +/// A trait to handle Lightning channel key material without concretizing the channel type or +/// the signature mechanism. +pub trait ChannelSigner { /// Gets the per-commitment point for a specific commitment number /// /// Note that the commitment number starts at `(1 << 48) - 1` and counts backwards. fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1) -> PublicKey; + /// Gets the commitment secret for a specific commitment number as part of the revocation process /// /// An external signer implementation should error here if the commitment was already signed @@ -234,6 +231,7 @@ pub trait BaseSign { /// Note that the commitment number starts at `(1 << 48) - 1` and counts backwards. // TODO: return a Result so we can signal a validation error fn release_commitment_secret(&self, idx: u64) -> [u8; 32]; + /// Validate the counterparty's signatures on the holder commitment transaction and HTLCs. /// /// This is required in order for the signer to make sure that releasing a commitment @@ -249,12 +247,35 @@ pub trait BaseSign { /// irrelevant or duplicate preimages. fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction, preimages: Vec) -> Result<(), ()>; + /// Returns the holder's channel public keys and basepoints. fn pubkeys(&self) -> &ChannelPublicKeys; + /// Returns an arbitrary identifier describing the set of keys which are provided back to you in /// some [`SpendableOutputDescriptor`] types. This should be sufficient to identify this - /// [`BaseSign`] object uniquely and lookup or re-derive its keys. + /// [`EcdsaChannelSigner`] object uniquely and lookup or re-derive its keys. fn channel_keys_id(&self) -> [u8; 32]; + + /// Set the counterparty static channel data, including basepoints, + /// `counterparty_selected`/`holder_selected_contest_delay` and funding outpoint. + /// + /// This data is static, and will never change for a channel once set. For a given [`ChannelSigner`] + /// instance, LDK will call this method exactly once - either immediately after construction + /// (not including if done via [`SignerProvider::read_chan_signer`]) or when the funding + /// information has been generated. + /// + /// channel_parameters.is_populated() MUST be true. + fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters); +} + +/// A trait to sign Lightning channel transactions as described in +/// [BOLT 3](https://github.com/lightning/bolts/blob/master/03-transactions.md). +/// +/// Signing services could be implemented on a hardware wallet and should implement signing +/// policies in order to be secure. Please refer to the [VLS Policy +/// Controls](https://gitlab.com/lightning-signer/validating-lightning-signer/-/blob/main/docs/policy-controls.md) +/// for an example of such policies. +pub trait EcdsaChannelSigner: ChannelSigner { /// Create a signature for a counterparty's commitment transaction and associated HTLC transactions. /// /// Note that if signing fails or is rejected, the channel will be force-closed. @@ -383,27 +404,18 @@ pub trait BaseSign { fn sign_holder_anchor_input( &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1, ) -> Result; - /// Signs a channel announcement message with our funding key and our node secret key (aka - /// node_id or network_key), proving it comes from one of the channel participants. + /// Signs a channel announcement message with our funding key proving it comes from one of the + /// channel participants. /// - /// The first returned signature should be from our node secret key, the second from our - /// funding key. + /// Channel announcements also require a signature from each node's network key. Our node + /// signature is computed through [`NodeSigner::sign_gossip_message`]. /// /// Note that if this fails or is rejected, the channel will not be publicly announced and /// our counterparty may (though likely will not) close the channel on us for violating the /// protocol. - fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1) - -> Result<(Signature, Signature), ()>; - /// Set the counterparty static channel data, including basepoints, - /// `counterparty_selected`/`holder_selected_contest_delay` and funding outpoint. - /// - /// This data is static, and will never change for a channel once set. For a given [`BaseSign`] - /// instance, LDK will call this method exactly once - either immediately after construction - /// (not including if done via [`SignerProvider::read_chan_signer`]) or when the funding - /// information has been generated. - /// - /// channel_parameters.is_populated() MUST be true. - fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters); + fn sign_channel_announcement_with_funding_key( + &self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1 + ) -> Result; } /// A writeable signer. @@ -413,7 +425,7 @@ pub trait BaseSign { /// /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor -pub trait Sign: BaseSign + Writeable {} +pub trait WriteableEcdsaChannelSigner: EcdsaChannelSigner + Writeable {} /// Specifies the recipient of an invoice. /// @@ -438,16 +450,6 @@ pub trait EntropySource { /// A trait that can handle cryptographic operations at the scope level of a node. pub trait NodeSigner { - /// Get node secret key based on the provided [`Recipient`]. - /// - /// The `node_id`/`network_key` is the public key that corresponds to this secret key. - /// - /// This method must return the same value each time it is called with a given [`Recipient`] - /// parameter. - /// - /// Errors if the [`Recipient`] variant is not supported by the implementation. - fn get_node_secret(&self, recipient: Recipient) -> Result; - /// Get secret key material as bytes for use in encrypting and decrypting inbound payment data. /// /// If the implementor of this trait supports [phantom node payments], then every node that is @@ -462,42 +464,50 @@ pub trait NodeSigner { /// [phantom node payments]: PhantomKeysManager fn get_inbound_payment_key_material(&self) -> KeyMaterial; - /// Get node id based on the provided [`Recipient`]. This public key corresponds to the secret in - /// [`get_node_secret`]. + /// Get node id based on the provided [`Recipient`]. /// /// This method must return the same value each time it is called with a given [`Recipient`] /// parameter. /// /// Errors if the [`Recipient`] variant is not supported by the implementation. - /// - /// [`get_node_secret`]: Self::get_node_secret fn get_node_id(&self, recipient: Recipient) -> Result; - /// Gets the ECDH shared secret of our [`node secret`] and `other_key`, multiplying by `tweak` if + /// Gets the ECDH shared secret of our node secret and `other_key`, multiplying by `tweak` if /// one is provided. Note that this tweak can be applied to `other_key` instead of our node /// secret, though this is less efficient. /// - /// Errors if the [`Recipient`] variant is not supported by the implementation. + /// Note that if this fails while attempting to forward an HTLC, LDK will panic. The error + /// should be resolved to allow LDK to resume forwarding HTLCs. /// - /// [`node secret`]: Self::get_node_secret + /// Errors if the [`Recipient`] variant is not supported by the implementation. fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result; /// Sign an invoice. + /// /// By parameterizing by the raw invoice bytes instead of the hash, we allow implementors of /// this trait to parse the invoice and make sure they're signing what they expect, rather than /// blindly signing the hash. - /// The hrp is ascii bytes, while the invoice data is base32. + /// + /// The `hrp_bytes` are ASCII bytes, while the `invoice_data` is base32. /// /// The secret key used to sign the invoice is dependent on the [`Recipient`]. /// /// Errors if the [`Recipient`] variant is not supported by the implementation. fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result; + + /// Sign a gossip message. + /// + /// Note that if this fails, LDK may panic and the message will not be broadcast to the network + /// or a possible channel counterparty. If LDK panics, the error should be resolved to allow the + /// message to be broadcast, as otherwise it may prevent one from receiving funds over the + /// corresponding channel. + fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result; } /// A trait that can return signer instances for individual channels. pub trait SignerProvider { - /// A type which implements [`Sign`] which will be returned by [`Self::derive_channel_signer`]. - type Signer : Sign; + /// A type which implements [`WriteableEcdsaChannelSigner`] which will be returned by [`Self::derive_channel_signer`]. + type Signer : WriteableEcdsaChannelSigner; /// Generates a unique `channel_keys_id` that can be used to obtain a [`Self::Signer`] through /// [`SignerProvider::derive_channel_signer`]. The `user_channel_id` is provided to allow @@ -512,12 +522,12 @@ pub trait SignerProvider { /// To derive a new `Signer`, a fresh `channel_keys_id` should be obtained through /// [`SignerProvider::generate_channel_keys_id`]. Otherwise, an existing `Signer` can be /// re-derived from its `channel_keys_id`, which can be obtained through its trait method - /// [`BaseSign::channel_keys_id`]. + /// [`ChannelSigner::channel_keys_id`]. fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer; - /// Reads a [`Signer`] for this [`KeysInterface`] from the given input stream. + /// Reads a [`Signer`] for this [`SignerProvider`] from the given input stream. /// This is only called during deserialization of other objects which contain - /// [`Sign`]-implementing objects (i.e., [`ChannelMonitor`]s and [`ChannelManager`]s). + /// [`WriteableEcdsaChannelSigner`]-implementing objects (i.e., [`ChannelMonitor`]s and [`ChannelManager`]s). /// The bytes are exactly those which `::write()` writes, and /// contain no versioning scheme. You may wish to include your own version prefix and ensure /// you've read all of the provided bytes to ensure no corruption occurred. @@ -543,11 +553,8 @@ pub trait SignerProvider { fn get_shutdown_scriptpubkey(&self) -> ShutdownScript; } -/// A trait to describe an object which can get user secrets and key material. -pub trait KeysInterface: EntropySource + NodeSigner + SignerProvider {} - #[derive(Clone)] -/// A simple implementation of [`Sign`] that just keeps the private keys in memory. +/// 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 /// a secure external signer. @@ -567,8 +574,6 @@ pub struct InMemorySigner { pub commitment_seed: [u8; 32], /// Holder public keys and basepoints. pub(crate) holder_channel_pubkeys: ChannelPublicKeys, - /// Private key of our node secret, used for signing channel announcements. - node_secret: SecretKey, /// Counterparty public keys and counterparty/holder `selected_contest_delay`, populated on channel acceptance. channel_parameters: Option, /// The total value of this channel. @@ -581,7 +586,6 @@ impl InMemorySigner { /// Creates a new [`InMemorySigner`]. pub fn new( secp_ctx: &Secp256k1, - node_secret: SecretKey, funding_key: SecretKey, revocation_base_key: SecretKey, payment_key: SecretKey, @@ -602,7 +606,6 @@ impl InMemorySigner { delayed_payment_base_key, htlc_base_key, commitment_seed, - node_secret, channel_value_satoshis, holder_channel_pubkeys, channel_parameters: None, @@ -628,38 +631,38 @@ impl InMemorySigner { /// Returns the counterparty's pubkeys. /// - /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before. + /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before. pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().pubkeys } /// Returns the `contest_delay` value specified by our counterparty and applied on holder-broadcastable /// transactions, i.e., the amount of time that we have to wait to recover our funds if we /// broadcast a transaction. /// - /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before. + /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before. pub fn counterparty_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().selected_contest_delay } /// Returns the `contest_delay` value specified by us and applied on transactions broadcastable /// by our counterparty, i.e., the amount of time that they have to wait to recover their funds /// if they broadcast a transaction. /// - /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before. + /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before. pub fn holder_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().holder_selected_contest_delay } /// Returns whether the holder is the initiator. /// - /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before. + /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before. pub fn is_outbound(&self) -> bool { self.get_channel_parameters().is_outbound_from_holder } /// Funding outpoint /// - /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before. + /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before. pub fn funding_outpoint(&self) -> &OutPoint { self.get_channel_parameters().funding_outpoint.as_ref().unwrap() } /// Returns a [`ChannelTransactionParameters`] for this channel, to be used when verifying or /// building transactions. /// - /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before. + /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before. pub fn get_channel_parameters(&self) -> &ChannelTransactionParameters { self.channel_parameters.as_ref().unwrap() } /// Returns whether anchors should be used. /// - /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before. + /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before. pub fn opt_anchors(&self) -> bool { self.get_channel_parameters().opt_anchors.is_some() } @@ -733,7 +736,7 @@ impl InMemorySigner { } } -impl BaseSign for InMemorySigner { +impl ChannelSigner for InMemorySigner { fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1) -> PublicKey { let commitment_secret = SecretKey::from_slice(&chan_utils::build_commitment_secret(&self.commitment_seed, idx)).unwrap(); PublicKey::from_secret_key(secp_ctx, &commitment_secret) @@ -751,6 +754,18 @@ impl BaseSign for InMemorySigner { fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id } + fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) { + assert!(self.channel_parameters.is_none() || self.channel_parameters.as_ref().unwrap() == channel_parameters); + if self.channel_parameters.is_some() { + // The channel parameters were already set and they match, return early. + return; + } + assert!(channel_parameters.is_populated(), "Channel parameters must be fully populated"); + self.channel_parameters = Some(channel_parameters.clone()); + } +} + +impl EcdsaChannelSigner for InMemorySigner { fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _preimages: Vec, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { let trusted_tx = commitment_tx.trust(); let keys = trusted_tx.keys(); @@ -873,20 +888,11 @@ impl BaseSign for InMemorySigner { Ok(sign(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key)) } - fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1) - -> Result<(Signature, Signature), ()> { + fn sign_channel_announcement_with_funding_key( + &self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1 + ) -> Result { let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]); - Ok((sign(secp_ctx, &msghash, &self.node_secret), sign(secp_ctx, &msghash, &self.funding_key))) - } - - fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) { - assert!(self.channel_parameters.is_none() || self.channel_parameters.as_ref().unwrap() == channel_parameters); - if self.channel_parameters.is_some() { - // The channel parameters were already set and they match, return early. - return; - } - assert!(channel_parameters.is_populated(), "Channel parameters must be fully populated"); - self.channel_parameters = Some(channel_parameters.clone()); + Ok(sign(secp_ctx, &msghash, &self.funding_key)) } } @@ -894,7 +900,7 @@ const SERIALIZATION_VERSION: u8 = 1; const MIN_SERIALIZATION_VERSION: u8 = 1; -impl Sign for InMemorySigner {} +impl WriteableEcdsaChannelSigner for InMemorySigner {} impl Writeable for InMemorySigner { fn write(&self, writer: &mut W) -> Result<(), Error> { @@ -916,8 +922,8 @@ impl Writeable for InMemorySigner { } } -impl ReadableArgs for InMemorySigner { - fn read(reader: &mut R, node_secret: SecretKey) -> Result { +impl Readable for InMemorySigner { + fn read(reader: &mut R) -> Result { let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION); let funding_key = Readable::read(reader)?; @@ -942,7 +948,6 @@ impl ReadableArgs for InMemorySigner { payment_key, delayed_payment_base_key, htlc_base_key, - node_secret, commitment_seed, channel_value_satoshis, holder_channel_pubkeys, @@ -952,8 +957,8 @@ impl ReadableArgs for InMemorySigner { } } -/// Simple [`KeysInterface`] implementation that takes a 32-byte seed for use as a BIP 32 extended -/// key and derives keys from that. +/// Simple implementation of [`EntropySource`], [`NodeSigner`], and [`SignerProvider`] that takes a +/// 32-byte seed for use as a BIP 32 extended key and derives keys from that. /// /// Your `node_id` is seed/0'. /// Unilateral closes may use seed/1'. @@ -975,9 +980,8 @@ pub struct KeysManager { 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, @@ -1023,15 +1027,16 @@ 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 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, @@ -1045,9 +1050,8 @@ impl KeysManager { 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, @@ -1060,7 +1064,7 @@ impl KeysManager { Err(_) => panic!("Your rng is busted"), } } - /// Derive an old [`Sign`] containing per-channel secrets based on a key derivation parameters. + /// 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 mut unique_start = Sha256::engine(); @@ -1070,7 +1074,9 @@ impl KeysManager { // We only seriously intend to rely on the channel_master_key for true secure // entropy, everything else just ensures uniqueness. We rely on the unique_start (ie // starting_time provided in the constructor) to be unique. - let child_privkey = self.channel_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(chan_id as u32).expect("key space exhausted")).expect("Your RNG is busted"); + let child_privkey = self.channel_master_key.ckd_priv(&self.secp_ctx, + ChildNumber::from_hardened_idx((chan_id as u32) % (1 << 31)).expect("key space exhausted") + ).expect("Your RNG is busted"); unique_start.input(&child_privkey.private_key[..]); let seed = Sha256::from_engine(unique_start).into_inner(); @@ -1098,7 +1104,6 @@ impl KeysManager { InMemorySigner::new( &self.secp_ctx, - self.node_secret, funding_key, revocation_base_key, payment_key, @@ -1243,25 +1248,14 @@ impl KeysManager { 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) } } impl NodeSigner for KeysManager { - fn get_node_secret(&self, recipient: Recipient) -> Result { - match recipient { - Recipient::Node => Ok(self.node_secret.clone()), - Recipient::PhantomNode => Err(()) - } - } - fn get_node_id(&self, recipient: Recipient) -> Result { match recipient { Recipient::Node => Ok(self.node_id.clone()), @@ -1270,7 +1264,10 @@ impl NodeSigner for KeysManager { } fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result { - let mut node_secret = self.get_node_secret(recipient)?; + let mut node_secret = match recipient { + Recipient::Node => Ok(self.node_secret.clone()), + Recipient::PhantomNode => Err(()) + }?; if let Some(tweak) = tweak { node_secret = node_secret.mul_tweak(tweak).map_err(|_| ())?; } @@ -1284,10 +1281,15 @@ impl NodeSigner for KeysManager { fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result { let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data); let secret = match recipient { - Recipient::Node => self.get_node_secret(Recipient::Node)?, - Recipient::PhantomNode => return Err(()), - }; - Ok(self.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), &secret)) + Recipient::Node => Ok(&self.node_secret), + Recipient::PhantomNode => Err(()) + }?; + Ok(self.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), secret)) + } + + fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result { + let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]); + Ok(sign(&self.secp_ctx, &msg_hash, &self.node_secret)) } } @@ -1296,7 +1298,12 @@ impl SignerProvider for KeysManager { fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] { let child_idx = self.channel_child_index.fetch_add(1, Ordering::AcqRel); - assert!(child_idx <= core::u32::MAX as usize); + // `child_idx` is the only thing guaranteed to make each channel unique without a restart + // (though `user_channel_id` should help, depending on user behavior). If it manages to + // roll over, we may generate duplicate keys for two different channels, which could result + // in loss of funds. Because we only support 32-bit+ systems, assert that our `AtomicUsize` + // doesn't reach `u32::MAX`. + assert!(child_idx < core::u32::MAX as usize, "2^32 channels opened without restart"); let mut id = [0; 32]; id[0..4].copy_from_slice(&(child_idx as u32).to_be_bytes()); id[4..8].copy_from_slice(&self.starting_time_nanos.to_be_bytes()); @@ -1310,7 +1317,7 @@ impl SignerProvider for KeysManager { } fn read_chan_signer(&self, reader: &[u8]) -> Result { - InMemorySigner::read(&mut io::Cursor::new(reader), self.node_secret.clone()) + InMemorySigner::read(&mut io::Cursor::new(reader)) } fn get_destination_script(&self) -> Script { @@ -1322,8 +1329,6 @@ impl SignerProvider for KeysManager { } } -impl KeysInterface for KeysManager {} - /// Similar to [`KeysManager`], but allows the node using this struct to receive phantom node /// payments. /// @@ -1359,13 +1364,6 @@ impl EntropySource for PhantomKeysManager { } impl NodeSigner for PhantomKeysManager { - fn get_node_secret(&self, recipient: Recipient) -> Result { - match recipient { - Recipient::Node => self.inner.get_node_secret(Recipient::Node), - Recipient::PhantomNode => Ok(self.phantom_secret.clone()), - } - } - fn get_node_id(&self, recipient: Recipient) -> Result { match recipient { Recipient::Node => self.inner.get_node_id(Recipient::Node), @@ -1374,7 +1372,10 @@ impl NodeSigner for PhantomKeysManager { } fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result { - let mut node_secret = self.get_node_secret(recipient)?; + let mut node_secret = match recipient { + Recipient::Node => self.inner.node_secret.clone(), + Recipient::PhantomNode => self.phantom_secret.clone(), + }; if let Some(tweak) = tweak { node_secret = node_secret.mul_tweak(tweak).map_err(|_| ())?; } @@ -1387,8 +1388,15 @@ impl NodeSigner for PhantomKeysManager { fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result { let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data); - let secret = self.get_node_secret(recipient)?; - Ok(self.inner.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), &secret)) + let secret = match recipient { + Recipient::Node => &self.inner.node_secret, + Recipient::PhantomNode => &self.phantom_secret, + }; + Ok(self.inner.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), secret)) + } + + fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result { + self.inner.sign_gossip_message(msg) } } @@ -1416,8 +1424,6 @@ impl SignerProvider for PhantomKeysManager { } } -impl KeysInterface for PhantomKeysManager {} - impl PhantomKeysManager { /// Constructs a [`PhantomKeysManager`] given a 32-byte seed and an additional `cross_node_seed` /// that is shared across all nodes that intend to participate in [phantom node payments] @@ -1454,8 +1460,63 @@ impl PhantomKeysManager { } } -// Ensure that BaseSign can have a vtable +// Ensure that EcdsaChannelSigner can have a vtable #[test] pub fn dyn_sign() { - let _signer: Box; + let _signer: Box; +} + +#[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(); + } + } + }