pubkey.mx raw

   1  // Copyright (c) 2013-2014 The btcsuite developers
   2  // Copyright (c) 2015-2022 The Decred developers
   3  // Use of this source code is governed by an ISC
   4  // license that can be found in the LICENSE file.
   5  
   6  package secp256k1
   7  
   8  // References:
   9  //   [SEC1] Elliptic Curve Cryptography
  10  //     https://www.secg.org/sec1-v2.pdf
  11  //
  12  //   [SEC2] Recommended Elliptic Curve Domain Parameters
  13  //     https://www.secg.org/sec2-v2.pdf
  14  //
  15  //   [ANSI X9.62-1998] Public Key Cryptography For The Financial Services
  16  //     Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)
  17  
  18  import (
  19  	"fmt"
  20  )
  21  
  22  const (
  23  	// PubKeyBytesLenCompressed is the number of bytes of a serialized
  24  	// compressed public key.
  25  	PubKeyBytesLenCompressed = 33
  26  	// PubKeyBytesLenUncompressed is the number of bytes of a serialized
  27  	// uncompressed public key.
  28  	PubKeyBytesLenUncompressed = 65
  29  	// PubKeyFormatCompressedEven is the identifier prefix byte for a public key
  30  	// whose Y coordinate is even when serialized in the compressed format per
  31  	// section 2.3.4 of [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.4).
  32  	PubKeyFormatCompressedEven byte = 0x02
  33  	// PubKeyFormatCompressedOdd is the identifier prefix byte for a public key
  34  	// whose Y coordinate is odd when serialized in the compressed format per
  35  	// section 2.3.4 of [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.4).
  36  	PubKeyFormatCompressedOdd byte = 0x03
  37  	// PubKeyFormatUncompressed is the identifier prefix byte for a public key
  38  	// when serialized according in the uncompressed format per section 2.3.3 of
  39  	// [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.3).
  40  	PubKeyFormatUncompressed byte = 0x04
  41  	// PubKeyFormatHybridEven is the identifier prefix byte for a public key
  42  	// whose Y coordinate is even when serialized according to the hybrid format
  43  	// per section 4.3.6 of [ANSI X9.62-1998].
  44  	//
  45  	// NOTE: This format makes little sense in practice an therefore this
  46  	// package will not produce public keys serialized in this format.  However,
  47  	// it will parse them since they exist in the wild.
  48  	PubKeyFormatHybridEven byte = 0x06
  49  	// PubKeyFormatHybridOdd is the identifier prefix byte for a public key
  50  	// whose Y coordingate is odd when serialized according to the hybrid format
  51  	// per section 4.3.6 of [ANSI X9.62-1998].
  52  	//
  53  	// NOTE: This format makes little sense in practice an therefore this
  54  	// package will not produce public keys serialized in this format.  However,
  55  	// it will parse them since they exist in the wild.
  56  	PubKeyFormatHybridOdd byte = 0x07
  57  )
  58  
  59  // PublicKey provides facilities for efficiently working with secp256k1 public
  60  // keys within this package and includes functions to serialize in both
  61  // uncompressed and compressed SEC (Standards for Efficient Cryptography)
  62  // formats.
  63  type PublicKey struct {
  64  	x FieldVal
  65  	y FieldVal
  66  }
  67  
  68  // NewPublicKey instantiates a new public key with the given x and y
  69  // coordinates.
  70  //
  71  // It should be noted that, unlike ParsePubKey, since this accepts arbitrary x
  72  // and y coordinates, it allows creation of public keys that are not valid
  73  // points on the secp256k1 curve.  The IsOnCurve method of the returned instance
  74  // can be used to determine validity.
  75  func NewPublicKey(x, y *FieldVal) *PublicKey {
  76  	var pubKey PublicKey
  77  	pubKey.x.Set(x)
  78  	pubKey.y.Set(y)
  79  	return &pubKey
  80  }
  81  
  82  // ParsePubKey parses a secp256k1 public key encoded according to the format
  83  // specified by ANSI X9.62-1998, which means it is also compatible with the
  84  // SEC (Standards for Efficient Cryptography) specification which is a subset of
  85  // the former.  In other words, it supports the uncompressed, compressed, and
  86  // hybrid formats as follows:
  87  //
  88  // Compressed:
  89  //
  90  //	<format byte = 0x02/0x03><32-byte X coordinate>
  91  //
  92  // Uncompressed:
  93  //
  94  //	<format byte = 0x04><32-byte X coordinate><32-byte Y coordinate>
  95  //
  96  // Hybrid:
  97  //
  98  //	<format byte = 0x05/0x06><32-byte X coordinate><32-byte Y coordinate>
  99  //
 100  // NOTE: The hybrid format makes little sense in practice an therefore this
 101  // package will not produce public keys serialized in this format.  However,
 102  // this function will properly parse them since they exist in the wild.
 103  func ParsePubKey(serialized []byte) (key *PublicKey, err error) {
 104  	var x, y FieldVal
 105  	switch len(serialized) {
 106  	case PubKeyBytesLenUncompressed:
 107  		// Reject unsupported public key formats for the given length.
 108  		format := serialized[0]
 109  		switch format {
 110  		case PubKeyFormatUncompressed:
 111  		case PubKeyFormatHybridEven, PubKeyFormatHybridOdd:
 112  		default:
 113  			str := fmt.Sprintf(
 114  				"invalid public key: unsupported format: %x",
 115  				format,
 116  			)
 117  			return nil, makeError(ErrPubKeyInvalidFormat, str)
 118  		}
 119  		// Parse the x and y coordinates while ensuring that they are in the
 120  		// allowed range.
 121  		if overflow := x.SetByteSlice(serialized[1:33]); overflow {
 122  			str := "invalid public key: x >= field prime"
 123  			return nil, makeError(ErrPubKeyXTooBig, str)
 124  		}
 125  		if overflow := y.SetByteSlice(serialized[33:]); overflow {
 126  			str := "invalid public key: y >= field prime"
 127  			return nil, makeError(ErrPubKeyYTooBig, str)
 128  		}
 129  		// Ensure the oddness of the y coordinate matches the specified format
 130  		// for hybrid public keys.
 131  		if format == PubKeyFormatHybridEven || format == PubKeyFormatHybridOdd {
 132  			wantOddY := format == PubKeyFormatHybridOdd
 133  			if y.IsOdd() != wantOddY {
 134  				str := fmt.Sprintf(
 135  					"invalid public key: y oddness does not "+
 136  						"match specified value of %v", wantOddY,
 137  				)
 138  				return nil, makeError(ErrPubKeyMismatchedOddness, str)
 139  			}
 140  		}
 141  		// Reject public keys that are not on the secp256k1 curve.
 142  		if !isOnCurve(&x, &y) {
 143  			str := fmt.Sprintf(
 144  				"invalid public key: [%v,%v] not on secp256k1 "+
 145  					"curve", x, y,
 146  			)
 147  			return nil, makeError(ErrPubKeyNotOnCurve, str)
 148  		}
 149  	case PubKeyBytesLenCompressed:
 150  		// Reject unsupported public key formats for the given length.
 151  		format := serialized[0]
 152  		switch format {
 153  		case PubKeyFormatCompressedEven, PubKeyFormatCompressedOdd:
 154  		default:
 155  			str := fmt.Sprintf(
 156  				"invalid public key: unsupported format: %x",
 157  				format,
 158  			)
 159  			return nil, makeError(ErrPubKeyInvalidFormat, str)
 160  		}
 161  		// Parse the x coordinate while ensuring that it is in the allowed
 162  		// range.
 163  		if overflow := x.SetByteSlice(serialized[1:33]); overflow {
 164  			str := "invalid public key: x >= field prime"
 165  			return nil, makeError(ErrPubKeyXTooBig, str)
 166  		}
 167  		// Attempt to calculate the y coordinate for the given x coordinate such
 168  		// that the result pair is a point on the secp256k1 curve and the
 169  		// solution with desired oddness is chosen.
 170  		wantOddY := format == PubKeyFormatCompressedOdd
 171  		if !DecompressY(&x, wantOddY, &y) {
 172  			str := fmt.Sprintf(
 173  				"invalid public key: x coordinate %v is not on "+
 174  					"the secp256k1 curve", x,
 175  			)
 176  			return nil, makeError(ErrPubKeyNotOnCurve, str)
 177  		}
 178  		y.Normalize()
 179  	default:
 180  		str := fmt.Sprintf(
 181  			"malformed public key: invalid length: %d",
 182  			len(serialized),
 183  		)
 184  		return nil, makeError(ErrPubKeyInvalidLen, str)
 185  	}
 186  	return NewPublicKey(&x, &y), nil
 187  }
 188  
 189  // SerializeUncompressed serializes a public key in the 65-byte uncompressed
 190  // format.
 191  func (p PublicKey) SerializeUncompressed() []byte {
 192  	// 0x04 || 32-byte x coordinate || 32-byte y coordinate
 193  	var b [PubKeyBytesLenUncompressed]byte
 194  	b[0] = PubKeyFormatUncompressed
 195  	p.x.PutBytesUnchecked(b[1:33])
 196  	p.y.PutBytesUnchecked(b[33:65])
 197  	return b[:]
 198  }
 199  
 200  // SerializeCompressed serializes a public key in the 33-byte compressed format.
 201  func (p PublicKey) SerializeCompressed() []byte {
 202  	// Choose the format byte depending on the oddness of the Y coordinate.
 203  	format := PubKeyFormatCompressedEven
 204  	if p.y.IsOdd() {
 205  		format = PubKeyFormatCompressedOdd
 206  	}
 207  	// 0x02 or 0x03 || 32-byte x coordinate
 208  	var b [PubKeyBytesLenCompressed]byte
 209  	b[0] = format
 210  	p.x.PutBytesUnchecked(b[1:33])
 211  	return b[:]
 212  }
 213  
 214  // IsEqual compares this public key instance to the one passed, returning true
 215  // if both public keys are equivalent.  A public key is equivalent to another,
 216  // if they both have the same X and Y coordinates.
 217  func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool {
 218  	return p.x.Equals(&otherPubKey.x) && p.y.Equals(&otherPubKey.y)
 219  }
 220  
 221  // AsJacobian converts the public key into a Jacobian point with Z=1 and stores
 222  // the result in the provided result param.  This allows the public key to be
 223  // treated a Jacobian point in the secp256k1 group in calculations.
 224  func (p *PublicKey) AsJacobian(result *JacobianPoint) {
 225  	result.X.Set(&p.x)
 226  	result.Y.Set(&p.y)
 227  	result.Z.SetInt(1)
 228  }
 229  
 230  // IsOnCurve returns whether or not the public key represents a point on the
 231  // secp256k1 curve.
 232  func (p *PublicKey) IsOnCurve() bool {
 233  	return isOnCurve(&p.x, &p.y)
 234  }
 235