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.
This commit is contained in:
parent
909c3f8df9
commit
a20594b0bf
@ -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<len(e); i++ {
|
||||
for i := 0; i < len(e); i++ {
|
||||
err := writeElement(w, e[i])
|
||||
if err != nil {
|
||||
return err
|
||||
@ -320,7 +321,7 @@ func writeElement(w io.Writer, element interface{}) error {
|
||||
e.Weight,
|
||||
e.Operation,
|
||||
)
|
||||
if err != nil{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
@ -455,16 +456,14 @@ func readElement(r io.Reader, element interface{}) error {
|
||||
*e = sigs
|
||||
return nil
|
||||
case **btcec.Signature:
|
||||
sigBytes, err := wire.ReadVarBytes(r, 0, 73, "signature")
|
||||
var b [64]byte
|
||||
if _, err := io.ReadFull(r, b[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
err = deserializeSigFromWire(e, b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sig, err := btcec.ParseSignature(sigBytes, btcec.S256())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = sig
|
||||
case *[][32]byte:
|
||||
// How many to read
|
||||
var sliceSize uint16
|
||||
@ -593,7 +592,7 @@ func readElement(r io.Reader, element interface{}) error {
|
||||
return err
|
||||
}
|
||||
*e = make([]ChannelOperation, nChannels)
|
||||
for i:=uint64(0); i < nChannels; i++ {
|
||||
for i := uint64(0); i < nChannels; i++ {
|
||||
err := readElement(r, &((*e)[i]))
|
||||
if err != nil {
|
||||
return err
|
||||
|
105
lnwire/signature.go
Normal file
105
lnwire/signature.go
Normal file
@ -0,0 +1,105 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
)
|
||||
|
||||
// serializeSigToWire serializes a *Signature to [64]byte in the format
|
||||
// specified by the Lightning RFC.
|
||||
func serializeSigToWire(b *[64]byte, e *btcec.Signature) error {
|
||||
|
||||
// Serialize the signature with all the checks that entails.
|
||||
sig := e.Serialize()
|
||||
|
||||
// Extract lengths of R and S. The DER representation is laid out as
|
||||
// 0x30 <length> 0x02 <length r> r 0x02 <length s> 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 <length> 0x02 <length r> r 0x02 <length s> 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}
|
||||
}
|
88
lnwire/signature_test.go
Normal file
88
lnwire/signature_test.go
Normal file
@ -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())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user