diff --git a/internal_test.go b/internal_test.go new file mode 100644 index 00000000..6615fd36 --- /dev/null +++ b/internal_test.go @@ -0,0 +1,11 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +const ( + TstPubkeyUncompressed = pubkeyUncompressed + TstPubkeyCompressed = pubkeyCompressed + TstPubkeyHybrid = pubkeyHybrid +) diff --git a/pubkey.go b/pubkey.go index 497fa4b7..3315746d 100644 --- a/pubkey.go +++ b/pubkey.go @@ -16,8 +16,8 @@ func isOdd(a *big.Int) bool { const ( pubkeyCompressed byte = 0x2 // y_bit + x coord - pubkeyUncompressed = 0x4 // x coord + y coord - pubkeyHybrid = 0x6 // y_bit + x coord + y coord + pubkeyUncompressed byte = 0x4 // x coord + y coord + pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord ) // ParsePubKey parses a public key for a koblitz curve from a bytestring into a @@ -88,3 +88,42 @@ func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *ecdsa.PublicKey, e } return &pubkey, nil } + +// PublicKey is an ecdsa.PublicKey with additional functions to +// serialize in uncompressed, compressed, and hybrid formats. +type PublicKey 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], p.X.Bytes()) + copy(b[33:], p.Y.Bytes()) + return b +} + +// SerializeCompressed serializes a public key in a 33-byte compressed format. +func (p *PublicKey) SerializeCompressed() []byte { + b := make([]byte, 33) + format := pubkeyCompressed + if isOdd(p.Y) { + format |= 0x1 + } + b[0] = format + copy(b[1:33], p.X.Bytes()) + return b +} + +// SerializeHybrid serializes a public key in a 65-byte hybrid format. +func (p *PublicKey) SerializeHybrid() []byte { + b := make([]byte, 65) + format := pubkeyHybrid + if isOdd(p.Y) { + format |= 0x1 + } + b[0] = format + copy(b[1:33], p.X.Bytes()) + copy(b[33:], p.Y.Bytes()) + return b +} diff --git a/pubkey_test.go b/pubkey_test.go index 05ada643..ef01d0db 100644 --- a/pubkey_test.go +++ b/pubkey_test.go @@ -5,13 +5,16 @@ package btcec_test import ( + "bytes" "github.com/conformal/btcec" + "github.com/davecgh/go-spew/spew" "testing" ) type pubKeyTest struct { name string key []byte + format byte isValid bool } @@ -30,6 +33,7 @@ var pubKeyTests = []pubKeyTest{ 0xb4, 0x12, 0xa3, }, isValid: true, + format: btcec.TstPubkeyUncompressed, }, pubKeyTest{ name: "uncompressed x changed", @@ -82,6 +86,7 @@ var pubKeyTests = []pubKeyTest{ 0xb4, 0x12, 0xa3, }, isValid: true, + format: btcec.TstPubkeyHybrid, }, pubKeyTest{ name: "uncompressed as hybrid wrong", @@ -105,6 +110,7 @@ var pubKeyTests = []pubKeyTest{ 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, }, isValid: true, + format: btcec.TstPubkeyCompressed, }, // from tx fdeb8e72524e8dab0da507ddbaf5f88fe4a933eb10a66bc4745bb0aa11ea393c pubKeyTest{ @@ -115,6 +121,7 @@ var pubKeyTests = []pubKeyTest{ 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e, }, isValid: true, + format: btcec.TstPubkeyCompressed, }, pubKeyTest{ name: "compressed claims uncompressed (ybit = 0)", @@ -195,7 +202,7 @@ var pubKeyTests = []pubKeyTest{ func TestPubKeys(t *testing.T) { for _, test := range pubKeyTests { - _, err := btcec.ParsePubKey(test.key, btcec.S256()) + pk, err := btcec.ParsePubKey(test.key, btcec.S256()) if err != nil { if test.isValid { t.Errorf("%s pubkey failed when shouldn't %v", @@ -206,6 +213,22 @@ func TestPubKeys(t *testing.T) { if !test.isValid { t.Errorf("%s counted as valid when it should fail", test.name) + continue + } + var pkStr []byte + switch test.format { + case btcec.TstPubkeyUncompressed: + pkStr = (*btcec.PublicKey)(pk).SerializeUncompressed() + case btcec.TstPubkeyCompressed: + pkStr = (*btcec.PublicKey)(pk).SerializeCompressed() + case btcec.TstPubkeyHybrid: + pkStr = (*btcec.PublicKey)(pk).SerializeHybrid() + } + if !bytes.Equal(test.key, pkStr) { + t.Errorf("%s pubkey: serialized keys do not match.", + test.name) + spew.Dump(test.key) + spew.Dump(pkStr) } } } diff --git a/signature_test.go b/signature_test.go index 33ff8a4c..a64e3cfd 100644 --- a/signature_test.go +++ b/signature_test.go @@ -154,7 +154,7 @@ var signatureTests = []signatureTest{ // This test is now passing (used to be failing) because there // are signatures in the blockchain that have trailing zero - // bytes before the hashtype. So ParseSignature was fixed to + // bytes before the hashtype. So ParseSignature was fixed to // permit buffers with trailing nonsense after the actual // signature. isValid: true, diff --git a/test_coverage.txt b/test_coverage.txt index 92ee58d5..4e0a3273 100644 --- a/test_coverage.txt +++ b/test_coverage.txt @@ -2,19 +2,22 @@ github.com/conformal/btcec/signature.go ParseSignature 100.00% (41/41) github.com/conformal/btcec/btcec.go KoblitzCurve.doubleJacobian 100.00% (21/21) github.com/conformal/btcec/btcec.go KoblitzCurve.ScalarMult 100.00% (9/9) -github.com/conformal/btcec/btcec.go initS256 100.00% (7/7) +github.com/conformal/btcec/pubkey.go PublicKey.SerializeHybrid 100.00% (8/8) github.com/conformal/btcec/btcec.go KoblitzCurve.IsOnCurve 100.00% (7/7) +github.com/conformal/btcec/pubkey.go PublicKey.SerializeCompressed 100.00% (7/7) +github.com/conformal/btcec/btcec.go initS256 100.00% (7/7) +github.com/conformal/btcec/pubkey.go PublicKey.SerializeUncompressed 100.00% (5/5) github.com/conformal/btcec/btcec.go zForAffine 100.00% (4/4) -github.com/conformal/btcec/btcec.go KoblitzCurve.Add 100.00% (3/3) github.com/conformal/btcec/btcec.go KoblitzCurve.QPlus1Div4 100.00% (3/3) +github.com/conformal/btcec/btcec.go KoblitzCurve.Add 100.00% (3/3) github.com/conformal/btcec/btcec.go S256 100.00% (2/2) -github.com/conformal/btcec/btcec.go KoblitzCurve.Params 100.00% (1/1) github.com/conformal/btcec/btcec.go KoblitzCurve.ScalarBaseMult 100.00% (1/1) -github.com/conformal/btcec/btcec.go initAll 100.00% (1/1) github.com/conformal/btcec/pubkey.go isOdd 100.00% (1/1) +github.com/conformal/btcec/btcec.go initAll 100.00% (1/1) +github.com/conformal/btcec/btcec.go KoblitzCurve.Params 100.00% (1/1) github.com/conformal/btcec/pubkey.go ParsePubKey 96.88% (31/32) github.com/conformal/btcec/btcec.go KoblitzCurve.addJacobian 91.67% (55/60) github.com/conformal/btcec/btcec.go KoblitzCurve.affineFromJacobian 90.00% (9/10) github.com/conformal/btcec/btcec.go KoblitzCurve.Double 0.00% (0/2) -github.com/conformal/btcec ------------------------------- 95.61% (196/205) +github.com/conformal/btcec ------------------------------- 96.00% (216/225)