Skip to content

Commit 5dd106d

Browse files
Move invoice signing behind KeysInterface
1 parent df0a0d1 commit 5dd106d

File tree

4 files changed

+52
-10
lines changed

4 files changed

+52
-10
lines changed

lightning-invoice/src/lib.rs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,7 +1291,8 @@ impl<S> Display for SignOrCreationError<S> {
12911291
}
12921292

12931293
mod utils {
1294-
use {CreationError, Currency, Invoice, InvoiceBuilder};
1294+
use {Currency, Invoice, InvoiceBuilder, SemanticError, SignedRawInvoice};
1295+
use bech32::{FromBase32, ToBase32, u5};
12951296
use bitcoin_hashes::Hash;
12961297
use lightning::chain;
12971298
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
@@ -1301,15 +1302,14 @@ mod utils {
13011302
use lightning::routing::network_graph::RoutingFees;
13021303
use lightning::routing::router::RouteHintHop;
13031304
use lightning::util::logger::Logger;
1304-
use secp256k1::Secp256k1;
13051305
use std::ops::Deref;
13061306

13071307
/// Utility to construct an invoice.
13081308
#[allow(dead_code)]
13091309
pub fn from_channelmanager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
13101310
channelmanager: &ChannelManager<Signer, M, T, K, F, L>, amt_msat: Option<u64>, description: String,
1311-
network: Currency,
1312-
) -> Result<Invoice, CreationError>
1311+
network: Currency, keys_manager: &dyn KeysInterface<Signer = Signer>
1312+
) -> Result<Invoice, SemanticError>
13131313
where
13141314
M::Target: chain::Watch<Signer>,
13151315
T::Target: BroadcasterInterface,
@@ -1347,7 +1347,6 @@ mod utils {
13471347
7200, // default invoice expiry is 2 hours
13481348
0,
13491349
);
1350-
let secp_ctx = Secp256k1::new();
13511350
let our_node_pubkey = channelmanager.get_our_node_id();
13521351
let mut invoice = InvoiceBuilder::new(network)
13531352
.description(description)
@@ -1364,9 +1363,29 @@ mod utils {
13641363
invoice = invoice.route(hint);
13651364
}
13661365

1367-
invoice.build_signed(|msg_hash| {
1368-
secp_ctx.sign_recoverable(msg_hash, &channelmanager.get_our_node_secret())
1369-
})
1366+
let raw_invoice = invoice.build_raw().unwrap();
1367+
let hrp_str = raw_invoice.hrp.to_string();
1368+
let hrp_bytes = hrp_str.as_bytes();
1369+
let data_without_signature = raw_invoice.data.to_base32();
1370+
let mut invoice_preimage = Vec::<u8>::from(hrp_bytes);
1371+
1372+
let mut data_part = Vec::from(data_without_signature);
1373+
let overhang = (data_part.len() * 5) % 8;
1374+
if overhang > 0 {
1375+
// add padding if data does not end at a byte boundary
1376+
data_part.push(u5::try_from_u8(0).unwrap());
1377+
1378+
// if overhang is in (1..3) we need to add u5(0) padding two times
1379+
if overhang < 3 {
1380+
data_part.push(u5::try_from_u8(0).unwrap());
1381+
}
1382+
}
1383+
1384+
invoice_preimage.extend_from_slice(&Vec::<u8>::from_base32(&data_part)
1385+
.expect("No padding error may occur due to appended zero above."));
1386+
let signature = keys_manager.sign_invoice(invoice_preimage);
1387+
let signed_raw_invoice: Result<SignedRawInvoice, SemanticError> = raw_invoice.sign(|_| Ok(signature));
1388+
Invoice::from_signed(signed_raw_invoice.unwrap())
13701389
}
13711390

13721391
}
@@ -1696,7 +1715,7 @@ mod test {
16961715
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
16971716
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
16981717
let _chan = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
1699-
let invoice = ::utils::from_channelmanager(&nodes[1].node, Some(10_000), "test".to_string(), Currency::BitcoinTestnet).unwrap();
1718+
let invoice = ::utils::from_channelmanager(&nodes[1].node, Some(10_000), "test".to_string(), Currency::BitcoinTestnet, nodes[1].keys_manager).unwrap();
17001719
assert_eq!(invoice.amount_pico_btc(), Some(100_000));
17011720
assert_eq!(invoice.min_final_cltv_expiry(), Some(&9));
17021721
assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));

lightning/src/chain/keysinterface.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash;
2525
use bitcoin::hash_types::WPubkeyHash;
2626

2727
use bitcoin::secp256k1::key::{SecretKey, PublicKey};
28-
use bitcoin::secp256k1::{Secp256k1, Signature, Signing};
28+
use bitcoin::secp256k1::{Message, Secp256k1, Signature, Signing};
29+
use bitcoin::secp256k1::recovery::RecoverableSignature;
2930
use bitcoin::secp256k1;
3031

3132
use util::{byte_utils, transaction_utils};
@@ -391,6 +392,11 @@ pub trait KeysInterface: Send + Sync {
391392
/// contain no versioning scheme. You may wish to include your own version prefix and ensure
392393
/// you've read all of the provided bytes to ensure no corruption occurred.
393394
fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError>;
395+
396+
/// Sign an invoice's preimage. By parameterizing by the preimage instead of the hash, we allow
397+
/// implementors of this trait to parse the invoice and make sure they're signing what they
398+
/// expect, rather than blindly signing the hash.
399+
fn sign_invoice(&self, invoice_preimage: Vec<u8>) -> RecoverableSignature;
394400
}
395401

396402
#[derive(Clone)]
@@ -1047,6 +1053,15 @@ impl KeysInterface for KeysManager {
10471053
fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
10481054
InMemorySigner::read(&mut std::io::Cursor::new(reader))
10491055
}
1056+
1057+
fn sign_invoice(&self, invoice_preimage: Vec<u8>) -> RecoverableSignature {
1058+
let secp_ctx = Secp256k1::new();
1059+
let mut raw_hash: [u8; 32] = Default::default();
1060+
raw_hash.copy_from_slice(&Sha256::hash(&invoice_preimage)[..]);
1061+
let msg_hash = Message::from_slice(&raw_hash[..])
1062+
.expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
1063+
secp_ctx.sign_recoverable(&msg_hash, &self.get_node_secret())
1064+
}
10501065
}
10511066

10521067
// Ensure that BaseSign can have a vtable

lightning/src/ln/channel.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4843,6 +4843,7 @@ mod tests {
48434843
use bitcoin::secp256k1::{Secp256k1, Message, Signature, All};
48444844
use bitcoin::secp256k1::ffi::Signature as FFISignature;
48454845
use bitcoin::secp256k1::key::{SecretKey,PublicKey};
4846+
use bitcoin::secp256k1::recovery::RecoverableSignature;
48464847
use bitcoin::hashes::sha256::Hash as Sha256;
48474848
use bitcoin::hashes::Hash;
48484849
use bitcoin::hash_types::{Txid, WPubkeyHash};
@@ -4888,6 +4889,7 @@ mod tests {
48884889
}
48894890
fn get_secure_random_bytes(&self) -> [u8; 32] { [0; 32] }
48904891
fn read_chan_signer(&self, _data: &[u8]) -> Result<Self::Signer, DecodeError> { panic!(); }
4892+
fn sign_invoice(&self, _invoice_preimage: Vec<u8>) -> RecoverableSignature { panic!(); }
48914893
}
48924894

48934895
fn public_from_secret_hex(secp_ctx: &Secp256k1<All>, hex: &str) -> PublicKey {

lightning/src/util/test_utils.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use bitcoin::network::constants::Network;
3232
use bitcoin::hash_types::{BlockHash, Txid};
3333

3434
use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1, Signature};
35+
use bitcoin::secp256k1::recovery::RecoverableSignature;
3536

3637
use regex;
3738

@@ -75,6 +76,7 @@ impl keysinterface::KeysInterface for OnlyReadsKeysInterface {
7576
fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, msgs::DecodeError> {
7677
EnforcingSigner::read(&mut std::io::Cursor::new(reader))
7778
}
79+
fn sign_invoice(&self, _invoice_preimage: Vec<u8>) -> RecoverableSignature { unreachable!(); }
7880
}
7981

8082
pub struct TestChainMonitor<'a> {
@@ -483,6 +485,10 @@ impl keysinterface::KeysInterface for TestKeysInterface {
483485
disable_revocation_policy_check: self.disable_revocation_policy_check,
484486
})
485487
}
488+
489+
fn sign_invoice(&self, invoice_preimage: Vec<u8>) -> RecoverableSignature {
490+
self.backing.sign_invoice(invoice_preimage)
491+
}
486492
}
487493

488494

0 commit comments

Comments
 (0)