diff --git a/nodesigner.go b/nodesigner.go index c3e6a458..b62e12f2 100644 --- a/nodesigner.go +++ b/nodesigner.go @@ -67,8 +67,9 @@ func (n *nodeSigner) SignDigestCompact(hash []byte) ([]byte, error) { isCompressedKey := true // btcec.SignCompact returns a pubkey-recoverable signature - sig, err := btcec.SignCompact(btcec.S256(), n.privKey, hash, - isCompressedKey) + sig, err := btcec.SignCompact( + btcec.S256(), n.privKey, hash, isCompressedKey, + ) if err != nil { return nil, fmt.Errorf("can't sign the hash: %v", err) } diff --git a/rpcserver.go b/rpcserver.go index e6e694d0..225f5fa2 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -520,10 +520,18 @@ func (r *rpcServer) NewWitnessAddress(ctx context.Context, return &lnrpc.NewAddressResponse{Address: addr.String()}, nil } +var ( + // signedMsgPrefix is a special prefix that we'll prepend to any + // messages we sign/verify. We do this to ensure that we don't + // accidentally sign a sighash, or other sensitive material. By + // prepending this fragment, we mind message signing to our particular + // context. + signedMsgPrefix = []byte("Lightning Signed Message:") +) + // SignMessage signs a message with the resident node's private key. The -// returned signature string is zbase32 encoded and pubkey recoverable, -// meaning that only the message digest and signature are needed for -// verification. +// returned signature string is zbase32 encoded and pubkey recoverable, meaning +// that only the message digest and signature are needed for verification. func (r *rpcServer) SignMessage(ctx context.Context, in *lnrpc.SignMessageRequest) (*lnrpc.SignMessageResponse, error) { @@ -531,6 +539,7 @@ func (r *rpcServer) SignMessage(ctx context.Context, return nil, fmt.Errorf("need a message to sign") } + in.Msg = append(signedMsgPrefix, in.Msg...) sigBytes, err := r.server.nodeSigner.SignCompact(in.Msg) if err != nil { return nil, err @@ -540,9 +549,9 @@ func (r *rpcServer) SignMessage(ctx context.Context, return &lnrpc.SignMessageResponse{Signature: sig}, nil } -// VerifyMessage verifies a signature over a msg. The signature must be -// zbase32 encoded and signed by an active node in the resident node's -// channel database. In addition to returning the validity of the signature, +// VerifyMessage verifies a signature over a msg. The signature must be zbase32 +// encoded and signed by an active node in the resident node's channel +// database. In addition to returning the validity of the signature, // VerifyMessage also returns the recovered pubkey from the signature. func (r *rpcServer) VerifyMessage(ctx context.Context, in *lnrpc.VerifyMessageRequest) (*lnrpc.VerifyMessageResponse, error) { @@ -558,6 +567,7 @@ func (r *rpcServer) VerifyMessage(ctx context.Context, } // The signature is over the double-sha256 hash of the message. + in.Msg = append(signedMsgPrefix, in.Msg...) digest := chainhash.DoubleHashB(in.Msg) // RecoverCompact both recovers the pubkey and validates the signature. @@ -572,6 +582,7 @@ func (r *rpcServer) VerifyMessage(ctx context.Context, // Query the channel graph to ensure a node in the network with active // channels signed the message. + // // TODO(phlip9): Require valid nodes to have capital in active channels. graph := r.server.chanDB.ChannelGraph() _, active, err := graph.HasLightningNode(pub)