lightning/ln/
our_peer_storage.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//! `DecryptedOurPeerStorage` enables storage of encrypted serialized channel data.
11//! It provides encryption of data to maintain data integrity and
12//! security during transmission.
13
14use bitcoin::hashes::sha256::Hash as Sha256;
15use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine};
16use bitcoin::secp256k1::PublicKey;
17
18use crate::ln::types::ChannelId;
19use crate::sign::PeerStorageKey;
20
21use crate::crypto::chacha20poly1305rfc::ChaCha20Poly1305RFC;
22use crate::prelude::*;
23
24/// [`DecryptedOurPeerStorage`] is used to store serialised channel information that allows for the creation of a
25/// `peer_storage` backup.
26///
27/// This structure is designed to serialize channel data for backup and supports encryption
28/// using `ChaCha20Poly1305RFC` for transmission.
29///
30/// # Key Methods
31/// - [`DecryptedOurPeerStorage::new`]: Returns [`DecryptedOurPeerStorage`] with the given data.
32/// - [`DecryptedOurPeerStorage::encrypt`]: Returns [`EncryptedOurPeerStorage`] created from encrypting the provided data.
33/// - [`DecryptedOurPeerStorage::into_vec`]: Returns the data in [`Vec<u8>`] format.
34///
35/// ## Example
36/// ```
37/// use lightning::ln::our_peer_storage::DecryptedOurPeerStorage;
38/// use lightning::sign::{KeysManager, NodeSigner};
39/// let seed = [1u8; 32];
40/// let keys_mgr = KeysManager::new(&seed, 42, 42, true);
41/// let key = keys_mgr.get_peer_storage_key();
42/// let decrypted_ops = DecryptedOurPeerStorage::new(vec![1, 2, 3]);
43/// let our_peer_storage = decrypted_ops.encrypt(&key, &[0u8; 32]);
44/// let decrypted_data = our_peer_storage.decrypt(&key).unwrap();
45/// assert_eq!(decrypted_data.into_vec(), vec![1, 2, 3]);
46/// ```
47pub struct DecryptedOurPeerStorage {
48	data: Vec<u8>,
49}
50
51impl DecryptedOurPeerStorage {
52	/// Returns [`DecryptedOurPeerStorage`] with the given data.
53	pub fn new(data: Vec<u8>) -> Self {
54		Self { data }
55	}
56
57	/// Returns data stored in [`Vec<u8>`] format.
58	pub fn into_vec(self) -> Vec<u8> {
59		self.data
60	}
61
62	/// Encrypts the data inside [`DecryptedOurPeerStorage`] using [`PeerStorageKey`] and `random_bytes`
63	/// and returns [`EncryptedOurPeerStorage`].
64	pub fn encrypt(self, key: &PeerStorageKey, random_bytes: &[u8; 32]) -> EncryptedOurPeerStorage {
65		let mut data = self.data;
66		let plaintext_len = data.len();
67		let nonce = derive_nonce(key, random_bytes);
68
69		let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b"");
70		let mut tag = [0; 16];
71		chacha.encrypt_full_message_in_place(&mut data[0..plaintext_len], &mut tag);
72
73		data.extend_from_slice(&tag);
74
75		// Append `random_bytes` in front of the encrypted_blob.
76		data.extend_from_slice(random_bytes);
77
78		EncryptedOurPeerStorage { cipher: data }
79	}
80}
81
82/// [`EncryptedOurPeerStorage`] represents encrypted state of the corresponding [`DecryptedOurPeerStorage`].
83///
84/// # Key Methods
85/// - [`EncryptedOurPeerStorage::new`]: Returns [`EncryptedOurPeerStorage`] with the given encrypted cipher.
86/// - [`EncryptedOurPeerStorage::decrypt`]: Returns [`DecryptedOurPeerStorage`] created from decrypting the cipher.
87/// - [`EncryptedOurPeerStorage::into_vec`]: Returns the cipher in [`Vec<u8>`] format.
88pub struct EncryptedOurPeerStorage {
89	cipher: Vec<u8>,
90}
91
92impl EncryptedOurPeerStorage {
93	// Ciphertext is of the form: random_bytes(32 bytes) + encrypted_data + tag(16 bytes).
94	const MIN_CIPHERTEXT_LEN: usize = 32 + 16;
95
96	/// Returns [`EncryptedOurPeerStorage`] if cipher is of appropriate length, else returns error.
97	pub fn new(cipher: Vec<u8>) -> Result<Self, ()> {
98		if cipher.len() < Self::MIN_CIPHERTEXT_LEN {
99			return Err(());
100		}
101		return Ok(Self { cipher });
102	}
103
104	/// Returns cipher in the format [`Vec<u8>`].
105	pub fn into_vec(self) -> Vec<u8> {
106		self.cipher
107	}
108
109	/// Returns [`DecryptedOurPeerStorage`] if it successfully decrypts the ciphertext with the `key`,
110	/// else returns error.
111	pub fn decrypt(self, key: &PeerStorageKey) -> Result<DecryptedOurPeerStorage, ()> {
112		let mut cipher = self.cipher;
113		let cyphertext_len = cipher.len();
114
115		if cipher.len() < Self::MIN_CIPHERTEXT_LEN {
116			return Err(());
117		}
118
119		// Ciphertext is of the form: encrypted_data + tag(16 bytes) + random_bytes(32 bytes).
120		let (data_mut, random_bytes) = cipher.split_at_mut(cyphertext_len - 32);
121		let (encrypted_data, tag) = data_mut.split_at_mut(data_mut.len() - 16);
122
123		let nonce = derive_nonce(key, random_bytes);
124
125		let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b"");
126
127		if chacha.check_decrypt_in_place(encrypted_data, tag).is_err() {
128			return Err(());
129		}
130
131		// Remove tag(16 bytes) + random_bytes(32 bytes).
132		cipher.truncate(cyphertext_len - 16 - 32);
133
134		Ok(DecryptedOurPeerStorage { data: cipher })
135	}
136}
137
138/// Nonce for encryption and decryption: Hmac(Sha256(key) + random_bytes).
139fn derive_nonce(key: &PeerStorageKey, random_bytes: &[u8]) -> [u8; 12] {
140	let key_hash = Sha256::hash(&key.inner);
141
142	let mut hmac = HmacEngine::<Sha256>::new(key_hash.as_byte_array());
143	hmac.input(&random_bytes);
144	let mut nonce = [0u8; 12];
145	// First 4 bytes of the nonce should be 0.
146	nonce[4..].copy_from_slice(&Hmac::from_engine(hmac).to_byte_array()[0..8]);
147
148	nonce
149}
150
151/// [`PeerStorageMonitorHolder`] represents a single channel sent over the wire.
152/// This would be used inside [`ChannelManager`] to determine
153/// if the user has lost channel states so that we can do something about it.
154///
155/// The main idea here is to just enable node to figure out that it has lost some data
156/// using peer storage backups.
157///
158/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
159///
160/// TODO(aditya): Write FundRecoverer to use `monitor_bytes` to drop onchain.
161pub(crate) struct PeerStorageMonitorHolder {
162	/// Channel Id of the channel.
163	pub(crate) channel_id: ChannelId,
164	/// Node Id of the channel partner.
165	pub(crate) counterparty_node_id: PublicKey,
166	/// Minimum seen secret to determine if we have lost state.
167	pub(crate) min_seen_secret: u64,
168	/// Whole serialised ChannelMonitor to recover funds.
169	pub(crate) monitor_bytes: Vec<u8>,
170}
171
172impl_writeable_tlv_based!(PeerStorageMonitorHolder, {
173	(0, channel_id, required),
174	(2, counterparty_node_id, required),
175	(4, min_seen_secret, required),
176	(6, monitor_bytes, required_vec),
177});
178
179#[cfg(test)]
180mod tests {
181	use crate::ln::our_peer_storage::{derive_nonce, DecryptedOurPeerStorage};
182	use crate::sign::PeerStorageKey;
183
184	#[test]
185	fn test_peer_storage_encryption_decryption() {
186		let key1 = PeerStorageKey { inner: [0u8; 32] };
187		let key2 = PeerStorageKey { inner: [1u8; 32] };
188		let random_bytes1 = [200; 32];
189		let random_bytes2 = [201; 32];
190
191		// Happy Path
192		let decrypted_ops = DecryptedOurPeerStorage::new(vec![42u8; 32]);
193		let decrypted_ops_res: DecryptedOurPeerStorage =
194			decrypted_ops.encrypt(&key1, &random_bytes1).decrypt(&key1).unwrap();
195		assert_eq!(decrypted_ops_res.into_vec(), vec![42u8; 32]);
196
197		// Changing Key
198		let decrypted_ops_wrong_key = DecryptedOurPeerStorage::new(vec![42u8; 32]);
199		let decrypted_ops_wrong_key_res =
200			decrypted_ops_wrong_key.encrypt(&key2, &random_bytes2).decrypt(&key1);
201		assert!(decrypted_ops_wrong_key_res.is_err());
202
203		// Nonce derivation happy path
204		let nonce = derive_nonce(&key1, &random_bytes1);
205		let nonce_happy_path = derive_nonce(&key1, &random_bytes1);
206		assert_eq!(nonce, nonce_happy_path);
207
208		// Nonce derivation with different `random_bytes` & `key`
209		let nonce_diff_random_bytes = derive_nonce(&key1, &random_bytes2);
210		let nonce_diff_key = derive_nonce(&key2, &random_bytes1);
211		let nonce_diff_key_random_bytes = derive_nonce(&key2, &random_bytes2);
212		assert_ne!(nonce, nonce_diff_random_bytes);
213		assert_ne!(nonce, nonce_diff_key);
214		assert_ne!(nonce, nonce_diff_key_random_bytes);
215	}
216}