github.com/cloudflare/circl@v1.5.0/ecc/goldilocks/scalar.go (about) 1 package goldilocks 2 3 import ( 4 "encoding/binary" 5 "math/bits" 6 ) 7 8 // ScalarSize is the size (in bytes) of scalars. 9 const ScalarSize = 56 // 448 / 8 10 11 // _N is the number of 64-bit words to store scalars. 12 const _N = 7 // 448 / 64 13 14 // Scalar represents a positive integer stored in little-endian order. 15 type Scalar [ScalarSize]byte 16 17 type scalar64 [_N]uint64 18 19 func (z *scalar64) fromScalar(x *Scalar) { 20 z[0] = binary.LittleEndian.Uint64(x[0*8 : 1*8]) 21 z[1] = binary.LittleEndian.Uint64(x[1*8 : 2*8]) 22 z[2] = binary.LittleEndian.Uint64(x[2*8 : 3*8]) 23 z[3] = binary.LittleEndian.Uint64(x[3*8 : 4*8]) 24 z[4] = binary.LittleEndian.Uint64(x[4*8 : 5*8]) 25 z[5] = binary.LittleEndian.Uint64(x[5*8 : 6*8]) 26 z[6] = binary.LittleEndian.Uint64(x[6*8 : 7*8]) 27 } 28 29 func (z *scalar64) toScalar(x *Scalar) { 30 binary.LittleEndian.PutUint64(x[0*8:1*8], z[0]) 31 binary.LittleEndian.PutUint64(x[1*8:2*8], z[1]) 32 binary.LittleEndian.PutUint64(x[2*8:3*8], z[2]) 33 binary.LittleEndian.PutUint64(x[3*8:4*8], z[3]) 34 binary.LittleEndian.PutUint64(x[4*8:5*8], z[4]) 35 binary.LittleEndian.PutUint64(x[5*8:6*8], z[5]) 36 binary.LittleEndian.PutUint64(x[6*8:7*8], z[6]) 37 } 38 39 // add calculates z = x + y. Assumes len(z) > max(len(x),len(y)). 40 func add(z, x, y []uint64) uint64 { 41 l, L, zz := len(x), len(y), y 42 if l > L { 43 l, L, zz = L, l, x 44 } 45 c := uint64(0) 46 for i := 0; i < l; i++ { 47 z[i], c = bits.Add64(x[i], y[i], c) 48 } 49 for i := l; i < L; i++ { 50 z[i], c = bits.Add64(zz[i], 0, c) 51 } 52 return c 53 } 54 55 // sub calculates z = x - y. Assumes len(z) > max(len(x),len(y)). 56 func sub(z, x, y []uint64) uint64 { 57 l, L, zz := len(x), len(y), y 58 if l > L { 59 l, L, zz = L, l, x 60 } 61 c := uint64(0) 62 for i := 0; i < l; i++ { 63 z[i], c = bits.Sub64(x[i], y[i], c) 64 } 65 for i := l; i < L; i++ { 66 z[i], c = bits.Sub64(zz[i], 0, c) 67 } 68 return c 69 } 70 71 // mulWord calculates z = x * y. Assumes len(z) >= len(x)+1. 72 func mulWord(z, x []uint64, y uint64) { 73 for i := range z { 74 z[i] = 0 75 } 76 carry := uint64(0) 77 for i := range x { 78 hi, lo := bits.Mul64(x[i], y) 79 lo, cc := bits.Add64(lo, z[i], 0) 80 hi, _ = bits.Add64(hi, 0, cc) 81 z[i], cc = bits.Add64(lo, carry, 0) 82 carry, _ = bits.Add64(hi, 0, cc) 83 } 84 z[len(x)] = carry 85 } 86 87 // Cmov moves x into z if b=1. 88 func (z *scalar64) Cmov(b uint64, x *scalar64) { 89 m := uint64(0) - b 90 for i := range z { 91 z[i] = (z[i] &^ m) | (x[i] & m) 92 } 93 } 94 95 // leftShift shifts to the left the words of z returning the more significant word. 96 func (z *scalar64) leftShift(low uint64) uint64 { 97 high := z[_N-1] 98 for i := _N - 1; i > 0; i-- { 99 z[i] = z[i-1] 100 } 101 z[0] = low 102 return high 103 } 104 105 // reduceOneWord calculates z = z + 2^448*x such that the result fits in a Scalar. 106 func (z *scalar64) reduceOneWord(x uint64) { 107 prod := (&scalar64{})[:] 108 mulWord(prod, residue448[:], x) 109 cc := add(z[:], z[:], prod) 110 mulWord(prod, residue448[:], cc) 111 add(z[:], z[:], prod) 112 } 113 114 // modOrder reduces z mod order. 115 func (z *scalar64) modOrder() { 116 var o64, x scalar64 117 o64.fromScalar(&order) 118 // Performs: while (z >= order) { z = z-order } 119 // At most 8 (eight) iterations reduce 3 bits by subtracting. 120 for i := 0; i < 8; i++ { 121 c := sub(x[:], z[:], o64[:]) // (c || x) = z-order 122 z.Cmov(1-c, &x) // if c != 0 { z = x } 123 } 124 } 125 126 // FromBytes stores z = x mod order, where x is a number stored in little-endian order. 127 func (z *Scalar) FromBytes(x []byte) { 128 n := len(x) 129 nCeil := (n + 7) >> 3 130 for i := range z { 131 z[i] = 0 132 } 133 if nCeil < _N { 134 copy(z[:], x) 135 return 136 } 137 copy(z[:], x[8*(nCeil-_N):]) 138 var z64 scalar64 139 z64.fromScalar(z) 140 for i := nCeil - _N - 1; i >= 0; i-- { 141 low := binary.LittleEndian.Uint64(x[8*i:]) 142 high := z64.leftShift(low) 143 z64.reduceOneWord(high) 144 } 145 z64.modOrder() 146 z64.toScalar(z) 147 } 148 149 // divBy4 calculates z = x/4 mod order. 150 func (z *Scalar) divBy4(x *Scalar) { z.Mul(x, &invFour) } 151 152 // Red reduces z mod order. 153 func (z *Scalar) Red() { var t scalar64; t.fromScalar(z); t.modOrder(); t.toScalar(z) } 154 155 // Neg calculates z = -z mod order. 156 func (z *Scalar) Neg() { z.Sub(&order, z) } 157 158 // Add calculates z = x+y mod order. 159 func (z *Scalar) Add(x, y *Scalar) { 160 var z64, x64, y64, t scalar64 161 x64.fromScalar(x) 162 y64.fromScalar(y) 163 c := add(z64[:], x64[:], y64[:]) 164 add(t[:], z64[:], residue448[:]) 165 z64.Cmov(c, &t) 166 z64.modOrder() 167 z64.toScalar(z) 168 } 169 170 // Sub calculates z = x-y mod order. 171 func (z *Scalar) Sub(x, y *Scalar) { 172 var z64, x64, y64, t scalar64 173 x64.fromScalar(x) 174 y64.fromScalar(y) 175 c := sub(z64[:], x64[:], y64[:]) 176 sub(t[:], z64[:], residue448[:]) 177 z64.Cmov(c, &t) 178 z64.modOrder() 179 z64.toScalar(z) 180 } 181 182 // Mul calculates z = x*y mod order. 183 func (z *Scalar) Mul(x, y *Scalar) { 184 var z64, x64, y64 scalar64 185 prod := (&[_N + 1]uint64{})[:] 186 x64.fromScalar(x) 187 y64.fromScalar(y) 188 mulWord(prod, x64[:], y64[_N-1]) 189 copy(z64[:], prod[:_N]) 190 z64.reduceOneWord(prod[_N]) 191 for i := _N - 2; i >= 0; i-- { 192 h := z64.leftShift(0) 193 z64.reduceOneWord(h) 194 mulWord(prod, x64[:], y64[i]) 195 c := add(z64[:], z64[:], prod[:_N]) 196 z64.reduceOneWord(prod[_N] + c) 197 } 198 z64.modOrder() 199 z64.toScalar(z) 200 } 201 202 // IsZero returns true if z=0. 203 func (z *Scalar) IsZero() bool { z.Red(); return *z == Scalar{} }