github.com/chain5j/chain5j-pkg@v1.0.7/crypto/signature/prime256v1/pubkey.go (about)

     1  package prime256v1
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/elliptic"
     6  	"errors"
     7  	"fmt"
     8  	"math/big"
     9  )
    10  
    11  // These constants define the lengths of serialized public keys.
    12  const (
    13  	PubKeyBytesLenCompressed   = 33
    14  	PubKeyBytesLenUncompressed = 65
    15  
    16  	pubkeyCompressed   byte = 0x2 // y_bit + x coord
    17  	pubkeyUncompressed byte = 0x4 // x coord + y coord
    18  )
    19  
    20  func isOdd(a *big.Int) bool {
    21  	return a.Bit(0) == 1
    22  }
    23  
    24  // decompressPoint decompresses a point on the given curve given the X point and
    25  // the solution to use.
    26  func decompressPoint(curve elliptic.Curve, x *big.Int, ybit bool) (*big.Int, error) {
    27  	// Y = +-sqrt(x^3 - 3x + B)
    28  	x3 := new(big.Int).Mul(x, x)
    29  	xThree := new(big.Int).Mul(x, big.NewInt(int64(3)))
    30  	x3.Mul(x3, x)
    31  
    32  	x3.Sub(x3, xThree)
    33  	x3.Add(x3, curve.Params().B)
    34  
    35  	var q = new(big.Int).Div(new(big.Int).Add(curve.Params().P,
    36  		big.NewInt(1)), big.NewInt(4))
    37  	// now calculate sqrt mod p of x2 + B
    38  	// This code used to do a full sqrt based on tonelli/shanks,
    39  	// but this was replaced by the algorithms referenced in
    40  	// https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294
    41  	y := new(big.Int).Exp(x3, q, curve.Params().P)
    42  
    43  	if ybit != isOdd(y) {
    44  		y.Sub(curve.Params().P, y)
    45  	}
    46  	if ybit != isOdd(y) {
    47  		return nil, fmt.Errorf("ybit doesn't match oddness")
    48  	}
    49  	return y, nil
    50  }
    51  
    52  // NewPublicKey instantiates a new public key with the given X,Y coordinates.
    53  func NewPublicKey(curve elliptic.Curve, x *big.Int, y *big.Int) *PublicKey {
    54  	return &PublicKey{
    55  		Curve: curve,
    56  		X:     x,
    57  		Y:     y,
    58  	}
    59  }
    60  
    61  // ParsePubKey parses a public key for curve from a bytestring into a
    62  // ecdsa.Publickey, verifying that it is valid. It supports compressed and
    63  // uncompressed signature formats, but not the hybrid format.
    64  func ParsePubKey(curve elliptic.Curve, pubKeyStr []byte) (key *PublicKey, err error) {
    65  	pubkey := PublicKey{
    66  		Curve: curve,
    67  	}
    68  
    69  	if len(pubKeyStr) == 0 {
    70  		return nil, errors.New("pubkey string is empty")
    71  	}
    72  
    73  	format := pubKeyStr[0]
    74  	ybit := (format & 0x1) == 0x1
    75  	format &= ^byte(0x1)
    76  
    77  	switch len(pubKeyStr) {
    78  	case PubKeyBytesLenUncompressed:
    79  		if format != pubkeyUncompressed {
    80  			return nil, fmt.Errorf("invalid magic in pubkey str: "+
    81  				"%d", pubKeyStr[0])
    82  		}
    83  
    84  		pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
    85  		pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:])
    86  	case PubKeyBytesLenCompressed:
    87  		// format is 0x2 | solution, <X coordinate>
    88  		// solution determines which solution of the curve we use.
    89  		// / y^2 = x^3 + a*x + Curve.B
    90  		if format != pubkeyCompressed {
    91  			return nil, fmt.Errorf("invalid magic in compressed "+
    92  				"pubkey string: %d", pubKeyStr[0])
    93  		}
    94  		pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
    95  		pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit)
    96  		if err != nil {
    97  			return nil, err
    98  		}
    99  	default: // wrong!
   100  		return nil, fmt.Errorf("invalid pub key length %d",
   101  			len(pubKeyStr))
   102  	}
   103  
   104  	if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 {
   105  		return nil, fmt.Errorf("pubkey X parameter is >= to P")
   106  	}
   107  	if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 {
   108  		return nil, fmt.Errorf("pubkey Y parameter is >= to P")
   109  	}
   110  	if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
   111  		return nil, fmt.Errorf("pubkey [%v,%v] isn't on secp256k1 curve",
   112  			pubkey.X, pubkey.Y)
   113  	}
   114  	return &pubkey, nil
   115  }
   116  
   117  // PublicKey is an ecdsa.PublicKey with additional functions to
   118  // serialize in uncompressed and compressed formats.
   119  type PublicKey ecdsa.PublicKey
   120  
   121  // ToECDSA returns the public key as a *ecdsa.PublicKey.
   122  func (p PublicKey) ToECDSA() *ecdsa.PublicKey {
   123  	ecpk := ecdsa.PublicKey(p)
   124  	return &ecpk
   125  }
   126  
   127  // Serialize serializes a public key in a 33-byte compressed format.
   128  // It is the default serialization method.
   129  func (p PublicKey) Serialize() []byte {
   130  	return p.SerializeCompressed()
   131  }
   132  
   133  // SerializeCompressed serializes a public key in a 33-byte compressed format.
   134  func (p PublicKey) SerializeCompressed() []byte {
   135  	if p.Curve != elliptic.P256() {
   136  		return nil
   137  	}
   138  	b := make([]byte, 0, PubKeyBytesLenCompressed)
   139  	format := pubkeyCompressed
   140  	if isOdd(p.Y) {
   141  		format |= 0x1
   142  	}
   143  	b = append(b, format)
   144  	return paddedAppend(32, b, p.X.Bytes())
   145  }
   146  
   147  // SerializeUncompressed serializes a public key in a 65-byte uncompressed
   148  // format.
   149  func (p PublicKey) SerializeUncompressed() []byte {
   150  	b := make([]byte, 0, PubKeyBytesLenUncompressed)
   151  	b = append(b, pubkeyUncompressed)
   152  	b = paddedAppend(32, b, p.X.Bytes())
   153  	return paddedAppend(32, b, p.Y.Bytes())
   154  }
   155  
   156  // IsEqual compares this PublicKey instance to the one passed, returning true if
   157  // both PublicKeys are equivalent. A PublicKey is equivalent to another, if they
   158  // both have the same X and Y coordinate.
   159  func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool {
   160  	return p.X.Cmp(otherPubKey.X) == 0 &&
   161  		p.Y.Cmp(otherPubKey.Y) == 0
   162  }
   163  
   164  // paddedAppend appends the src byte slice to dst, returning the new slice.
   165  // If the length of the source is smaller than the passed size, leading zero
   166  // bytes are appended to the dst slice before appending src.
   167  func paddedAppend(size uint, dst, src []byte) []byte {
   168  	for i := 0; i < int(size)-len(src); i++ {
   169  		dst = append(dst, 0)
   170  	}
   171  	return append(dst, src...)
   172  }
   173  
   174  // GetCurve satisfies the chainec PublicKey interface.
   175  func (p PublicKey) GetCurve() elliptic.Curve {
   176  	return p.Curve
   177  }
   178  
   179  // GetX satisfies the chainec PublicKey interface.
   180  func (p PublicKey) GetX() *big.Int {
   181  	return p.X
   182  }
   183  
   184  // GetY satisfies the chainec PublicKey interface.
   185  func (p PublicKey) GetY() *big.Int {
   186  	return p.Y
   187  }
   188  
   189  func DecompressPubkey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int) {
   190  	if curve != elliptic.P256() {
   191  		return nil, nil
   192  	}
   193  	key, err := ParsePubKey(curve, pubkey)
   194  	if err != nil {
   195  		return nil, nil
   196  	}
   197  	return key.X, key.Y
   198  }
   199  
   200  func CompressPubkey(curve elliptic.Curve, x, y *big.Int) []byte {
   201  	publicKey := NewPublicKey(curve, x, y)
   202  	return publicKey.SerializeCompressed()
   203  }