X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fkeysinterface.rs;h=7538d0a83033c03f2f92f17a0dab8ff5c9be1ec5;hb=b54fe5fcc761e7d50c4388d605f1fcbde5aec41a;hp=a7965d4f3419a15c4c97460fca7cc77951521800;hpb=34cdca91baa0187d13969855129073c764f4c895;p=rust-lightning diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index a7965d4f..7538d0a8 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -18,6 +18,7 @@ use bitcoin::network::constants::Network; use bitcoin::util::bip32::{ExtendedPrivKey, ExtendedPubKey, ChildNumber}; use bitcoin::util::bip143; +use bitcoin::bech32::u5; use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::sha256::HashEngine as Sha256State; use bitcoin::hashes::sha256::Hash as Sha256; @@ -42,6 +43,7 @@ use prelude::*; use core::sync::atomic::{AtomicUsize, Ordering}; use io::{self, Error}; use ln::msgs::{DecodeError, MAX_VALUE_MSAT}; +use 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. @@ -398,11 +400,12 @@ pub trait KeysInterface { /// you've read all of the provided bytes to ensure no corruption occurred. fn read_chan_signer(&self, reader: &[u8]) -> Result; - /// Sign an invoice's preimage (note that this is the preimage of the invoice, not the HTLC's - /// preimage). By parameterizing by the preimage instead of the hash, we allow implementors of + /// 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. - fn sign_invoice(&self, invoice_preimage: Vec) -> Result; + /// The hrp is ascii bytes, while the invoice data is base32. + fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5]) -> Result; /// Get secret key material as bytes for use in encrypting and decrypting inbound payment data. /// @@ -526,7 +529,8 @@ impl InMemorySigner { /// described by descriptor, returning the witness stack for the input. /// /// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig, - /// or is not spending the outpoint described by `descriptor.outpoint`. + /// is not spending the outpoint described by `descriptor.outpoint`, + /// or if an output descriptor script_pubkey does not match the one we can spend. pub fn sign_counterparty_payment_input(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &StaticPaymentOutputDescriptor, secp_ctx: &Secp256k1) -> Result>, ()> { // TODO: We really should be taking the SigHashCache as a parameter here instead of // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only @@ -540,6 +544,9 @@ impl InMemorySigner { let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey(); let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]); let remotesig = secp_ctx.sign(&sighash, &self.payment_key); + let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey(); + + if payment_script != descriptor.output.script_pubkey { return Err(()); } let mut witness = Vec::with_capacity(2); witness.push(remotesig.serialize_der().to_vec()); @@ -552,8 +559,9 @@ impl InMemorySigner { /// described by descriptor, returning the witness stack for the input. /// /// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig, - /// is not spending the outpoint described by `descriptor.outpoint`, or does not have a - /// sequence set to `descriptor.to_self_delay`. + /// is not spending the outpoint described by `descriptor.outpoint`, does not have a + /// sequence set to `descriptor.to_self_delay`, or if an output descriptor + /// script_pubkey does not match the one we can spend. pub fn sign_dynamic_p2wsh_input(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &DelayedPaymentOutputDescriptor, secp_ctx: &Secp256k1) -> Result>, ()> { // TODO: We really should be taking the SigHashCache as a parameter here instead of // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only @@ -570,6 +578,9 @@ impl InMemorySigner { let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey); let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]); let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key); + let payment_script = bitcoin::Address::p2wsh(&witness_script, Network::Bitcoin).script_pubkey(); + + if descriptor.output.script_pubkey != payment_script { return Err(()); } let mut witness = Vec::with_capacity(3); witness.push(local_delayedsig.serialize_der().to_vec()); @@ -927,8 +938,9 @@ impl KeysManager { /// output to the given change destination (if sufficient change value remains). The /// transaction will have a feerate, at least, of the given value. /// - /// Returns `Err(())` if the output value is greater than the input value minus required fee or - /// if a descriptor was duplicated. + /// Returns `Err(())` if the output value is greater than the input value minus required fee, + /// if a descriptor was duplicated, or if an output descriptor script_pubkey + /// does not match the one we can spend. /// /// We do not enforce that outputs meet the dust limit or that any output scripts are standard. /// @@ -996,7 +1008,7 @@ impl KeysManager { self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id), descriptor.channel_keys_id)); } - spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&spend_tx, input_idx, &descriptor, &secp_ctx).unwrap(); + spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?; }, SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => { if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id { @@ -1004,7 +1016,7 @@ impl KeysManager { self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id), descriptor.channel_keys_id)); } - spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&spend_tx, input_idx, &descriptor, &secp_ctx).unwrap(); + spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?; }, SpendableOutputDescriptor::StaticOutput { ref output, .. } => { let derivation_idx = if output.script_pubkey == self.destination_script { @@ -1029,6 +1041,10 @@ impl KeysManager { assert_eq!(pubkey.key, self.shutdown_pubkey); } let witness_script = bitcoin::Address::p2pkh(&pubkey, Network::Testnet).script_pubkey(); + let payment_script = bitcoin::Address::p2wpkh(&pubkey, Network::Testnet).expect("uncompressed key found").script_pubkey(); + + if payment_script != output.script_pubkey { return Err(()); }; + let sighash = hash_to_message!(&bip143::SigHashCache::new(&spend_tx).signature_hash(input_idx, &witness_script, output.value, SigHashType::All)[..]); let sig = secp_ctx.sign(&sighash, &secret.private_key.key); spend_tx.input[input_idx].witness.push(sig.serialize_der().to_vec()); @@ -1092,8 +1108,9 @@ impl KeysInterface for KeysManager { InMemorySigner::read(&mut io::Cursor::new(reader)) } - fn sign_invoice(&self, invoice_preimage: Vec) -> Result { - Ok(self.secp_ctx.sign_recoverable(&hash_to_message!(&Sha256::hash(&invoice_preimage)), &self.get_node_secret())) + fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5]) -> Result { + let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data); + Ok(self.secp_ctx.sign_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), &self.get_node_secret())) } }