1 package org.bouncycastle.math.ec; 2 3 import java.math.BigInteger; 4 5 public abstract class WNafUtil 6 { 7 public static final String PRECOMP_NAME = "bc_wnaf"; 8 9 private static final int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 }; 10 11 private static final byte[] EMPTY_BYTES = new byte[0]; 12 private static final int[] EMPTY_INTS = new int[0]; 13 private static final ECPoint[] EMPTY_POINTS = new ECPoint[0]; 14 generateCompactNaf(BigInteger k)15 public static int[] generateCompactNaf(BigInteger k) 16 { 17 if ((k.bitLength() >>> 16) != 0) 18 { 19 throw new IllegalArgumentException("'k' must have bitlength < 2^16"); 20 } 21 if (k.signum() == 0) 22 { 23 return EMPTY_INTS; 24 } 25 26 BigInteger _3k = k.shiftLeft(1).add(k); 27 28 int bits = _3k.bitLength(); 29 int[] naf = new int[bits >> 1]; 30 31 BigInteger diff = _3k.xor(k); 32 33 int highBit = bits - 1, length = 0, zeroes = 0; 34 for (int i = 1; i < highBit; ++i) 35 { 36 if (!diff.testBit(i)) 37 { 38 ++zeroes; 39 continue; 40 } 41 42 int digit = k.testBit(i) ? -1 : 1; 43 naf[length++] = (digit << 16) | zeroes; 44 zeroes = 1; 45 ++i; 46 } 47 48 naf[length++] = (1 << 16) | zeroes; 49 50 if (naf.length > length) 51 { 52 naf = trim(naf, length); 53 } 54 55 return naf; 56 } 57 generateCompactWindowNaf(int width, BigInteger k)58 public static int[] generateCompactWindowNaf(int width, BigInteger k) 59 { 60 if (width == 2) 61 { 62 return generateCompactNaf(k); 63 } 64 65 if (width < 2 || width > 16) 66 { 67 throw new IllegalArgumentException("'width' must be in the range [2, 16]"); 68 } 69 if ((k.bitLength() >>> 16) != 0) 70 { 71 throw new IllegalArgumentException("'k' must have bitlength < 2^16"); 72 } 73 if (k.signum() == 0) 74 { 75 return EMPTY_INTS; 76 } 77 78 int[] wnaf = new int[k.bitLength() / width + 1]; 79 80 // 2^width and a mask and sign bit set accordingly 81 int pow2 = 1 << width; 82 int mask = pow2 - 1; 83 int sign = pow2 >>> 1; 84 85 boolean carry = false; 86 int length = 0, pos = 0; 87 88 while (pos <= k.bitLength()) 89 { 90 if (k.testBit(pos) == carry) 91 { 92 ++pos; 93 continue; 94 } 95 96 k = k.shiftRight(pos); 97 98 int digit = k.intValue() & mask; 99 if (carry) 100 { 101 ++digit; 102 } 103 104 carry = (digit & sign) != 0; 105 if (carry) 106 { 107 digit -= pow2; 108 } 109 110 int zeroes = length > 0 ? pos - 1 : pos; 111 wnaf[length++] = (digit << 16) | zeroes; 112 pos = width; 113 } 114 115 // Reduce the WNAF array to its actual length 116 if (wnaf.length > length) 117 { 118 wnaf = trim(wnaf, length); 119 } 120 121 return wnaf; 122 } 123 generateJSF(BigInteger g, BigInteger h)124 public static byte[] generateJSF(BigInteger g, BigInteger h) 125 { 126 int digits = Math.max(g.bitLength(), h.bitLength()) + 1; 127 byte[] jsf = new byte[digits]; 128 129 BigInteger k0 = g, k1 = h; 130 int j = 0, d0 = 0, d1 = 0; 131 132 int offset = 0; 133 while ((d0 | d1) != 0 || k0.bitLength() > offset || k1.bitLength() > offset) 134 { 135 int n0 = ((k0.intValue() >>> offset) + d0) & 7, n1 = ((k1.intValue() >>> offset) + d1) & 7; 136 137 int u0 = n0 & 1; 138 if (u0 != 0) 139 { 140 u0 -= (n0 & 2); 141 if ((n0 + u0) == 4 && (n1 & 3) == 2) 142 { 143 u0 = -u0; 144 } 145 } 146 147 int u1 = n1 & 1; 148 if (u1 != 0) 149 { 150 u1 -= (n1 & 2); 151 if ((n1 + u1) == 4 && (n0 & 3) == 2) 152 { 153 u1 = -u1; 154 } 155 } 156 157 if ((d0 << 1) == 1 + u0) 158 { 159 d0 ^= 1; 160 } 161 if ((d1 << 1) == 1 + u1) 162 { 163 d1 ^= 1; 164 } 165 166 if (++offset == 30) 167 { 168 offset = 0; 169 k0 = k0.shiftRight(30); 170 k1 = k1.shiftRight(30); 171 } 172 173 jsf[j++] = (byte)((u0 << 4) | (u1 & 0xF)); 174 } 175 176 // Reduce the JSF array to its actual length 177 if (jsf.length > j) 178 { 179 jsf = trim(jsf, j); 180 } 181 182 return jsf; 183 } 184 generateNaf(BigInteger k)185 public static byte[] generateNaf(BigInteger k) 186 { 187 if (k.signum() == 0) 188 { 189 return EMPTY_BYTES; 190 } 191 192 BigInteger _3k = k.shiftLeft(1).add(k); 193 194 int digits = _3k.bitLength() - 1; 195 byte[] naf = new byte[digits]; 196 197 BigInteger diff = _3k.xor(k); 198 199 for (int i = 1; i < digits; ++i) 200 { 201 if (diff.testBit(i)) 202 { 203 naf[i - 1] = (byte)(k.testBit(i) ? -1 : 1); 204 ++i; 205 } 206 } 207 208 naf[digits - 1] = 1; 209 210 return naf; 211 } 212 213 /** 214 * Computes the Window NAF (non-adjacent Form) of an integer. 215 * @param width The width <code>w</code> of the Window NAF. The width is 216 * defined as the minimal number <code>w</code>, such that for any 217 * <code>w</code> consecutive digits in the resulting representation, at 218 * most one is non-zero. 219 * @param k The integer of which the Window NAF is computed. 220 * @return The Window NAF of the given width, such that the following holds: 221 * <code>k = ∑<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup> 222 * </code>, where the <code>k<sub>i</sub></code> denote the elements of the 223 * returned <code>byte[]</code>. 224 */ generateWindowNaf(int width, BigInteger k)225 public static byte[] generateWindowNaf(int width, BigInteger k) 226 { 227 if (width == 2) 228 { 229 return generateNaf(k); 230 } 231 232 if (width < 2 || width > 8) 233 { 234 throw new IllegalArgumentException("'width' must be in the range [2, 8]"); 235 } 236 if (k.signum() == 0) 237 { 238 return EMPTY_BYTES; 239 } 240 241 byte[] wnaf = new byte[k.bitLength() + 1]; 242 243 // 2^width and a mask and sign bit set accordingly 244 int pow2 = 1 << width; 245 int mask = pow2 - 1; 246 int sign = pow2 >>> 1; 247 248 boolean carry = false; 249 int length = 0, pos = 0; 250 251 while (pos <= k.bitLength()) 252 { 253 if (k.testBit(pos) == carry) 254 { 255 ++pos; 256 continue; 257 } 258 259 k = k.shiftRight(pos); 260 261 int digit = k.intValue() & mask; 262 if (carry) 263 { 264 ++digit; 265 } 266 267 carry = (digit & sign) != 0; 268 if (carry) 269 { 270 digit -= pow2; 271 } 272 273 length += (length > 0) ? pos - 1 : pos; 274 wnaf[length++] = (byte)digit; 275 pos = width; 276 } 277 278 // Reduce the WNAF array to its actual length 279 if (wnaf.length > length) 280 { 281 wnaf = trim(wnaf, length); 282 } 283 284 return wnaf; 285 } 286 getNafWeight(BigInteger k)287 public static int getNafWeight(BigInteger k) 288 { 289 if (k.signum() == 0) 290 { 291 return 0; 292 } 293 294 BigInteger _3k = k.shiftLeft(1).add(k); 295 BigInteger diff = _3k.xor(k); 296 297 return diff.bitCount(); 298 } 299 getWNafPreCompInfo(ECPoint p)300 public static WNafPreCompInfo getWNafPreCompInfo(ECPoint p) 301 { 302 return getWNafPreCompInfo(p.getCurve().getPreCompInfo(p, PRECOMP_NAME)); 303 } 304 getWNafPreCompInfo(PreCompInfo preCompInfo)305 public static WNafPreCompInfo getWNafPreCompInfo(PreCompInfo preCompInfo) 306 { 307 if ((preCompInfo != null) && (preCompInfo instanceof WNafPreCompInfo)) 308 { 309 return (WNafPreCompInfo)preCompInfo; 310 } 311 312 return new WNafPreCompInfo(); 313 } 314 315 /** 316 * Determine window width to use for a scalar multiplication of the given size. 317 * 318 * @param bits the bit-length of the scalar to multiply by 319 * @return the window size to use 320 */ getWindowSize(int bits)321 public static int getWindowSize(int bits) 322 { 323 return getWindowSize(bits, DEFAULT_WINDOW_SIZE_CUTOFFS); 324 } 325 326 /** 327 * Determine window width to use for a scalar multiplication of the given size. 328 * 329 * @param bits the bit-length of the scalar to multiply by 330 * @param windowSizeCutoffs a monotonically increasing list of bit sizes at which to increment the window width 331 * @return the window size to use 332 */ getWindowSize(int bits, int[] windowSizeCutoffs)333 public static int getWindowSize(int bits, int[] windowSizeCutoffs) 334 { 335 int w = 0; 336 for (; w < windowSizeCutoffs.length; ++w) 337 { 338 if (bits < windowSizeCutoffs[w]) 339 { 340 break; 341 } 342 } 343 return w + 2; 344 } 345 mapPointWithPrecomp(ECPoint p, int width, boolean includeNegated, ECPointMap pointMap)346 public static ECPoint mapPointWithPrecomp(ECPoint p, int width, boolean includeNegated, 347 ECPointMap pointMap) 348 { 349 ECCurve c = p.getCurve(); 350 WNafPreCompInfo wnafPreCompP = precompute(p, width, includeNegated); 351 352 ECPoint q = pointMap.map(p); 353 WNafPreCompInfo wnafPreCompQ = getWNafPreCompInfo(c.getPreCompInfo(q, PRECOMP_NAME)); 354 355 ECPoint twiceP = wnafPreCompP.getTwice(); 356 if (twiceP != null) 357 { 358 ECPoint twiceQ = pointMap.map(twiceP); 359 wnafPreCompQ.setTwice(twiceQ); 360 } 361 362 ECPoint[] preCompP = wnafPreCompP.getPreComp(); 363 ECPoint[] preCompQ = new ECPoint[preCompP.length]; 364 for (int i = 0; i < preCompP.length; ++i) 365 { 366 preCompQ[i] = pointMap.map(preCompP[i]); 367 } 368 wnafPreCompQ.setPreComp(preCompQ); 369 370 if (includeNegated) 371 { 372 ECPoint[] preCompNegQ = new ECPoint[preCompQ.length]; 373 for (int i = 0; i < preCompNegQ.length; ++i) 374 { 375 preCompNegQ[i] = preCompQ[i].negate(); 376 } 377 wnafPreCompQ.setPreCompNeg(preCompNegQ); 378 } 379 380 c.setPreCompInfo(q, PRECOMP_NAME, wnafPreCompQ); 381 382 return q; 383 } 384 precompute(ECPoint p, int width, boolean includeNegated)385 public static WNafPreCompInfo precompute(ECPoint p, int width, boolean includeNegated) 386 { 387 ECCurve c = p.getCurve(); 388 WNafPreCompInfo wnafPreCompInfo = getWNafPreCompInfo(c.getPreCompInfo(p, PRECOMP_NAME)); 389 390 int iniPreCompLen = 0, reqPreCompLen = 1 << Math.max(0, width - 2); 391 392 ECPoint[] preComp = wnafPreCompInfo.getPreComp(); 393 if (preComp == null) 394 { 395 preComp = EMPTY_POINTS; 396 } 397 else 398 { 399 iniPreCompLen = preComp.length; 400 } 401 402 if (iniPreCompLen < reqPreCompLen) 403 { 404 preComp = resizeTable(preComp, reqPreCompLen); 405 406 if (reqPreCompLen == 1) 407 { 408 preComp[0] = p.normalize(); 409 } 410 else 411 { 412 int curPreCompLen = iniPreCompLen; 413 if (curPreCompLen == 0) 414 { 415 preComp[0] = p; 416 curPreCompLen = 1; 417 } 418 419 ECFieldElement iso = null; 420 421 if (reqPreCompLen == 2) 422 { 423 preComp[1] = p.threeTimes(); 424 } 425 else 426 { 427 ECPoint twiceP = wnafPreCompInfo.getTwice(), last = preComp[curPreCompLen - 1]; 428 if (twiceP == null) 429 { 430 twiceP = preComp[0].twice(); 431 wnafPreCompInfo.setTwice(twiceP); 432 433 /* 434 * For Fp curves with Jacobian projective coordinates, use a (quasi-)isomorphism 435 * where 'twiceP' is "affine", so that the subsequent additions are cheaper. This 436 * also requires scaling the initial point's X, Y coordinates, and reversing the 437 * isomorphism as part of the subsequent normalization. 438 * 439 * NOTE: The correctness of this optimization depends on: 440 * 1) additions do not use the curve's A, B coefficients. 441 * 2) no special cases (i.e. Q +/- Q) when calculating 1P, 3P, 5P, ... 442 */ 443 if (!twiceP.isInfinity() && ECAlgorithms.isFpCurve(c) && c.getFieldSize() >= 64) 444 { 445 switch (c.getCoordinateSystem()) 446 { 447 case ECCurve.COORD_JACOBIAN: 448 case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: 449 case ECCurve.COORD_JACOBIAN_MODIFIED: 450 { 451 iso = twiceP.getZCoord(0); 452 twiceP = c.createPoint(twiceP.getXCoord().toBigInteger(), twiceP.getYCoord() 453 .toBigInteger()); 454 455 ECFieldElement iso2 = iso.square(), iso3 = iso2.multiply(iso); 456 last = last.scaleX(iso2).scaleY(iso3); 457 458 if (iniPreCompLen == 0) 459 { 460 preComp[0] = last; 461 } 462 break; 463 } 464 } 465 } 466 } 467 468 while (curPreCompLen < reqPreCompLen) 469 { 470 /* 471 * Compute the new ECPoints for the precomputation array. The values 1, 3, 472 * 5, ..., 2^(width-1)-1 times p are computed 473 */ 474 preComp[curPreCompLen++] = last = last.add(twiceP); 475 } 476 } 477 478 /* 479 * Having oft-used operands in affine form makes operations faster. 480 */ 481 c.normalizeAll(preComp, iniPreCompLen, reqPreCompLen - iniPreCompLen, iso); 482 } 483 } 484 485 wnafPreCompInfo.setPreComp(preComp); 486 487 if (includeNegated) 488 { 489 ECPoint[] preCompNeg = wnafPreCompInfo.getPreCompNeg(); 490 491 int pos; 492 if (preCompNeg == null) 493 { 494 pos = 0; 495 preCompNeg = new ECPoint[reqPreCompLen]; 496 } 497 else 498 { 499 pos = preCompNeg.length; 500 if (pos < reqPreCompLen) 501 { 502 preCompNeg = resizeTable(preCompNeg, reqPreCompLen); 503 } 504 } 505 506 while (pos < reqPreCompLen) 507 { 508 preCompNeg[pos] = preComp[pos].negate(); 509 ++pos; 510 } 511 512 wnafPreCompInfo.setPreCompNeg(preCompNeg); 513 } 514 515 c.setPreCompInfo(p, PRECOMP_NAME, wnafPreCompInfo); 516 517 return wnafPreCompInfo; 518 } 519 trim(byte[] a, int length)520 private static byte[] trim(byte[] a, int length) 521 { 522 byte[] result = new byte[length]; 523 System.arraycopy(a, 0, result, 0, result.length); 524 return result; 525 } 526 trim(int[] a, int length)527 private static int[] trim(int[] a, int length) 528 { 529 int[] result = new int[length]; 530 System.arraycopy(a, 0, result, 0, result.length); 531 return result; 532 } 533 resizeTable(ECPoint[] a, int length)534 private static ECPoint[] resizeTable(ECPoint[] a, int length) 535 { 536 ECPoint[] result = new ECPoint[length]; 537 System.arraycopy(a, 0, result, 0, a.length); 538 return result; 539 } 540 } 541