Skip to content

Commit dbc466b

Browse files
authored
Allow to sign and verify arbitrary messages
* Node / KeysManager: add feature to sign any msg and verify signature * Test coverage: sign & verify node methods * Fix cargo fmt errors * New Error for message signing and rename error for wallet signing * Better docs for sign and verify methods. Also we expose them via bindings. * Node: corrections in sign/verify msg docs, verify_signature now accepts public key as a parameter
1 parent e104c21 commit dbc466b

File tree

5 files changed

+54
-5
lines changed

5 files changed

+54
-5
lines changed

bindings/ldk_node.udl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ interface Node {
6060
boolean remove_payment([ByRef]PaymentHash payment_hash);
6161
sequence<PeerDetails> list_peers();
6262
sequence<ChannelDetails> list_channels();
63+
[Throws=NodeError]
64+
string sign_message([ByRef]sequence<u8> msg);
65+
boolean verify_signature([ByRef]sequence<u8> msg, [ByRef]string sig, [ByRef]PublicKey pkey);
6366
};
6467

6568
[Error]
@@ -75,7 +78,8 @@ enum NodeError {
7578
"ChannelClosingFailed",
7679
"PersistenceFailed",
7780
"WalletOperationFailed",
78-
"WalletSigningFailed",
81+
"OnchainTxSigningFailed",
82+
"MessageSigningFailed",
7983
"TxSyncFailed",
8084
"GossipUpdateFailed",
8185
"InvalidAddress",

src/error.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ pub enum Error {
2525
PersistenceFailed,
2626
/// A wallet operation failed.
2727
WalletOperationFailed,
28-
/// A siging operation failed.
29-
WalletSigningFailed,
28+
/// A signing operation for transaction failed.
29+
OnchainTxSigningFailed,
30+
/// A signing operation for message failed.
31+
MessageSigningFailed,
3032
/// A transaction sync operation failed.
3133
TxSyncFailed,
3234
/// A gossip updating operation failed.
@@ -71,7 +73,8 @@ impl fmt::Display for Error {
7173
Self::ChannelClosingFailed => write!(f, "Failed to close channel."),
7274
Self::PersistenceFailed => write!(f, "Failed to persist data."),
7375
Self::WalletOperationFailed => write!(f, "Failed to conduct wallet operation."),
74-
Self::WalletSigningFailed => write!(f, "Failed to sign given transaction."),
76+
Self::OnchainTxSigningFailed => write!(f, "Failed to sign given transaction."),
77+
Self::MessageSigningFailed => write!(f, "Failed to sign given message."),
7578
Self::TxSyncFailed => write!(f, "Failed to sync transactions."),
7679
Self::GossipUpdateFailed => write!(f, "Failed to update gossip data."),
7780
Self::InvalidAddress => write!(f, "The given address is invalid."),
@@ -96,7 +99,7 @@ impl std::error::Error for Error {}
9699
impl From<bdk::Error> for Error {
97100
fn from(e: bdk::Error) -> Self {
98101
match e {
99-
bdk::Error::Signer(_) => Self::WalletSigningFailed,
102+
bdk::Error::Signer(_) => Self::OnchainTxSigningFailed,
100103
_ => Self::WalletOperationFailed,
101104
}
102105
}

src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,22 @@ impl Node {
15561556
})
15571557
.collect()
15581558
}
1559+
1560+
/// Creates a digital ECDSA signature of a message with the node's secret key.
1561+
///
1562+
/// A receiver knowing the corresponding `PublicKey` (e.g. the node’s id) and the message
1563+
/// can be sure that the signature was generated by the caller.
1564+
/// Signatures are EC recoverable, meaning that given the message and the
1565+
/// signature the PublicKey of the signer can be extracted.
1566+
pub fn sign_message(&self, msg: &[u8]) -> Result<String, Error> {
1567+
self.keys_manager.sign_message(msg)
1568+
}
1569+
1570+
/// Verifies that the given ECDSA signature was created for the given message with the
1571+
/// secret key corresponding to the given public key.
1572+
pub fn verify_signature(&self, msg: &[u8], sig: &str, pkey: &PublicKey) -> bool {
1573+
self.keys_manager.verify_signature(msg, sig, pkey)
1574+
}
15591575
}
15601576

15611577
impl Drop for Node {

src/test/functional_tests.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,3 +363,18 @@ fn onchain_spend_receive() {
363363
assert!(node_b.onchain_balance().unwrap().get_spendable() > 99000);
364364
assert!(node_b.onchain_balance().unwrap().get_spendable() < 100000);
365365
}
366+
367+
#[test]
368+
fn sign_verify_msg() {
369+
let (_, electrsd) = setup_bitcoind_and_electrsd();
370+
let esplora_url = electrsd.esplora_url.as_ref().unwrap();
371+
let node = Builder::from_config(random_config(esplora_url)).build();
372+
373+
node.start().unwrap();
374+
375+
// Tests arbitrary message signing and later verification
376+
let msg = "OK computer".as_bytes();
377+
let sig = node.sign_message(msg).unwrap();
378+
let pkey = node.node_id();
379+
assert!(node.verify_signature(msg, sig.as_str(), &pkey));
380+
}

src/wallet.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use lightning::chain::keysinterface::{
1313
use lightning::ln::msgs::{DecodeError, UnsignedGossipMessage};
1414
use lightning::ln::script::ShutdownScript;
1515

16+
use lightning::util::message_signing;
17+
1618
use bdk::blockchain::{Blockchain, EsploraBlockchain};
1719
use bdk::database::BatchDatabase;
1820
use bdk::wallet::AddressIndex;
@@ -373,6 +375,15 @@ where
373375
secp_ctx,
374376
)
375377
}
378+
379+
pub fn sign_message(&self, msg: &[u8]) -> Result<String, Error> {
380+
message_signing::sign(msg, &self.inner.get_node_secret_key())
381+
.or(Err(Error::MessageSigningFailed))
382+
}
383+
384+
pub fn verify_signature(&self, msg: &[u8], sig: &str, pkey: &PublicKey) -> bool {
385+
message_signing::verify(msg, sig, pkey)
386+
}
376387
}
377388

378389
impl<D> NodeSigner for WalletKeysManager<D>

0 commit comments

Comments
 (0)