// Copyright (c) 2013-2014 The btcsuite developers
// Copyright (c) 2015-2024 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 (
	
)

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 (,  *FieldVal) *PublicKey {
	var  PublicKey
	.x.Set()
	.y.Set()
	return &
}

// 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 ( []byte) ( *PublicKey,  error) {
	var ,  FieldVal
	switch len() {
	case PubKeyBytesLenUncompressed:
		// Reject unsupported public key formats for the given length.
		 := [0]
		switch  {
		case PubKeyFormatUncompressed:
		case PubKeyFormatHybridEven, PubKeyFormatHybridOdd:
		default:
			 := fmt.Sprintf("invalid public key: unsupported format: %x",
				)
			return nil, makeError(ErrPubKeyInvalidFormat, )
		}

		// Parse the x and y coordinates while ensuring that they are in the
		// allowed range.
		if  := .SetByteSlice([1:33]);  {
			 := "invalid public key: x >= field prime"
			return nil, makeError(ErrPubKeyXTooBig, )
		}
		if  := .SetByteSlice([33:]);  {
			 := "invalid public key: y >= field prime"
			return nil, makeError(ErrPubKeyYTooBig, )
		}

		// Ensure the oddness of the y coordinate matches the specified format
		// for hybrid public keys.
		if  == PubKeyFormatHybridEven ||  == PubKeyFormatHybridOdd {
			 :=  == PubKeyFormatHybridOdd
			if .IsOdd() !=  {
				 := fmt.Sprintf("invalid public key: y oddness does not "+
					"match specified value of %v", )
				return nil, makeError(ErrPubKeyMismatchedOddness, )
			}
		}

		// Reject public keys that are not on the secp256k1 curve.
		if !isOnCurve(&, &) {
			 := fmt.Sprintf("invalid public key: [%v,%v] not on secp256k1 "+
				"curve", , )
			return nil, makeError(ErrPubKeyNotOnCurve, )
		}

	case PubKeyBytesLenCompressed:
		// Reject unsupported public key formats for the given length.
		 := [0]
		switch  {
		case PubKeyFormatCompressedEven, PubKeyFormatCompressedOdd:
		default:
			 := fmt.Sprintf("invalid public key: unsupported format: %x",
				)
			return nil, makeError(ErrPubKeyInvalidFormat, )
		}

		// Parse the x coordinate while ensuring that it is in the allowed
		// range.
		if  := .SetByteSlice([1:33]);  {
			 := "invalid public key: x >= field prime"
			return nil, makeError(ErrPubKeyXTooBig, )
		}

		// 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.
		 :=  == PubKeyFormatCompressedOdd
		if !DecompressY(&, , &) {
			 := fmt.Sprintf("invalid public key: x coordinate %v is not on "+
				"the secp256k1 curve", )
			return nil, makeError(ErrPubKeyNotOnCurve, )
		}

	default:
		 := fmt.Sprintf("malformed public key: invalid length: %d",
			len())
		return nil, makeError(ErrPubKeyInvalidLen, )
	}

	return NewPublicKey(&, &), nil
}

// SerializeUncompressed serializes a public key in the 65-byte uncompressed
// format.
func ( PublicKey) () []byte {
	// 0x04 || 32-byte x coordinate || 32-byte y coordinate
	var  [PubKeyBytesLenUncompressed]byte
	[0] = PubKeyFormatUncompressed
	.x.PutBytesUnchecked([1:33])
	.y.PutBytesUnchecked([33:65])
	return [:]
}

// SerializeCompressed serializes a public key in the 33-byte compressed format.
func ( PublicKey) () []byte {
	// Choose the format byte depending on the oddness of the Y coordinate.
	 := PubKeyFormatCompressedEven
	if .y.IsOdd() {
		 = PubKeyFormatCompressedOdd
	}

	// 0x02 or 0x03 || 32-byte x coordinate
	var  [PubKeyBytesLenCompressed]byte
	[0] = 
	.x.PutBytesUnchecked([1:33])
	return [:]
}

// IsEqual compares this public key instance to the one passed, returning true
// if both public keys are equivalent.  A public key is equivalent to another,
// if they both have the same X and Y coordinates.
func ( *PublicKey) ( *PublicKey) bool {
	return .x.Equals(&.x) && .y.Equals(&.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 ( *PublicKey) ( *JacobianPoint) {
	.X.Set(&.x)
	.Y.Set(&.y)
	.Z.SetInt(1)
}

// IsOnCurve returns whether or not the public key represents a point on the
// secp256k1 curve.
func ( *PublicKey) () bool {
	return isOnCurve(&.x, &.y)
}