From a20594b0bfc5755a7189fdd4755a9f324b35833b Mon Sep 17 00:00:00 2001 From: Alex Akselrod Date: Thu, 8 Dec 2016 13:56:37 -0700 Subject: [PATCH] lnwire: switch to using a fixed 64-byte encoding for signatures (#86) This commit modifies the encoding of signatures on the wire to use a fixed-size 64-byte format. This change is required as the current spec draft dictates that all signatures be encoded as `R` and `S` as 32-byte big-endian integers. With this, signatures are now always a _fixed_ size slice of bytes on the wire, which is nice to have. Fixes #83. --- lnwire/lnwire.go | 37 +++++++------- lnwire/signature.go | 105 +++++++++++++++++++++++++++++++++++++++ lnwire/signature_test.go | 88 ++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 19 deletions(-) create mode 100644 lnwire/signature.go create mode 100644 lnwire/signature_test.go diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index 48302f68..a641f92a 100644 --- a/lnwire/lnwire.go +++ b/lnwire/lnwire.go @@ -64,10 +64,10 @@ func (c CreditsAmount) ToSatoshi() int64 { type ChannelOperation struct { NodePubKey1, NodePubKey2 [33]byte - ChannelId *wire.OutPoint - Capacity int64 - Weight float64 - Operation byte + ChannelId *wire.OutPoint + Capacity int64 + Weight float64 + Operation byte } // writeElement is a one-stop shop to write the big endian representation of @@ -171,12 +171,13 @@ func writeElement(w io.Writer, element interface{}) error { } } case *btcec.Signature: - sig := e.Serialize() - if len(sig) > 73 { - return fmt.Errorf("Signature too long!") + var b [64]byte + err := serializeSigToWire(&b, e) + if err != nil { + return err } - - if err := wire.WriteVarBytes(w, 0, sig); err != nil { + // Write buffer + if _, err = w.Write(b[:]); err != nil { return err } case *wire.ShaHash: @@ -305,7 +306,7 @@ func writeElement(w io.Writer, element interface{}) error { if err != nil { return err } - for i:=0; i 0x02 r 0x02 s + // which means the length of R is the 4th byte and the length of S + // is the second byte after R ends. 0x02 signifies a length-prefixed, + // zero-padded, big-endian bigint. 0x30 sigifies a DER signature. + // See the Serialize() method for btcec.Signature for details. + rLen := uint8(sig[3]) + sLen := uint8(sig[5+rLen]) + + // Check to make sure R and S can both fit into their intended buffers. + // We check S first because these code blocks decrement sLen and + // rLen in the case of a 33-byte 0-padded integer returned from + // Serialize() and rLen is used in calculating array indices for + // S. We can track this with additional variables, but it's more + // efficient to just check S first. + if sLen > 32 { + if (sLen > 33) || (sig[6+rLen] != 0x00) { + return fmt.Errorf("S is over 32 bytes long " + + "without padding") + } else { + sLen -= 1 + copy(b[64-sLen:], sig[7+rLen:]) + } + } else { + copy(b[64-sLen:], sig[6+rLen:]) + } + + // Do the same for R as we did for S + if rLen > 32 { + if (rLen > 33) || (sig[4] != 0x00) { + return fmt.Errorf("R is over 32 bytes long " + + "without padding") + } else { + rLen -= 1 + copy(b[32-rLen:], sig[5:5+rLen]) + } + } else { + copy(b[32-rLen:], sig[4:4+rLen]) + } + return nil +} + +// deserializeSigFromWire deserializes a *Signature from [64]byte in the format +// specified by the Lightning RFC. +func deserializeSigFromWire(e **btcec.Signature, b [64]byte) error { + + // 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)) + + // Create a canonical serialized signature. DER format is: + // 0x30 0x02 r 0x02 s + sigBytes := make([]byte, 6+rLen+sLen, 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 + + // Parse the signature with strict checks. + sig, err := btcec.ParseDERSignature(sigBytes, btcec.S256()) + if err != nil { + return err + } + *e = sig + return nil +} + +// extractCanonicalPadding is a utility function to extract the canonical +// padding of a big-endian integer from the wire encoding (a 0-padded +// big-endian integer) such that it passes btcec.canonicalPadding test. +func extractCanonicalPadding(b []byte) []byte { + for i := 0; i < len(b); i++ { + // Found first non-zero byte. + if b[i] > 0 { + // If the MSB is set, we need zero padding. + if b[i]&0x80 == 0x80 { + return append([]byte{0x00}, b[i:]...) + } else { + return b[i:] + } + } + } + return []byte{0x00} +} diff --git a/lnwire/signature_test.go b/lnwire/signature_test.go new file mode 100644 index 00000000..75e90129 --- /dev/null +++ b/lnwire/signature_test.go @@ -0,0 +1,88 @@ +package lnwire + +import ( + "fmt" + "math/big" + "testing" + + "github.com/roasbeef/btcd/btcec" +) + +func TestSignatureSerializeDeserialize(t *testing.T) { + // Local-scoped closure to serialize and deserialize a Signature and + // check for errors as well as check if the results are correct. + signatureSerializeDeserialize := func(e btcec.Signature) error { + var b [64]byte + err := serializeSigToWire(&b, &e) + if err != nil { + return err + } + var e2 *btcec.Signature + err = deserializeSigFromWire(&e2, b) + if err != nil { + return err + } + if e.R.Cmp(e2.R) != 0 { + return fmt.Errorf("Pre/post-serialize Rs don't match"+ + ": %s, %s", e.R, e2.R) + } + if e.S.Cmp(e2.S) != 0 { + return fmt.Errorf("Pre/post-serialize Ss don't match"+ + ": %s, %s", e.S, e2.S) + } + return nil + } + + sig := btcec.Signature{} + + // Check R = N-1, S = 128. + sig.R = big.NewInt(1) // Allocate a big.Int before we call .Sub. + sig.R.Sub(btcec.S256().N, sig.R) + sig.S = big.NewInt(128) + err := signatureSerializeDeserialize(sig) + if err != nil { + t.Fatalf("R = N-1, S = 128: %s", err.Error()) + } + + // Check R = N-1, S = 127. + sig.S = big.NewInt(127) + err = signatureSerializeDeserialize(sig) + if err != nil { + t.Fatalf("R = N-1, S = 127: %s", err.Error()) + } + + // Check R = N-1, S = N>>1. + sig.S.Set(btcec.S256().N) + sig.S.Rsh(sig.S, 1) + err = signatureSerializeDeserialize(sig) + if err != nil { + t.Fatalf("R = N-1, S = N>>1: %s", err.Error()) + } + + // Check R = N-1, S = N. + sig.S.Set(btcec.S256().N) + err = signatureSerializeDeserialize(sig) + if err.Error() != "signature S isn't 1 or more" { + t.Fatalf("R = N-1, S = N should become R = N-1, S = 0: %s", + err.Error()) + } + + // Check R = N-1, S = N-1. + sig.S.Sub(sig.S, big.NewInt(1)) + err = signatureSerializeDeserialize(sig) + if err.Error() != "Pre/post-serialize Ss don't match: 115792089237316"+ + "195423570985008687907852837564279074904382605163141518161494"+ + "336, 1" { + t.Fatalf("R = N-1, S = N-1 should become R = N-1, S = 1: %s", + err.Error()) + } + + // Check R = 2N, S = 128 + sig.R.Mul(btcec.S256().N, big.NewInt(2)) + sig.S.Set(big.NewInt(127)) + err = signatureSerializeDeserialize(sig) + if err.Error() != "R is over 32 bytes long without padding" { + t.Fatalf("R = 2N, S = 128, R should be over 32 bytes: %s", + err.Error()) + } +}