Skip to content

Commit a743bea

Browse files
committed
Add OurPeerStorage for serialized Peer Storage backups
Introduce the OurPeerStorage struct to manage serialized channel data for peer storage backups. This struct facilitates the distribution of peer storage to channel partners and includes versioning and timestamping for comparison between retrieved peer storage instances. - Add the OurPeerStorage struct with fields for version, timestamp, and serialized channel data (ser_channels). - Implement methods to encrypt and decrypt peer storage securely. - Add functionality to update channel data within OurPeerStorage.
1 parent 46c5b42 commit a743bea

File tree

4 files changed

+191
-3
lines changed

4 files changed

+191
-3
lines changed

fuzz/src/full_stack.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ use lightning::routing::router::{
5656
InFlightHtlcs, PaymentParameters, Route, RouteParameters, Router,
5757
};
5858
use lightning::routing::utxo::UtxoLookup;
59-
use lightning::sign::PeerStorageKey;
60-
use lightning::sign::{EntropySource, InMemorySigner, NodeSigner, Recipient, SignerProvider};
59+
use lightning::sign::{
60+
EntropySource, InMemorySigner, NodeSigner, PeerStorageKey, Recipient, SignerProvider,
61+
};
6162
use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
6263
use lightning::util::config::{ChannelConfig, UserConfig};
6364
use lightning::util::errors::APIError;

lightning/src/ln/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub mod chan_utils;
2424
mod features;
2525
pub mod script;
2626
pub mod types;
27+
pub mod our_peer_storage;
2728

2829
// TODO: These modules were moved from lightning-invoice and need to be better integrated into this
2930
// crate now:

lightning/src/ln/our_peer_storage.rs

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
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+
14+
use bitcoin::hashes::sha256::Hash as Sha256;
15+
use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine};
16+
17+
use crate::sign::PeerStorageKey;
18+
19+
use crate::crypto::chacha20poly1305rfc::ChaCha20Poly1305RFC;
20+
use crate::prelude::*;
21+
22+
/// [`DecryptedOurPeerStorage`] is used to store serialised channel information that allows for the creation of a
23+
/// `peer_storage` backup.
24+
///
25+
/// This structure is designed to serialize channel data for backup and supports encryption
26+
/// using `ChaCha20Poly1305RFC` for transmission.
27+
///
28+
/// # Key Methods
29+
/// - [`DecryptedOurPeerStorage::new`]: Returns [`DecryptedOurPeerStorage`] with the given data.
30+
/// - [`DecryptedOurPeerStorage::encrypt`]: Returns [`EncryptedOurPeerStorage`] created from encrypting the provided data.
31+
/// - [`DecryptedOurPeerStorage::into_vec`]: Returns the data in [`Vec<u8>`] format.
32+
///
33+
/// ## Example
34+
/// ```
35+
/// use lightning::ln::our_peer_storage::DecryptedOurPeerStorage;
36+
/// use lightning::sign::{KeysManager, NodeSigner};
37+
/// let seed = [1u8; 32];
38+
/// let keys_mgr = KeysManager::new(&seed, 42, 42);
39+
/// let key = keys_mgr.get_peer_storage_key();
40+
/// let decrypted_ops = DecryptedOurPeerStorage::new(vec![1, 2, 3]);
41+
/// let our_peer_storage = decrypted_ops.encrypt(&key, [0u8; 32]);
42+
/// let decrypted_data = our_peer_storage.decrypt(&key).unwrap();
43+
/// assert_eq!(decrypted_data.into_vec(), vec![1, 2, 3]);
44+
/// ```
45+
pub struct DecryptedOurPeerStorage {
46+
data: Vec<u8>,
47+
}
48+
49+
impl DecryptedOurPeerStorage {
50+
/// Returns [`DecryptedOurPeerStorage`] with the given data.
51+
pub fn new(data: Vec<u8>) -> Self {
52+
Self { data }
53+
}
54+
55+
/// Returns data stored in [`Vec<u8>`] format.
56+
pub fn into_vec(self) -> Vec<u8> {
57+
self.data
58+
}
59+
60+
/// Encrypts the data inside [`DecryptedOurPeerStorage`] using [`PeerStorageKey`] and `random_bytes`
61+
/// and returns [`EncryptedOurPeerStorage`].
62+
pub fn encrypt(self, key: &PeerStorageKey, random_bytes: [u8; 32]) -> EncryptedOurPeerStorage {
63+
let mut data = self.data;
64+
let plaintext_len = data.len();
65+
let nonce = derive_nonce(key, &random_bytes);
66+
67+
let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b"");
68+
let mut tag = [0; 16];
69+
chacha.encrypt_full_message_in_place(&mut data[0..plaintext_len], &mut tag);
70+
71+
data.extend_from_slice(&tag);
72+
73+
// Prepend `random_bytes` in front of the encrypted_blob.
74+
data.splice(0..0, random_bytes);
75+
76+
EncryptedOurPeerStorage { cipher: data }
77+
}
78+
}
79+
80+
/// [`EncryptedOurPeerStorage`] represents encrypted state of the corresponding [`DecryptedOurPeerStorage`].
81+
///
82+
/// # Key Methods
83+
/// - [`EncryptedOurPeerStorage::new`]: Returns [`EncryptedOurPeerStorage`] with the given encrypted cipher.
84+
/// - [`EncryptedOurPeerStorage::decrypt`]: Returns [`DecryptedOurPeerStorage`] created from decrypting the cipher.
85+
/// - [`EncryptedOurPeerStorage::into_vec`]: Returns the cipher in [`Vec<u8>`] format.
86+
pub struct EncryptedOurPeerStorage {
87+
cipher: Vec<u8>,
88+
}
89+
90+
impl EncryptedOurPeerStorage {
91+
// Ciphertext is of the form: random_bytes(32 bytes) + encrypted_data + tag(16 bytes).
92+
const MIN_CIPHERTEXT_LEN: usize = 32 + 16;
93+
94+
/// Returns [`EncryptedOurPeerStorage`] if cipher is of appropriate length, else returns error.
95+
pub fn new(cipher: Vec<u8>) -> Result<Self, ()> {
96+
if cipher.len() < Self::MIN_CIPHERTEXT_LEN {
97+
return Err(());
98+
}
99+
return Ok(Self { cipher });
100+
}
101+
102+
/// Returns cipher in the format [`Vec<u8>`].
103+
pub fn into_vec(self) -> Vec<u8> {
104+
self.cipher
105+
}
106+
107+
/// Returns [`DecryptedOurPeerStorage`] if it successfully decrypts the ciphertext with the `key`,
108+
/// else returns error.
109+
pub fn decrypt(self, key: &PeerStorageKey) -> Result<DecryptedOurPeerStorage, ()> {
110+
let mut cipher = self.cipher;
111+
let cyphertext_len = cipher.len();
112+
113+
if cipher.len() < Self::MIN_CIPHERTEXT_LEN {
114+
return Err(());
115+
}
116+
117+
// Ciphertext is of the form: random_bytes(32 bytes) + encrypted_data + tag(16 bytes).
118+
let (data_mut, tag) = cipher.split_at_mut(cyphertext_len - 16);
119+
let (random_bytes, encrypted_data) = data_mut.split_at_mut(32);
120+
121+
let nonce = derive_nonce(key, random_bytes);
122+
123+
let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b"");
124+
125+
if chacha.check_decrypt_in_place(encrypted_data, tag).is_err() {
126+
return Err(());
127+
}
128+
129+
cipher.truncate(cyphertext_len - 16);
130+
cipher.drain(0..32);
131+
132+
Ok(DecryptedOurPeerStorage { data: cipher })
133+
}
134+
}
135+
136+
/// Nonce for encryption and decryption: Hmac(Sha256(key) + random_bytes).
137+
fn derive_nonce(key: &PeerStorageKey, random_bytes: &[u8]) -> [u8; 12] {
138+
let key_hash = Sha256::const_hash(&key.inner);
139+
140+
let mut hmac = HmacEngine::<Sha256>::new(key_hash.as_byte_array());
141+
hmac.input(&random_bytes);
142+
let mut nonce = [0u8; 12];
143+
// First 4 bytes of the nonce should be 0.
144+
nonce[4..].copy_from_slice(&Hmac::from_engine(hmac).to_byte_array()[0..8]);
145+
146+
nonce
147+
}
148+
149+
#[cfg(test)]
150+
mod tests {
151+
use crate::ln::our_peer_storage::{derive_nonce, DecryptedOurPeerStorage};
152+
use crate::sign::PeerStorageKey;
153+
154+
#[test]
155+
fn test_peer_storage_encryption_decryption() {
156+
let key1 = PeerStorageKey { inner: [0u8; 32] };
157+
let key2 = PeerStorageKey { inner: [1u8; 32] };
158+
let random_bytes1 = [200; 32];
159+
let random_bytes2 = [201; 32];
160+
161+
// Happy Path
162+
let decrypted_ops = DecryptedOurPeerStorage::new(vec![42u8; 32]);
163+
let decrypted_ops_res: DecryptedOurPeerStorage =
164+
decrypted_ops.encrypt(&key1, random_bytes1).decrypt(&key1).unwrap();
165+
assert_eq!(decrypted_ops_res.into_vec(), vec![42u8; 32]);
166+
167+
// Changing Key
168+
let decrypted_ops_wrong_key = DecryptedOurPeerStorage::new(vec![42u8; 32]);
169+
let decrypted_ops_wrong_key_res =
170+
decrypted_ops_wrong_key.encrypt(&key2, random_bytes2).decrypt(&key1);
171+
assert!(decrypted_ops_wrong_key_res.is_err());
172+
173+
// Nonce derivation happy path
174+
let nonce = derive_nonce(&key1, &random_bytes1);
175+
let nonce_happy_path = derive_nonce(&key1, &random_bytes1);
176+
assert_eq!(nonce, nonce_happy_path);
177+
178+
// Nonce derivation with different `random_bytes` & `key`
179+
let nonce_diff_random_bytes = derive_nonce(&key1, &random_bytes2);
180+
let nonce_diff_key = derive_nonce(&key2, &random_bytes1);
181+
let nonce_diff_key_random_bytes = derive_nonce(&key2, &random_bytes2);
182+
assert_ne!(nonce, nonce_diff_random_bytes);
183+
assert_ne!(nonce, nonce_diff_key);
184+
assert_ne!(nonce, nonce_diff_key_random_bytes);
185+
}
186+
}

lightning/src/sign/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,7 @@ pub trait ChannelSigner {
796796
fn channel_keys_id(&self) -> [u8; 32];
797797
}
798798

799-
/// Represents Secret Key used for encrypting Peer Storage.
799+
/// Represents the secret key material used for encrypting Peer Storage.
800800
#[derive(Clone, Copy, PartialEq, Eq)]
801801
pub struct PeerStorageKey {
802802
/// In chanmon_consistency we derive this key.

0 commit comments

Comments
 (0)