Add low_r signature grinding
authorDevrandom <c1.devrandom@niftybox.net>
Fri, 25 Mar 2022 19:34:02 +0000 (20:34 +0100)
committerDevrandom <c1.devrandom@niftybox.net>
Fri, 25 Mar 2022 19:34:02 +0000 (20:34 +0100)
default on, can be turned off via a feature gate

.github/workflows/build.yml
lightning/Cargo.toml
lightning/src/chain/keysinterface.rs
lightning/src/ln/chan_utils.rs
lightning/src/ln/channel.rs
lightning/src/ln/channelmanager.rs
lightning/src/util/crypto.rs

index aaccb76a40e98b6eb7a585f81d99eb41ca511b31..a72f4d4ce0d9ff119a5415abada7e4ca05f5b641 100644 (file)
@@ -115,6 +115,9 @@ jobs:
           cargo test --verbose --color always --no-default-features --features no-std
           # check if there is a conflict between no-std and the default std feature
           cargo test --verbose --color always --features no-std
+          # check that things still pass without grind_signatures
+          # note that outbound_commitment_test only runs in this mode, because of hardcoded signature values
+          cargo test --verbose --color always --no-default-features --features std
           # check if there is a conflict between no-std and the c_bindings cfg
           RUSTFLAGS="--cfg=c_bindings" cargo test --verbose --color always --no-default-features --features=no-std
           cd ..
index f38bb80511189ccaf92cce9bbb4a734949ff0e3b..2b0c2d16b7a3bf9f108157ad981fc1893dab6107 100644 (file)
@@ -32,7 +32,10 @@ _bench_unstable = []
 no-std = ["hashbrown", "bitcoin/no-std", "core2/alloc"]
 std = ["bitcoin/std"]
 
-default = ["std"]
+# Generates low-r bitcoin signatures, which saves 1 byte in 50% of the cases
+grind_signatures = []
+
+default = ["std", "grind_signatures"]
 
 [dependencies]
 bitcoin = { version = "0.27", default-features = false, features = ["secp-recovery"] }
index 1daeec4ef62354a1fb9f4a5597ad7678a270ffe3..be31036220a5a720e27d561697aad4a5daa7869c 100644 (file)
@@ -31,7 +31,7 @@ use bitcoin::secp256k1::recovery::RecoverableSignature;
 use bitcoin::secp256k1;
 
 use util::{byte_utils, transaction_utils};
-use util::crypto::hkdf_extract_expand_twice;
+use util::crypto::{hkdf_extract_expand_twice, sign};
 use util::ser::{Writeable, Writer, Readable, ReadableArgs};
 
 use chain::transaction::OutPoint;
@@ -590,7 +590,7 @@ impl InMemorySigner {
                let remotepubkey = self.pubkeys().payment_point;
                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 remotesig = sign(secp_ctx, &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(()); }
@@ -624,7 +624,7 @@ impl InMemorySigner {
                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!(&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 local_delayedsig = sign(secp_ctx, &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(()); }
@@ -673,7 +673,7 @@ impl BaseSign for InMemorySigner {
                        let htlc_sighashtype = if self.opt_anchors() { SigHashType::SinglePlusAnyoneCanPay } else { SigHashType::All };
                        let htlc_sighash = hash_to_message!(&bip143::SigHashCache::new(&htlc_tx).signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype)[..]);
                        let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key).map_err(|_| ())?;
-                       htlc_sigs.push(secp_ctx.sign(&htlc_sighash, &holder_htlc_key));
+                       htlc_sigs.push(sign(secp_ctx, &htlc_sighash, &holder_htlc_key));
                }
 
                Ok((commitment_sig, htlc_sigs))
@@ -714,7 +714,7 @@ impl BaseSign for InMemorySigner {
                };
                let mut sighash_parts = bip143::SigHashCache::new(justice_tx);
                let sighash = hash_to_message!(&sighash_parts.signature_hash(input, &witness_script, amount, SigHashType::All)[..]);
-               return Ok(secp_ctx.sign(&sighash, &revocation_key))
+               return Ok(sign(secp_ctx, &sighash, &revocation_key))
        }
 
        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, ()> {
@@ -728,7 +728,7 @@ impl BaseSign for InMemorySigner {
                };
                let mut sighash_parts = bip143::SigHashCache::new(justice_tx);
                let sighash = hash_to_message!(&sighash_parts.signature_hash(input, &witness_script, amount, SigHashType::All)[..]);
-               return Ok(secp_ctx.sign(&sighash, &revocation_key))
+               return Ok(sign(secp_ctx, &sighash, &revocation_key))
        }
 
        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, ()> {
@@ -742,7 +742,7 @@ impl BaseSign for InMemorySigner {
                        } else { return Err(()) };
                        let mut sighash_parts = bip143::SigHashCache::new(htlc_tx);
                        let sighash = hash_to_message!(&sighash_parts.signature_hash(input, &witness_script, amount, SigHashType::All)[..]);
-                       return Ok(secp_ctx.sign(&sighash, &htlc_key))
+                       return Ok(sign(secp_ctx, &sighash, &htlc_key))
                }
                Err(())
        }
@@ -756,7 +756,7 @@ impl BaseSign for InMemorySigner {
        fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>)
        -> Result<(Signature, Signature), ()> {
                let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
-               Ok((secp_ctx.sign(&msghash, &self.node_secret), secp_ctx.sign(&msghash, &self.funding_key)))
+               Ok((sign(secp_ctx, &msghash, &self.node_secret), sign(secp_ctx, &msghash, &self.funding_key)))
        }
 
        fn ready_channel(&mut self, channel_parameters: &ChannelTransactionParameters) {
@@ -1102,7 +1102,7 @@ impl KeysManager {
                                        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);
+                                       let sig = sign(secp_ctx, &sighash, &secret.private_key.key);
                                        spend_tx.input[input_idx].witness.push(sig.serialize_der().to_vec());
                                        spend_tx.input[input_idx].witness[0].push(SigHashType::All as u8);
                                        spend_tx.input[input_idx].witness.push(pubkey.key.serialize().to_vec());
index 6dc05c2ef3c09e2fcd5fc16ad8a2ebe2427343d3..370c0cc8edfe6737f3f1d65f5dab59ea688ee854 100644 (file)
@@ -39,6 +39,7 @@ use util::transaction_utils::sort_outputs;
 use ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI};
 use core::ops::Deref;
 use chain;
+use util::crypto::sign;
 
 pub(crate) const MAX_HTLCS: u16 = 483;
 
@@ -841,7 +842,7 @@ impl HolderCommitmentTransaction {
        pub fn dummy() -> Self {
                let secp_ctx = Secp256k1::new();
                let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
-               let dummy_sig = secp_ctx.sign(&secp256k1::Message::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[42; 32]).unwrap());
+               let dummy_sig = sign(&secp_ctx, &secp256k1::Message::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[42; 32]).unwrap());
 
                let keys = TxCreationKeys {
                        per_commitment_point: dummy_key.clone(),
@@ -936,7 +937,7 @@ impl BuiltCommitmentTransaction {
        /// because we are about to broadcast a holder transaction.
        pub fn sign<T: secp256k1::Signing>(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) -> Signature {
                let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis);
-               secp_ctx.sign(&sighash, funding_key)
+               sign(secp_ctx, &sighash, funding_key)
        }
 }
 
@@ -1060,7 +1061,7 @@ impl<'a> TrustedClosingTransaction<'a> {
        /// because we are about to broadcast a holder transaction.
        pub fn sign<T: secp256k1::Signing>(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) -> Signature {
                let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis);
-               secp_ctx.sign(&sighash, funding_key)
+               sign(secp_ctx, &sighash, funding_key)
        }
 }
 
@@ -1415,7 +1416,7 @@ impl<'a> TrustedCommitmentTransaction<'a> {
                        let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, self.opt_anchors(), &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key);
 
                        let sighash = hash_to_message!(&bip143::SigHashCache::new(&htlc_tx).signature_hash(0, &htlc_redeemscript, this_htlc.amount_msat / 1000, SigHashType::All)[..]);
-                       ret.push(secp_ctx.sign(&sighash, &holder_htlc_key));
+                       ret.push(sign(secp_ctx, &sighash, &holder_htlc_key));
                }
                Ok(ret)
        }
index 71e0ea121a929f2533c3af47714a50264e83b962..0b0e092468d008c8c245a2c90198b2858ee7eebd 100644 (file)
@@ -6628,6 +6628,7 @@ mod tests {
                }
        }
 
+       #[cfg(not(feature = "grind_signatures"))]
        #[test]
        fn outbound_commitment_test() {
                // Test vectors from BOLT 3 Appendices C and F (anchors):
index 0d1ee4fb07cae6ad2cad127382764a8b1da939c9..185a06df937f5252304cf283afa39cc2cfcdfd11 100644 (file)
@@ -69,6 +69,7 @@ use core::ops::Deref;
 
 #[cfg(any(test, feature = "std"))]
 use std::time::Instant;
+use util::crypto::sign;
 
 mod inbound_payment {
        use alloc::string::ToString;
@@ -3060,7 +3061,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                        excess_data: Vec::new(),
                };
                let msghash = hash_to_message!(&Sha256dHash::hash(&announcement.encode()[..])[..]);
-               let node_announce_sig = self.secp_ctx.sign(&msghash, &self.our_network_key);
+               let node_announce_sig = sign(&self.secp_ctx, &msghash, &self.our_network_key);
 
                let mut channel_state_lock = self.channel_state.lock().unwrap();
                let channel_state = &mut *channel_state_lock;
index f8a3f847d4348e5cbe13033fef25645125cdf6d4..300ddacb020566dac6fc77ac537077918b76e925 100644 (file)
@@ -1,6 +1,7 @@
 use bitcoin::hashes::{Hash, HashEngine};
 use bitcoin::hashes::hmac::{Hmac, HmacEngine};
 use bitcoin::hashes::sha256::Hash as Sha256;
+use bitcoin::secp256k1::{Message, Secp256k1, SecretKey, Signature, Signing};
 
 macro_rules! hkdf_extract_expand {
        ($salt: expr, $ikm: expr) => {{
@@ -36,3 +37,12 @@ pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]
 pub fn hkdf_extract_expand_thrice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32], [u8; 32]) {
        hkdf_extract_expand!(salt, ikm, 3)
 }
+
+#[inline]
+pub fn sign<C: Signing>(ctx: &Secp256k1<C>, msg: &Message, sk: &SecretKey) -> Signature {
+       #[cfg(feature = "grind_signatures")]
+       let sig = ctx.sign_low_r(msg, sk);
+       #[cfg(not(feature = "grind_signatures"))]
+       let sig = ctx.sign(msg, sk);
+       sig
+}