Skip to content

Commit 5671d29

Browse files
committed
Remove unreachable Err cases on derive_*_revocation_key
The `derive_{public,private}_revocation_key` methods hash the two input keys and then multiply the two input keys by hashed values before adding them together. Because addition can fail if the tweak is the inverse of the secret key this method currently returns a `Result`. However, it is not cryptographically possible to reach the error case - in order to create an issue, the point-multiplied-by-hash values must be the inverse of each other, however each point commits the SHA-256 hash of both keys together. Thus, because changing either key changes the hashes (and the ultimate points added together) in an unpredictable way, there should be no way to construct such points.
1 parent 2746190 commit 5671d29

File tree

4 files changed

+40
-42
lines changed

4 files changed

+40
-42
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2443,7 +2443,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
24432443
let secret = self.get_secret(commitment_number).unwrap();
24442444
let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
24452445
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
2446-
let revocation_pubkey = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint));
2446+
let revocation_pubkey = chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint);
24472447
let delayed_key = chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.counterparty_commitment_params.counterparty_delayed_payment_base_key);
24482448

24492449
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);
@@ -2556,26 +2556,18 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
25562556
} else { return (claimable_outpoints, to_counterparty_output_info); };
25572557

25582558
if let Some(transaction) = tx {
2559-
let revokeable_p2wsh_opt =
2560-
if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(
2561-
&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint)
2562-
{
2563-
let delayed_key = chan_utils::derive_public_key(&self.secp_ctx,
2564-
&per_commitment_point,
2565-
&self.counterparty_commitment_params.counterparty_delayed_payment_base_key);
2566-
Some(chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
2567-
self.counterparty_commitment_params.on_counterparty_tx_csv,
2568-
&delayed_key).to_v0_p2wsh())
2569-
} else {
2570-
debug_assert!(false, "Failed to derive a revocation pubkey key for a commitment state we accepted");
2571-
None
2572-
};
2573-
if let Some(revokeable_p2wsh) = revokeable_p2wsh_opt {
2574-
for (idx, outp) in transaction.output.iter().enumerate() {
2575-
if outp.script_pubkey == revokeable_p2wsh {
2576-
to_counterparty_output_info =
2577-
Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value));
2578-
}
2559+
let revocation_pubkey = chan_utils::derive_public_revocation_key(
2560+
&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint);
2561+
let delayed_key = chan_utils::derive_public_key(&self.secp_ctx,
2562+
&per_commitment_point,
2563+
&self.counterparty_commitment_params.counterparty_delayed_payment_base_key);
2564+
let revokeable_p2wsh = chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
2565+
self.counterparty_commitment_params.on_counterparty_tx_csv,
2566+
&delayed_key).to_v0_p2wsh();
2567+
for (idx, outp) in transaction.output.iter().enumerate() {
2568+
if outp.script_pubkey == revokeable_p2wsh {
2569+
to_counterparty_output_info =
2570+
Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value));
25792571
}
25802572
}
25812573
}

lightning/src/chain/keysinterface.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -742,9 +742,9 @@ impl BaseSign for InMemorySigner {
742742
}
743743

744744
fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
745-
let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key).map_err(|_| ())?;
745+
let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key);
746746
let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
747-
let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?;
747+
let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
748748
let witness_script = {
749749
let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint);
750750
chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.holder_selected_contest_delay(), &counterparty_delayedpubkey)
@@ -755,9 +755,9 @@ impl BaseSign for InMemorySigner {
755755
}
756756

757757
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, ()> {
758-
let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key).map_err(|_| ())?;
758+
let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key);
759759
let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
760-
let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?;
760+
let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
761761
let witness_script = {
762762
let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
763763
let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
@@ -770,11 +770,10 @@ impl BaseSign for InMemorySigner {
770770

771771
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, ()> {
772772
let htlc_key = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key);
773-
let witness_script = if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint) {
774-
let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
775-
let htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
776-
chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey)
777-
} else { return Err(()) };
773+
let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
774+
let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
775+
let htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
776+
let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey);
778777
let mut sighash_parts = sighash::SighashCache::new(htlc_tx);
779778
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
780779
Ok(sign(secp_ctx, &sighash, &htlc_key))

lightning/src/ln/chan_utils.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -347,10 +347,9 @@ pub fn derive_public_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_com
347347
/// commitment transaction, thus per_commitment_secret always come from cheater
348348
/// and revocation_base_secret always come from punisher, which is the broadcaster
349349
/// of the transaction spending with this key knowledge.
350-
///
351-
/// Note that this is infallible iff we trust that at least one of the two input keys are randomly
352-
/// generated (ie our own).
353-
pub fn derive_private_revocation_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_secret: &SecretKey, countersignatory_revocation_base_secret: &SecretKey) -> Result<SecretKey, SecpError> {
350+
pub fn derive_private_revocation_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>,
351+
per_commitment_secret: &SecretKey, countersignatory_revocation_base_secret: &SecretKey)
352+
-> SecretKey {
354353
let countersignatory_revocation_base_point = PublicKey::from_secret_key(&secp_ctx, &countersignatory_revocation_base_secret);
355354
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
356355

@@ -369,9 +368,12 @@ pub fn derive_private_revocation_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1
369368
Sha256::from_engine(sha).into_inner()
370369
};
371370

372-
let countersignatory_contrib = countersignatory_revocation_base_secret.clone().mul_tweak(&Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())?;
373-
let broadcaster_contrib = per_commitment_secret.clone().mul_tweak(&Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())?;
371+
let countersignatory_contrib = countersignatory_revocation_base_secret.clone().mul_tweak(&Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())
372+
.expect("Multiplying a secret key by a hash is expected to never fail per secp256k1 docs");
373+
let broadcaster_contrib = per_commitment_secret.clone().mul_tweak(&Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())
374+
.expect("Multiplying a secret key by a hash is expected to never fail per secp256k1 docs");
374375
countersignatory_contrib.add_tweak(&Scalar::from_be_bytes(broadcaster_contrib.secret_bytes()).unwrap())
376+
.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.")
375377
}
376378

377379
/// Derives a per-commitment-transaction revocation public key from its constituent parts. This is
@@ -385,7 +387,9 @@ pub fn derive_private_revocation_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1
385387
///
386388
/// Note that this is infallible iff we trust that at least one of the two input keys are randomly
387389
/// generated (ie our own).
388-
pub fn derive_public_revocation_key<T: secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, countersignatory_revocation_base_point: &PublicKey) -> Result<PublicKey, SecpError> {
390+
pub fn derive_public_revocation_key<T: secp256k1::Verification>(secp_ctx: &Secp256k1<T>,
391+
per_commitment_point: &PublicKey, countersignatory_revocation_base_point: &PublicKey)
392+
-> PublicKey {
389393
let rev_append_commit_hash_key = {
390394
let mut sha = Sha256::engine();
391395
sha.input(&countersignatory_revocation_base_point.serialize());
@@ -401,9 +405,12 @@ pub fn derive_public_revocation_key<T: secp256k1::Verification>(secp_ctx: &Secp2
401405
Sha256::from_engine(sha).into_inner()
402406
};
403407

404-
let countersignatory_contrib = countersignatory_revocation_base_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())?;
405-
let broadcaster_contrib = per_commitment_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())?;
408+
let countersignatory_contrib = countersignatory_revocation_base_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())
409+
.expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs");
410+
let broadcaster_contrib = per_commitment_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())
411+
.expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs");
406412
countersignatory_contrib.combine(&broadcaster_contrib)
413+
.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.")
407414
}
408415

409416
/// The set of public keys which are used in the creation of one commitment transaction.
@@ -479,7 +486,7 @@ impl TxCreationKeys {
479486
pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> Result<TxCreationKeys, SecpError> {
480487
Ok(TxCreationKeys {
481488
per_commitment_point: per_commitment_point.clone(),
482-
revocation_key: derive_public_revocation_key(&secp_ctx, &per_commitment_point, &countersignatory_revocation_base)?,
489+
revocation_key: derive_public_revocation_key(&secp_ctx, &per_commitment_point, &countersignatory_revocation_base),
483490
broadcaster_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_htlc_base),
484491
countersignatory_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &countersignatory_htlc_base),
485492
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base),

lightning/src/ln/channel.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7968,10 +7968,10 @@ mod tests {
79687968
assert_eq!(chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &base_secret),
79697969
SecretKey::from_slice(&hex::decode("cbced912d3b21bf196a766651e436aff192362621ce317704ea2f75d87e7be0f").unwrap()[..]).unwrap());
79707970

7971-
assert_eq!(chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &base_point).unwrap().serialize()[..],
7971+
assert_eq!(chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..],
79727972
hex::decode("02916e326636d19c33f13e8c0c3a03dd157f332f3e99c317c141dd865eb01f8ff0").unwrap()[..]);
79737973

7974-
assert_eq!(chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_secret, &base_secret).unwrap(),
7974+
assert_eq!(chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_secret, &base_secret),
79757975
SecretKey::from_slice(&hex::decode("d09ffff62ddb2297ab000cc85bcb4283fdeb6aa052affbc9dddcf33b61078110").unwrap()[..]).unwrap());
79767976
}
79777977

0 commit comments

Comments
 (0)