aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/btcsuite/btcd/btcec/pubkey.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/btcsuite/btcd/btcec/pubkey.go')
-rw-r--r--vendor/github.com/btcsuite/btcd/btcec/pubkey.go172
1 files changed, 172 insertions, 0 deletions
diff --git a/vendor/github.com/btcsuite/btcd/btcec/pubkey.go b/vendor/github.com/btcsuite/btcd/btcec/pubkey.go
new file mode 100644
index 000000000..f145414ec
--- /dev/null
+++ b/vendor/github.com/btcsuite/btcd/btcec/pubkey.go
@@ -0,0 +1,172 @@
+// Copyright (c) 2013-2014 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+package btcec
+
+import (
+ "crypto/ecdsa"
+ "errors"
+ "fmt"
+ "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
+}
+
+// decompressPoint decompresses a point on the given curve given the X point and
+// the solution to use.
+func decompressPoint(curve *KoblitzCurve, x *big.Int, ybit bool) (*big.Int, error) {
+ // TODO: This will probably only work for secp256k1 due to
+ // optimizations.
+
+ // Y = +-sqrt(x^3 + B)
+ x3 := new(big.Int).Mul(x, x)
+ x3.Mul(x3, x)
+ x3.Add(x3, curve.Params().B)
+
+ // now calculate sqrt mod p of x2 + B
+ // This code used to do a full sqrt based on tonelli/shanks,
+ // but this was replaced by the algorithms referenced in
+ // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294
+ y := new(big.Int).Exp(x3, curve.QPlus1Div4(), curve.Params().P)
+
+ if ybit != isOdd(y) {
+ y.Sub(curve.Params().P, y)
+ }
+ if ybit != isOdd(y) {
+ return nil, fmt.Errorf("ybit doesn't match oddness")
+ }
+ return y, nil
+}
+
+const (
+ pubkeyCompressed byte = 0x2 // y_bit + x 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
+// ecdsa.Publickey, verifying that it is valid. It supports compressed,
+// uncompressed and hybrid signature formats.
+func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err error) {
+ pubkey := PublicKey{}
+ pubkey.Curve = curve
+
+ if len(pubKeyStr) == 0 {
+ return nil, errors.New("pubkey string is empty")
+ }
+
+ format := pubKeyStr[0]
+ ybit := (format & 0x1) == 0x1
+ format &= ^byte(0x1)
+
+ switch len(pubKeyStr) {
+ case PubKeyBytesLenUncompressed:
+ if format != pubkeyUncompressed && format != pubkeyHybrid {
+ return nil, fmt.Errorf("invalid magic in pubkey str: "+
+ "%d", pubKeyStr[0])
+ }
+
+ pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
+ pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:])
+ // hybrid keys have extra information, make use of it.
+ if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) {
+ return nil, fmt.Errorf("ybit doesn't match oddness")
+ }
+ case PubKeyBytesLenCompressed:
+ // format is 0x2 | solution, <X coordinate>
+ // solution determines which solution of the curve we use.
+ /// y^2 = x^3 + Curve.B
+ if format != pubkeyCompressed {
+ return nil, fmt.Errorf("invalid magic in compressed "+
+ "pubkey string: %d", pubKeyStr[0])
+ }
+ pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
+ pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit)
+ if err != nil {
+ return nil, err
+ }
+ default: // wrong!
+ return nil, fmt.Errorf("invalid pub key length %d",
+ len(pubKeyStr))
+ }
+
+ if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 {
+ return nil, fmt.Errorf("pubkey X parameter is >= to P")
+ }
+ if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 {
+ return nil, fmt.Errorf("pubkey Y parameter is >= to P")
+ }
+ if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
+ return nil, fmt.Errorf("pubkey isn't on secp256k1 curve")
+ }
+ return &pubkey, nil
+}
+
+// PublicKey is an ecdsa.PublicKey with additional functions to
+// serialize in uncompressed, compressed, and hybrid formats.
+type PublicKey ecdsa.PublicKey
+
+// ToECDSA returns the public key as a *ecdsa.PublicKey.
+func (p *PublicKey) ToECDSA() *ecdsa.PublicKey {
+ return (*ecdsa.PublicKey)(p)
+}
+
+// SerializeUncompressed serializes a public key in a 65-byte uncompressed
+// format.
+func (p *PublicKey) SerializeUncompressed() []byte {
+ 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, 0, PubKeyBytesLenCompressed)
+ format := pubkeyCompressed
+ if isOdd(p.Y) {
+ format |= 0x1
+ }
+ 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, 0, PubKeyBytesLenHybrid)
+ format := pubkeyHybrid
+ if isOdd(p.Y) {
+ format |= 0x1
+ }
+ b = append(b, format)
+ b = paddedAppend(32, b, p.X.Bytes())
+ return paddedAppend(32, b, p.Y.Bytes())
+}
+
+// IsEqual compares this PublicKey instance to the one passed, returning true if
+// both PublicKeys are equivalent. A PublicKey is equivalent to another, if they
+// both have the same X and Y coordinate.
+func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool {
+ return p.X.Cmp(otherPubKey.X) == 0 &&
+ p.Y.Cmp(otherPubKey.Y) == 0
+}
+
+// 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)
+ }
+ return append(dst, src...)
+}