diff --git a/channeldb/graph.go b/channeldb/graph.go index b5326bb8c..8367aaf68 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -2727,7 +2727,7 @@ func (l *LightningNode) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement, return nodeAnn, nil } - sig, err := lnwire.NewSigFromRawSignature(l.AuthSigBytes) + sig, err := lnwire.NewSigFromECDSARawSignature(l.AuthSigBytes) if err != nil { return nil, err } diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 526f7d79b..305fd359e 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -2199,25 +2199,25 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, BitcoinKey2: info.BitcoinKey2Bytes, ExtraOpaqueData: edge.ExtraOpaqueData, } - chanAnn.NodeSig1, err = lnwire.NewSigFromRawSignature( + chanAnn.NodeSig1, err = lnwire.NewSigFromECDSARawSignature( info.AuthProof.NodeSig1Bytes, ) if err != nil { return nil, nil, err } - chanAnn.NodeSig2, err = lnwire.NewSigFromRawSignature( + chanAnn.NodeSig2, err = lnwire.NewSigFromECDSARawSignature( info.AuthProof.NodeSig2Bytes, ) if err != nil { return nil, nil, err } - chanAnn.BitcoinSig1, err = lnwire.NewSigFromRawSignature( + chanAnn.BitcoinSig1, err = lnwire.NewSigFromECDSARawSignature( info.AuthProof.BitcoinSig1Bytes, ) if err != nil { return nil, nil, err } - chanAnn.BitcoinSig2, err = lnwire.NewSigFromRawSignature( + chanAnn.BitcoinSig2, err = lnwire.NewSigFromECDSARawSignature( info.AuthProof.BitcoinSig2Bytes, ) if err != nil { diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 21b11c57d..3a6510f16 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -5494,9 +5494,15 @@ func TestChannelLinkFail(t *testing.T) { // Flip a bit on the signature, rendering it // invalid. - sig[19] ^= 1 + sigCopy := sig.Copy() + copyBytes := sigCopy.RawBytes() + copyBytes[19] ^= 1 + modifiedSig, err := lnwire.NewSigFromWireECDSA( + copyBytes, + ) + require.NoError(t, err) commitSig := &lnwire.CommitSig{ - CommitSig: sig, + CommitSig: modifiedSig, HtlcSigs: htlcSigs, } diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 58b7a8294..9342ca6d1 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -1283,7 +1283,7 @@ func marshallChannelUpdate(update *lnwire.ChannelUpdate) *lnrpc.ChannelUpdate { } return &lnrpc.ChannelUpdate{ - Signature: update.Signature[:], + Signature: update.Signature.RawBytes(), ChainHash: update.ChainHash[:], ChanId: update.ShortChannelID.ToUint64(), Timestamp: update.Timestamp, diff --git a/lnrpc/signrpc/signer_server.go b/lnrpc/signrpc/signer_server.go index 01fc7696d..abbd6592c 100644 --- a/lnrpc/signrpc/signer_server.go +++ b/lnrpc/signrpc/signer_server.go @@ -713,7 +713,7 @@ func (s *Server) VerifyMessage(_ context.Context, } // The signature must be fixed-size LN wire format encoded. - wireSig, err := lnwire.NewSigFromRawSignature(in.Signature) + wireSig, err := lnwire.NewSigFromECDSARawSignature(in.Signature) if err != nil { return nil, fmt.Errorf("failed to decode signature: %v", err) } diff --git a/lnwallet/chancloser/chancloser.go b/lnwallet/chancloser/chancloser.go index af71e7b92..448e196c8 100644 --- a/lnwallet/chancloser/chancloser.go +++ b/lnwallet/chancloser/chancloser.go @@ -785,16 +785,18 @@ func (c *ChanCloser) proposeCloseSigned(fee btcutil.Amount) (*lnwire.ClosingSign return nil, err } - // We'll note our last signature and proposed fee so when the remote party - // responds we'll be able to decide if we've agreed on fees or not. + // We'll note our last signature and proposed fee so when the remote + // party responds we'll be able to decide if we've agreed on fees or + // not. c.lastFeeProposal = fee + parsedSig, err := lnwire.NewSigFromSignature(rawSig) if err != nil { return nil, err } - chancloserLog.Infof("ChannelPoint(%v): proposing fee of %v sat to close "+ - "chan", c.chanPoint, int64(fee)) + chancloserLog.Infof("ChannelPoint(%v): proposing fee of %v sat to "+ + "close chan", c.chanPoint, int64(fee)) // We'll assemble a ClosingSigned message using this information and return // it to the caller so we can kick off the final stage of the channel diff --git a/lnwallet/channel.go b/lnwallet/channel.go index ad08557a7..9406e0869 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -320,7 +320,7 @@ type PaymentDescriptor struct { // local node. This signature is generated by the remote node and // stored by the local node in the case that local node needs to // broadcast their commitment transaction. - sig *ecdsa.Signature + sig input.Signature // addCommitHeight[Remote|Local] encodes the height of the commitment // which included this HTLC on either the remote or local commitment @@ -4331,7 +4331,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, var ( htlcIndex uint64 sigHash func() ([]byte, error) - sig *ecdsa.Signature + sig input.Signature err error ) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index fc8a7bef7..93bf4c747 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -5331,7 +5331,11 @@ func TestInvalidCommitSigError(t *testing.T) { // Before the signature gets to Bob, we'll mutate it, such that the // signature is now actually invalid. - aliceSig[0] ^= 88 + aliceSigCopy := aliceSig.Copy() + aliceSigCopyBytes := aliceSigCopy.RawBytes() + aliceSigCopyBytes[0] ^= 88 + aliceSig, err = lnwire.NewSigFromWireECDSA(aliceSigCopyBytes) + require.NoError(t, err) // Bob should reject this new state, and return the proper error. err = bobChannel.ReceiveNewCommitment(aliceSig, aliceHtlcSigs) diff --git a/lnwallet/rpcwallet/rpcwallet.go b/lnwallet/rpcwallet/rpcwallet.go index 536ce6260..2dc9ae33c 100644 --- a/lnwallet/rpcwallet/rpcwallet.go +++ b/lnwallet/rpcwallet/rpcwallet.go @@ -454,11 +454,20 @@ func (r *RPCKeyRing) SignMessage(keyLoc keychain.KeyLocator, "signer instance: %v", err) } - wireSig, err := lnwire.NewSigFromRawSignature(resp.Signature) + wireSig, err := lnwire.NewSigFromECDSARawSignature(resp.Signature) if err != nil { - return nil, fmt.Errorf("error parsing raw signature: %v", err) + return nil, fmt.Errorf("unable to create sig: %w", err) } - return wireSig.ToSignature() + sig, err := wireSig.ToSignature() + if err != nil { + return nil, fmt.Errorf("unable to parse sig: %w", err) + } + ecdsaSig, ok := sig.(*ecdsa.Signature) + if !ok { + return nil, fmt.Errorf("unexpected signature type: %T", sig) + } + + return ecdsaSig, nil } // SignMessageCompact signs the given message, single or double SHA256 hashing diff --git a/lnwallet/sigpool.go b/lnwallet/sigpool.go index 30dc37ae9..0ebc3a931 100644 --- a/lnwallet/sigpool.go +++ b/lnwallet/sigpool.go @@ -5,7 +5,6 @@ import ( "sync" "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwire" @@ -36,7 +35,7 @@ type VerifyJob struct { // Sig is the raw signature generated using the above public key. This // is the signature to be verified. - Sig *ecdsa.Signature + Sig input.Signature // SigHash is a function closure generates the sighashes that the // passed signature is known to have signed. diff --git a/lnwire/fuzz_test.go b/lnwire/fuzz_test.go index 8d052e27f..8ff38db1b 100644 --- a/lnwire/fuzz_test.go +++ b/lnwire/fuzz_test.go @@ -596,12 +596,12 @@ func FuzzCustomMessage(f *testing.F) { // does not mutate them. func FuzzParseRawSignature(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { - sig, err := NewSigFromRawSignature(data) + sig, err := NewSigFromECDSARawSignature(data) if err != nil { return } - sig2, err := NewSigFromRawSignature(sig.ToSignatureBytes()) + sig2, err := NewSigFromECDSARawSignature(sig.ToSignatureBytes()) require.NoError(t, err, "failed to reparse signature") require.Equal(t, sig, sig2, "signature mismatch") @@ -614,10 +614,10 @@ func FuzzParseRawSignature(f *testing.F) { func FuzzConvertFixedSignature(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { var sig Sig - if len(data) > len(sig) { + if len(data) > len(sig.bytes[:]) { return } - copy(sig[:], data) + copy(sig.bytes[:], data) derSig, err := sig.ToSignature() if err != nil { diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index 5f042b746..46257cce5 100644 --- a/lnwire/lnwire.go +++ b/lnwire/lnwire.go @@ -183,7 +183,7 @@ func WriteElement(w *bytes.Buffer, element interface{}) error { case Sig: // Write buffer - if _, err := w.Write(e[:]); err != nil { + if _, err := w.Write(e.bytes[:]); err != nil { return err } @@ -618,7 +618,7 @@ func ReadElement(r io.Reader, element interface{}) error { *e = sigs case *Sig: - if _, err := io.ReadFull(r, e[:]); err != nil { + if _, err := io.ReadFull(r, e.bytes[:]); err != nil { return err } diff --git a/lnwire/signature.go b/lnwire/signature.go index f0bed72cb..57c27e4d1 100644 --- a/lnwire/signature.go +++ b/lnwire/signature.go @@ -5,15 +5,10 @@ import ( "fmt" "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/lightningnetwork/lnd/input" ) -// Sig is a fixed-sized ECDSA signature. Unlike Bitcoin, we use fixed sized -// signatures on the wire, instead of DER encoded signatures. This type -// provides several methods to convert to/from a regular Bitcoin DER encoded -// signature (raw bytes and *ecdsa.Signature). -type Sig [64]byte - var ( errSigTooShort = errors.New("malformed signature: too short") errBadLength = errors.New("malformed signature: bad length") @@ -23,14 +18,74 @@ var ( errSTooLong = errors.New("S is over 32 bytes long without padding") ) -// NewSigFromRawSignature returns a Sig from a Bitcoin raw signature encoded in -// the canonical DER encoding. -func NewSigFromRawSignature(sig []byte) (Sig, error) { - var b Sig +// sigType represents the type of signature that is carried within the Sig. +// Today this can either be an ECDSA sig or a schnorr sig. Both of these can +// fit cleanly into 64 bytes. +type sigType uint + +const ( + // sigTypeECDSA represents an ECDSA signature. + sigTypeECDSA sigType = iota + + // sigTypeSchnorr represents a schnorr signature. + sigTypeSchnorr +) + +// Sig is a fixed-sized ECDSA signature or 64-byte schnorr signature. For the +// ECDSA sig, unlike Bitcoin, we use fixed sized signatures on the wire, +// instead of DER encoded signatures. This type provides several methods to +// convert to/from a regular Bitcoin DER encoded signature (raw bytes and +// *ecdsa.Signature). +type Sig struct { + bytes [64]byte + + sigType sigType +} + +// ForceSchnorr forces the signature to be interpreted as a schnorr signature. +// This is useful when reading an HTLC sig off the wire for a taproot channel. +// In this case, in order to obtain an input.Signature, we need to know that +// the sig is a schnorr sig. +func (s *Sig) ForceSchnorr() { + s.sigType = sigTypeSchnorr +} + +// RawBytes returns the raw bytes of signature. +func (s *Sig) RawBytes() []byte { + return s.bytes[:] +} + +// Copy copies the signature into a new Sig instance. +func (s *Sig) Copy() Sig { + var sCopy Sig + copy(sCopy.bytes[:], s.bytes[:]) + sCopy.sigType = s.sigType + + return sCopy +} + +// NewSigFromWireECDSA returns a Sig instance based on an ECDSA signature +// that's already in the 64-byte format we expect. +func NewSigFromWireECDSA(sig []byte) (Sig, error) { + if len(sig) != 64 { + return Sig{}, fmt.Errorf("%w: %v bytes", errSigTooShort, + len(sig)) + } + + var s Sig + copy(s.bytes[:], sig) + + return s, nil +} + +// NewSigFromECDSARawSignature returns a Sig from a Bitcoin raw signature +// encoded in the canonical DER encoding. +func NewSigFromECDSARawSignature(sig []byte) (Sig, error) { + var b [64]byte // Check the total length is above the minimal. if len(sig) < ecdsa.MinSigLen { - return b, errSigTooShort + return Sig{}, errSigTooShort } // The DER representation is laid out as: @@ -46,7 +101,7 @@ func NewSigFromRawSignature(sig []byte) (Sig, error) { // siglen should be less than the entire message and greater than // the minimal message size. if sigLen+2 > len(sig) || sigLen+2 < ecdsa.MinSigLen { - return b, errBadLength + return Sig{}, errBadLength } // Reading , remaining: [r 0x02 s] @@ -56,7 +111,7 @@ func NewSigFromRawSignature(sig []byte) (Sig, error) { // Assuming s is one byte, then we have 0x30, , 0x20, // , 0x20, , s, a total of 7 bytes. if rLen <= 0 || rLen+7 > len(sig) { - return b, errBadRLength + return Sig{}, errBadRLength } // Reading , remaining: [s] @@ -67,7 +122,7 @@ func NewSigFromRawSignature(sig []byte) (Sig, error) { // We know r is rLen bytes, and we have 0x30, , 0x20, // , 0x20, , a total of rLen+6 bytes. if sLen <= 0 || sLen+rLen+6 > len(sig) { - return b, errBadSLength + return Sig{}, errBadSLength } // Check to make sure R and S can both fit into their intended buffers. @@ -78,7 +133,7 @@ func NewSigFromRawSignature(sig []byte) (Sig, error) { // check S first. if sLen > 32 { if (sLen > 33) || (sig[6+rLen] != 0x00) { - return b, errSTooLong + return Sig{}, errSTooLong } sLen-- copy(b[64-sLen:], sig[7+rLen:]) @@ -89,7 +144,7 @@ func NewSigFromRawSignature(sig []byte) (Sig, error) { // Do the same for R as we did for S if rLen > 32 { if (rLen > 33) || (sig[4] != 0x00) { - return b, errRTooLong + return Sig{}, errRTooLong } rLen-- copy(b[32-rLen:], sig[5:5+rLen]) @@ -97,11 +152,24 @@ func NewSigFromRawSignature(sig []byte) (Sig, error) { copy(b[32-rLen:], sig[4:4+rLen]) } - return b, nil + return Sig{ + bytes: b, + sigType: sigTypeECDSA, + }, nil +} + +// NewSigFromSchnorrRawSignature converts a raw schnorr signature into an +// lnwire.Sig. +func NewSigFromSchnorrRawSignature(sig []byte) (Sig, error) { + var s Sig + copy(s.bytes[:], sig) + s.sigType = sigTypeSchnorr + + return s, nil } // NewSigFromSignature creates a new signature as used on the wire, from an -// existing ecdsa.Signature. +// existing ecdsa.Signature or schnorr.Signature. func NewSigFromSignature(e input.Signature) (Sig, error) { if e == nil { return Sig{}, fmt.Errorf("cannot decode empty signature") @@ -113,45 +181,85 @@ func NewSigFromSignature(e input.Signature) (Sig, error) { return Sig{}, fmt.Errorf("cannot decode empty signature") } - // Serialize the signature with all the checks that entails. - return NewSigFromRawSignature(e.Serialize()) -} + switch ecSig := e.(type) { + // If this is a schnorr signature, then we can just pack it as normal, + // since the default encoding is already 64 bytes. + case *schnorr.Signature: + return NewSigFromSchnorrRawSignature(e.Serialize()) -// ToSignature converts the fixed-sized signature to a ecdsa.Signature objects -// which can be used for signature validation checks. -func (b *Sig) ToSignature() (*ecdsa.Signature, error) { - // Parse the signature with strict checks. - sigBytes := b.ToSignatureBytes() - sig, err := ecdsa.ParseDERSignature(sigBytes) - if err != nil { - return nil, err + // For ECDSA signatures, we'll need to do a bit more work to map the + // signature into a compact 64 byte form. + case *ecdsa.Signature: + // Serialize the signature with all the checks that entails. + return NewSigFromECDSARawSignature(e.Serialize()) + + default: + return Sig{}, fmt.Errorf("unknown wire sig type: %T", ecSig) } - - return sig, nil } -// ToSignatureBytes serializes the target fixed-sized signature into the raw -// bytes of a DER encoding. -func (b *Sig) ToSignatureBytes() []byte { - // Extract canonically-padded bigint representations from buffer - r := extractCanonicalPadding(b[0:32]) - s := extractCanonicalPadding(b[32:64]) - rLen := uint8(len(r)) - sLen := uint8(len(s)) +// ToSignature converts the fixed-sized signature to a input.Signature which +// can be used for signature validation checks. +func (s *Sig) ToSignature() (input.Signature, error) { + switch s.sigType { + case sigTypeSchnorr: + return schnorr.ParseSignature(s.bytes[:]) - // Create a canonical serialized signature. DER format is: - // 0x30 0x02 r 0x02 s - sigBytes := make([]byte, 6+rLen+sLen) - sigBytes[0] = 0x30 // DER signature magic value - sigBytes[1] = 4 + rLen + sLen // Length of rest of signature - sigBytes[2] = 0x02 // Big integer magic value - sigBytes[3] = rLen // Length of R - sigBytes[rLen+4] = 0x02 // Big integer magic value - sigBytes[rLen+5] = sLen // Length of S - copy(sigBytes[4:], r) // Copy R - copy(sigBytes[rLen+6:], s) // Copy S + case sigTypeECDSA: + // Parse the signature with strict checks. + sigBytes := s.ToSignatureBytes() + sig, err := ecdsa.ParseDERSignature(sigBytes) + if err != nil { + return nil, err + } - return sigBytes + return sig, nil + + default: + return nil, fmt.Errorf("unknown sig type: %v", s.sigType) + } +} + +// ToSignatureBytes serializes the target fixed-sized signature into the +// encoding of the primary domain for the signature. For ECDSA signatures, this +// is the raw bytes of a DER encoding. +func (s *Sig) ToSignatureBytes() []byte { + switch s.sigType { + // For ECDSA signatures, we'll convert to DER encoding. + case sigTypeECDSA: + // Extract canonically-padded bigint representations from buffer + r := extractCanonicalPadding(s.bytes[0:32]) + s := extractCanonicalPadding(s.bytes[32:64]) + rLen := uint8(len(r)) + sLen := uint8(len(s)) + + // Create a canonical serialized signature. DER format is: + // 0x30 0x02 r 0x02 s + sigBytes := make([]byte, 6+rLen+sLen) + sigBytes[0] = 0x30 // DER signature magic value + sigBytes[1] = 4 + rLen + sLen // Length of rest of signature + sigBytes[2] = 0x02 // Big integer magic value + sigBytes[3] = rLen // Length of R + sigBytes[rLen+4] = 0x02 // Big integer magic value + sigBytes[rLen+5] = sLen // Length of S + copy(sigBytes[4:], r) // Copy R + copy(sigBytes[rLen+6:], s) // Copy S + + return sigBytes + + // For schnorr signatures, we can use the same internal 64 bytes. + case sigTypeSchnorr: + // We'll make a copy of the signature so we don't return a + // refrence into the raw slice. + var sig [64]byte + copy(sig[:], s.bytes[:]) + return sig[:] + + default: + // TODO(roasbeef): can only be called via public methods so + // never reachable? + panic("sig type not set") + } } // extractCanonicalPadding is a utility function to extract the canonical diff --git a/lnwire/signature_test.go b/lnwire/signature_test.go index 48ce212a0..eee5eb4a0 100644 --- a/lnwire/signature_test.go +++ b/lnwire/signature_test.go @@ -21,11 +21,13 @@ func TestSignatureSerializeDeserialize(t *testing.T) { return err } - e2, err := sig.ToSignature() + e2Input, err := sig.ToSignature() if err != nil { return err } + e2 := e2Input.(*ecdsa.Signature) + if !e.IsEqual(e2) { return fmt.Errorf("pre/post-serialize sigs don't " + "match") @@ -188,16 +190,18 @@ func TestNewSigFromRawSignature(t *testing.T) { rawSig: normalSig, expectedErr: nil, expectedSig: Sig{ - // r value - 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, - 0x49, 0x61, 0xa1, 0xd3, 0xa1, 0xa2, 0x5f, 0xdf, - 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, - 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, - // s value - 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07, 0xde, - 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, - 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, - 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + bytes: [64]byte{ + // r value + 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, + 0x49, 0x61, 0xa1, 0xd3, 0xa1, 0xa2, 0x5f, 0xdf, + 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, + 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, + // s value + 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07, 0xde, + 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, + 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, }, }, { @@ -266,7 +270,7 @@ func TestNewSigFromRawSignature(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { - result, err := NewSigFromRawSignature(tc.rawSig) + result, err := NewSigFromECDSARawSignature(tc.rawSig) require.Equal(t, tc.expectedErr, err) require.Equal(t, tc.expectedSig, result) }) diff --git a/lnwire/writer.go b/lnwire/writer.go index 9c8841055..671ebfdc0 100644 --- a/lnwire/writer.go +++ b/lnwire/writer.go @@ -154,7 +154,7 @@ func WriteShortChannelID(buf *bytes.Buffer, shortChanID ShortChannelID) error { // WriteSig appends the signature to the provided buffer. func WriteSig(buf *bytes.Buffer, sig Sig) error { - return WriteBytes(buf, sig[:]) + return WriteBytes(buf, sig.bytes[:]) } // WriteSigs appends the slice of signatures to the provided buffer with its diff --git a/lnwire/writer_test.go b/lnwire/writer_test.go index 68594e59a..185685c41 100644 --- a/lnwire/writer_test.go +++ b/lnwire/writer_test.go @@ -147,7 +147,9 @@ func TestWriteShortChannelID(t *testing.T) { func TestWriteSig(t *testing.T) { buf := new(bytes.Buffer) - data := Sig{1, 2, 3} + data := Sig{ + bytes: [64]byte{1, 2, 3}, + } expectedBytes := [64]byte{1, 2, 3} err := WriteSig(buf, data) @@ -158,14 +160,14 @@ func TestWriteSig(t *testing.T) { func TestWriteSigs(t *testing.T) { buf := new(bytes.Buffer) - sig1, sig2, sig3 := Sig{1}, Sig{2}, Sig{3} + sig1, sig2, sig3 := Sig{bytes: [64]byte{1}}, Sig{bytes: [64]byte{2}}, Sig{bytes: [64]byte{3}} data := []Sig{sig1, sig2, sig3} // First two bytes encode the length of the slice. expectedBytes := []byte{0, 3} - expectedBytes = append(expectedBytes, sig1[:]...) - expectedBytes = append(expectedBytes, sig2[:]...) - expectedBytes = append(expectedBytes, sig3[:]...) + expectedBytes = append(expectedBytes, sig1.bytes[:]...) + expectedBytes = append(expectedBytes, sig2.bytes[:]...) + expectedBytes = append(expectedBytes, sig3.bytes[:]...) err := WriteSigs(buf, data) diff --git a/netann/channel_announcement.go b/netann/channel_announcement.go index 480b8cf3e..0ae8d606d 100644 --- a/netann/channel_announcement.go +++ b/netann/channel_announcement.go @@ -36,25 +36,25 @@ func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof, if err != nil { return nil, nil, nil, err } - chanAnn.BitcoinSig1, err = lnwire.NewSigFromRawSignature( + chanAnn.BitcoinSig1, err = lnwire.NewSigFromECDSARawSignature( chanProof.BitcoinSig1Bytes, ) if err != nil { return nil, nil, nil, err } - chanAnn.BitcoinSig2, err = lnwire.NewSigFromRawSignature( + chanAnn.BitcoinSig2, err = lnwire.NewSigFromECDSARawSignature( chanProof.BitcoinSig2Bytes, ) if err != nil { return nil, nil, nil, err } - chanAnn.NodeSig1, err = lnwire.NewSigFromRawSignature( + chanAnn.NodeSig1, err = lnwire.NewSigFromECDSARawSignature( chanProof.NodeSig1Bytes, ) if err != nil { return nil, nil, nil, err } - chanAnn.NodeSig2, err = lnwire.NewSigFromRawSignature( + chanAnn.NodeSig2, err = lnwire.NewSigFromECDSARawSignature( chanProof.NodeSig2Bytes, ) if err != nil { diff --git a/netann/channel_announcement_test.go b/netann/channel_announcement_test.go index 01aa767f8..bc2460b9a 100644 --- a/netann/channel_announcement_test.go +++ b/netann/channel_announcement_test.go @@ -17,7 +17,7 @@ func TestCreateChanAnnouncement(t *testing.T) { t.Parallel() key := [33]byte{0x1} - sig := lnwire.Sig{0x1} + var sig lnwire.Sig features := lnwire.NewRawFeatureVector(lnwire.AnchorsRequired) var featuresBuf bytes.Buffer if err := features.Encode(&featuresBuf); err != nil { diff --git a/netann/channel_update.go b/netann/channel_update.go index ca26acac6..b6555f37b 100644 --- a/netann/channel_update.go +++ b/netann/channel_update.go @@ -143,7 +143,9 @@ func ChannelUpdateFromEdge(info *channeldb.ChannelEdgeInfo, update := UnsignedChannelUpdateFromEdge(info, policy) var err error - update.Signature, err = lnwire.NewSigFromRawSignature(policy.SigBytes) + update.Signature, err = lnwire.NewSigFromECDSARawSignature( + policy.SigBytes, + ) if err != nil { return nil, err } diff --git a/routing/router_test.go b/routing/router_test.go index 27a43d474..6b80929d8 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -506,7 +506,7 @@ func TestChannelUpdateValidation(t *testing.T) { // Set up a channel update message with an invalid signature to be // returned to the sender. - var invalidSignature [64]byte + var invalidSignature lnwire.Sig errChanUpdate := lnwire.ChannelUpdate{ Signature: invalidSignature, FeeRate: 500, diff --git a/server.go b/server.go index 206ab43f1..82f2697b0 100644 --- a/server.go +++ b/server.go @@ -843,7 +843,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, "self node announcement: %v", err) } selfNode.AuthSigBytes = authSig.Serialize() - nodeAnn.Signature, err = lnwire.NewSigFromRawSignature( + nodeAnn.Signature, err = lnwire.NewSigFromECDSARawSignature( selfNode.AuthSigBytes, ) if err != nil { diff --git a/watchtower/blob/justice_kit.go b/watchtower/blob/justice_kit.go index c18433c37..7b39eeb40 100644 --- a/watchtower/blob/justice_kit.go +++ b/watchtower/blob/justice_kit.go @@ -398,7 +398,7 @@ func (b *JusticeKit) encodeV0(w io.Writer) error { } // Write 64-byte revocation signature for commit to-local output. - _, err = w.Write(b.CommitToLocalSig[:]) + _, err = w.Write(b.CommitToLocalSig.RawBytes()) if err != nil { return err } @@ -410,7 +410,7 @@ func (b *JusticeKit) encodeV0(w io.Writer) error { } // Write 64-byte commit to-remote signature, which may be blank. - _, err = w.Write(b.CommitToRemoteSig[:]) + _, err = w.Write(b.CommitToRemoteSig.RawBytes()) return err } @@ -472,14 +472,20 @@ func (b *JusticeKit) decodeV0(r io.Reader) error { } // Read 64-byte revocation signature for commit to-local output. - _, err = io.ReadFull(r, b.CommitToLocalSig[:]) + var localSig [64]byte + _, err = io.ReadFull(r, localSig[:]) + if err != nil { + return err + } + + b.CommitToLocalSig, err = lnwire.NewSigFromWireECDSA(localSig[:]) if err != nil { return err } var ( commitToRemotePubkey PubKey - commitToRemoteSig lnwire.Sig + commitToRemoteSig [64]byte ) // Read 33-byte commit to-remote public key, which may be discarded. @@ -498,7 +504,12 @@ func (b *JusticeKit) decodeV0(r io.Reader) error { // valid compressed public key was read from the reader. if btcec.IsCompressedPubKey(commitToRemotePubkey[:]) { b.CommitToRemotePubKey = commitToRemotePubkey - b.CommitToRemoteSig = commitToRemoteSig + b.CommitToRemoteSig, err = lnwire.NewSigFromWireECDSA( + commitToRemoteSig[:], + ) + if err != nil { + return err + } } return nil diff --git a/watchtower/blob/justice_kit_test.go b/watchtower/blob/justice_kit_test.go index b6b7b59ad..7db8d9a36 100644 --- a/watchtower/blob/justice_kit_test.go +++ b/watchtower/blob/justice_kit_test.go @@ -28,8 +28,10 @@ func makePubKey(i uint64) blob.PubKey { } func makeSig(i int) lnwire.Sig { - var sig lnwire.Sig - binary.BigEndian.PutUint64(sig[:8], uint64(i)) + var sigBytes [64]byte + binary.BigEndian.PutUint64(sigBytes[:8], uint64(i)) + + sig, _ := lnwire.NewSigFromWireECDSA(sigBytes[:]) return sig } diff --git a/watchtower/lookout/justice_descriptor_test.go b/watchtower/lookout/justice_descriptor_test.go index aa1b10f7b..c37ed2693 100644 --- a/watchtower/lookout/justice_descriptor_test.go +++ b/watchtower/lookout/justice_descriptor_test.go @@ -320,8 +320,8 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) { require.Nil(t, err) // Complete our justice kit by copying the signatures into the payload. - copy(justiceKit.CommitToLocalSig[:], toLocalSig[:]) - copy(justiceKit.CommitToRemoteSig[:], toRemoteSig[:]) + justiceKit.CommitToLocalSig = toLocalSig + justiceKit.CommitToRemoteSig = toRemoteSig justiceDesc := &lookout.JusticeDescriptor{ BreachedCommitTx: breachTxn, diff --git a/watchtower/lookout/lookout_test.go b/watchtower/lookout/lookout_test.go index 34e5590e7..c7305d9e0 100644 --- a/watchtower/lookout/lookout_test.go +++ b/watchtower/lookout/lookout_test.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/lookout" "github.com/lightningnetwork/lnd/watchtower/wtdb" @@ -57,6 +58,12 @@ func makeArray64(i uint64) [64]byte { return arr } +func makeTestSig(i uint64) lnwire.Sig { + sigBytes := makeArray64(i) + sig, _ := lnwire.NewSigFromWireECDSA(sigBytes[:]) + return sig +} + func makeAddrSlice(size int) []byte { addr := make([]byte, size) if _, err := io.ReadFull(rand.Reader, addr); err != nil { @@ -141,7 +148,7 @@ func TestLookoutBreachMatching(t *testing.T) { RevocationPubKey: makePubKey(1), LocalDelayPubKey: makePubKey(1), CSVDelay: 144, - CommitToLocalSig: makeArray64(1), + CommitToLocalSig: makeTestSig(1), } blob2 := &blob.JusticeKit{ BlobType: blobType, @@ -149,7 +156,7 @@ func TestLookoutBreachMatching(t *testing.T) { RevocationPubKey: makePubKey(2), LocalDelayPubKey: makePubKey(2), CSVDelay: 144, - CommitToLocalSig: makeArray64(2), + CommitToLocalSig: makeTestSig(2), } key1 := blob.NewBreachKeyFromHash(&hash1) diff --git a/watchtower/wtclient/backup_task.go b/watchtower/wtclient/backup_task.go index 2a02da479..62cc3d367 100644 --- a/watchtower/wtclient/backup_task.go +++ b/watchtower/wtclient/backup_task.go @@ -369,24 +369,26 @@ func (t *backupTask) craftSessionPayload( // Re-encode the DER signature into a fixed-size 64 byte // signature. - signature, err := lnwire.NewSigFromRawSignature(rawSignature) + signature, err := lnwire.NewSigFromECDSARawSignature( + rawSignature, + ) if err != nil { return hint, nil, err } // Finally, copy the serialized signature into the justice kit, // using the input's witness type to select the appropriate - // field. + // field switch inp.WitnessType() { case input.CommitmentRevoke: - copy(justiceKit.CommitToLocalSig[:], signature[:]) + justiceKit.CommitToLocalSig = signature case input.CommitSpendNoDelayTweakless: fallthrough case input.CommitmentNoDelay: fallthrough case input.CommitmentToRemoteConfirmed: - copy(justiceKit.CommitToRemoteSig[:], signature[:]) + justiceKit.CommitToRemoteSig = signature default: return hint, nil, fmt.Errorf("invalid witness type: %v", inp.WitnessType()) diff --git a/watchtower/wtclient/backup_task_internal_test.go b/watchtower/wtclient/backup_task_internal_test.go index 43b818044..4639b8164 100644 --- a/watchtower/wtclient/backup_task_internal_test.go +++ b/watchtower/wtclient/backup_task_internal_test.go @@ -618,8 +618,9 @@ func testBackupTask(t *testing.T, test backupTaskTest) { // moment, it is tested indirectly by other packages and integration // tests. // TODO(conner): include signature validation checks - - emptyToLocalSig := bytes.Equal(jKit.CommitToLocalSig[:], zeroSig[:]) + emptyToLocalSig := bytes.Equal( + jKit.CommitToLocalSig.RawBytes(), zeroSig[:], + ) if hasToLocal { require.False(t, emptyToLocalSig, "to-local signature should "+ "not be empty") @@ -628,7 +629,9 @@ func testBackupTask(t *testing.T, test backupTaskTest) { "be empty") } - emptyToRemoteSig := bytes.Equal(jKit.CommitToRemoteSig[:], zeroSig[:]) + emptyToRemoteSig := bytes.Equal( + jKit.CommitToRemoteSig.RawBytes(), zeroSig[:], + ) if hasToRemote { require.False(t, emptyToRemoteSig, "to-remote signature "+ "should not be empty") diff --git a/zpay32/decode.go b/zpay32/decode.go index b881d5869..d37a34cf9 100644 --- a/zpay32/decode.go +++ b/zpay32/decode.go @@ -91,8 +91,10 @@ func Decode(invoice string, net *chaincfg.Params) (*Invoice, error) { if err != nil { return nil, err } - var sig lnwire.Sig - copy(sig[:], sigBase256[:64]) + sig, err := lnwire.NewSigFromWireECDSA(sigBase256[:64]) + if err != nil { + return nil, err + } recoveryID := sigBase256[64] // The signature is over the hrp + the data the invoice, encoded in @@ -121,7 +123,7 @@ func Decode(invoice string, net *chaincfg.Params) (*Invoice, error) { } } else { headerByte := recoveryID + 27 + 4 - compactSign := append([]byte{headerByte}, sig[:]...) + compactSign := append([]byte{headerByte}, sig.RawBytes()...) pubkey, _, err := ecdsa.RecoverCompact(compactSign, hash) if err != nil { return nil, err diff --git a/zpay32/encode.go b/zpay32/encode.go index a30d0c391..f8e1795e1 100644 --- a/zpay32/encode.go +++ b/zpay32/encode.go @@ -91,8 +91,10 @@ func (invoice *Invoice) Encode(signer MessageSigner) (string, error) { // From the header byte we can extract the recovery ID, and the last 64 // bytes encode the signature. recoveryID := sign[0] - 27 - 4 - var sig lnwire.Sig - copy(sig[:], sign[1:]) + sig, err := lnwire.NewSigFromWireECDSA(sign[1:]) + if err != nil { + return "", err + } // If the pubkey field was explicitly set, it must be set to the pubkey // used to create the signature. @@ -112,7 +114,10 @@ func (invoice *Invoice) Encode(signer MessageSigner) (string, error) { } // Convert the signature to base32 before writing it to the buffer. - signBase32, err := bech32.ConvertBits(append(sig[:], recoveryID), 8, 5, true) + signBase32, err := bech32.ConvertBits( + append(sig.RawBytes(), recoveryID), + 8, 5, true, + ) if err != nil { return "", err }