Skip to content

Commit 2746190

Browse files
committed
Remove unreachable Err cases on derive_{public,private}_key
The `derive_{public,private}_key` methods hash the two input keys and then add them to the input public key. 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 SHA-256 hash of the `base_point` (and other data) must be the inverse of the `base_point`('s secret key). Because changing the `base_point` changes the hash in an unpredictable way, there should be no way to construct such a `base_point`.
1 parent 440c3ee commit 2746190

File tree

4 files changed

+32
-45
lines changed

4 files changed

+32
-45
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2444,7 +2444,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
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);
24462446
let revocation_pubkey = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint));
2447-
let delayed_key = ignore_error!(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));
2447+
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);
24502450
let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
@@ -2560,17 +2560,12 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
25602560
if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(
25612561
&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint)
25622562
{
2563-
if let Ok(delayed_key) = chan_utils::derive_public_key(&self.secp_ctx,
2563+
let delayed_key = chan_utils::derive_public_key(&self.secp_ctx,
25642564
&per_commitment_point,
2565-
&self.counterparty_commitment_params.counterparty_delayed_payment_base_key)
2566-
{
2567-
Some(chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
2568-
self.counterparty_commitment_params.on_counterparty_tx_csv,
2569-
&delayed_key).to_v0_p2wsh())
2570-
} else {
2571-
debug_assert!(false, "Failed to derive a delayed payment key for a commitment state we accepted");
2572-
None
2573-
}
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())
25742569
} else {
25752570
debug_assert!(false, "Failed to derive a revocation pubkey key for a commitment state we accepted");
25762571
None

lightning/src/chain/keysinterface.rs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -655,8 +655,7 @@ impl InMemorySigner {
655655
if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
656656
if spend_tx.input[input_idx].sequence.0 != descriptor.to_self_delay as u32 { return Err(()); }
657657

658-
let delayed_payment_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key)
659-
.expect("We constructed the payment_base_key, so we can only fail here if the RNG is busted.");
658+
let delayed_payment_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key);
660659
let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
661660
let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
662661
let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
@@ -710,7 +709,7 @@ impl BaseSign for InMemorySigner {
710709
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, self.opt_anchors(), &keys);
711710
let htlc_sighashtype = if self.opt_anchors() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
712711
let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]);
713-
let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key).map_err(|_| ())?;
712+
let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key);
714713
htlc_sigs.push(sign(secp_ctx, &htlc_sighash, &holder_htlc_key));
715714
}
716715

@@ -747,7 +746,7 @@ impl BaseSign for InMemorySigner {
747746
let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
748747
let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?;
749748
let witness_script = {
750-
let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint).map_err(|_| ())?;
749+
let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint);
751750
chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.holder_selected_contest_delay(), &counterparty_delayedpubkey)
752751
};
753752
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
@@ -760,8 +759,8 @@ impl BaseSign for InMemorySigner {
760759
let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
761760
let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?;
762761
let witness_script = {
763-
let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint).map_err(|_| ())?;
764-
let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint).map_err(|_| ())?;
762+
let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
763+
let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
765764
chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey)
766765
};
767766
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
@@ -770,19 +769,15 @@ impl BaseSign for InMemorySigner {
770769
}
771770

772771
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, ()> {
773-
if let Ok(htlc_key) = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key) {
774-
let witness_script = if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint) {
775-
if let Ok(counterparty_htlcpubkey) = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint) {
776-
if let Ok(htlcpubkey) = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint) {
777-
chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey)
778-
} else { return Err(()) }
779-
} else { return Err(()) }
780-
} else { return Err(()) };
781-
let mut sighash_parts = sighash::SighashCache::new(htlc_tx);
782-
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
783-
return Ok(sign(secp_ctx, &sighash, &htlc_key))
784-
}
785-
Err(())
772+
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(()) };
778+
let mut sighash_parts = sighash::SighashCache::new(htlc_tx);
779+
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
780+
Ok(sign(secp_ctx, &sighash, &htlc_key))
786781
}
787782

788783
fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {

lightning/src/ln/chan_utils.rs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -316,32 +316,29 @@ impl Readable for CounterpartyCommitmentSecrets {
316316

317317
/// Derives a per-commitment-transaction private key (eg an htlc key or delayed_payment key)
318318
/// from the base secret and the per_commitment_point.
319-
///
320-
/// Note that this is infallible iff we trust that at least one of the two input keys are randomly
321-
/// generated (ie our own).
322-
pub fn derive_private_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_secret: &SecretKey) -> Result<SecretKey, SecpError> {
319+
pub fn derive_private_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_secret: &SecretKey) -> SecretKey {
323320
let mut sha = Sha256::engine();
324321
sha.input(&per_commitment_point.serialize());
325322
sha.input(&PublicKey::from_secret_key(&secp_ctx, &base_secret).serialize());
326323
let res = Sha256::from_engine(sha).into_inner();
327324

328325
base_secret.clone().add_tweak(&Scalar::from_be_bytes(res).unwrap())
326+
.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.")
329327
}
330328

331329
/// Derives a per-commitment-transaction public key (eg an htlc key or a delayed_payment key)
332330
/// from the base point and the per_commitment_key. This is the public equivalent of
333331
/// derive_private_key - using only public keys to derive a public key instead of private keys.
334-
///
335-
/// Note that this is infallible iff we trust that at least one of the two input keys are randomly
336-
/// generated (ie our own).
337-
pub fn derive_public_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_point: &PublicKey) -> Result<PublicKey, SecpError> {
332+
pub fn derive_public_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_point: &PublicKey) -> PublicKey {
338333
let mut sha = Sha256::engine();
339334
sha.input(&per_commitment_point.serialize());
340335
sha.input(&base_point.serialize());
341336
let res = Sha256::from_engine(sha).into_inner();
342337

343-
let hashkey = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&res)?);
338+
let hashkey = PublicKey::from_secret_key(&secp_ctx,
339+
&SecretKey::from_slice(&res).expect("Hashes should always be valid keys unless SHA-256 is broken"));
344340
base_point.combine(&hashkey)
341+
.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.")
345342
}
346343

347344
/// Derives a per-commitment-transaction revocation key from its constituent parts.
@@ -483,9 +480,9 @@ impl TxCreationKeys {
483480
Ok(TxCreationKeys {
484481
per_commitment_point: per_commitment_point.clone(),
485482
revocation_key: derive_public_revocation_key(&secp_ctx, &per_commitment_point, &countersignatory_revocation_base)?,
486-
broadcaster_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_htlc_base)?,
487-
countersignatory_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &countersignatory_htlc_base)?,
488-
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
483+
broadcaster_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_htlc_base),
484+
countersignatory_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &countersignatory_htlc_base),
485+
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base),
489486
})
490487
}
491488

@@ -1506,7 +1503,7 @@ impl<'a> TrustedCommitmentTransaction<'a> {
15061503
let keys = &inner.keys;
15071504
let txid = inner.built.txid;
15081505
let mut ret = Vec::with_capacity(inner.htlcs.len());
1509-
let holder_htlc_key = derive_private_key(secp_ctx, &inner.keys.per_commitment_point, htlc_base_key).map_err(|_| ())?;
1506+
let holder_htlc_key = derive_private_key(secp_ctx, &inner.keys.per_commitment_point, htlc_base_key);
15101507

15111508
for this_htlc in inner.htlcs.iter() {
15121509
assert!(this_htlc.transaction_output_index.is_some());

lightning/src/ln/channel.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7962,10 +7962,10 @@ mod tests {
79627962
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
79637963
assert_eq!(per_commitment_point.serialize()[..], hex::decode("025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486").unwrap()[..]);
79647964

7965-
assert_eq!(chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &base_point).unwrap().serialize()[..],
7965+
assert_eq!(chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..],
79667966
hex::decode("0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5").unwrap()[..]);
79677967

7968-
assert_eq!(chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &base_secret).unwrap(),
7968+
assert_eq!(chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &base_secret),
79697969
SecretKey::from_slice(&hex::decode("cbced912d3b21bf196a766651e436aff192362621ce317704ea2f75d87e7be0f").unwrap()[..]).unwrap());
79707970

79717971
assert_eq!(chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &base_point).unwrap().serialize()[..],

0 commit comments

Comments
 (0)