package org.bouncycastle.math.ec.test; import java.math.BigInteger; import java.security.SecureRandom; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import junit.framework.TestCase; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.x9.ECNamedCurveTable; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Times; /** * Compares the performance of the the window NAF point multiplication against conventional point * multiplication. */ public class ECPointPerformanceTest extends TestCase { static final int MILLIS_PER_ROUND = 200; static final int MILLIS_WARMUP = 1000; static final int MULTS_PER_CHECK = 16; static final int NUM_ROUNDS = 10; private static String[] COORD_NAMES = new String[]{ "AFFINE", "HOMOGENEOUS", "JACOBIAN", "JACOBIAN-CHUDNOVSKY", "JACOBIAN-MODIFIED", "LAMBDA-AFFINE", "LAMBDA-PROJECTIVE", "SKEWED" }; private void randMult(String curveName) throws Exception { X9ECParameters spec = ECNamedCurveTable.getByName(curveName); if (spec != null) { randMult(curveName, spec); } spec = CustomNamedCurves.getByName(curveName); if (spec != null) { randMult(curveName + " (custom)", spec); } } private void randMult(String label, X9ECParameters spec) throws Exception { ECCurve C = spec.getCurve(); ECPoint G = (ECPoint)spec.getG(); BigInteger n = spec.getN(); SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN"); random.setSeed(System.currentTimeMillis()); System.out.println(label); int[] coords = ECCurve.getAllCoordinateSystems(); for (int i = 0; i < coords.length; ++i) { int coord = coords[i]; if (C.supportsCoordinateSystem(coord)) { ECCurve c = C; ECPoint g = G; boolean defaultCoord = (c.getCoordinateSystem() == coord); if (!defaultCoord) { c = C.configure().setCoordinateSystem(coord).create(); g = c.importPoint(G); } double avgRate = randMult(random, g, n); String coordName = COORD_NAMES[coord]; StringBuffer sb = new StringBuffer(); sb.append(" "); sb.append(defaultCoord ? '*' : ' '); sb.append(coordName); for (int j = sb.length(); j < 30; ++j) { sb.append(' '); } sb.append(": "); sb.append(avgRate); sb.append(" mults/sec"); for (int j = sb.length(); j < 64; ++j) { sb.append(' '); } sb.append('('); sb.append(1000.0 / avgRate); sb.append(" millis/mult)"); System.out.println(sb.toString()); } } } private double randMult(SecureRandom random, ECPoint g, BigInteger n) throws Exception { BigInteger[] ks = new BigInteger[128]; for (int i = 0; i < ks.length; ++i) { ks[i] = new BigInteger(n.bitLength() - 1, random); } int ki = 0; ECPoint p = g; { long startTime = Times.nanoTime(); long goalTime = startTime + 1000000L * MILLIS_WARMUP; do { BigInteger k = ks[ki]; p = g.multiply(k); if ((ki & 1) != 0) { g = p; } if (++ki == ks.length) { ki = 0; } } while (Times.nanoTime() < goalTime); } double minRate = Double.MAX_VALUE, maxRate = Double.MIN_VALUE, totalRate = 0.0; for (int i = 1; i <= NUM_ROUNDS; i++) { long startTime = Times.nanoTime(); long goalTime = startTime + 1000000L * MILLIS_PER_ROUND; long count = 0, endTime; do { ++count; for (int j = 0; j < MULTS_PER_CHECK; ++j) { BigInteger k = ks[ki]; p = g.multiply(k); if ((ki & 1) != 0) { g = p; } if (++ki == ks.length) { ki = 0; } } endTime = Times.nanoTime(); } while (endTime < goalTime); double roundElapsed = (double)(endTime - startTime); double roundRate = count * MULTS_PER_CHECK * 1000000000L / roundElapsed; minRate = Math.min(minRate, roundRate); maxRate = Math.max(maxRate, roundRate); totalRate += roundRate; } return (totalRate - minRate - maxRate) / (NUM_ROUNDS - 2); } public void testMultiply() throws Exception { SortedSet names = new TreeSet(AllTests.enumToList(ECNamedCurveTable.getNames())); names.addAll(AllTests.enumToList(CustomNamedCurves.getNames())); Set oids = new HashSet(); Iterator it = names.iterator(); while (it.hasNext()) { String name = (String)it.next(); ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name); if (oid == null) { oid = CustomNamedCurves.getOID(name); } if (oid != null && !oids.add(oid)) { continue; } randMult(name); } } }