From 3fbf8d0bd20f6a857ab6c52a03d8c50204eaa1eb Mon Sep 17 00:00:00 2001 From: sputn1ck Date: Tue, 6 Feb 2024 00:28:59 +0100 Subject: [PATCH] signmessage: add signmessage cmd This commit adds the signmessage command which allows a user to sign a message with the nodes identity key, similiar to `lncli signmessage`. --- README.md | 2 + cmd/chantools/root.go | 1 + cmd/chantools/signmessage.go | 90 ++++++++++++++++++++++++++++++++++++ doc/chantools_signmessage.md | 26 +++++++++++ go.mod | 1 + go.sum | 2 + 6 files changed, 122 insertions(+) create mode 100644 cmd/chantools/signmessage.go create mode 100644 doc/chantools_signmessage.md diff --git a/README.md b/README.md index 22e11ef..4553b29 100644 --- a/README.md +++ b/README.md @@ -429,6 +429,7 @@ Available Commands: rescuefunding Rescue funds locked in a funding multisig output that never resulted in a proper channel; this is the command the initiator of the channel needs to run rescuetweakedkey Attempt to rescue funds locked in an address with a key that was affected by a specific bug in lnd showrootkey Extract and show the BIP32 HD root key from the 24 word lnd aezeed + signmessage Sign a message with the nodes identity pubkey. signrescuefunding Rescue funds locked in a funding multisig output that never resulted in a proper channel; this is the command the remote node (the non-initiator) of the channel needs to run summary Compile a summary about the current state of channels sweeptimelock Sweep the force-closed state after the time lock has expired @@ -488,6 +489,7 @@ Legend: | [rescueclosed](doc/chantools_rescueclosed.md) | :pencil: (:pushpin:) Rescue funds in a legacy (pre `STATIC_REMOTE_KEY`) channel output | | [rescuefunding](doc/chantools_rescuefunding.md) | :pencil: (:pushpin:) Rescue funds from a funding transaction. Deprecated, use [zombierecovery](doc/chantools_zombierecovery.md) instead | | [showrootkey](doc/chantools_showrootkey.md) | :pencil: Display the master root key (`xprv`) from your seed (DO NOT SHARE WITH ANYONE) | +| [signmessage](doc/chantools_signmessage.md) | :pencil: Sign a message with the nodes identity pubkey. | | [signrescuefunding](doc/chantools_signrescuefunding.md) | :pencil: (:pushpin:) Sign to funds from a funding transaction. Deprecated, use [zombierecovery](doc/chantools_zombierecovery.md) instead | | [summary](doc/chantools_summary.md) | Create a summary of channel funds from a `channel.db` file | | [sweepremoteclosed](doc/chantools_sweepremoteclosed.md) | :pencil: Find channel funds from remotely force closed channels and sweep them | diff --git a/cmd/chantools/root.go b/cmd/chantools/root.go index 18dc036..93a35b1 100644 --- a/cmd/chantools/root.go +++ b/cmd/chantools/root.go @@ -123,6 +123,7 @@ func main() { newRescueFundingCommand(), newRescueTweakedKeyCommand(), newShowRootKeyCommand(), + newSignMessageCommand(), newSignRescueFundingCommand(), newSummaryCommand(), newSweepTimeLockCommand(), diff --git a/cmd/chantools/signmessage.go b/cmd/chantools/signmessage.go new file mode 100644 index 0000000..52de1c6 --- /dev/null +++ b/cmd/chantools/signmessage.go @@ -0,0 +1,90 @@ +package main + +import ( + "fmt" + + chantools_lnd "github.com/lightninglabs/chantools/lnd" + "github.com/lightningnetwork/lnd/keychain" + "github.com/spf13/cobra" + "github.com/tv42/zbase32" +) + +var ( + signedMsgPrefix = []byte("Lightning Signed Message:") +) + +type signMessageCommand struct { + Msg string + + rootKey *rootKey + cmd *cobra.Command +} + +func newSignMessageCommand() *cobra.Command { + cc := &signMessageCommand{} + cc.cmd = &cobra.Command{ + Use: "signmessage", + Short: "Sign a message with the node's private key.", + Long: `Sign msg with the resident node's private key. + Returns the signature as a zbase32 string.`, + Example: `chantools signmessage --msg=foobar`, + RunE: cc.Execute, + } + cc.cmd.Flags().StringVar( + &cc.Msg, "msg", "", "the message to sign", + ) + + cc.rootKey = newRootKey(cc.cmd, "decrypting the backup") + + return cc.cmd +} + +func (c *signMessageCommand) Execute(_ *cobra.Command, _ []string) error { + if c.Msg == "" { + return fmt.Errorf("please enter a valid msg") + } + + extendedKey, err := c.rootKey.read() + if err != nil { + return fmt.Errorf("error reading root key: %w", err) + } + + signer := &chantools_lnd.Signer{ + ExtendedKey: extendedKey, + ChainParams: chainParams, + } + + // Create the key locator for the node key. + keyLocator := keychain.KeyLocator{ + Family: keychain.KeyFamilyNodeKey, + Index: 0, + } + + // Fetch the private key for node key. + privKey, err := signer.FetchPrivKey(&keychain.KeyDescriptor{ + KeyLocator: keyLocator, + }) + if err != nil { + return err + } + + // Create a new signer. + privKeyMsgSigner := keychain.NewPrivKeyMessageSigner( + privKey, keyLocator, + ) + + // Prepend the special lnd prefix. + // See: https://github.com/lightningnetwork/lnd/blob/63e698ec4990e678089533561fd95cfd684b67db/rpcserver.go#L1576 . + msg := []byte(c.Msg) + msg = append(signedMsgPrefix, msg...) + sigBytes, err := privKeyMsgSigner.SignMessageCompact(msg, true) + if err != nil { + return err + } + + // Encode the signature. + sig := zbase32.EncodeToString(sigBytes) + fmt.Println(sig) + + return nil +} diff --git a/doc/chantools_signmessage.md b/doc/chantools_signmessage.md new file mode 100644 index 0000000..54906e7 --- /dev/null +++ b/doc/chantools_signmessage.md @@ -0,0 +1,26 @@ +## chantools signmessage + +Signs a message with the nodes key, results in the same signature as +`lncli signmessage` + +### Synopsis + +``` +chantools signmessage [flags] +``` + +### Examples + +``` +chantools signmessage --msg=foobar +``` + +### Options + +``` + --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag + -h, --help help for signmessage + --msg string the message to sign + --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --single_hash single hash the msg instead of double hash (lnd default is false) +``` \ No newline at end of file diff --git a/go.mod b/go.mod index 5982129..4acb47d 100644 --- a/go.mod +++ b/go.mod @@ -126,6 +126,7 @@ require ( github.com/stretchr/objx v0.5.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect + github.com/tv42/zbase32 v0.0.0-20220222190657-f76a9fc892fa // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect diff --git a/go.sum b/go.sum index 502c50b..e7767ae 100644 --- a/go.sum +++ b/go.sum @@ -686,6 +686,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/zbase32 v0.0.0-20220222190657-f76a9fc892fa h1:2EwhXkNkeMjX9iFYGWLPQLPhw9O58BhnYgtYKeqybcY= +github.com/tv42/zbase32 v0.0.0-20220222190657-f76a9fc892fa/go.mod h1:is48sjgBanWcA5CQrPBu9Y5yABY/T2awj/zI65bq704= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=