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 }