1 package org.bouncycastle.math.ec; 2 3 import java.math.BigInteger; 4 5 public abstract class WNafUtil 6 { 7 private static int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 }; 8 generateCompactNaf(BigInteger k)9 public static int[] generateCompactNaf(BigInteger k) 10 { 11 if ((k.bitLength() >>> 16) != 0) 12 { 13 throw new IllegalArgumentException("'k' must have bitlength < 2^16"); 14 } 15 16 BigInteger _3k = k.shiftLeft(1).add(k); 17 18 int digits = _3k.bitLength() - 1; 19 int[] naf = new int[(digits + 1) >> 1]; 20 21 int length = 0, zeroes = 0; 22 for (int i = 1; i <= digits; ++i) 23 { 24 boolean _3kBit = _3k.testBit(i); 25 boolean kBit = k.testBit(i); 26 27 if (_3kBit == kBit) 28 { 29 ++zeroes; 30 } 31 else 32 { 33 int digit = kBit ? -1 : 1; 34 naf[length++] = (digit << 16) | zeroes; 35 zeroes = 0; 36 } 37 } 38 39 if (naf.length > length) 40 { 41 naf = trim(naf, length); 42 } 43 44 return naf; 45 } 46 generateCompactWindowNaf(int width, BigInteger k)47 public static int[] generateCompactWindowNaf(int width, BigInteger k) 48 { 49 if (width == 2) 50 { 51 return generateCompactNaf(k); 52 } 53 54 if (width < 2 || width > 16) 55 { 56 throw new IllegalArgumentException("'width' must be in the range [2, 16]"); 57 } 58 if ((k.bitLength() >>> 16) != 0) 59 { 60 throw new IllegalArgumentException("'k' must have bitlength < 2^16"); 61 } 62 63 int[] wnaf = new int[k.bitLength() / width + 1]; 64 65 // 2^width and a mask and sign bit set accordingly 66 int pow2 = 1 << width; 67 int mask = pow2 - 1; 68 int sign = pow2 >>> 1; 69 70 boolean carry = false; 71 int length = 0, pos = 0; 72 73 while (pos <= k.bitLength()) 74 { 75 if (k.testBit(pos) == carry) 76 { 77 ++pos; 78 continue; 79 } 80 81 k = k.shiftRight(pos); 82 83 int digit = k.intValue() & mask; 84 if (carry) 85 { 86 ++digit; 87 } 88 89 carry = (digit & sign) != 0; 90 if (carry) 91 { 92 digit -= pow2; 93 } 94 95 int zeroes = length > 0 ? pos - 1 : pos; 96 wnaf[length++] = (digit << 16) | zeroes; 97 pos = width; 98 } 99 100 // Reduce the WNAF array to its actual length 101 if (wnaf.length > length) 102 { 103 wnaf = trim(wnaf, length); 104 } 105 106 return wnaf; 107 } 108 generateJSF(BigInteger g, BigInteger h)109 public static byte[] generateJSF(BigInteger g, BigInteger h) 110 { 111 int digits = Math.max(g.bitLength(), h.bitLength()) + 1; 112 byte[] jsf = new byte[digits]; 113 114 BigInteger k0 = g, k1 = h; 115 int j = 0, d0 = 0, d1 = 0; 116 117 while (k0.signum() > 0 || k1.signum() > 0 || d0 > 0 || d1 > 0) 118 { 119 int n0 = (k0.intValue() + d0) & 7, n1 = (k1.intValue() + d1) & 7; 120 121 int u0 = n0 & 1; 122 if (u0 != 0) 123 { 124 u0 -= (n0 & 2); 125 if ((n0 + u0) == 4 && (n1 & 3) == 2) 126 { 127 u0 = -u0; 128 } 129 } 130 131 int u1 = n1 & 1; 132 if (u1 != 0) 133 { 134 u1 -= (n1 & 2); 135 if ((n1 + u1) == 4 && (n0 & 3) == 2) 136 { 137 u1 = -u1; 138 } 139 } 140 141 if ((d0 << 1) == 1 + u0) 142 { 143 d0 = 1 - d0; 144 } 145 if ((d1 << 1) == 1 + u1) 146 { 147 d1 = 1 - d1; 148 } 149 150 k0 = k0.shiftRight(1); 151 k1 = k1.shiftRight(1); 152 153 jsf[j++] = (byte)((u0 << 4) | (u1 & 0xF)); 154 } 155 156 // Reduce the JSF array to its actual length 157 if (jsf.length > j) 158 { 159 jsf = trim(jsf, j); 160 } 161 162 return jsf; 163 } 164 generateNaf(BigInteger k)165 public static byte[] generateNaf(BigInteger k) 166 { 167 BigInteger _3k = k.shiftLeft(1).add(k); 168 169 int digits = _3k.bitLength() - 1; 170 byte[] naf = new byte[digits]; 171 172 for (int i = 1; i <= digits; ++i) 173 { 174 boolean _3kBit = _3k.testBit(i); 175 boolean kBit = k.testBit(i); 176 177 naf[i - 1] = (byte)(_3kBit == kBit ? 0 : kBit ? -1 : 1); 178 } 179 180 return naf; 181 } 182 183 /** 184 * Computes the Window NAF (non-adjacent Form) of an integer. 185 * @param width The width <code>w</code> of the Window NAF. The width is 186 * defined as the minimal number <code>w</code>, such that for any 187 * <code>w</code> consecutive digits in the resulting representation, at 188 * most one is non-zero. 189 * @param k The integer of which the Window NAF is computed. 190 * @return The Window NAF of the given width, such that the following holds: 191 * <code>k = ∑<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup> 192 * </code>, where the <code>k<sub>i</sub></code> denote the elements of the 193 * returned <code>byte[]</code>. 194 */ generateWindowNaf(int width, BigInteger k)195 public static byte[] generateWindowNaf(int width, BigInteger k) 196 { 197 if (width == 2) 198 { 199 return generateNaf(k); 200 } 201 202 if (width < 2 || width > 8) 203 { 204 throw new IllegalArgumentException("'width' must be in the range [2, 8]"); 205 } 206 207 byte[] wnaf = new byte[k.bitLength() + 1]; 208 209 // 2^width and a mask and sign bit set accordingly 210 int pow2 = 1 << width; 211 int mask = pow2 - 1; 212 int sign = pow2 >>> 1; 213 214 boolean carry = false; 215 int length = 0, pos = 0; 216 217 while (pos <= k.bitLength()) 218 { 219 if (k.testBit(pos) == carry) 220 { 221 ++pos; 222 continue; 223 } 224 225 k = k.shiftRight(pos); 226 227 int digit = k.intValue() & mask; 228 if (carry) 229 { 230 ++digit; 231 } 232 233 carry = (digit & sign) != 0; 234 if (carry) 235 { 236 digit -= pow2; 237 } 238 239 length += (length > 0) ? pos - 1 : pos; 240 wnaf[length++] = (byte)digit; 241 pos = width; 242 } 243 244 // Reduce the WNAF array to its actual length 245 if (wnaf.length > length) 246 { 247 wnaf = trim(wnaf, length); 248 } 249 250 return wnaf; 251 } 252 getWNafPreCompInfo(PreCompInfo preCompInfo)253 public static WNafPreCompInfo getWNafPreCompInfo(PreCompInfo preCompInfo) 254 { 255 if ((preCompInfo != null) && (preCompInfo instanceof WNafPreCompInfo)) 256 { 257 return (WNafPreCompInfo)preCompInfo; 258 } 259 260 return new WNafPreCompInfo(); 261 } 262 263 /** 264 * Determine window width to use for a scalar multiplication of the given size. 265 * 266 * @param bits the bit-length of the scalar to multiply by 267 * @return the window size to use 268 */ getWindowSize(int bits)269 public static int getWindowSize(int bits) 270 { 271 return getWindowSize(bits, DEFAULT_WINDOW_SIZE_CUTOFFS); 272 } 273 274 /** 275 * Determine window width to use for a scalar multiplication of the given size. 276 * 277 * @param bits the bit-length of the scalar to multiply by 278 * @param windowSizeCutoffs a monotonically increasing list of bit sizes at which to increment the window width 279 * @return the window size to use 280 */ getWindowSize(int bits, int[] windowSizeCutoffs)281 public static int getWindowSize(int bits, int[] windowSizeCutoffs) 282 { 283 int w = 0; 284 for (; w < windowSizeCutoffs.length; ++w) 285 { 286 if (bits < windowSizeCutoffs[w]) 287 { 288 break; 289 } 290 } 291 return w + 2; 292 } 293 precompute(ECPoint p, int width, boolean includeNegated)294 public static WNafPreCompInfo precompute(ECPoint p, int width, boolean includeNegated) 295 { 296 ECCurve c = p.getCurve(); 297 WNafPreCompInfo wnafPreCompInfo = getWNafPreCompInfo(c.getPreCompInfo(p)); 298 299 ECPoint[] preComp = wnafPreCompInfo.getPreComp(); 300 if (preComp == null) 301 { 302 preComp = new ECPoint[]{ p }; 303 } 304 305 int preCompLen = preComp.length; 306 int reqPreCompLen = 1 << Math.max(0, width - 2); 307 308 if (preCompLen < reqPreCompLen) 309 { 310 ECPoint twiceP = wnafPreCompInfo.getTwiceP(); 311 if (twiceP == null) 312 { 313 twiceP = preComp[0].twice().normalize(); 314 wnafPreCompInfo.setTwiceP(twiceP); 315 } 316 317 preComp = resizeTable(preComp, reqPreCompLen); 318 319 /* 320 * TODO Okeya/Sakurai paper has precomputation trick and "Montgomery's Trick" to speed this up. 321 * Also, co-Z arithmetic could avoid the subsequent normalization too. 322 */ 323 for (int i = preCompLen; i < reqPreCompLen; i++) 324 { 325 /* 326 * Compute the new ECPoints for the precomputation array. The values 1, 3, 5, ..., 327 * 2^(width-1)-1 times p are computed 328 */ 329 preComp[i] = twiceP.add(preComp[i - 1]); 330 } 331 332 /* 333 * Having oft-used operands in affine form makes operations faster. 334 */ 335 c.normalizeAll(preComp); 336 } 337 338 wnafPreCompInfo.setPreComp(preComp); 339 340 if (includeNegated) 341 { 342 ECPoint[] preCompNeg = wnafPreCompInfo.getPreCompNeg(); 343 344 int pos; 345 if (preCompNeg == null) 346 { 347 pos = 0; 348 preCompNeg = new ECPoint[reqPreCompLen]; 349 } 350 else 351 { 352 pos = preCompNeg.length; 353 if (pos < reqPreCompLen) 354 { 355 preCompNeg = resizeTable(preCompNeg, reqPreCompLen); 356 } 357 } 358 359 while (pos < reqPreCompLen) 360 { 361 preCompNeg[pos] = preComp[pos].negate(); 362 ++pos; 363 } 364 365 wnafPreCompInfo.setPreCompNeg(preCompNeg); 366 } 367 368 c.setPreCompInfo(p, wnafPreCompInfo); 369 370 return wnafPreCompInfo; 371 } 372 trim(byte[] a, int length)373 private static byte[] trim(byte[] a, int length) 374 { 375 byte[] result = new byte[length]; 376 System.arraycopy(a, 0, result, 0, result.length); 377 return result; 378 } 379 trim(int[] a, int length)380 private static int[] trim(int[] a, int length) 381 { 382 int[] result = new int[length]; 383 System.arraycopy(a, 0, result, 0, result.length); 384 return result; 385 } 386 resizeTable(ECPoint[] a, int length)387 private static ECPoint[] resizeTable(ECPoint[] a, int length) 388 { 389 ECPoint[] result = new ECPoint[length]; 390 System.arraycopy(a, 0, result, 0, a.length); 391 return result; 392 } 393 } 394