github.com/emmansun/gmsm@v0.29.1/sm9/bn256/elliptic.go (about)

     1  package bn256
     2  
     3  import (
     4  	"io"
     5  	"math/big"
     6  )
     7  
     8  // A Curve represents a short-form Weierstrass curve with a=0.
     9  //
    10  // The behavior of Add, Double, and ScalarMult when the input is not a point on
    11  // the curve is undefined.
    12  //
    13  // Note that the conventional point at infinity (0, 0) is not considered on the
    14  // curve, although it can be returned by Add, Double, ScalarMult, or
    15  // ScalarBaseMult (but not the Unmarshal or UnmarshalCompressed functions).
    16  type Curve interface {
    17  	// Params returns the parameters for the curve.
    18  	Params() *CurveParams
    19  	// IsOnCurve reports whether the given (x,y) lies on the curve.
    20  	IsOnCurve(x, y *big.Int) bool
    21  	// Add returns the sum of (x1,y1) and (x2,y2)
    22  	Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int)
    23  	// Double returns 2*(x,y)
    24  	Double(x1, y1 *big.Int) (x, y *big.Int)
    25  	// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
    26  	ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int)
    27  	// ScalarBaseMult returns k*G, where G is the base point of the group
    28  	// and k is an integer in big-endian form.
    29  	ScalarBaseMult(k []byte) (x, y *big.Int)
    30  }
    31  
    32  var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f}
    33  
    34  // GenerateKey returns a public/private key pair. The private key is
    35  // generated using the given reader, which must return random data.
    36  func GenerateKey(curve Curve, rand io.Reader) (priv []byte, x, y *big.Int, err error) {
    37  	N := curve.Params().N
    38  	bitSize := N.BitLen()
    39  	byteLen := (bitSize + 7) / 8
    40  	priv = make([]byte, byteLen)
    41  
    42  	for x == nil {
    43  		_, err = io.ReadFull(rand, priv)
    44  		if err != nil {
    45  			return
    46  		}
    47  		// We have to mask off any excess bits in the case that the size of the
    48  		// underlying field is not a whole number of bytes.
    49  		priv[0] &= mask[bitSize%8]
    50  		// This is because, in tests, rand will return all zeros and we don't
    51  		// want to get the point at infinity and loop forever.
    52  		priv[1] ^= 0x42
    53  
    54  		// If the scalar is out of range, sample another random number.
    55  		if new(big.Int).SetBytes(priv).Cmp(N) >= 0 {
    56  			continue
    57  		}
    58  
    59  		x, y = curve.ScalarBaseMult(priv)
    60  	}
    61  	return
    62  }
    63  
    64  // Marshal converts a point on the curve into the uncompressed form specified in
    65  // SEC 1, Version 2.0, Section 2.3.3. If the point is not on the curve (or is
    66  // the conventional point at infinity), the behavior is undefined.
    67  func Marshal(curve Curve, x, y *big.Int) []byte {
    68  	panicIfNotOnCurve(curve, x, y)
    69  
    70  	byteLen := (curve.Params().BitSize + 7) / 8
    71  
    72  	ret := make([]byte, 1+2*byteLen)
    73  	ret[0] = 4 // uncompressed point
    74  
    75  	x.FillBytes(ret[1 : 1+byteLen])
    76  	y.FillBytes(ret[1+byteLen : 1+2*byteLen])
    77  
    78  	return ret
    79  }
    80  
    81  // MarshalCompressed converts a point on the curve into the compressed form
    82  // specified in SEC 1, Version 2.0, Section 2.3.3. If the point is not on the
    83  // curve (or is the conventional point at infinity), the behavior is undefined.
    84  func MarshalCompressed(curve Curve, x, y *big.Int) []byte {
    85  	panicIfNotOnCurve(curve, x, y)
    86  	byteLen := (curve.Params().BitSize + 7) / 8
    87  	compressed := make([]byte, 1+byteLen)
    88  	compressed[0] = byte(y.Bit(0)) | 2
    89  	x.FillBytes(compressed[1:])
    90  	return compressed
    91  }
    92  
    93  // unmarshaler is implemented by curves with their own constant-time Unmarshal.
    94  //
    95  // There isn't an equivalent interface for Marshal/MarshalCompressed because
    96  // that doesn't involve any mathematical operations, only FillBytes and Bit.
    97  type unmarshaler interface {
    98  	Unmarshal([]byte) (x, y *big.Int)
    99  	UnmarshalCompressed([]byte) (x, y *big.Int)
   100  }
   101  
   102  // Unmarshal converts a point, serialized by Marshal, into an x, y pair. It is
   103  // an error if the point is not in uncompressed form, is not on the curve, or is
   104  // the point at infinity. On error, x = nil.
   105  func Unmarshal(curve Curve, data []byte) (x, y *big.Int) {
   106  	if c, ok := curve.(unmarshaler); ok {
   107  		return c.Unmarshal(data)
   108  	}
   109  
   110  	byteLen := (curve.Params().BitSize + 7) / 8
   111  	if len(data) != 1+2*byteLen {
   112  		return nil, nil
   113  	}
   114  	if data[0] != 4 { // uncompressed form
   115  		return nil, nil
   116  	}
   117  	p := curve.Params().P
   118  	x = new(big.Int).SetBytes(data[1 : 1+byteLen])
   119  	y = new(big.Int).SetBytes(data[1+byteLen:])
   120  	if x.Cmp(p) >= 0 || y.Cmp(p) >= 0 {
   121  		return nil, nil
   122  	}
   123  	if !curve.IsOnCurve(x, y) {
   124  		return nil, nil
   125  	}
   126  	return
   127  }
   128  
   129  // UnmarshalCompressed converts a point, serialized by MarshalCompressed, into
   130  // an x, y pair. It is an error if the point is not in compressed form, is not
   131  // on the curve, or is the point at infinity. On error, x = nil.
   132  func UnmarshalCompressed(curve Curve, data []byte) (x, y *big.Int) {
   133  	if c, ok := curve.(unmarshaler); ok {
   134  		return c.UnmarshalCompressed(data)
   135  	}
   136  
   137  	byteLen := (curve.Params().BitSize + 7) / 8
   138  	if len(data) != 1+byteLen {
   139  		return nil, nil
   140  	}
   141  	if data[0] != 2 && data[0] != 3 { // compressed form
   142  		return nil, nil
   143  	}
   144  	p := curve.Params().P
   145  	x = new(big.Int).SetBytes(data[1:])
   146  	if x.Cmp(p) >= 0 {
   147  		return nil, nil
   148  	}
   149  	// y² = x³ + b
   150  	y = curve.Params().polynomial(x)
   151  	y = y.ModSqrt(y, p)
   152  	if y == nil {
   153  		return nil, nil
   154  	}
   155  	if byte(y.Bit(0)) != data[0]&1 {
   156  		y.Neg(y).Mod(y, p)
   157  	}
   158  	if !curve.IsOnCurve(x, y) {
   159  		return nil, nil
   160  	}
   161  	return
   162  }
   163  
   164  func panicIfNotOnCurve(curve Curve, x, y *big.Int) {
   165  	// (0, 0) is the point at infinity by convention. It's ok to operate on it,
   166  	// although IsOnCurve is documented to return false for it. See Issue 37294.
   167  	if x.Sign() == 0 && y.Sign() == 0 {
   168  		return
   169  	}
   170  
   171  	if !curve.IsOnCurve(x, y) {
   172  		panic("sm9/elliptic: attempted operation on invalid point")
   173  	}
   174  }
   175  
   176  func bigFromHex(s string) *big.Int {
   177  	b, ok := new(big.Int).SetString(s, 16)
   178  	if !ok {
   179  		panic("sm9/elliptic: internal error: invalid encoding")
   180  	}
   181  	return b
   182  }