...

Source file src/github.com/decred/dcrd/dcrec/secp256k1/v4/pubkey.go

Documentation: github.com/decred/dcrd/dcrec/secp256k1/v4

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

View as plain text