Skip to content

Commit b8ed4d2

Browse files
authored
Merge pull request #1989 from jkczyz/2023-01-stateless-offers
Stateless BOLT 12 message verification
2 parents 8d50c91 + 8afe694 commit b8ed4d2

File tree

12 files changed

+1778
-316
lines changed

12 files changed

+1778
-316
lines changed

lightning/src/ln/inbound_payment.rs

+69-5
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
1919
use crate::ln::msgs;
2020
use crate::ln::msgs::MAX_VALUE_MSAT;
2121
use crate::util::chacha20::ChaCha20;
22-
use crate::util::crypto::hkdf_extract_expand_thrice;
22+
use crate::util::crypto::hkdf_extract_expand_4x;
2323
use crate::util::errors::APIError;
2424
use crate::util::logger::Logger;
2525

26-
use core::convert::TryInto;
26+
use core::convert::{TryFrom, TryInto};
2727
use core::ops::Deref;
2828

29-
const IV_LEN: usize = 16;
29+
pub(crate) const IV_LEN: usize = 16;
3030
const METADATA_LEN: usize = 16;
3131
const METADATA_KEY_LEN: usize = 32;
3232
const AMT_MSAT_LEN: usize = 8;
@@ -48,21 +48,85 @@ pub struct ExpandedKey {
4848
/// The key used to authenticate a user-provided payment hash and metadata as previously
4949
/// registered with LDK.
5050
user_pmt_hash_key: [u8; 32],
51+
/// The base key used to derive signing keys and authenticate messages for BOLT 12 Offers.
52+
offers_base_key: [u8; 32],
5153
}
5254

5355
impl ExpandedKey {
5456
/// Create a new [`ExpandedKey`] for generating an inbound payment hash and secret.
5557
///
5658
/// It is recommended to cache this value and not regenerate it for each new inbound payment.
5759
pub fn new(key_material: &KeyMaterial) -> ExpandedKey {
58-
let (metadata_key, ldk_pmt_hash_key, user_pmt_hash_key) =
59-
hkdf_extract_expand_thrice(b"LDK Inbound Payment Key Expansion", &key_material.0);
60+
let (metadata_key, ldk_pmt_hash_key, user_pmt_hash_key, offers_base_key) =
61+
hkdf_extract_expand_4x(b"LDK Inbound Payment Key Expansion", &key_material.0);
6062
Self {
6163
metadata_key,
6264
ldk_pmt_hash_key,
6365
user_pmt_hash_key,
66+
offers_base_key,
6467
}
6568
}
69+
70+
/// Returns an [`HmacEngine`] used to construct [`Offer::metadata`].
71+
///
72+
/// [`Offer::metadata`]: crate::offers::offer::Offer::metadata
73+
#[allow(unused)]
74+
pub(crate) fn hmac_for_offer(
75+
&self, nonce: Nonce, iv_bytes: &[u8; IV_LEN]
76+
) -> HmacEngine<Sha256> {
77+
let mut hmac = HmacEngine::<Sha256>::new(&self.offers_base_key);
78+
hmac.input(iv_bytes);
79+
hmac.input(&nonce.0);
80+
hmac
81+
}
82+
}
83+
84+
/// A 128-bit number used only once.
85+
///
86+
/// Needed when constructing [`Offer::metadata`] and deriving [`Offer::signing_pubkey`] from
87+
/// [`ExpandedKey`]. Must not be reused for any other derivation without first hashing.
88+
///
89+
/// [`Offer::metadata`]: crate::offers::offer::Offer::metadata
90+
/// [`Offer::signing_pubkey`]: crate::offers::offer::Offer::signing_pubkey
91+
#[allow(unused)]
92+
#[derive(Clone, Copy, Debug, PartialEq)]
93+
pub(crate) struct Nonce(pub(crate) [u8; Self::LENGTH]);
94+
95+
impl Nonce {
96+
/// Number of bytes in the nonce.
97+
pub const LENGTH: usize = 16;
98+
99+
/// Creates a `Nonce` from the given [`EntropySource`].
100+
pub fn from_entropy_source<ES: Deref>(entropy_source: ES) -> Self
101+
where
102+
ES::Target: EntropySource,
103+
{
104+
let mut bytes = [0u8; Self::LENGTH];
105+
let rand_bytes = entropy_source.get_secure_random_bytes();
106+
bytes.copy_from_slice(&rand_bytes[..Self::LENGTH]);
107+
108+
Nonce(bytes)
109+
}
110+
111+
/// Returns a slice of the underlying bytes of size [`Nonce::LENGTH`].
112+
pub fn as_slice(&self) -> &[u8] {
113+
&self.0
114+
}
115+
}
116+
117+
impl TryFrom<&[u8]> for Nonce {
118+
type Error = ();
119+
120+
fn try_from(bytes: &[u8]) -> Result<Self, ()> {
121+
if bytes.len() != Self::LENGTH {
122+
return Err(());
123+
}
124+
125+
let mut copied_bytes = [0u8; Self::LENGTH];
126+
copied_bytes.copy_from_slice(bytes);
127+
128+
Ok(Self(copied_bytes))
129+
}
66130
}
67131

68132
enum Method {

0 commit comments

Comments
 (0)