diff --git a/internal_test.go b/internal_test.go index 9e45f732..4f71bbb2 100644 --- a/internal_test.go +++ b/internal_test.go @@ -30,11 +30,6 @@ func (f *fieldVal) TstSetRawInts(raw [10]uint32) *fieldVal { return f } -// TstPad makes the internal pad function available to the test package. -func TstPad(size int, b []byte) []byte { - return pad(size, b) -} - // TstFieldJacobianToBigAffine makes the internal fieldJacobianToBigAffine // function available to the test package. func (curve *KoblitzCurve) TstFieldJacobianToBigAffine(x, y, z *fieldVal) (*big.Int, *big.Int) { diff --git a/pubkey.go b/pubkey.go index bf575c3f..cf0da666 100644 --- a/pubkey.go +++ b/pubkey.go @@ -11,6 +11,13 @@ import ( "math/big" ) +// These constants define the lengths of serialized public keys. +const ( + PubKeyBytesLenCompressed = 33 + PubKeyBytesLenUncompressed = 65 + PubKeyBytesLenHybrid = 65 +) + func isOdd(a *big.Int) bool { return a.Bit(0) == 1 } @@ -63,7 +70,7 @@ func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err err format &= ^byte(0x1) switch len(pubKeyStr) { - case 65: // normal public key + case PubKeyBytesLenUncompressed: if format != pubkeyUncompressed && format != pubkeyHybrid { return nil, fmt.Errorf("invalid magic in pubkey str: "+ "%d", pubKeyStr[0]) @@ -75,7 +82,7 @@ func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err err if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) { return nil, fmt.Errorf("ybit doesn't match oddness") } - case 33: // compressed public key + case PubKeyBytesLenCompressed: // format is 0x2 | solution, // solution determines which solution of the curve we use. /// y^2 = x^3 + Curve.B @@ -117,45 +124,41 @@ func (p *PublicKey) ToECDSA() *ecdsa.PublicKey { // SerializeUncompressed serializes a public key in a 65-byte uncompressed // format. func (p *PublicKey) SerializeUncompressed() []byte { - b := make([]byte, 65) - b[0] = pubkeyUncompressed - copy(b[1:33], pad(32, p.X.Bytes())) - copy(b[33:], pad(32, p.Y.Bytes())) - return b + b := make([]byte, 0, PubKeyBytesLenUncompressed) + b = append(b, pubkeyUncompressed) + b = paddedAppend(32, b, p.X.Bytes()) + return paddedAppend(32, b, p.Y.Bytes()) } // SerializeCompressed serializes a public key in a 33-byte compressed format. func (p *PublicKey) SerializeCompressed() []byte { - b := make([]byte, 33) + b := make([]byte, 0, PubKeyBytesLenCompressed) format := pubkeyCompressed if isOdd(p.Y) { format |= 0x1 } - b[0] = format - copy(b[1:33], pad(32, p.X.Bytes())) - return b + b = append(b, format) + return paddedAppend(32, b, p.X.Bytes()) } // SerializeHybrid serializes a public key in a 65-byte hybrid format. func (p *PublicKey) SerializeHybrid() []byte { - b := make([]byte, 65) + b := make([]byte, 0, PubKeyBytesLenHybrid) format := pubkeyHybrid if isOdd(p.Y) { format |= 0x1 } - b[0] = format - copy(b[1:33], pad(32, p.X.Bytes())) - copy(b[33:], pad(32, p.Y.Bytes())) - return b + b = append(b, format) + b = paddedAppend(32, b, p.X.Bytes()) + return paddedAppend(32, b, p.Y.Bytes()) } -func pad(size int, b []byte) []byte { - // Prevent a possible panic if the input exceeds the expected size. - if len(b) > size { - size = len(b) +// paddedAppend appends the src byte slice to dst, returning the new slice. +// If the length of the source is smaller than the passed size, leading zero +// bytes are appended to the dst slice before appending src. +func paddedAppend(size uint, dst, src []byte) []byte { + for i := 0; i < int(size)-len(src); i++ { + dst = append(dst, 0) } - - p := make([]byte, size) - copy(p[size-len(b):], b) - return p + return append(dst, src...) } diff --git a/pubkey_test.go b/pubkey_test.go index 5ea36eb6..44ed016c 100644 --- a/pubkey_test.go +++ b/pubkey_test.go @@ -262,71 +262,3 @@ func TestPubKeys(t *testing.T) { } } } - -func TestPad(t *testing.T) { - tests := []struct { - name string // Short name to describe the test. - inSize int // The size to pad to. - inBytes []byte // The input bytes to pad. - expected []byte // The expected padded result. - }{ - { - name: "same size - no padding needed", - inSize: 32, - inBytes: []byte{ - 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, - 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, - 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, - 0x21, 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, - }, - expected: []byte{ - 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, - 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, - 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, - 0x21, 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, - }, - }, - { - name: "short 1 byte", - inSize: 32, - inBytes: []byte{ - 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, - 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, - 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, - 0x21, 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, - }, - expected: []byte{ - 0x00, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, - 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, - 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, - 0x21, 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, - }, - }, - { - name: "more bytes than pad size", - inSize: 31, - inBytes: []byte{ - 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, - 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, - 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, - 0x21, 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, - }, - expected: []byte{ - 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, - 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, - 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, - 0x21, 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, - }, - }, - } - - t.Logf("Running %d tests.", len(tests)) - for i, test := range tests { - result := btcec.TstPad(test.inSize, test.inBytes) - if !bytes.Equal(result, test.expected) { - t.Errorf("pad #%d (%s) unexpected result:\n"+ - "got: %x\nwant: %x", i, test.name, result, - test.expected) - } - } -} diff --git a/test_coverage.txt b/test_coverage.txt index ed5c99f3..6b492e72 100644 --- a/test_coverage.txt +++ b/test_coverage.txt @@ -24,42 +24,43 @@ github.com/conformal/btcec/btcec.go KoblitzCurve.Add 100.00% (10/10) github.com/conformal/btcec/field.go fieldVal.Zero 100.00% (10/10) github.com/conformal/btcec/btcec.go KoblitzCurve.doubleJacobian 100.00% (9/9) github.com/conformal/btcec/btcec.go initS256 100.00% (9/9) -github.com/conformal/btcec/pubkey.go PublicKey.SerializeHybrid 100.00% (8/8) github.com/conformal/btcec/signature.go canonicalizeInt 100.00% (8/8) +github.com/conformal/btcec/pubkey.go PublicKey.SerializeHybrid 100.00% (7/7) github.com/conformal/btcec/btcec.go KoblitzCurve.Double 100.00% (7/7) -github.com/conformal/btcec/pubkey.go PublicKey.SerializeCompressed 100.00% (7/7) -github.com/conformal/btcec/privkey.go PrivKeyFromBytes 100.00% (6/6) -github.com/conformal/btcec/pubkey.go pad 100.00% (5/5) +github.com/conformal/btcec/pubkey.go PublicKey.SerializeCompressed 100.00% (6/6) github.com/conformal/btcec/field.go fieldVal.SetByteSlice 100.00% (5/5) -github.com/conformal/btcec/pubkey.go PublicKey.SerializeUncompressed 100.00% (5/5) -github.com/conformal/btcec/signature.go canonicalPadding 100.00% (4/4) github.com/conformal/btcec/btcec.go KoblitzCurve.bigAffineToField 100.00% (4/4) -github.com/conformal/btcec/field.go fieldVal.SetHex 100.00% (4/4) github.com/conformal/btcec/btcec.go KoblitzCurve.IsOnCurve 100.00% (4/4) -github.com/conformal/btcec/field.go fieldVal.SetInt 100.00% (3/3) +github.com/conformal/btcec/pubkey.go PublicKey.SerializeUncompressed 100.00% (4/4) +github.com/conformal/btcec/field.go fieldVal.SetHex 100.00% (4/4) +github.com/conformal/btcec/signature.go canonicalPadding 100.00% (4/4) +github.com/conformal/btcec/privkey.go PrivKeyFromBytes 100.00% (3/3) github.com/conformal/btcec/field.go fieldVal.Bytes 100.00% (3/3) -github.com/conformal/btcec/field.go fieldVal.String 100.00% (2/2) -github.com/conformal/btcec/field.go fieldVal.Equals 100.00% (2/2) -github.com/conformal/btcec/field.go fieldVal.IsZero 100.00% (2/2) -github.com/conformal/btcec/btcec.go S256 100.00% (2/2) -github.com/conformal/btcec/field.go fieldVal.AddInt 100.00% (2/2) +github.com/conformal/btcec/field.go fieldVal.SetInt 100.00% (3/3) +github.com/conformal/btcec/pubkey.go paddedAppend 100.00% (3/3) github.com/conformal/btcec/field.go fieldVal.Set 100.00% (2/2) +github.com/conformal/btcec/field.go fieldVal.String 100.00% (2/2) +github.com/conformal/btcec/btcec.go S256 100.00% (2/2) +github.com/conformal/btcec/field.go fieldVal.IsZero 100.00% (2/2) +github.com/conformal/btcec/field.go fieldVal.Equals 100.00% (2/2) +github.com/conformal/btcec/field.go fieldVal.AddInt 100.00% (2/2) github.com/conformal/btcec/pubkey.go isOdd 100.00% (1/1) -github.com/conformal/btcec/field.go fieldVal.Negate 100.00% (1/1) -github.com/conformal/btcec/btcec.go initAll 100.00% (1/1) -github.com/conformal/btcec/signature.go ParseSignature 100.00% (1/1) -github.com/conformal/btcec/signature.go ParseDERSignature 100.00% (1/1) -github.com/conformal/btcec/field.go fieldVal.Mul 100.00% (1/1) -github.com/conformal/btcec/btcec.go KoblitzCurve.Params 100.00% (1/1) -github.com/conformal/btcec/field.go fieldVal.Square 100.00% (1/1) github.com/conformal/btcec/field.go fieldVal.IsOdd 100.00% (1/1) github.com/conformal/btcec/btcec.go KoblitzCurve.ScalarBaseMult 100.00% (1/1) github.com/conformal/btcec/btcec.go KoblitzCurve.QPlus1Div4 100.00% (1/1) +github.com/conformal/btcec/signature.go ParseSignature 100.00% (1/1) +github.com/conformal/btcec/signature.go ParseDERSignature 100.00% (1/1) +github.com/conformal/btcec/field.go fieldVal.Negate 100.00% (1/1) +github.com/conformal/btcec/btcec.go initAll 100.00% (1/1) +github.com/conformal/btcec/field.go fieldVal.Square 100.00% (1/1) +github.com/conformal/btcec/btcec.go KoblitzCurve.Params 100.00% (1/1) +github.com/conformal/btcec/field.go fieldVal.Mul 100.00% (1/1) github.com/conformal/btcec/pubkey.go ParsePubKey 92.86% (26/28) +github.com/conformal/btcec/signature.go SignCompact 90.91% (20/22) github.com/conformal/btcec/pubkey.go decompressPoint 88.89% (8/9) github.com/conformal/btcec/signature.go recoverKeyFromSignature 86.96% (20/23) -github.com/conformal/btcec/signature.go SignCompact 86.36% (19/22) github.com/conformal/btcec/signature.go RecoverCompact 77.78% (7/9) github.com/conformal/btcec/signature.go hashToInt 77.78% (7/9) -github.com/conformal/btcec ------------------------------------- 98.59% (909/922) +github.com/conformal/btcec/pubkey.go PublicKey.ToECDSA 0.00% (0/1) +github.com/conformal/btcec ------------------------------------- 98.58% (902/915)