| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- // Copyright (c) 2013-2014 The btcsuite developers
- // Copyright (c) 2015-2021 The Decred developers
- // Use of this source code is governed by an ISC
- // license that can be found in the LICENSE file.
- package secp256k1
- // References:
- // [SEC1] Elliptic Curve Cryptography
- // https://www.secg.org/sec1-v2.pdf
- //
- // [SEC2] Recommended Elliptic Curve Domain Parameters
- // https://www.secg.org/sec2-v2.pdf
- //
- // [ANSI X9.62-1998] Public Key Cryptography For The Financial Services
- // Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)
- import (
- "fmt"
- )
- const (
- // PubKeyBytesLenCompressed is the number of bytes of a serialized
- // compressed public key.
- PubKeyBytesLenCompressed = 33
- // PubKeyBytesLenUncompressed is the number of bytes of a serialized
- // uncompressed public key.
- PubKeyBytesLenUncompressed = 65
- // PubKeyFormatCompressedEven is the identifier prefix byte for a public key
- // whose Y coordinate is even when serialized in the compressed format per
- // section 2.3.4 of [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.4).
- PubKeyFormatCompressedEven byte = 0x02
- // PubKeyFormatCompressedOdd is the identifier prefix byte for a public key
- // whose Y coordinate is odd when serialized in the compressed format per
- // section 2.3.4 of [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.4).
- PubKeyFormatCompressedOdd byte = 0x03
- // PubKeyFormatUncompressed is the identifier prefix byte for a public key
- // when serialized according in the uncompressed format per section 2.3.3 of
- // [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.3).
- PubKeyFormatUncompressed byte = 0x04
- // PubKeyFormatHybridEven is the identifier prefix byte for a public key
- // whose Y coordinate is even when serialized according to the hybrid format
- // per section 4.3.6 of [ANSI X9.62-1998].
- //
- // NOTE: This format makes little sense in practice an therefore this
- // package will not produce public keys serialized in this format. However,
- // it will parse them since they exist in the wild.
- PubKeyFormatHybridEven byte = 0x06
- // PubKeyFormatHybridOdd is the identifier prefix byte for a public key
- // whose Y coordingate is odd when serialized according to the hybrid format
- // per section 4.3.6 of [ANSI X9.62-1998].
- //
- // NOTE: This format makes little sense in practice an therefore this
- // package will not produce public keys serialized in this format. However,
- // it will parse them since they exist in the wild.
- PubKeyFormatHybridOdd byte = 0x07
- )
- // PublicKey provides facilities for efficiently working with secp256k1 public
- // keys within this package and includes functions to serialize in both
- // uncompressed and compressed SEC (Standards for Efficient Cryptography)
- // formats.
- type PublicKey struct {
- x FieldVal
- y FieldVal
- }
- // NewPublicKey instantiates a new public key with the given x and y
- // coordinates.
- //
- // It should be noted that, unlike ParsePubKey, since this accepts arbitrary x
- // and y coordinates, it allows creation of public keys that are not valid
- // points on the secp256k1 curve. The IsOnCurve method of the returned instance
- // can be used to determine validity.
- func NewPublicKey(x, y *FieldVal) *PublicKey {
- var pubKey PublicKey
- pubKey.x.Set(x)
- pubKey.y.Set(y)
- return &pubKey
- }
- // ParsePubKey parses a secp256k1 public key encoded according to the format
- // specified by ANSI X9.62-1998, which means it is also compatible with the
- // SEC (Standards for Efficient Cryptography) specification which is a subset of
- // the former. In other words, it supports the uncompressed, compressed, and
- // hybrid formats as follows:
- //
- // Compressed:
- // <format byte = 0x02/0x03><32-byte X coordinate>
- // Uncompressed:
- // <format byte = 0x04><32-byte X coordinate><32-byte Y coordinate>
- // Hybrid:
- // <format byte = 0x05/0x06><32-byte X coordinate><32-byte Y coordinate>
- //
- // NOTE: The hybrid format makes little sense in practice an therefore this
- // package will not produce public keys serialized in this format. However,
- // this function will properly parse them since they exist in the wild.
- func ParsePubKey(serialized []byte) (key *PublicKey, err error) {
- var x, y FieldVal
- switch len(serialized) {
- case PubKeyBytesLenUncompressed:
- // Reject unsupported public key formats for the given length.
- format := serialized[0]
- switch format {
- case PubKeyFormatUncompressed:
- case PubKeyFormatHybridEven, PubKeyFormatHybridOdd:
- default:
- str := fmt.Sprintf("invalid public key: unsupported format: %x",
- format)
- return nil, makeError(ErrPubKeyInvalidFormat, str)
- }
- // Parse the x and y coordinates while ensuring that they are in the
- // allowed range.
- if overflow := x.SetByteSlice(serialized[1:33]); overflow {
- str := "invalid public key: x >= field prime"
- return nil, makeError(ErrPubKeyXTooBig, str)
- }
- if overflow := y.SetByteSlice(serialized[33:]); overflow {
- str := "invalid public key: y >= field prime"
- return nil, makeError(ErrPubKeyYTooBig, str)
- }
- // Ensure the oddness of the y coordinate matches the specified format
- // for hybrid public keys.
- if format == PubKeyFormatHybridEven || format == PubKeyFormatHybridOdd {
- wantOddY := format == PubKeyFormatHybridOdd
- if y.IsOdd() != wantOddY {
- str := fmt.Sprintf("invalid public key: y oddness does not "+
- "match specified value of %v", wantOddY)
- return nil, makeError(ErrPubKeyMismatchedOddness, str)
- }
- }
- // Reject public keys that are not on the secp256k1 curve.
- if !isOnCurve(&x, &y) {
- str := fmt.Sprintf("invalid public key: [%v,%v] not on secp256k1 "+
- "curve", x, y)
- return nil, makeError(ErrPubKeyNotOnCurve, str)
- }
- case PubKeyBytesLenCompressed:
- // Reject unsupported public key formats for the given length.
- format := serialized[0]
- switch format {
- case PubKeyFormatCompressedEven, PubKeyFormatCompressedOdd:
- default:
- str := fmt.Sprintf("invalid public key: unsupported format: %x",
- format)
- return nil, makeError(ErrPubKeyInvalidFormat, str)
- }
- // Parse the x coordinate while ensuring that it is in the allowed
- // range.
- if overflow := x.SetByteSlice(serialized[1:33]); overflow {
- str := "invalid public key: x >= field prime"
- return nil, makeError(ErrPubKeyXTooBig, str)
- }
- // Attempt to calculate the y coordinate for the given x coordinate such
- // that the result pair is a point on the secp256k1 curve and the
- // solution with desired oddness is chosen.
- wantOddY := format == PubKeyFormatCompressedOdd
- if !DecompressY(&x, wantOddY, &y) {
- str := fmt.Sprintf("invalid public key: x coordinate %v is not on "+
- "the secp256k1 curve", x)
- return nil, makeError(ErrPubKeyNotOnCurve, str)
- }
- y.Normalize()
- default:
- str := fmt.Sprintf("malformed public key: invalid length: %d",
- len(serialized))
- return nil, makeError(ErrPubKeyInvalidLen, str)
- }
- return NewPublicKey(&x, &y), nil
- }
- // SerializeUncompressed serializes a public key in the 65-byte uncompressed
- // format.
- func (p PublicKey) SerializeUncompressed() []byte {
- // 0x04 || 32-byte x coordinate || 32-byte y coordinate
- var b [PubKeyBytesLenUncompressed]byte
- b[0] = PubKeyFormatUncompressed
- p.x.PutBytesUnchecked(b[1:33])
- p.y.PutBytesUnchecked(b[33:65])
- return b[:]
- }
- // SerializeCompressed serializes a public key in the 33-byte compressed format.
- func (p PublicKey) SerializeCompressed() []byte {
- // Choose the format byte depending on the oddness of the Y coordinate.
- format := PubKeyFormatCompressedEven
- if p.y.IsOdd() {
- format = PubKeyFormatCompressedOdd
- }
- // 0x02 or 0x03 || 32-byte x coordinate
- var b [PubKeyBytesLenCompressed]byte
- b[0] = format
- p.x.PutBytesUnchecked(b[1:33])
- return b[:]
- }
- // 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.Equals(&otherPubKey.x) && p.y.Equals(&otherPubKey.y)
- }
- // AsJacobian converts the public key into a Jacobian point with Z=1 and stores
- // the result in the provided result param. This allows the public key to be
- // treated a Jacobian point in the secp256k1 group in calculations.
- func (p *PublicKey) AsJacobian(result *JacobianPoint) {
- result.X.Set(&p.x)
- result.Y.Set(&p.y)
- result.Z.SetInt(1)
- }
- // IsOnCurve returns whether or not the public key represents a point on the
- // secp256k1 curve.
- func (p *PublicKey) IsOnCurve() bool {
- return isOnCurve(&p.x, &p.y)
- }
|