Skip to content

Add KeysInterface trait to handle keys management, first part: when C… #214

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

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ hex = "0.3"
honggfuzz = { version = "0.5", optional = true }
rust-crypto = "0.2"
secp256k1 = { version = "0.11", features=["fuzztarget"] }
rand = "0.4"

[build-dependencies]
cc = "1.0"
Expand Down
13 changes: 11 additions & 2 deletions fuzz/fuzz_targets/full_stack_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ extern crate bitcoin;
extern crate crypto;
extern crate lightning;
extern crate secp256k1;
extern crate rand;

use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::transaction::{Transaction, TxOut};
Expand All @@ -14,6 +15,8 @@ use crypto::digest::Digest;

use lightning::chain::chaininterface::{BroadcasterInterface,ConfirmationTarget,ChainListener,FeeEstimator,ChainWatchInterfaceUtil};
use lightning::chain::transaction::OutPoint;
use lightning::chain::keysinterface::KeysInterface;
use lightning::chain::keysinterface;
use lightning::ln::channelmonitor;
use lightning::ln::channelmanager::ChannelManager;
use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor};
Expand All @@ -30,6 +33,8 @@ use utils::test_logger;
use secp256k1::key::{PublicKey,SecretKey};
use secp256k1::Secp256k1;

use rand::{thread_rng,Rng};

use std::cell::RefCell;
use std::collections::HashMap;
use std::cmp;
Expand Down Expand Up @@ -236,8 +241,12 @@ pub fn do_test(data: &[u8], logger: &Arc<Logger>) {
let broadcast = Arc::new(TestBroadcaster{});
let monitor = channelmonitor::SimpleManyChannelMonitor::new(watch.clone(), broadcast.clone());

let channelmanager = ChannelManager::new(our_network_key, slice_to_be32(get_slice!(4)), get_slice!(1)[0] != 0, Network::Bitcoin, fee_est.clone(), monitor.clone(), watch.clone(), broadcast.clone(), Arc::clone(&logger)).unwrap();
let router = Arc::new(Router::new(PublicKey::from_secret_key(&secp_ctx, &our_network_key), watch.clone(), Arc::clone(&logger)));
let mut seed = [0; 32];
let mut rng = thread_rng();
rng.fill_bytes(&mut seed);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fuzz targets must be fully deterministic - if you need to read bytes, read them from the input buffer, though ideally you'd keep consistency with the existing input data.

let keys_manager = Arc::new(keysinterface::KeysManager::new(&seed, Network::Testnet, Arc::clone(&logger)).unwrap());
let channelmanager = ChannelManager::new(slice_to_be32(get_slice!(4)), get_slice!(1)[0] != 0, Network::Bitcoin, fee_est.clone(), monitor.clone(), watch.clone(), broadcast.clone(), Arc::clone(&logger), keys_manager.clone()).unwrap();
let router = Arc::new(Router::new(PublicKey::from_secret_key(&secp_ctx, &keys_manager.get_node_secret()), watch.clone(), Arc::clone(&logger)));

let peers = RefCell::new([false; 256]);
let mut loss_detector = MoneyLossDetector::new(&peers, channelmanager.clone(), monitor.clone(), PeerManager::new(MessageHandler {
Expand Down
233 changes: 233 additions & 0 deletions src/chain/keysinterface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
//! WalletInterface is *not* a wallet, only an interface to bridge between
//! user wallet and ChannelMonitor. If this last one discover on-chain outputs they will
//! be send with associate data as events::Event::SpendableOutputs to be at the disposal of wallet.
//!
//! KeysInterface is no more a wallet, just an entity to get secret from user wallet and derive
//! appropriate keyring materials to others lightning components, as such node_id, destination_script.
//!

use bitcoin::blockdata::transaction::OutPoint;
use bitcoin::blockdata::script::{Script, Builder};
use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::constants::genesis_block;
use bitcoin::network::constants::Network;
use bitcoin::network::serialize::BitcoinHash;
use bitcoin::util::hash::Sha256dHash;
use bitcoin::util::bip32::{ExtendedPrivKey, ExtendedPubKey, ChildNumber};

use secp256k1::key::{SecretKey, PublicKey};
use secp256k1::Secp256k1;
use secp256k1;

use crypto::hkdf::{hkdf_extract,hkdf_expand};

use util::events;
use util::sha2::Sha256;
use util::logger::Logger;

use std::sync::Arc;

/// A trait to describe a wallet which sould receive data to be able to spend onchain outputs
/// fron a lightning channel
pub trait WalletInterface: Send + Sync {
/// Handle an incoming SpendableOutputs event from SimpleManyChannelMonitor containing a
/// CustomOutputScriptDesctitpor. Follow doc of the latter to know how to spend the output.
fn handle_spendable_output(&self, event: events::Event);
}

/// Hacky custom output script descriptors to ease spending of onchain outputs by user wallet
/// Maybe should be changed by real ones when merged into rust-bitcoin.
/// StaticOutputs commit to a static pubkey, i.e one derived once for node operation lifetime.
/// DynamicOutputs commit to a dynamic local_delayedpubkey, i.e one which change for each per_commitment_point
pub enum CustomOutputScriptDescriptor {
/// Outpoint commits to a P2PWKH, should be spend by the following witness :
/// <signature> <pubkey>
/// With pubkey being bip32 /1' from HMAC-Sha512 of user-provided seed as master private key
StaticOutput {
/// Outpoint spendable by user wallet
outpoint: OutPoint,
},
/// Outpoint commits to a P2WSH, should be spend by the following witness :
/// <local_delayedsig> 0 <witnessScript>
/// With input nSequence set to_self_delay.
DynamicOutput {
/// Outpoint spendable by user wallet
outpoint: OutPoint,
/// local_delayedkey = delayed_payment_basepoint_secret + SHA256(per_commitment_point || delayed_payment_basepoint
local_delayedkey: SecretKey,
/// witness redeemScript encumbering output
witness_script: Script,
/// nSequence input must commit to self_delay to satisfy script's OP_CSV
to_self_delay: u16,
}
}

impl CustomOutputScriptDescriptor {
/// Build a StaticOuput descriptor
pub fn static_key(outpoint: OutPoint) -> Self {
CustomOutputScriptDescriptor::StaticOutput {
outpoint,
}
}

/// Build a DynamicOuput descriptor
pub fn dynamic_key(outpoint: OutPoint, local_delayedkey: SecretKey, witness_script: Script, to_self_delay: u16) -> Self {
CustomOutputScriptDescriptor::DynamicOutput {
outpoint,
local_delayedkey,
witness_script,
to_self_delay,
}
}
}

/// A trait to describe an object which should get secrets from user wallet and apply derivation
/// to provide keys materials downstream
/// node_id /0'
/// destination_pubkey /1'
/// shutdown_pubkey /2'
/// channel_master_pubkey /3/N'
pub trait KeysInterface: Send + Sync {
/// Get node secret key to derive node_id
fn get_node_secret(&self) -> SecretKey;
/// Get destination redeemScript to encumber static protocol exit points. For now
/// redeemScript is a pay-2-public-key-hash.
fn get_destination_script(&self) -> Script;
/// Get shutdown_pubkey to use as PublicKey at channel closure
fn get_shutdown_pubkey(&self) -> PublicKey;
/// Get a new set of ChannelKeys from per-channel random key /3/N'
/// For Channel N, keys correspond to ChannelKeys::new_from_seed(/3/N')
fn get_channel_keys(&self) -> Option<ChannelKeys>;
}

/// Set of lightning keys needed to operate a channel as described in BOLT 3
pub struct ChannelKeys {
/// Private key of anchor tx
pub funding_key: SecretKey,
/// Local secret key for blinded revocation pubkey
pub revocation_base_key: SecretKey,
/// Local secret key used in commitment tx htlc outputs
pub payment_base_key: SecretKey,
/// Local secret key used in HTLC tx
pub delayed_payment_base_key: SecretKey,
/// Local htlc secret key used in commitment tx htlc outputs
pub htlc_base_key: SecretKey,
/// Commitment seed
pub commitment_seed: [u8; 32],
}

impl ChannelKeys {
/// Generate a set of lightning keys needed to operate a channel as described in BOLT 3 from
/// used-provided seed
pub fn new_from_seed(seed: &[u8; 32]) -> Result<ChannelKeys, secp256k1::Error> {
let mut prk = [0; 32];
hkdf_extract(Sha256::new(), b"rust-lightning key gen salt", seed, &mut prk);
let secp_ctx = Secp256k1::without_caps();

let mut okm = [0; 32];
hkdf_expand(Sha256::new(), &prk, b"rust-lightning funding key info", &mut okm);
let funding_key = SecretKey::from_slice(&secp_ctx, &okm)?;

hkdf_expand(Sha256::new(), &prk, b"rust-lightning revocation base key info", &mut okm);
let revocation_base_key = SecretKey::from_slice(&secp_ctx, &okm)?;

hkdf_expand(Sha256::new(), &prk, b"rust-lightning payment base key info", &mut okm);
let payment_base_key = SecretKey::from_slice(&secp_ctx, &okm)?;

hkdf_expand(Sha256::new(), &prk, b"rust-lightning delayed payment base key info", &mut okm);
let delayed_payment_base_key = SecretKey::from_slice(&secp_ctx, &okm)?;

hkdf_expand(Sha256::new(), &prk, b"rust-lightning htlc base key info", &mut okm);
let htlc_base_key = SecretKey::from_slice(&secp_ctx, &okm)?;

hkdf_expand(Sha256::new(), &prk, b"rust-lightning local commitment seed info", &mut okm);

Ok(ChannelKeys {
funding_key: funding_key,
revocation_base_key: revocation_base_key,
payment_base_key: payment_base_key,
delayed_payment_base_key: delayed_payment_base_key,
htlc_base_key: htlc_base_key,
commitment_seed: okm
})
}
}

/// Utility for storing/deriving lightning keys materials
pub struct KeysManager {
genesis_hash: Sha256dHash,
secp_ctx: Secp256k1<secp256k1::All>,
master_key: SecretKey,
node_secret: SecretKey,
destination_script: Script,
shutdown_pubkey: PublicKey,
channel_master_key: ExtendedPrivKey,

logger: Arc<Logger>,
}

impl KeysManager {
/// Constructs and empty KeysManager
pub fn new(seed: &[u8;32], network: Network, logger: Arc<Logger>) -> Option<KeysManager> {
let secp_ctx = Secp256k1::new();
match ExtendedPrivKey::new_master(&secp_ctx, network.clone(), seed) {
Ok(master_key) => {
let node_secret = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(0)) {
Ok(node_secret) => node_secret.secret_key,
Err(_) => return None,
};
let destination_script = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(1)) {
Ok(destination_key) => Builder::new().push_slice(&ExtendedPubKey::from_private(&secp_ctx, &destination_key).public_key.serialize()[..])
.push_opcode(opcodes::All::OP_CHECKSIG)
.into_script(),
Err(_) => return None,
};
let shutdown_pubkey = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(2)) {
Ok(shutdown_key) => ExtendedPubKey::from_private(&secp_ctx, &shutdown_key).public_key,
Err(_) => return None,
};
let channel_master_key = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(3)) {
Ok(channel_master_key) => channel_master_key,
Err(_) => return None,
};
return Some(KeysManager {
genesis_hash: genesis_block(network).header.bitcoin_hash(),
secp_ctx,
master_key: master_key.secret_key,
node_secret,
destination_script,
shutdown_pubkey,
channel_master_key,

logger,
});
},
Err(_) => return None,
};
}
}

impl KeysInterface for KeysManager {
fn get_node_secret(&self) -> SecretKey {
self.node_secret.clone()
}

fn get_destination_script(&self) -> Script {
self.destination_script.clone()
}

fn get_shutdown_pubkey(&self) -> PublicKey {
self.shutdown_pubkey.clone()
}

fn get_channel_keys(&self) -> Option<ChannelKeys> {
let channel_pubkey = ExtendedPubKey::from_private(&self.secp_ctx, &self. channel_master_key);
let mut seed = [0; 32];
for (arr, slice) in seed.iter_mut().zip((&channel_pubkey.public_key.serialize()[0..32]).iter()) {
*arr = *slice;
}
if let Ok(channel_keys) = ChannelKeys::new_from_seed(&seed) {
return Some(channel_keys);
} else { None }
}
}
1 change: 1 addition & 0 deletions src/chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

pub mod chaininterface;
pub mod transaction;
pub mod keysinterface;
Loading