github.com/cloudflare/circl@v1.5.0/sign/ed25519/mult.go (about) 1 package ed25519 2 3 import ( 4 "crypto/subtle" 5 "encoding/binary" 6 "math/bits" 7 8 "github.com/cloudflare/circl/internal/conv" 9 "github.com/cloudflare/circl/math" 10 fp "github.com/cloudflare/circl/math/fp25519" 11 ) 12 13 var paramD = fp.Elt{ 14 0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75, 15 0xab, 0xd8, 0x41, 0x41, 0x4d, 0x0a, 0x70, 0x00, 16 0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c, 17 0x73, 0xfe, 0x6f, 0x2b, 0xee, 0x6c, 0x03, 0x52, 18 } 19 20 // mLSBRecoding parameters. 21 const ( 22 fxT = 257 23 fxV = 2 24 fxW = 3 25 fx2w1 = 1 << (uint(fxW) - 1) 26 numWords64 = (paramB * 8 / 64) 27 ) 28 29 // mLSBRecoding is the odd-only modified LSB-set. 30 // 31 // Reference: 32 // 33 // "Efficient and secure algorithms for GLV-based scalar multiplication and 34 // their implementation on GLV–GLS curves" by (Faz-Hernandez et al.) 35 // http://doi.org/10.1007/s13389-014-0085-7. 36 func mLSBRecoding(L []int8, k []byte) { 37 const ee = (fxT + fxW*fxV - 1) / (fxW * fxV) 38 const dd = ee * fxV 39 const ll = dd * fxW 40 if len(L) == (ll + 1) { 41 var m [numWords64 + 1]uint64 42 for i := 0; i < numWords64; i++ { 43 m[i] = binary.LittleEndian.Uint64(k[8*i : 8*i+8]) 44 } 45 condAddOrderN(&m) 46 L[dd-1] = 1 47 for i := 0; i < dd-1; i++ { 48 kip1 := (m[(i+1)/64] >> (uint(i+1) % 64)) & 0x1 49 L[i] = int8(kip1<<1) - 1 50 } 51 { // right-shift by d 52 right := uint(dd % 64) 53 left := uint(64) - right 54 lim := ((numWords64+1)*64 - dd) / 64 55 j := dd / 64 56 for i := 0; i < lim; i++ { 57 m[i] = (m[i+j] >> right) | (m[i+j+1] << left) 58 } 59 m[lim] = m[lim+j] >> right 60 } 61 for i := dd; i < ll; i++ { 62 L[i] = L[i%dd] * int8(m[0]&0x1) 63 div2subY(m[:], int64(L[i]>>1), numWords64) 64 } 65 L[ll] = int8(m[0]) 66 } 67 } 68 69 // absolute returns always a positive value. 70 func absolute(x int32) int32 { 71 mask := x >> 31 72 return (x + mask) ^ mask 73 } 74 75 // condAddOrderN updates x = x+order if x is even, otherwise x remains unchanged. 76 func condAddOrderN(x *[numWords64 + 1]uint64) { 77 isOdd := (x[0] & 0x1) - 1 78 c := uint64(0) 79 for i := 0; i < numWords64; i++ { 80 orderWord := binary.LittleEndian.Uint64(order[8*i : 8*i+8]) 81 o := isOdd & orderWord 82 x0, c0 := bits.Add64(x[i], o, c) 83 x[i] = x0 84 c = c0 85 } 86 x[numWords64], _ = bits.Add64(x[numWords64], 0, c) 87 } 88 89 // div2subY update x = (x/2) - y. 90 func div2subY(x []uint64, y int64, l int) { 91 s := uint64(y >> 63) 92 for i := 0; i < l-1; i++ { 93 x[i] = (x[i] >> 1) | (x[i+1] << 63) 94 } 95 x[l-1] = (x[l-1] >> 1) 96 97 b := uint64(0) 98 x0, b0 := bits.Sub64(x[0], uint64(y), b) 99 x[0] = x0 100 b = b0 101 for i := 1; i < l-1; i++ { 102 x0, b0 := bits.Sub64(x[i], s, b) 103 x[i] = x0 104 b = b0 105 } 106 x[l-1], _ = bits.Sub64(x[l-1], s, b) 107 } 108 109 func (P *pointR1) fixedMult(scalar []byte) { 110 if len(scalar) != paramB { 111 panic("wrong scalar size") 112 } 113 const ee = (fxT + fxW*fxV - 1) / (fxW * fxV) 114 const dd = ee * fxV 115 const ll = dd * fxW 116 117 L := make([]int8, ll+1) 118 mLSBRecoding(L[:], scalar) 119 S := &pointR3{} 120 P.SetIdentity() 121 for ii := ee - 1; ii >= 0; ii-- { 122 P.double() 123 for j := 0; j < fxV; j++ { 124 dig := L[fxW*dd-j*ee+ii-ee] 125 for i := (fxW-1)*dd - j*ee + ii - ee; i >= (2*dd - j*ee + ii - ee); i = i - dd { 126 dig = 2*dig + L[i] 127 } 128 idx := absolute(int32(dig)) 129 sig := L[dd-j*ee+ii-ee] 130 Tabj := &tabSign[fxV-j-1] 131 for k := 0; k < fx2w1; k++ { 132 S.cmov(&Tabj[k], subtle.ConstantTimeEq(int32(k), idx)) 133 } 134 S.cneg(subtle.ConstantTimeEq(int32(sig), -1)) 135 P.mixAdd(S) 136 } 137 } 138 } 139 140 const ( 141 omegaFix = 7 142 omegaVar = 5 143 ) 144 145 // doubleMult returns P=mG+nQ. 146 func (P *pointR1) doubleMult(Q *pointR1, m, n []byte) { 147 nafFix := math.OmegaNAF(conv.BytesLe2BigInt(m), omegaFix) 148 nafVar := math.OmegaNAF(conv.BytesLe2BigInt(n), omegaVar) 149 150 if len(nafFix) > len(nafVar) { 151 nafVar = append(nafVar, make([]int32, len(nafFix)-len(nafVar))...) 152 } else if len(nafFix) < len(nafVar) { 153 nafFix = append(nafFix, make([]int32, len(nafVar)-len(nafFix))...) 154 } 155 156 var TabQ [1 << (omegaVar - 2)]pointR2 157 Q.oddMultiples(TabQ[:]) 158 P.SetIdentity() 159 for i := len(nafFix) - 1; i >= 0; i-- { 160 P.double() 161 // Generator point 162 if nafFix[i] != 0 { 163 idxM := absolute(nafFix[i]) >> 1 164 R := tabVerif[idxM] 165 if nafFix[i] < 0 { 166 R.neg() 167 } 168 P.mixAdd(&R) 169 } 170 // Variable input point 171 if nafVar[i] != 0 { 172 idxN := absolute(nafVar[i]) >> 1 173 S := TabQ[idxN] 174 if nafVar[i] < 0 { 175 S.neg() 176 } 177 P.add(&S) 178 } 179 } 180 }