Skip to content

Commit 7ff74d5

Browse files
committed
Add support for deriving and signing a new static remote key
The `remote_key` derived by default in `KeysManager` depends on the chanel's `channel_keys_id`, which generally has sufficient entropy that without it the `remote_key` cannot be re-derived. In disaster case where there is no remaining state except the `KeysManager`'s `seed`, this results in lost funds, even if the counterparty force-closes the channel. Luckily, because of the `static_remote_key` feature, there's no need for this. If the `remote_key` we derive is one of a countable set, we can simply scan the chain for outputs to our `remote_key`s. Here we set up such new derivation, adding logic to derive one of 1000 possible `remote_key`s (which translates to 2000 potential `script_pubkey`s on chain). We also update the spending code to check which of the two derivation formats where used and sign with the correct key.
1 parent 7926fb9 commit 7ff74d5

File tree

6 files changed

+120
-25
lines changed

6 files changed

+120
-25
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,9 @@ impl SignerProvider for KeyProvider {
389389
&secp_ctx,
390390
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, self.node_secret[31]]).unwrap(),
391391
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, self.node_secret[31]]).unwrap(),
392+
// We leave both the v1 and v2 derivation to_remote keys the same as there's not any
393+
// real reason to fuzz differences here.
394+
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, self.node_secret[31]]).unwrap(),
392395
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, self.node_secret[31]]).unwrap(),
393396
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, self.node_secret[31]]).unwrap(),
394397
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, self.node_secret[31]]).unwrap(),

fuzz/src/full_stack.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,9 @@ impl SignerProvider for KeyProvider {
477477
e = SecretKey::from_slice(&key).unwrap();
478478
key[30] = 6 + if inbound { 0 } else { 6 };
479479
f = key;
480-
let signer = InMemorySigner::new(&secp_ctx, a, b, c, d, e, f, keys_id, keys_id);
480+
// We leave both the v1 and v2 derivation to_remote keys the same as there's not any real
481+
// reason to fuzz differences here, and it keeps us consistent with past behavior.
482+
let signer = InMemorySigner::new(&secp_ctx, a, b, c, c, d, e, f, keys_id, keys_id);
481483

482484
TestChannelSigner::new_with_revoked(DynSigner::new(signer), state, false, false)
483485
}

lightning/src/chain/channelmonitor.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6878,6 +6878,7 @@ mod tests {
68786878
SecretKey::from_slice(&[41; 32]).unwrap(),
68796879
SecretKey::from_slice(&[41; 32]).unwrap(),
68806880
SecretKey::from_slice(&[41; 32]).unwrap(),
6881+
SecretKey::from_slice(&[41; 32]).unwrap(),
68816882
[41; 32],
68826883
[0; 32],
68836884
[0; 32],
@@ -7140,6 +7141,7 @@ mod tests {
71407141
SecretKey::from_slice(&[41; 32]).unwrap(),
71417142
SecretKey::from_slice(&[41; 32]).unwrap(),
71427143
SecretKey::from_slice(&[41; 32]).unwrap(),
7144+
SecretKey::from_slice(&[41; 32]).unwrap(),
71437145
[41; 32],
71447146
[0; 32],
71457147
[0; 32],

lightning/src/chain/onchaintx.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,7 @@ mod tests {
13071307
SecretKey::from_slice(&[41; 32]).unwrap(),
13081308
SecretKey::from_slice(&[41; 32]).unwrap(),
13091309
SecretKey::from_slice(&[41; 32]).unwrap(),
1310+
SecretKey::from_slice(&[41; 32]).unwrap(),
13101311
[41; 32],
13111312
[0; 32],
13121313
[0; 32],

lightning/src/ln/channel.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15987,6 +15987,7 @@ mod tests {
1598715987
SecretKey::from_slice(&<Vec<u8>>::from_hex("30ff4956bbdd3222d44cc5e8a1261dab1e07957bdac5ae88fe3261ef321f3749").unwrap()[..]).unwrap(),
1598815988
SecretKey::from_slice(&<Vec<u8>>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
1598915989
SecretKey::from_slice(&<Vec<u8>>::from_hex("1111111111111111111111111111111111111111111111111111111111111111").unwrap()[..]).unwrap(),
15990+
SecretKey::from_slice(&<Vec<u8>>::from_hex("1111111111111111111111111111111111111111111111111111111111111111").unwrap()[..]).unwrap(),
1599015991
SecretKey::from_slice(&<Vec<u8>>::from_hex("3333333333333333333333333333333333333333333333333333333333333333").unwrap()[..]).unwrap(),
1599115992
SecretKey::from_slice(&<Vec<u8>>::from_hex("1111111111111111111111111111111111111111111111111111111111111111").unwrap()[..]).unwrap(),
1599215993

lightning/src/sign/mod.rs

Lines changed: 110 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ use crate::chain::transaction::OutPoint;
4141
use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
4242
use crate::ln::chan_utils;
4343
use crate::ln::chan_utils::{
44-
get_revokeable_redeemscript, make_funding_redeemscript, ChannelPublicKeys,
45-
ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction,
44+
get_counterparty_payment_script, get_revokeable_redeemscript, make_funding_redeemscript,
45+
ChannelPublicKeys, ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction,
4646
HTLCOutputInCommitment, HolderCommitmentTransaction,
4747
};
4848
use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
@@ -56,6 +56,7 @@ use crate::ln::msgs::PartialSignatureWithNonce;
5656
use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
5757
use crate::ln::script::ShutdownScript;
5858
use crate::offers::invoice::UnsignedBolt12Invoice;
59+
use crate::types::features::ChannelTypeFeatures;
5960
use crate::types::payment::PaymentPreimage;
6061
use crate::util::async_poll::AsyncResult;
6162
use crate::util::ser::{ReadableArgs, Writeable};
@@ -140,6 +141,16 @@ pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ +
140141
pub(crate) const P2TR_KEY_PATH_WITNESS_WEIGHT: u64 = 1 /* witness items */
141142
+ 1 /* schnorr sig len */ + 64 /* schnorr sig */;
142143

144+
/// If a [`KeysManager`] is built with [`KeysManager::new`] with `v2_remote_key_derivation` set,
145+
/// the script which we receive funds to on-chain when our counterparty force-closes a channel is
146+
/// one of this many possible derivation paths.
147+
///
148+
/// Keeping this limited allows for scanning the chain to find lost funds if our state is destroyed,
149+
/// while this being more than a handful provides some privacy by not constantly reusing the same
150+
/// scripts on-chain across channels.
151+
// Note that this MUST remain below the maximum BIP 32 derivation paths (2^31)
152+
pub const STATIC_PAYMENT_KEY_COUNT: u16 = 1000;
153+
143154
/// Information about a spendable output to our "payment key".
144155
///
145156
/// See [`SpendableOutputDescriptor::StaticPaymentOutput`] for more details on how to spend this.
@@ -371,7 +382,7 @@ impl SpendableOutputDescriptor {
371382
if let Some(basepoint) = delayed_payment_basepoint.as_ref() {
372383
// Required to derive signing key: privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)
373384
let add_tweak = basepoint.derive_add_tweak(&per_commitment_point);
374-
let payment_key = DelayedPaymentKey(add_public_key_tweak(
385+
let delayed_payment_key = DelayedPaymentKey(add_public_key_tweak(
375386
secp_ctx,
376387
&basepoint.to_public_key(),
377388
&add_tweak,
@@ -381,7 +392,7 @@ impl SpendableOutputDescriptor {
381392
Some(get_revokeable_redeemscript(
382393
&revocation_pubkey,
383394
*to_self_delay,
384-
&payment_key,
395+
&delayed_payment_key,
385396
)),
386397
Some(add_tweak),
387398
)
@@ -1140,8 +1151,12 @@ pub struct InMemorySigner {
11401151
funding_key: sealed::MaybeTweakedSecretKey,
11411152
/// Holder secret key for blinded revocation pubkey.
11421153
pub revocation_base_key: SecretKey,
1143-
/// Holder secret key used for our balance in counterparty-broadcasted commitment transactions.
1144-
pub payment_key: SecretKey,
1154+
/// Holder secret key used for our balance in counterparty-broadcasted commitment transactions,
1155+
/// old-style derivation.
1156+
payment_key_v1: SecretKey,
1157+
/// Holder secret key used for our balance in counterparty-broadcasted commitment transactions,
1158+
/// new-style derivation.
1159+
payment_key_v2: SecretKey,
11451160
/// Holder secret key used in an HTLC transaction.
11461161
pub delayed_payment_base_key: SecretKey,
11471162
/// Holder HTLC secret key used in commitment transaction HTLC outputs.
@@ -1160,7 +1175,8 @@ impl PartialEq for InMemorySigner {
11601175
fn eq(&self, other: &Self) -> bool {
11611176
self.funding_key == other.funding_key
11621177
&& self.revocation_base_key == other.revocation_base_key
1163-
&& self.payment_key == other.payment_key
1178+
&& self.payment_key_v1 == other.payment_key_v1
1179+
&& self.payment_key_v2 == other.payment_key_v2
11641180
&& self.delayed_payment_base_key == other.delayed_payment_base_key
11651181
&& self.htlc_base_key == other.htlc_base_key
11661182
&& self.commitment_seed == other.commitment_seed
@@ -1174,7 +1190,8 @@ impl Clone for InMemorySigner {
11741190
Self {
11751191
funding_key: self.funding_key.clone(),
11761192
revocation_base_key: self.revocation_base_key.clone(),
1177-
payment_key: self.payment_key.clone(),
1193+
payment_key_v1: self.payment_key_v1.clone(),
1194+
payment_key_v2: self.payment_key_v2.clone(),
11781195
delayed_payment_base_key: self.delayed_payment_base_key.clone(),
11791196
htlc_base_key: self.htlc_base_key.clone(),
11801197
commitment_seed: self.commitment_seed.clone(),
@@ -1186,24 +1203,57 @@ impl Clone for InMemorySigner {
11861203
}
11871204

11881205
impl InMemorySigner {
1189-
/// Creates a new [`InMemorySigner`].
1206+
#[cfg(any(feature = "_test_utils", test))]
11901207
pub fn new<C: Signing>(
11911208
secp_ctx: &Secp256k1<C>, funding_key: SecretKey, revocation_base_key: SecretKey,
1192-
payment_key: SecretKey, delayed_payment_base_key: SecretKey, htlc_base_key: SecretKey,
1193-
commitment_seed: [u8; 32], channel_keys_id: [u8; 32], rand_bytes_unique_start: [u8; 32],
1209+
payment_key_v1: SecretKey, payment_key_v2: SecretKey, delayed_payment_base_key: SecretKey,
1210+
htlc_base_key: SecretKey, commitment_seed: [u8; 32], channel_keys_id: [u8; 32],
1211+
rand_bytes_unique_start: [u8; 32],
11941212
) -> InMemorySigner {
1213+
// TODO: Make the key used dynamic
11951214
let holder_channel_pubkeys = InMemorySigner::make_holder_keys(
11961215
secp_ctx,
11971216
&funding_key,
11981217
&revocation_base_key,
1199-
&payment_key,
1218+
&payment_key_v1,
12001219
&delayed_payment_base_key,
12011220
&htlc_base_key,
12021221
);
12031222
InMemorySigner {
12041223
funding_key: sealed::MaybeTweakedSecretKey::from(funding_key),
12051224
revocation_base_key,
1206-
payment_key,
1225+
payment_key_v1,
1226+
payment_key_v2,
1227+
delayed_payment_base_key,
1228+
htlc_base_key,
1229+
commitment_seed,
1230+
holder_channel_pubkeys,
1231+
channel_keys_id,
1232+
entropy_source: RandomBytes::new(rand_bytes_unique_start),
1233+
}
1234+
}
1235+
1236+
#[cfg(not(any(feature = "_test_utils", test)))]
1237+
fn new<C: Signing>(
1238+
secp_ctx: &Secp256k1<C>, funding_key: SecretKey, revocation_base_key: SecretKey,
1239+
payment_key_v1: SecretKey, payment_key_v2: SecretKey, delayed_payment_base_key: SecretKey,
1240+
htlc_base_key: SecretKey, commitment_seed: [u8; 32], channel_keys_id: [u8; 32],
1241+
rand_bytes_unique_start: [u8; 32],
1242+
) -> InMemorySigner {
1243+
// TODO: Make the key used dynamic
1244+
let holder_channel_pubkeys = InMemorySigner::make_holder_keys(
1245+
secp_ctx,
1246+
&funding_key,
1247+
&revocation_base_key,
1248+
&payment_key_v1,
1249+
&delayed_payment_base_key,
1250+
&htlc_base_key,
1251+
);
1252+
InMemorySigner {
1253+
funding_key: sealed::MaybeTweakedSecretKey::from(funding_key),
1254+
revocation_base_key,
1255+
payment_key_v1,
1256+
payment_key_v2,
12071257
delayed_payment_base_key,
12081258
htlc_base_key,
12091259
commitment_seed,
@@ -1264,14 +1314,28 @@ impl InMemorySigner {
12641314
return Err(());
12651315
}
12661316

1267-
let remotepubkey = bitcoin::PublicKey::new(self.holder_channel_pubkeys.payment_point);
1268-
let supports_anchors_zero_fee_htlc_tx = descriptor
1317+
let legacy_default_channel_type = ChannelTypeFeatures::only_static_remote_key();
1318+
let channel_type_features = descriptor
12691319
.channel_transaction_parameters
12701320
.as_ref()
1271-
.map(|params| params.channel_type_features.supports_anchors_zero_fee_htlc_tx())
1272-
.unwrap_or(false);
1321+
.map(|params| &params.channel_type_features)
1322+
.unwrap_or(&legacy_default_channel_type);
1323+
1324+
let payment_point_v1 = PublicKey::from_secret_key(secp_ctx, &self.payment_key_v1);
1325+
let payment_point_v2 = PublicKey::from_secret_key(secp_ctx, &self.payment_key_v2);
1326+
let spk_v1 = get_counterparty_payment_script(channel_type_features, &payment_point_v1);
1327+
let spk_v2 = get_counterparty_payment_script(channel_type_features, &payment_point_v2);
1328+
1329+
let (remotepubkey, payment_key) = if spk_v1 == descriptor.output.script_pubkey {
1330+
(bitcoin::PublicKey::new(payment_point_v1), &self.payment_key_v1)
1331+
} else {
1332+
if spk_v2 != descriptor.output.script_pubkey {
1333+
return Err(());
1334+
}
1335+
(bitcoin::PublicKey::new(payment_point_v2), &self.payment_key_v2)
1336+
};
12731337

1274-
let witness_script = if supports_anchors_zero_fee_htlc_tx {
1338+
let witness_script = if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
12751339
chan_utils::get_to_countersigner_keyed_anchor_redeemscript(&remotepubkey.inner)
12761340
} else {
12771341
ScriptBuf::new_p2pkh(&remotepubkey.pubkey_hash())
@@ -1286,8 +1350,8 @@ impl InMemorySigner {
12861350
)
12871351
.unwrap()[..]
12881352
);
1289-
let remotesig = sign_with_aux_rand(secp_ctx, &sighash, &self.payment_key, &self);
1290-
let payment_script = if supports_anchors_zero_fee_htlc_tx {
1353+
let remotesig = sign_with_aux_rand(secp_ctx, &sighash, payment_key, &self);
1354+
let payment_script = if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
12911355
witness_script.to_p2wsh()
12921356
} else {
12931357
ScriptBuf::new_p2wpkh(&remotepubkey.wpubkey_hash().unwrap())
@@ -1300,7 +1364,7 @@ impl InMemorySigner {
13001364
let mut witness = Vec::with_capacity(2);
13011365
witness.push(remotesig.serialize_der().to_vec());
13021366
witness[0].push(EcdsaSighashType::All as u8);
1303-
if supports_anchors_zero_fee_htlc_tx {
1367+
if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
13041368
witness.push(witness_script.to_bytes());
13051369
} else {
13061370
witness.push(remotepubkey.to_bytes());
@@ -1874,6 +1938,7 @@ pub struct KeysManager {
18741938
destination_script: ScriptBuf,
18751939
shutdown_pubkey: PublicKey,
18761940
channel_master_key: Xpriv,
1941+
static_payment_key: Xpriv,
18771942
channel_child_index: AtomicUsize,
18781943
peer_storage_key: PeerStorageKey,
18791944
receive_auth_key: ReceiveAuthKey,
@@ -1915,6 +1980,7 @@ impl KeysManager {
19151980
const INBOUND_PAYMENT_KEY_INDEX: ChildNumber = ChildNumber::Hardened { index: 5 };
19161981
const PEER_STORAGE_KEY_INDEX: ChildNumber = ChildNumber::Hardened { index: 6 };
19171982
const RECEIVE_AUTH_KEY_INDEX: ChildNumber = ChildNumber::Hardened { index: 7 };
1983+
const STATIC_PAYMENT_KEY_INDEX: ChildNumber = ChildNumber::Hardened { index: 8 };
19181984

19191985
let secp_ctx = Secp256k1::new();
19201986
// Note that when we aren't serializing the key, network doesn't matter
@@ -1962,6 +2028,10 @@ impl KeysManager {
19622028
.expect("Your RNG is busted")
19632029
.private_key;
19642030

2031+
let static_payment_key = master_key
2032+
.derive_priv(&secp_ctx, &STATIC_PAYMENT_KEY_INDEX)
2033+
.expect("Your RNG is busted");
2034+
19652035
let mut rand_bytes_engine = Sha256::engine();
19662036
rand_bytes_engine.input(&starting_time_secs.to_be_bytes());
19672037
rand_bytes_engine.input(&starting_time_nanos.to_be_bytes());
@@ -1984,6 +2054,7 @@ impl KeysManager {
19842054

19852055
channel_master_key,
19862056
channel_child_index: AtomicUsize::new(0),
2057+
static_payment_key,
19872058

19882059
entropy_source: RandomBytes::new(rand_bytes_unique_start),
19892060

@@ -2004,6 +2075,17 @@ impl KeysManager {
20042075
self.node_secret
20052076
}
20062077

2078+
fn derive_payment_key_v2(&self, key_idx: u64) -> SecretKey {
2079+
let idx = key_idx % u64::from(STATIC_PAYMENT_KEY_COUNT);
2080+
self.static_payment_key
2081+
.derive_priv(
2082+
&self.secp_ctx,
2083+
&ChildNumber::from_hardened_idx(idx as u32).expect("key space exhausted"),
2084+
)
2085+
.expect("Your RNG is busted")
2086+
.private_key
2087+
}
2088+
20072089
/// Derive an old [`EcdsaChannelSigner`] containing per-channel secrets based on a key derivation parameters.
20082090
pub fn derive_channel_keys(&self, params: &[u8; 32]) -> InMemorySigner {
20092091
let chan_id = u64::from_be_bytes(params[0..8].try_into().unwrap());
@@ -2044,16 +2126,20 @@ impl KeysManager {
20442126
}
20452127
let funding_key = key_step!(b"funding key", commitment_seed);
20462128
let revocation_base_key = key_step!(b"revocation base key", funding_key);
2047-
let payment_key = key_step!(b"payment key", revocation_base_key);
2048-
let delayed_payment_base_key = key_step!(b"delayed payment base key", payment_key);
2129+
let payment_key_v1 = key_step!(b"payment key", revocation_base_key);
2130+
let delayed_payment_base_key = key_step!(b"delayed payment base key", payment_key_v1);
20492131
let htlc_base_key = key_step!(b"HTLC base key", delayed_payment_base_key);
20502132
let prng_seed = self.get_secure_random_bytes();
20512133

2134+
let payment_key_v2_idx =
2135+
u64::from_le_bytes(commitment_seed[..8].try_into().expect("8 bytes"));
2136+
20522137
InMemorySigner::new(
20532138
&self.secp_ctx,
20542139
funding_key,
20552140
revocation_base_key,
2056-
payment_key,
2141+
payment_key_v1,
2142+
self.derive_payment_key_v2(payment_key_v2_idx),
20572143
delayed_payment_base_key,
20582144
htlc_base_key,
20592145
commitment_seed,

0 commit comments

Comments
 (0)