lightning/ln/
channel_keys.rs

1// This file is Copyright its original authors, visible in version control
2// history.
3//
4// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7// You may not use this file except in accordance with one or both of these
8// licenses.
9
10//! Keys used to generate commitment transactions.
11//! See: <https://github.com/lightning/bolts/blob/master/03-transactions.md#keys>
12
13use crate::io;
14use crate::ln::msgs::DecodeError;
15use crate::util::ser::Readable;
16use crate::util::ser::Writeable;
17use crate::util::ser::Writer;
18use bitcoin::hashes::sha256::Hash as Sha256;
19use bitcoin::hashes::Hash;
20use bitcoin::hashes::HashEngine;
21use bitcoin::secp256k1;
22use bitcoin::secp256k1::PublicKey;
23use bitcoin::secp256k1::Scalar;
24use bitcoin::secp256k1::Secp256k1;
25use bitcoin::secp256k1::SecretKey;
26
27macro_rules! doc_comment {
28	($x:expr, $($tt:tt)*) => {
29		#[doc = $x]
30		$($tt)*
31	};
32}
33macro_rules! basepoint_impl {
34	($BasepointT:ty $(, $KeyName: expr)?) => {
35		impl $BasepointT {
36			/// Get inner Public Key
37			pub fn to_public_key(&self) -> PublicKey {
38				self.0
39			}
40
41			$(doc_comment!(
42				concat!(
43				"Derives the \"tweak\" used in calculate [`", $KeyName, "::from_basepoint`].\n",
44				"\n",
45				"[`", $KeyName, "::from_basepoint`] calculates a private key as:\n",
46				"`privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)`\n",
47				"\n",
48				"This calculates the hash part in the tweak derivation process, which is used to\n",
49				"ensure that each key is unique and cannot be guessed by an external party."
50				),
51				pub fn derive_add_tweak(&self, per_commitment_point: &PublicKey) -> Sha256 {
52					let mut sha = Sha256::engine();
53					sha.input(&per_commitment_point.serialize());
54					sha.input(&self.to_public_key().serialize());
55					Sha256::from_engine(sha)
56				});
57			)?
58		}
59
60		impl From<PublicKey> for $BasepointT {
61			fn from(value: PublicKey) -> Self {
62				Self(value)
63			}
64		}
65	};
66}
67macro_rules! key_impl {
68	($BasepointT:ty, $KeyName:expr) => {
69		doc_comment! {
70			concat!("Derive a public ", $KeyName, " using one node's `per_commitment_point` and its countersignatory's `basepoint`"),
71			pub fn from_basepoint<T: secp256k1::Signing>(
72				secp_ctx: &Secp256k1<T>,
73				countersignatory_basepoint: &$BasepointT,
74				per_commitment_point: &PublicKey,
75			) -> Self {
76				Self(derive_public_key(secp_ctx, per_commitment_point, &countersignatory_basepoint.0))
77			}
78		}
79
80		doc_comment! {
81			concat!("Build a ", $KeyName, " directly from an already-derived private key"),
82			pub fn from_secret_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, sk: &SecretKey) -> Self {
83				Self(PublicKey::from_secret_key(&secp_ctx, &sk))
84			}
85		}
86
87		/// Get inner Public Key
88		pub fn to_public_key(&self) -> PublicKey {
89			self.0
90		}
91	}
92}
93macro_rules! key_read_write {
94	($SelfT:ty) => {
95		impl Writeable for $SelfT {
96			fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
97				self.0.serialize().write(w)
98			}
99		}
100
101		impl Readable for $SelfT {
102			fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
103				let key: PublicKey = Readable::read(r)?;
104				Ok(Self(key))
105			}
106		}
107	};
108}
109
110/// Base key used in conjunction with a `per_commitment_point` to generate a [`DelayedPaymentKey`].
111///
112/// The delayed payment key is used to pay the commitment state broadcaster their
113/// non-HTLC-encumbered funds after a delay to give their counterparty a chance to punish if the
114/// state broadcasted was previously revoked.
115#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
116pub struct DelayedPaymentBasepoint(pub PublicKey);
117basepoint_impl!(DelayedPaymentBasepoint, "DelayedPaymentKey");
118key_read_write!(DelayedPaymentBasepoint);
119
120/// A derived key built from a [`DelayedPaymentBasepoint`] and `per_commitment_point`.
121///
122/// The delayed payment key is used to pay the commitment state broadcaster their
123/// non-HTLC-encumbered funds after a delay. This delay gives their counterparty a chance to
124/// punish and claim all the channel funds if the state broadcasted was previously revoked.
125///
126/// [See the BOLT specs]
127/// <https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation>
128/// for more information on key derivation details.
129#[derive(PartialEq, Eq, Clone, Copy, Debug)]
130pub struct DelayedPaymentKey(pub PublicKey);
131
132impl DelayedPaymentKey {
133	key_impl!(DelayedPaymentBasepoint, "delayedpubkey");
134}
135key_read_write!(DelayedPaymentKey);
136
137/// Base key used in conjunction with a `per_commitment_point` to generate an [`HtlcKey`].
138///
139/// HTLC keys are used to ensure only the recipient of an HTLC can claim it on-chain with the HTLC
140/// preimage and that only the sender of an HTLC can claim it on-chain after it has timed out.
141/// Thus, both channel counterparties' HTLC keys will appears in each HTLC output's script.
142#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
143pub struct HtlcBasepoint(pub PublicKey);
144basepoint_impl!(HtlcBasepoint, "HtlcKey");
145key_read_write!(HtlcBasepoint);
146
147/// A derived key built from a [`HtlcBasepoint`] and `per_commitment_point`.
148///
149/// HTLC keys are used to ensure only the recipient of an HTLC can claim it on-chain with the HTLC
150/// preimage and that only the sender of an HTLC can claim it on-chain after it has timed out.
151/// Thus, both channel counterparties' HTLC keys will appears in each HTLC output's script.
152///
153/// [See the BOLT specs]
154/// <https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation>
155/// for more information on key derivation details.
156#[derive(PartialEq, Eq, Clone, Copy, Debug)]
157pub struct HtlcKey(pub PublicKey);
158
159impl HtlcKey {
160	key_impl!(HtlcBasepoint, "htlcpubkey");
161}
162key_read_write!(HtlcKey);
163
164/// Derives a per-commitment-transaction public key (eg an htlc key or a delayed_payment key)
165/// from the base point and the per_commitment_key. This is the public equivalent of
166/// derive_private_key - using only public keys to derive a public key instead of private keys.
167fn derive_public_key<T: secp256k1::Signing>(
168	secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_point: &PublicKey,
169) -> PublicKey {
170	let mut sha = Sha256::engine();
171	sha.input(&per_commitment_point.serialize());
172	sha.input(&base_point.serialize());
173	let res = Sha256::from_engine(sha);
174
175	add_public_key_tweak(secp_ctx, base_point, &res)
176}
177
178/// Adds a tweak to a public key to derive a new public key.
179///
180/// May panic if `tweak` is not the output of a SHA-256 hash.
181pub fn add_public_key_tweak<T: secp256k1::Signing>(
182	secp_ctx: &Secp256k1<T>, base_point: &PublicKey, tweak: &Sha256,
183) -> PublicKey {
184	let hashkey = PublicKey::from_secret_key(
185		&secp_ctx,
186		&SecretKey::from_slice(tweak.as_byte_array())
187			.expect("Hashes should always be valid keys unless SHA-256 is broken"),
188	);
189	base_point.combine(&hashkey)
190		.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.")
191}
192
193/// Master key used in conjunction with per_commitment_point to generate [htlcpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#key-derivation) for the latest state of a channel.
194/// A watcher can be given a [RevocationBasepoint] to generate per commitment [RevocationKey] to create justice transactions.
195#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
196pub struct RevocationBasepoint(pub PublicKey);
197basepoint_impl!(RevocationBasepoint);
198key_read_write!(RevocationBasepoint);
199
200/// The revocation key is used to allow a channel party to revoke their state - giving their
201/// counterparty the required material to claim all of their funds if they broadcast that state.
202///
203/// Each commitment transaction has a revocation key based on the basepoint and
204/// per_commitment_point which is used in both commitment and HTLC transactions.
205///
206/// See [the BOLT spec for derivation details]
207/// <https://github.com/lightning/bolts/blob/master/03-transactions.md#revocationpubkey-derivation>
208#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
209pub struct RevocationKey(pub PublicKey);
210
211impl RevocationKey {
212	/// Derives a per-commitment-transaction revocation public key from one party's per-commitment
213	/// point and the other party's [`RevocationBasepoint`]. This is the public equivalent of
214	/// [`chan_utils::derive_private_revocation_key`] - using only public keys to derive a public
215	/// key instead of private keys.
216	///
217	/// Note that this is infallible iff we trust that at least one of the two input keys are randomly
218	/// generated (ie our own).
219	///
220	/// [`chan_utils::derive_private_revocation_key`]: crate::ln::chan_utils::derive_private_revocation_key
221	pub fn from_basepoint<T: secp256k1::Verification>(
222		secp_ctx: &Secp256k1<T>, countersignatory_basepoint: &RevocationBasepoint,
223		per_commitment_point: &PublicKey,
224	) -> Self {
225		let rev_append_commit_hash_key = {
226			let mut sha = Sha256::engine();
227			sha.input(&countersignatory_basepoint.to_public_key().serialize());
228			sha.input(&per_commitment_point.serialize());
229
230			Sha256::from_engine(sha).to_byte_array()
231		};
232		let commit_append_rev_hash_key = {
233			let mut sha = Sha256::engine();
234			sha.input(&per_commitment_point.serialize());
235			sha.input(&countersignatory_basepoint.to_public_key().serialize());
236
237			Sha256::from_engine(sha).to_byte_array()
238		};
239
240		let countersignatory_contrib = countersignatory_basepoint.to_public_key().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())
241			.expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs");
242		let broadcaster_contrib = (&per_commitment_point).mul_tweak(&secp_ctx, &Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())
243			.expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs");
244		let pk = countersignatory_contrib.combine(&broadcaster_contrib)
245			.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.");
246		Self(pk)
247	}
248
249	/// Get inner Public Key
250	pub fn to_public_key(&self) -> PublicKey {
251		self.0
252	}
253}
254key_read_write!(RevocationKey);
255
256#[cfg(test)]
257mod test {
258	use super::derive_public_key;
259	use bitcoin::hex::FromHex;
260	use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
261
262	#[test]
263	fn test_key_derivation() {
264		// Test vectors from BOLT 3 Appendix E:
265		let secp_ctx = Secp256k1::new();
266
267		let base_secret = SecretKey::from_slice(
268			&<Vec<u8>>::from_hex(
269				"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
270			)
271			.unwrap()[..],
272		)
273		.unwrap();
274		let per_commitment_secret = SecretKey::from_slice(
275			&<Vec<u8>>::from_hex(
276				"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
277			)
278			.unwrap()[..],
279		)
280		.unwrap();
281
282		let base_point = PublicKey::from_secret_key(&secp_ctx, &base_secret);
283		assert_eq!(
284			base_point.serialize()[..],
285			<Vec<u8>>::from_hex(
286				"036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2"
287			)
288			.unwrap()[..]
289		);
290
291		let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
292		assert_eq!(
293			per_commitment_point.serialize()[..],
294			<Vec<u8>>::from_hex(
295				"025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486"
296			)
297			.unwrap()[..]
298		);
299
300		assert_eq!(
301			derive_public_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..],
302			<Vec<u8>>::from_hex(
303				"0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5"
304			)
305			.unwrap()[..]
306		);
307	}
308}