From 45f14e5e7c53bf869879edb3fadcb3159b341f02 Mon Sep 17 00:00:00 2001 From: Jack Tjaden Date: Mon, 13 Jan 2025 11:16:35 -0700 Subject: [PATCH 1/5] JCE: Implements RSA key gen benchmark --- examples/provider/CryptoBenchmark.java | 617 +++++++++++++++---------- 1 file changed, 361 insertions(+), 256 deletions(-) diff --git a/examples/provider/CryptoBenchmark.java b/examples/provider/CryptoBenchmark.java index 1253450..c940758 100644 --- a/examples/provider/CryptoBenchmark.java +++ b/examples/provider/CryptoBenchmark.java @@ -8,294 +8,399 @@ import java.security.Security; import java.security.spec.AlgorithmParameterSpec; import java.util.*; +import java.security.KeyPair; +import java.security.KeyPairGenerator; import com.wolfssl.provider.jce.WolfCryptProvider; import com.wolfssl.wolfcrypt.FeatureDetect; public class CryptoBenchmark { - /* Constants for benchmark configuration */ - private static final int WARMUP_ITERATIONS = 5; - private static final int TEST_ITERATIONS = 5; - private static final int DATA_SIZE = 1024 * 1024; - private static final int AES_BLOCK_SIZE = 16; - private static final int DES3_BLOCK_SIZE = 8; - private static final int GCM_TAG_LENGTH = 128; - - /* Class to store benchmark results */ - private static class BenchmarkResult { - /* Result fields */ - String provider; - String operation; - double throughput; - - /* Constructor */ - BenchmarkResult(String provider, String operation, double throughput) { - this.provider = provider; - this.operation = operation; - this.throughput = throughput; + /* Constants for benchmark configuration */ + private static final int WARMUP_ITERATIONS = 5; + private static final int TEST_ITERATIONS = 5; + private static final int DATA_SIZE = 1024 * 1024; + private static final int AES_BLOCK_SIZE = 16; + private static final int DES3_BLOCK_SIZE = 8; + private static final int GCM_TAG_LENGTH = 128; + private static final int[] RSA_KEY_SIZES = {2048, 3072, 4096}; + private static final int RSA_MIN_TIME_SECONDS = 1; /* minimum time to run each test */ + private static final int SMALL_MESSAGE_SIZE = 32; /* small message size for RSA ops */ + + /* Class to store benchmark results */ + private static class BenchmarkResult { + /* Result fields */ + String provider; + String operation; + double throughput; + + /* Constructor */ + BenchmarkResult(String provider, String operation, double throughput) { + this.provider = provider; + this.operation = operation; + this.throughput = throughput; + } + } + + /* List to store all benchmark results */ + private static final List results = new ArrayList<>(); + + /* Static AES key buffer */ + private static final byte[] STATIC_AES_KEY = new byte[] { + (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, + (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, + (byte)0xfe, (byte)0xde, (byte)0xba, (byte)0x98, + (byte)0x76, (byte)0x54, (byte)0x32, (byte)0x10, + (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, + (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, + (byte)0xf0, (byte)0xf1, (byte)0xf2, (byte)0xf3, + (byte)0xf4, (byte)0xf5, (byte)0xf6, (byte)0xf7 + }; + + /* Static DESede (Triple DES) key buffer */ + private static final byte[] STATIC_DES3_KEY = new byte[] { + (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, + (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, + (byte)0xfe, (byte)0xdc, (byte)0xba, (byte)0x98, + (byte)0x76, (byte)0x54, (byte)0x32, (byte)0x10, + (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, + (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67 + }; + + private static byte[] generateTestData(int size) { + return new byte[size]; + } + + private static void printProviderInfo(Provider provider) { + System.out.printf("%s version: %.1f%n", provider.getName(), provider.getVersion()); + } + + private static void printDeltaTable() { + /* Variables for table generation */ + Map> groupedResults; + String operation; + Map providerResults; + double wolfSpeed; + String provider; + double otherSpeed; + double deltaValue; + double deltaPercent; + boolean isRSAOperation; + + System.out.println("\nPerformance Delta (compared to wolfJCE)"); + System.out.println("-----------------------------------------------------------------------------"); + System.out.println("| Operation | Provider | Delta | Delta |"); + System.out.println("| | | Value* | (%) |"); + System.out.println("|------------------------------------------|----------|----------|----------|"); + + /* Group results by operation */ + groupedResults = new HashMap<>(); + for (BenchmarkResult result : results) { + groupedResults + .computeIfAbsent(result.operation, k -> new HashMap<>()) + .put(result.provider, result.throughput); + } + + /* Calculate and print deltas */ + for (Map.Entry> entry : groupedResults.entrySet()) { + operation = entry.getKey(); + providerResults = entry.getValue(); + wolfSpeed = providerResults.getOrDefault("wolfJCE", 0.0); + isRSAOperation = operation.startsWith("RSA"); + + for (Map.Entry providerEntry : providerResults.entrySet()) { + provider = providerEntry.getKey(); + if (!provider.equals("wolfJCE")) { + otherSpeed = providerEntry.getValue(); + + if (isRSAOperation) { + deltaValue = wolfSpeed - otherSpeed; + deltaPercent = ((wolfSpeed / otherSpeed) - 1.0) * 100; + } else { + deltaValue = wolfSpeed - otherSpeed; + deltaPercent = ((wolfSpeed / otherSpeed) - 1.0) * 100; + } + + System.out.printf("| %-40s | %-8s | %+8.2f | %+8.1f |%n", + operation, + provider, + deltaValue, + deltaPercent); } + } + } + System.out.println("-----------------------------------------------------------------------------"); + System.out.println("* Delta Value: MiB/s for symmetric ciphers, operations/second for RSA"); + } + + private static void runBenchmark(String algorithm, String mode, String padding, + String providerName) throws Exception { + SecretKey key; + byte[] ivBytes; + AlgorithmParameterSpec params; + byte[] testData; + byte[] encryptedData = null; + double dataSizeMiB; + Cipher cipher; + String cipherName = algorithm + "/" + mode + "/" + padding; + + /* Timing variables */ + long startTime; + long endTime; + long encryptTime; + long decryptTime; + double encryptThroughput; + double decryptThroughput; + double encryptTimeMS; + double decryptTimeMS; + + /* Use appropriate key based on algorithm */ + if (algorithm.equals("AES")) { + key = new SecretKeySpec(STATIC_AES_KEY, "AES"); + } else if (algorithm.equals("DESede")) { + key = new SecretKeySpec(STATIC_DES3_KEY, "DESede"); + } else { + throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); } - /* List to store all benchmark results */ - private static final List results = new ArrayList<>(); - - /* Static AES key buffer */ - private static final byte[] STATIC_AES_KEY = new byte[] { - (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, - (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, - (byte)0xfe, (byte)0xde, (byte)0xba, (byte)0x98, - (byte)0x76, (byte)0x54, (byte)0x32, (byte)0x10, - (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, - (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, - (byte)0xf0, (byte)0xf1, (byte)0xf2, (byte)0xf3, - (byte)0xf4, (byte)0xf5, (byte)0xf6, (byte)0xf7 - }; - - /* Static DESede (Triple DES) key buffer */ - private static final byte[] STATIC_DES3_KEY = new byte[] { - (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, - (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, - (byte)0xfe, (byte)0xdc, (byte)0xba, (byte)0x98, - (byte)0x76, (byte)0x54, (byte)0x32, (byte)0x10, - (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, - (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67 - }; - - private static byte[] generateTestData(int size) { - return new byte[size]; + /* Generate random IV */ + SecureRandom secureRandom = new SecureRandom(); + if (algorithm.equals("AES")){ + ivBytes = new byte[AES_BLOCK_SIZE]; + secureRandom.nextBytes(ivBytes); + } else if (algorithm.equals("DESede")) { + ivBytes = new byte[DES3_BLOCK_SIZE]; + secureRandom.nextBytes(ivBytes); + } else { + throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); } - private static void printProviderInfo(Provider provider) { - System.out.printf("%s version: %.1f%n", provider.getName(), provider.getVersion()); + if (mode.equals("GCM")) { + params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); + } else { + params = new IvParameterSpec(ivBytes); } - private static void printDeltaTable() { - /* Variables for table generation */ - Map> groupedResults; - String operation; - Map providerResults; - double wolfSpeed; - String provider; - double otherSpeed; - double deltaMiBs; - double deltaPercent; - - System.out.println("\nPerformance Delta (compared to wolfJCE)"); - System.out.println("-----------------------------------------------------------------------------"); - System.out.println("| Operation | Provider | Delta | Delta |"); - System.out.println("| | | (MiB/s) | (%) |"); - System.out.println("|------------------------------------------|----------|----------|----------|"); + testData = generateTestData(DATA_SIZE); - /* Group results by operation */ - groupedResults = new HashMap<>(); - for (BenchmarkResult result : results) { - groupedResults - .computeIfAbsent(result.operation, k -> new HashMap<>()) - .put(result.provider, result.throughput); + /* Initialize cipher with specific provider */ + cipher = Cipher.getInstance(cipherName, providerName); + + /* Warm up phase */ + for (int i = 0; i < WARMUP_ITERATIONS; i++) { + if (mode.equals("GCM")) { + secureRandom.nextBytes(ivBytes); + params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); } + cipher.init(Cipher.ENCRYPT_MODE, key, params); + encryptedData = cipher.doFinal(testData); - /* Calculate and print deltas */ - for (Map.Entry> entry : groupedResults.entrySet()) { - operation = entry.getKey(); - providerResults = entry.getValue(); - wolfSpeed = providerResults.getOrDefault("wolfJCE", 0.0); - - for (Map.Entry providerEntry : providerResults.entrySet()) { - provider = providerEntry.getKey(); - if (!provider.equals("wolfJCE")) { - otherSpeed = providerEntry.getValue(); - deltaMiBs = wolfSpeed - otherSpeed; - deltaPercent = ((wolfSpeed / otherSpeed) - 1.0) * 100; + cipher.init(Cipher.DECRYPT_MODE, key, params); + cipher.doFinal(encryptedData); + } - System.out.printf("| %-40s | %-8s | %+8.2f | %+8.1f |%n", - operation, - provider, - deltaMiBs, - deltaPercent); - } - } + /* Benchmark encryption */ + startTime = System.nanoTime(); + for (int i = 0; i < TEST_ITERATIONS; i++) { + if (mode.equals("GCM")) { + secureRandom.nextBytes(ivBytes); + params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); } - System.out.println("-----------------------------------------------------------------------------"); + cipher.init(Cipher.ENCRYPT_MODE, key, params); + encryptedData = cipher.doFinal(testData); } + endTime = System.nanoTime(); + encryptTime = (endTime - startTime) / TEST_ITERATIONS; - private static void runBenchmark(String algorithm, String mode, String padding, - String providerName) throws Exception { - SecretKey key; - byte[] ivBytes; - AlgorithmParameterSpec params; - byte[] testData; - byte[] encryptedData = null; - double dataSizeMiB; - Cipher cipher; - String cipherName = algorithm + "/" + mode + "/" + padding; - - /* Timing variables */ - long startTime; - long endTime; - long encryptTime; - long decryptTime; - double encryptThroughput; - double decryptThroughput; - double encryptTimeMS; - double decryptTimeMS; - - /* Use appropriate key based on algorithm */ - if (algorithm.equals("AES")) { - key = new SecretKeySpec(STATIC_AES_KEY, "AES"); - } else if (algorithm.equals("DESede")) { - key = new SecretKeySpec(STATIC_DES3_KEY, "DESede"); - } else { - throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); - } + dataSizeMiB = (DATA_SIZE * TEST_ITERATIONS) / (1024.0 * 1024.0); + encryptTimeMS = encryptTime / 1000000.0; + encryptThroughput = (DATA_SIZE / (encryptTime / 1000000000.0)) / (1024.0 * 1024.0); - /* Generate random IV */ - SecureRandom secureRandom = new SecureRandom(); - if (algorithm.equals("AES")){ - ivBytes = new byte[AES_BLOCK_SIZE]; - secureRandom.nextBytes(ivBytes); - } else if (algorithm.equals("DESede")) { - ivBytes = new byte[DES3_BLOCK_SIZE]; - secureRandom.nextBytes(ivBytes); - } else { - throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); - } + String testName = String.format("%s (%s)", cipherName, providerName); + System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", + testName + " enc", dataSizeMiB, encryptTimeMS, encryptThroughput); - if (mode.equals("GCM")) { - params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); - } else { - params = new IvParameterSpec(ivBytes); - } + results.add(new BenchmarkResult(providerName, cipherName + " enc", encryptThroughput)); - testData = generateTestData(DATA_SIZE); - - /* Initialize cipher with specific provider */ - cipher = Cipher.getInstance(cipherName, providerName); - - /* Warm up phase */ - for (int i = 0; i < WARMUP_ITERATIONS; i++) { - if (mode.equals("GCM")) { - secureRandom.nextBytes(ivBytes); - params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); - } - cipher.init(Cipher.ENCRYPT_MODE, key, params); - encryptedData = cipher.doFinal(testData); - - cipher.init(Cipher.DECRYPT_MODE, key, params); - cipher.doFinal(encryptedData); - } + /* Benchmark decryption */ + startTime = System.nanoTime(); + for (int i = 0; i < TEST_ITERATIONS; i++) { + cipher.init(Cipher.DECRYPT_MODE, key, params); + cipher.doFinal(encryptedData); + } + endTime = System.nanoTime(); + decryptTime = (endTime - startTime) / TEST_ITERATIONS; + + decryptTimeMS = decryptTime / 1000000.0; + decryptThroughput = (DATA_SIZE / (decryptTime / 1000000000.0)) / (1024.0 * 1024.0); + + System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", + testName + " dec", dataSizeMiB, decryptTimeMS, decryptThroughput); + + /* Store decryption result */ + results.add(new BenchmarkResult(providerName, cipherName + " dec", decryptThroughput)); + } + + /* Print RSA results in simpler format */ + private static void printRSAResults(int operations, double totalTime, String operation, + String providerName) { + double avgTimeMs = (totalTime * 1000.0) / operations; + double opsPerSec = operations / totalTime; + System.out.printf("%-12s %-8s %8d ops took %.3f sec, avg %.3f ms, %.3f ops/sec%n", + operation, + " ", + operations, + totalTime, + avgTimeMs, + opsPerSec); + + /* Store results for delta table */ + results.add(new BenchmarkResult(providerName, operation, opsPerSec)); + } + + /* Run RSA benchmarks */ + private static void runRSABenchmark(String providerName, int keySize) throws Exception { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", providerName); + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", providerName); + byte[] testData = generateTestData(SMALL_MESSAGE_SIZE); + + /* Key Generation benchmark */ + keyGen.initialize(keySize); + int keyGenOps = 0; + long startTime = System.nanoTime(); + double elapsedTime; + + do { + keyGen.generateKeyPair(); + keyGenOps++; + elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0; + } while (elapsedTime < RSA_MIN_TIME_SECONDS); + + String keyGenOp = String.format("RSA %d key gen", keySize); + printRSAResults(keyGenOps, elapsedTime, keyGenOp, providerName); + + /* For 2048-bit keys, also test public/private operations */ + if (keySize == 2048) { + KeyPair keyPair = keyGen.generateKeyPair(); + + /* Public key operations */ + int publicOps = 0; + startTime = System.nanoTime(); + + do { + cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic()); + cipher.doFinal(testData); + publicOps++; + elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0; + } while (elapsedTime < RSA_MIN_TIME_SECONDS); + + printRSAResults(publicOps, elapsedTime, "RSA 2048 public", providerName); + + /* Private key operations */ + cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic()); + byte[] encrypted = cipher.doFinal(testData); + + int privateOps = 0; + startTime = System.nanoTime(); + + do { + cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); + cipher.doFinal(encrypted); + privateOps++; + elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0; + } while (elapsedTime < RSA_MIN_TIME_SECONDS); + + printRSAResults(privateOps, elapsedTime, "RSA 2048 private", providerName); + } + } + + public static void main(String[] args) { + try { + /* Check if Bouncy Castle is available */ + boolean hasBouncyCastle = false; + Provider bcProvider = null; + try { + Class bcClass = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider"); + bcProvider = (Provider) bcClass.getDeclaredConstructor().newInstance(); + hasBouncyCastle = true; + } catch (Exception e) { + /* Bouncy Castle not available */ + } - /* Benchmark encryption */ - startTime = System.nanoTime(); - for (int i = 0; i < TEST_ITERATIONS; i++) { - if (mode.equals("GCM")) { - secureRandom.nextBytes(ivBytes); - params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); - } - cipher.init(Cipher.ENCRYPT_MODE, key, params); - encryptedData = cipher.doFinal(testData); - } - endTime = System.nanoTime(); - encryptTime = (endTime - startTime) / TEST_ITERATIONS; + /* Create provider list based on availability */ + java.util.List providerList = new java.util.ArrayList<>(); + java.util.List providerNameList = new java.util.ArrayList<>(); + + providerList.add(new WolfCryptProvider()); + providerNameList.add("wolfJCE"); - dataSizeMiB = (DATA_SIZE * TEST_ITERATIONS) / (1024.0 * 1024.0); - encryptTimeMS = encryptTime / 1000000.0; - encryptThroughput = (DATA_SIZE / (encryptTime / 1000000000.0)) / (1024.0 * 1024.0); + providerList.add(new com.sun.crypto.provider.SunJCE()); + providerNameList.add("SunJCE"); - String testName = String.format("%s (%s)", cipherName, providerName); - System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", - testName + " enc", dataSizeMiB, encryptTimeMS, encryptThroughput); + if (hasBouncyCastle && bcProvider != null) { + providerList.add(bcProvider); + providerNameList.add("BC"); + } - results.add(new BenchmarkResult(providerName, cipherName + " enc", encryptThroughput)); + Provider[] providers = providerList.toArray(new Provider[0]); + String[] providerNames = providerNameList.toArray(new String[0]); - /* Benchmark decryption */ - startTime = System.nanoTime(); - for (int i = 0; i < TEST_ITERATIONS; i++) { - cipher.init(Cipher.DECRYPT_MODE, key, params); - cipher.doFinal(encryptedData); + /* Print provider versions */ + for (Provider provider : providers) { + printProviderInfo(provider); + } + + System.out.println("-----------------------------------------------------------------------------"); + System.out.println(" Symmetric Cipher Benchmark"); + System.out.println("-----------------------------------------------------------------------------"); + System.out.println("| Operation | Size MiB | ms | MiB/s |"); + System.out.println("|------------------------------------------|----------|----------|----------|"); + + /* Run symmetric benchmarks */ + for (int i = 0; i < providers.length; i++) { + Security.insertProviderAt(providers[i], 1); + + runBenchmark("AES", "CBC", "NoPadding", providerNames[i]); + runBenchmark("AES", "CBC", "PKCS5Padding", providerNames[i]); + runBenchmark("AES", "GCM", "NoPadding", providerNames[i]); + + if (FeatureDetect.Des3Enabled()) { + runBenchmark("DESede", "CBC", "NoPadding", providerNames[i]); } - endTime = System.nanoTime(); - decryptTime = (endTime - startTime) / TEST_ITERATIONS; - decryptTimeMS = decryptTime / 1000000.0; - decryptThroughput = (DATA_SIZE / (decryptTime / 1000000000.0)) / (1024.0 * 1024.0); + if (i < providers.length - 1) { + System.out.println("|------------------------------------------|----------|----------|----------|"); + } - System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", - testName + " dec", dataSizeMiB, decryptTimeMS, decryptThroughput); + Security.removeProvider(providers[i].getName()); + } - /* Store decryption result */ - results.add(new BenchmarkResult(providerName, cipherName + " dec", decryptThroughput)); - } + System.out.println("-----------------------------------------------------------------------------"); + + /* Run RSA benchmarks */ + System.out.println("\nRSA Benchmark Results"); + System.out.println("-----------------------------------------------------------------------------"); + + for (Provider provider : providers) { + if (!provider.getName().equals("SunJCE")) { + Security.insertProviderAt(provider, 1); + System.out.println("\n" + provider.getName() + ":"); + + for (int keySize : RSA_KEY_SIZES) { + runRSABenchmark(provider.getName(), keySize); + } - public static void main(String[] args) { - try { - /* Check if Bouncy Castle is available */ - boolean hasBouncyCastle = false; - Provider bcProvider = null; - try { - Class bcClass = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider"); - bcProvider = (Provider) bcClass.getDeclaredConstructor().newInstance(); - hasBouncyCastle = true; - } catch (Exception e) { - /* Bouncy Castle not available */ - } - - /* Create provider list based on availability */ - java.util.List providerList = new java.util.ArrayList<>(); - java.util.List providerNameList = new java.util.ArrayList<>(); - - providerList.add(new WolfCryptProvider()); - providerNameList.add("wolfJCE"); - - providerList.add(new com.sun.crypto.provider.SunJCE()); - providerNameList.add("SunJCE"); - - if (hasBouncyCastle && bcProvider != null) { - providerList.add(bcProvider); - providerNameList.add("BC"); - } - - Provider[] providers = providerList.toArray(new Provider[0]); - String[] providerNames = providerNameList.toArray(new String[0]); - - /* Print provider versions */ - for (Provider provider : providers) { - printProviderInfo(provider); - } - - System.out.println("-----------------------------------------------------------------------------"); - System.out.println(" JCE Crypto Provider Benchmark"); - System.out.println("-----------------------------------------------------------------------------"); - - System.out.println("| Operation | Size MiB | ms | MiB/s |"); - System.out.println("|------------------------------------------|----------|----------|----------|"); - - /* Test each provider */ - for (int i = 0; i < providers.length; i++) { - Security.insertProviderAt(providers[i], 1); - - /* Run benchmarks for different algorithms */ - runBenchmark("AES", "CBC", "NoPadding", providerNames[i]); - runBenchmark("AES", "CBC", "PKCS5Padding", providerNames[i]); - runBenchmark("AES", "GCM", "NoPadding", providerNames[i]); - /* Only run DES3 benchmark if it's enabled */ - if (FeatureDetect.Des3Enabled()) { - runBenchmark("DESede", "CBC", "NoPadding", providerNames[i]); - } - - if (i < providers.length - 1) { - System.out.println("|------------------------------------------|----------|----------|----------|"); - } - - Security.removeProvider(providers[i].getName()); - } - - System.out.println("-----------------------------------------------------------------------------"); - - printDeltaTable(); - - } catch (Exception e) { - System.err.println("Benchmark failed: " + e.getMessage()); - e.printStackTrace(); + Security.removeProvider(provider.getName()); } + } + + System.out.println("-----------------------------------------------------------------------------"); + + /* Print delta table */ + printDeltaTable(); + + } catch (Exception e) { + System.err.println("Benchmark failed: " + e.getMessage()); + e.printStackTrace(); } + } } From 1464f77315ab8ea581f83286593adc0cb3296e99 Mon Sep 17 00:00:00 2001 From: Jack Tjaden Date: Tue, 21 Jan 2025 16:00:16 -0700 Subject: [PATCH 2/5] Fixed indents, runBenchmark name, Delta table displays more and clear info --- examples/provider/CryptoBenchmark.java | 748 +++++++++++++------------ 1 file changed, 390 insertions(+), 358 deletions(-) diff --git a/examples/provider/CryptoBenchmark.java b/examples/provider/CryptoBenchmark.java index c940758..bae3646 100644 --- a/examples/provider/CryptoBenchmark.java +++ b/examples/provider/CryptoBenchmark.java @@ -15,392 +15,424 @@ import com.wolfssl.wolfcrypt.FeatureDetect; public class CryptoBenchmark { - /* Constants for benchmark configuration */ - private static final int WARMUP_ITERATIONS = 5; - private static final int TEST_ITERATIONS = 5; - private static final int DATA_SIZE = 1024 * 1024; - private static final int AES_BLOCK_SIZE = 16; - private static final int DES3_BLOCK_SIZE = 8; - private static final int GCM_TAG_LENGTH = 128; - private static final int[] RSA_KEY_SIZES = {2048, 3072, 4096}; - private static final int RSA_MIN_TIME_SECONDS = 1; /* minimum time to run each test */ - private static final int SMALL_MESSAGE_SIZE = 32; /* small message size for RSA ops */ - - /* Class to store benchmark results */ - private static class BenchmarkResult { - /* Result fields */ - String provider; - String operation; - double throughput; - - /* Constructor */ - BenchmarkResult(String provider, String operation, double throughput) { - this.provider = provider; - this.operation = operation; - this.throughput = throughput; + /* Constants for benchmark configuration */ + private static final int WARMUP_ITERATIONS = 5; + private static final int TEST_ITERATIONS = 5; + private static final int DATA_SIZE = 1024 * 1024; + private static final int AES_BLOCK_SIZE = 16; + private static final int DES3_BLOCK_SIZE = 8; + private static final int GCM_TAG_LENGTH = 128; + private static final int[] RSA_KEY_SIZES = {2048, 3072, 4096}; + private static final int RSA_MIN_TIME_SECONDS = 1; /* minimum time to run each test */ + private static final int SMALL_MESSAGE_SIZE = 32; /* small message size for RSA ops */ + + /* Class to store benchmark results */ + private static class BenchmarkResult { + /* Result fields */ + String provider; + String operation; + double throughput; + + /* Constructor */ + BenchmarkResult(String provider, String operation, double throughput) { + this.provider = provider; + this.operation = operation; + this.throughput = throughput; + } } - } - - /* List to store all benchmark results */ - private static final List results = new ArrayList<>(); - - /* Static AES key buffer */ - private static final byte[] STATIC_AES_KEY = new byte[] { - (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, - (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, - (byte)0xfe, (byte)0xde, (byte)0xba, (byte)0x98, - (byte)0x76, (byte)0x54, (byte)0x32, (byte)0x10, - (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, - (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, - (byte)0xf0, (byte)0xf1, (byte)0xf2, (byte)0xf3, - (byte)0xf4, (byte)0xf5, (byte)0xf6, (byte)0xf7 - }; + + /* List to store all benchmark results */ + private static final List results = new ArrayList<>(); + + /* Static AES key buffer */ + private static final byte[] STATIC_AES_KEY = new byte[] { + (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, + (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, + (byte)0xfe, (byte)0xde, (byte)0xba, (byte)0x98, + (byte)0x76, (byte)0x54, (byte)0x32, (byte)0x10, + (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, + (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, + (byte)0xf0, (byte)0xf1, (byte)0xf2, (byte)0xf3, + (byte)0xf4, (byte)0xf5, (byte)0xf6, (byte)0xf7 + }; /* Static DESede (Triple DES) key buffer */ - private static final byte[] STATIC_DES3_KEY = new byte[] { - (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, - (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, - (byte)0xfe, (byte)0xdc, (byte)0xba, (byte)0x98, - (byte)0x76, (byte)0x54, (byte)0x32, (byte)0x10, - (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, - (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67 - }; - - private static byte[] generateTestData(int size) { - return new byte[size]; - } - - private static void printProviderInfo(Provider provider) { - System.out.printf("%s version: %.1f%n", provider.getName(), provider.getVersion()); - } - - private static void printDeltaTable() { - /* Variables for table generation */ - Map> groupedResults; - String operation; - Map providerResults; - double wolfSpeed; - String provider; - double otherSpeed; - double deltaValue; - double deltaPercent; - boolean isRSAOperation; - - System.out.println("\nPerformance Delta (compared to wolfJCE)"); - System.out.println("-----------------------------------------------------------------------------"); - System.out.println("| Operation | Provider | Delta | Delta |"); - System.out.println("| | | Value* | (%) |"); - System.out.println("|------------------------------------------|----------|----------|----------|"); - - /* Group results by operation */ - groupedResults = new HashMap<>(); - for (BenchmarkResult result : results) { - groupedResults - .computeIfAbsent(result.operation, k -> new HashMap<>()) - .put(result.provider, result.throughput); + private static final byte[] STATIC_DES3_KEY = new byte[] { + (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, + (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, + (byte)0xfe, (byte)0xdc, (byte)0xba, (byte)0x98, + (byte)0x76, (byte)0x54, (byte)0x32, (byte)0x10, + (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, + (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67 + }; + + private static byte[] generateTestData(int size) { + return new byte[size]; } - /* Calculate and print deltas */ - for (Map.Entry> entry : groupedResults.entrySet()) { - operation = entry.getKey(); - providerResults = entry.getValue(); - wolfSpeed = providerResults.getOrDefault("wolfJCE", 0.0); - isRSAOperation = operation.startsWith("RSA"); - - for (Map.Entry providerEntry : providerResults.entrySet()) { - provider = providerEntry.getKey(); - if (!provider.equals("wolfJCE")) { - otherSpeed = providerEntry.getValue(); - - if (isRSAOperation) { - deltaValue = wolfSpeed - otherSpeed; - deltaPercent = ((wolfSpeed / otherSpeed) - 1.0) * 100; - } else { - deltaValue = wolfSpeed - otherSpeed; - deltaPercent = ((wolfSpeed / otherSpeed) - 1.0) * 100; - } - - System.out.printf("| %-40s | %-8s | %+8.2f | %+8.1f |%n", - operation, - provider, - deltaValue, - deltaPercent); - } - } + private static void printProviderInfo(Provider provider) { + System.out.printf("%s version: %.1f%n", provider.getName(), provider.getVersion()); } - System.out.println("-----------------------------------------------------------------------------"); - System.out.println("* Delta Value: MiB/s for symmetric ciphers, operations/second for RSA"); - } - private static void runBenchmark(String algorithm, String mode, String padding, - String providerName) throws Exception { - SecretKey key; - byte[] ivBytes; - AlgorithmParameterSpec params; - byte[] testData; - byte[] encryptedData = null; - double dataSizeMiB; - Cipher cipher; - String cipherName = algorithm + "/" + mode + "/" + padding; - - /* Timing variables */ - long startTime; - long endTime; - long encryptTime; - long decryptTime; - double encryptThroughput; - double decryptThroughput; - double encryptTimeMS; - double decryptTimeMS; - - /* Use appropriate key based on algorithm */ - if (algorithm.equals("AES")) { - key = new SecretKeySpec(STATIC_AES_KEY, "AES"); - } else if (algorithm.equals("DESede")) { - key = new SecretKeySpec(STATIC_DES3_KEY, "DESede"); - } else { - throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); - } + private static void printDeltaTable() { + /* Variables for table generation */ + Map> groupedResults; + Map providerResults; + double wolfSpeed; + String provider; + double otherSpeed; + double deltaValue; + double deltaPercent; + + System.out.println("\nPerformance Delta (compared to wolfJCE)"); + System.out.println("-----------------------------------------------------------------------------"); + System.out.println("| Operation | Provider | Delta | Delta |"); + System.out.println("| | | Value* | (%) |"); + System.out.println("|------------------------------------------|----------|----------|----------|"); + + /* Group results by operation */ + groupedResults = new HashMap<>(); + for (BenchmarkResult result : results) { + groupedResults + .computeIfAbsent(result.operation, k -> new HashMap<>()) + .put(result.provider, result.throughput); + } - /* Generate random IV */ - SecureRandom secureRandom = new SecureRandom(); - if (algorithm.equals("AES")){ - ivBytes = new byte[AES_BLOCK_SIZE]; - secureRandom.nextBytes(ivBytes); - } else if (algorithm.equals("DESede")) { - ivBytes = new byte[DES3_BLOCK_SIZE]; - secureRandom.nextBytes(ivBytes); - } else { - throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); - } + /* Sort operations to group RSA operations together */ + List sortedOperations = new ArrayList<>(groupedResults.keySet()); + Collections.sort(sortedOperations, (a, b) -> { + boolean aIsRSA = a.startsWith("RSA"); + boolean bIsRSA = b.startsWith("RSA"); + + if (aIsRSA && !bIsRSA) return -1; + if (!aIsRSA && bIsRSA) return 1; + return a.compareTo(b); + }); + + /* Calculate and print deltas */ + for (String operation : sortedOperations) { + providerResults = groupedResults.get(operation); + wolfSpeed = providerResults.getOrDefault("wolfJCE", 0.0); + boolean isRSAOperation = operation.startsWith("RSA"); + + for (Map.Entry providerEntry : providerResults.entrySet()) { + provider = providerEntry.getKey(); + if (!provider.equals("wolfJCE")) { + otherSpeed = providerEntry.getValue(); + if (isRSAOperation) { + deltaValue = wolfSpeed - otherSpeed; + deltaPercent = ((wolfSpeed / otherSpeed) - 1.0) * 100; + } else { + deltaValue = wolfSpeed - otherSpeed; + deltaPercent = ((wolfSpeed / otherSpeed) - 1.0) * 100; + } + System.out.printf("| %-40s | %-8s | %+8.2f | %+8.1f |%n", + operation, + provider, + deltaValue, + deltaPercent); + } + } + } - if (mode.equals("GCM")) { - params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); - } else { - params = new IvParameterSpec(ivBytes); + System.out.println("-----------------------------------------------------------------------------"); + System.out.println("* Delta Value: MiB/s for symmetric ciphers, operations/second for RSA"); } - testData = generateTestData(DATA_SIZE); - - /* Initialize cipher with specific provider */ - cipher = Cipher.getInstance(cipherName, providerName); - - /* Warm up phase */ - for (int i = 0; i < WARMUP_ITERATIONS; i++) { - if (mode.equals("GCM")) { - secureRandom.nextBytes(ivBytes); - params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); - } - cipher.init(Cipher.ENCRYPT_MODE, key, params); - encryptedData = cipher.doFinal(testData); + private static void runEncDecBenchmark(String algorithm, String mode, String padding, + String providerName) throws Exception { + SecretKey key; + byte[] ivBytes; + AlgorithmParameterSpec params; + byte[] testData; + byte[] encryptedData = null; + double dataSizeMiB; + Cipher cipher; + String cipherName = algorithm + "/" + mode + "/" + padding; + + /* Timing variables */ + long startTime; + long endTime; + long encryptTime; + long decryptTime; + double encryptThroughput; + double decryptThroughput; + double encryptTimeMS; + double decryptTimeMS; + + /* Use appropriate key based on algorithm */ + if (algorithm.equals("AES")) { + key = new SecretKeySpec(STATIC_AES_KEY, "AES"); + } else if (algorithm.equals("DESede")) { + key = new SecretKeySpec(STATIC_DES3_KEY, "DESede"); + } else { + throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); + } - cipher.init(Cipher.DECRYPT_MODE, key, params); - cipher.doFinal(encryptedData); - } + /* Generate random IV */ + SecureRandom secureRandom = new SecureRandom(); + if (algorithm.equals("AES")){ + ivBytes = new byte[AES_BLOCK_SIZE]; + secureRandom.nextBytes(ivBytes); + } else if (algorithm.equals("DESede")) { + ivBytes = new byte[DES3_BLOCK_SIZE]; + secureRandom.nextBytes(ivBytes); + } else { + throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); + } - /* Benchmark encryption */ - startTime = System.nanoTime(); - for (int i = 0; i < TEST_ITERATIONS; i++) { - if (mode.equals("GCM")) { - secureRandom.nextBytes(ivBytes); - params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); - } - cipher.init(Cipher.ENCRYPT_MODE, key, params); - encryptedData = cipher.doFinal(testData); - } - endTime = System.nanoTime(); - encryptTime = (endTime - startTime) / TEST_ITERATIONS; + if (mode.equals("GCM")) { + params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); + } else { + params = new IvParameterSpec(ivBytes); + } - dataSizeMiB = (DATA_SIZE * TEST_ITERATIONS) / (1024.0 * 1024.0); - encryptTimeMS = encryptTime / 1000000.0; - encryptThroughput = (DATA_SIZE / (encryptTime / 1000000000.0)) / (1024.0 * 1024.0); + testData = generateTestData(DATA_SIZE); - String testName = String.format("%s (%s)", cipherName, providerName); - System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", - testName + " enc", dataSizeMiB, encryptTimeMS, encryptThroughput); + /* Initialize cipher with specific provider */ + cipher = Cipher.getInstance(cipherName, providerName); - results.add(new BenchmarkResult(providerName, cipherName + " enc", encryptThroughput)); + /* Warm up phase */ + for (int i = 0; i < WARMUP_ITERATIONS; i++) { + if (mode.equals("GCM")) { + secureRandom.nextBytes(ivBytes); + params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); + } + cipher.init(Cipher.ENCRYPT_MODE, key, params); + encryptedData = cipher.doFinal(testData); - /* Benchmark decryption */ - startTime = System.nanoTime(); - for (int i = 0; i < TEST_ITERATIONS; i++) { - cipher.init(Cipher.DECRYPT_MODE, key, params); - cipher.doFinal(encryptedData); - } - endTime = System.nanoTime(); - decryptTime = (endTime - startTime) / TEST_ITERATIONS; - - decryptTimeMS = decryptTime / 1000000.0; - decryptThroughput = (DATA_SIZE / (decryptTime / 1000000000.0)) / (1024.0 * 1024.0); - - System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", - testName + " dec", dataSizeMiB, decryptTimeMS, decryptThroughput); - - /* Store decryption result */ - results.add(new BenchmarkResult(providerName, cipherName + " dec", decryptThroughput)); - } - - /* Print RSA results in simpler format */ - private static void printRSAResults(int operations, double totalTime, String operation, - String providerName) { - double avgTimeMs = (totalTime * 1000.0) / operations; - double opsPerSec = operations / totalTime; - System.out.printf("%-12s %-8s %8d ops took %.3f sec, avg %.3f ms, %.3f ops/sec%n", - operation, - " ", - operations, - totalTime, - avgTimeMs, - opsPerSec); - - /* Store results for delta table */ - results.add(new BenchmarkResult(providerName, operation, opsPerSec)); - } - - /* Run RSA benchmarks */ - private static void runRSABenchmark(String providerName, int keySize) throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", providerName); - Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", providerName); - byte[] testData = generateTestData(SMALL_MESSAGE_SIZE); - - /* Key Generation benchmark */ - keyGen.initialize(keySize); - int keyGenOps = 0; - long startTime = System.nanoTime(); - double elapsedTime; - - do { - keyGen.generateKeyPair(); - keyGenOps++; - elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0; - } while (elapsedTime < RSA_MIN_TIME_SECONDS); - - String keyGenOp = String.format("RSA %d key gen", keySize); - printRSAResults(keyGenOps, elapsedTime, keyGenOp, providerName); - - /* For 2048-bit keys, also test public/private operations */ - if (keySize == 2048) { - KeyPair keyPair = keyGen.generateKeyPair(); - - /* Public key operations */ - int publicOps = 0; - startTime = System.nanoTime(); - - do { - cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic()); - cipher.doFinal(testData); - publicOps++; - elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0; - } while (elapsedTime < RSA_MIN_TIME_SECONDS); - - printRSAResults(publicOps, elapsedTime, "RSA 2048 public", providerName); - - /* Private key operations */ - cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic()); - byte[] encrypted = cipher.doFinal(testData); - - int privateOps = 0; - startTime = System.nanoTime(); - - do { - cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); - cipher.doFinal(encrypted); - privateOps++; - elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0; - } while (elapsedTime < RSA_MIN_TIME_SECONDS); - - printRSAResults(privateOps, elapsedTime, "RSA 2048 private", providerName); - } - } - - public static void main(String[] args) { - try { - /* Check if Bouncy Castle is available */ - boolean hasBouncyCastle = false; - Provider bcProvider = null; - try { - Class bcClass = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider"); - bcProvider = (Provider) bcClass.getDeclaredConstructor().newInstance(); - hasBouncyCastle = true; - } catch (Exception e) { - /* Bouncy Castle not available */ - } - - /* Create provider list based on availability */ - java.util.List providerList = new java.util.ArrayList<>(); - java.util.List providerNameList = new java.util.ArrayList<>(); - - providerList.add(new WolfCryptProvider()); - providerNameList.add("wolfJCE"); - - providerList.add(new com.sun.crypto.provider.SunJCE()); - providerNameList.add("SunJCE"); - - if (hasBouncyCastle && bcProvider != null) { - providerList.add(bcProvider); - providerNameList.add("BC"); - } - - Provider[] providers = providerList.toArray(new Provider[0]); - String[] providerNames = providerNameList.toArray(new String[0]); - - /* Print provider versions */ - for (Provider provider : providers) { - printProviderInfo(provider); - } - - System.out.println("-----------------------------------------------------------------------------"); - System.out.println(" Symmetric Cipher Benchmark"); - System.out.println("-----------------------------------------------------------------------------"); - System.out.println("| Operation | Size MiB | ms | MiB/s |"); - System.out.println("|------------------------------------------|----------|----------|----------|"); - - /* Run symmetric benchmarks */ - for (int i = 0; i < providers.length; i++) { - Security.insertProviderAt(providers[i], 1); - - runBenchmark("AES", "CBC", "NoPadding", providerNames[i]); - runBenchmark("AES", "CBC", "PKCS5Padding", providerNames[i]); - runBenchmark("AES", "GCM", "NoPadding", providerNames[i]); - - if (FeatureDetect.Des3Enabled()) { - runBenchmark("DESede", "CBC", "NoPadding", providerNames[i]); + cipher.init(Cipher.DECRYPT_MODE, key, params); + cipher.doFinal(encryptedData); } - if (i < providers.length - 1) { - System.out.println("|------------------------------------------|----------|----------|----------|"); + /* Benchmark encryption */ + startTime = System.nanoTime(); + for (int i = 0; i < TEST_ITERATIONS; i++) { + if (mode.equals("GCM")) { + secureRandom.nextBytes(ivBytes); + params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); + } + cipher.init(Cipher.ENCRYPT_MODE, key, params); + encryptedData = cipher.doFinal(testData); } + endTime = System.nanoTime(); + encryptTime = (endTime - startTime) / TEST_ITERATIONS; - Security.removeProvider(providers[i].getName()); - } + dataSizeMiB = (DATA_SIZE * TEST_ITERATIONS) / (1024.0 * 1024.0); + encryptTimeMS = encryptTime / 1000000.0; + encryptThroughput = (DATA_SIZE / (encryptTime / 1000000000.0)) / (1024.0 * 1024.0); - System.out.println("-----------------------------------------------------------------------------"); + String testName = String.format("%s (%s)", cipherName, providerName); + System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", + testName + " enc", dataSizeMiB, encryptTimeMS, encryptThroughput); - /* Run RSA benchmarks */ - System.out.println("\nRSA Benchmark Results"); - System.out.println("-----------------------------------------------------------------------------"); + results.add(new BenchmarkResult(providerName, cipherName + " enc", encryptThroughput)); - for (Provider provider : providers) { - if (!provider.getName().equals("SunJCE")) { - Security.insertProviderAt(provider, 1); - System.out.println("\n" + provider.getName() + ":"); + /* Benchmark decryption */ + startTime = System.nanoTime(); + for (int i = 0; i < TEST_ITERATIONS; i++) { + cipher.init(Cipher.DECRYPT_MODE, key, params); + cipher.doFinal(encryptedData); + } + endTime = System.nanoTime(); + decryptTime = (endTime - startTime) / TEST_ITERATIONS; - for (int keySize : RSA_KEY_SIZES) { - runRSABenchmark(provider.getName(), keySize); - } + decryptTimeMS = decryptTime / 1000000.0; + decryptThroughput = (DATA_SIZE / (decryptTime / 1000000000.0)) / (1024.0 * 1024.0); - Security.removeProvider(provider.getName()); - } - } + System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", + testName + " dec", dataSizeMiB, decryptTimeMS, decryptThroughput); - System.out.println("-----------------------------------------------------------------------------"); + /* Store decryption result */ + results.add(new BenchmarkResult(providerName, cipherName + " dec", decryptThroughput)); + } + + /* Print RSA results in simpler format */ + private static void printRSAResults(int operations, double totalTime, String operation, + String providerName, String mode) { + /* Variables for result calculations */ + double avgTimeMs; + double opsPerSec; + + /* Calculate metrics */ + avgTimeMs = (totalTime * 1000.0) / operations; + opsPerSec = operations / totalTime; + + /* Print formatted results */ + System.out.printf("%-12s %-8s %8d ops took %.3f sec, avg %.3f ms, %.3f ops/sec%n", + operation, + " ", + operations, + totalTime, + avgTimeMs, + opsPerSec); + + /* Store results for delta table */ + String fullOperation = String.format("%s (%s)", operation, mode); + results.add(new BenchmarkResult(providerName, fullOperation, opsPerSec)); + } - /* Print delta table */ - printDeltaTable(); + /* Run RSA benchmarks for specified provider and key size */ + private static void runRSABenchmark(String providerName, int keySize) throws Exception { + /* Variables for benchmark operations */ + KeyPairGenerator keyGen; + Cipher cipher; + byte[] testData; + int keyGenOps; + long startTime; + double elapsedTime; + KeyPair keyPair; + int publicOps; + int privateOps; + byte[] encrypted; + String keyGenOp; + String cipherMode = "RSA/ECB/PKCS1Padding"; + + /* Initialize key generator and cipher */ + keyGen = KeyPairGenerator.getInstance("RSA", providerName); + cipher = Cipher.getInstance(cipherMode, providerName); + testData = generateTestData(SMALL_MESSAGE_SIZE); + + /* Key Generation benchmark */ + keyGen.initialize(keySize); + keyGenOps = 0; + startTime = System.nanoTime(); + elapsedTime = 0; + + /* Run key generation benchmark */ + do { + keyGen.generateKeyPair(); + keyGenOps++; + elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0; + } while (elapsedTime < RSA_MIN_TIME_SECONDS); + + keyGenOp = String.format("RSA %d key gen", keySize); + printRSAResults(keyGenOps, elapsedTime, keyGenOp, providerName, cipherMode); + + /* For 2048-bit keys, test public/private operations */ + if (keySize == 2048) { + /* Generate key pair for public/private operations */ + keyPair = keyGen.generateKeyPair(); + + /* Public key operations benchmark */ + publicOps = 0; + startTime = System.nanoTime(); + + do { + cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic()); + cipher.doFinal(testData); + publicOps++; + elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0; + } while (elapsedTime < RSA_MIN_TIME_SECONDS); + + printRSAResults(publicOps, elapsedTime, "RSA 2048 public", providerName, cipherMode); + + /* Private key operations benchmark */ + cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic()); + encrypted = cipher.doFinal(testData); + + privateOps = 0; + startTime = System.nanoTime(); + + do { + cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); + cipher.doFinal(encrypted); + privateOps++; + elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000.0; + } while (elapsedTime < RSA_MIN_TIME_SECONDS); + + printRSAResults(privateOps, elapsedTime, "RSA 2048 private", providerName, cipherMode); + } + } - } catch (Exception e) { - System.err.println("Benchmark failed: " + e.getMessage()); - e.printStackTrace(); + public static void main(String[] args) { + try { + /* Check if Bouncy Castle is available */ + boolean hasBouncyCastle = false; + Provider bcProvider = null; + try { + Class bcClass = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider"); + bcProvider = (Provider) bcClass.getDeclaredConstructor().newInstance(); + hasBouncyCastle = true; + } catch (Exception e) { + /* Bouncy Castle not available */ + } + + /* Create provider list based on availability */ + java.util.List providerList = new java.util.ArrayList<>(); + java.util.List providerNameList = new java.util.ArrayList<>(); + + providerList.add(new WolfCryptProvider()); + providerNameList.add("wolfJCE"); + + providerList.add(new com.sun.crypto.provider.SunJCE()); + providerNameList.add("SunJCE"); + + if (hasBouncyCastle && bcProvider != null) { + providerList.add(bcProvider); + providerNameList.add("BC"); + } + + Provider[] providers = providerList.toArray(new Provider[0]); + String[] providerNames = providerNameList.toArray(new String[0]); + + /* Print provider versions */ + for (Provider provider : providers) { + printProviderInfo(provider); + } + + System.out.println("-----------------------------------------------------------------------------"); + System.out.println(" Symmetric Cipher Benchmark"); + System.out.println("-----------------------------------------------------------------------------"); + System.out.println("| Operation | Size MiB | ms | MiB/s |"); + System.out.println("|------------------------------------------|----------|----------|----------|"); + + /* Run symmetric benchmarks */ + for (int i = 0; i < providers.length; i++) { + Security.insertProviderAt(providers[i], 1); + + runEncDecBenchmark("AES", "CBC", "NoPadding", providerNames[i]); + runEncDecBenchmark("AES", "CBC", "PKCS5Padding", providerNames[i]); + runEncDecBenchmark("AES", "GCM", "NoPadding", providerNames[i]); + + if (FeatureDetect.Des3Enabled()) { + runEncDecBenchmark("DESede", "CBC", "NoPadding", providerNames[i]); + } + + if (i < providers.length - 1) { + System.out.println("|------------------------------------------|----------|----------|----------|"); + } + + Security.removeProvider(providers[i].getName()); + } + + System.out.println("-----------------------------------------------------------------------------"); + + /* Run RSA benchmarks */ + System.out.println("\nRSA Benchmark Results"); + System.out.println("-----------------------------------------------------------------------------"); + + for (Provider provider : providers) { + if (!provider.getName().equals("SunJCE")) { + Security.insertProviderAt(provider, 1); + System.out.println("\n" + provider.getName() + ":"); + + for (int keySize : RSA_KEY_SIZES) { + runRSABenchmark(provider.getName(), keySize); + } + + Security.removeProvider(provider.getName()); + } + } + + System.out.println("-----------------------------------------------------------------------------"); + + /* Print delta table */ + printDeltaTable(); + + } catch (Exception e) { + System.err.println("Benchmark failed: " + e.getMessage()); + e.printStackTrace(); + } } - } } From 8fd26746e1120eb9772c6b722728d54d3ce34fea Mon Sep 17 00:00:00 2001 From: Jack Tjaden Date: Tue, 28 Jan 2025 13:39:46 -0700 Subject: [PATCH 3/5] removal of table for symertric ciphers and addition of mode for RSA results --- examples/provider/CryptoBenchmark.java | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/examples/provider/CryptoBenchmark.java b/examples/provider/CryptoBenchmark.java index bae3646..23e0b3f 100644 --- a/examples/provider/CryptoBenchmark.java +++ b/examples/provider/CryptoBenchmark.java @@ -127,7 +127,7 @@ private static void printDeltaTable() { deltaPercent = ((wolfSpeed / otherSpeed) - 1.0) * 100; } System.out.printf("| %-40s | %-8s | %+8.2f | %+8.1f |%n", - operation, + operation.replace("RSA", "RSA/ECB/PKCS1Padding RSA"), provider, deltaValue, deltaPercent); @@ -223,7 +223,7 @@ private static void runEncDecBenchmark(String algorithm, String mode, String pad encryptThroughput = (DATA_SIZE / (encryptTime / 1000000000.0)) / (1024.0 * 1024.0); String testName = String.format("%s (%s)", cipherName, providerName); - System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", + System.out.printf(" %-40s %8.3f MiB %8.3f ms %8.3f MiB/s%n", testName + " enc", dataSizeMiB, encryptTimeMS, encryptThroughput); results.add(new BenchmarkResult(providerName, cipherName + " enc", encryptThroughput)); @@ -240,8 +240,8 @@ private static void runEncDecBenchmark(String algorithm, String mode, String pad decryptTimeMS = decryptTime / 1000000.0; decryptThroughput = (DATA_SIZE / (decryptTime / 1000000000.0)) / (1024.0 * 1024.0); - System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", - testName + " dec", dataSizeMiB, decryptTimeMS, decryptThroughput); + System.out.printf(" %-40s %8.3f MiB %8.3f ms %8.3f MiB/s%n", + testName + " dec", dataSizeMiB , decryptTimeMS, decryptThroughput); /* Store decryption result */ results.add(new BenchmarkResult(providerName, cipherName + " dec", decryptThroughput)); @@ -260,7 +260,7 @@ private static void printRSAResults(int operations, double totalTime, String ope /* Print formatted results */ System.out.printf("%-12s %-8s %8d ops took %.3f sec, avg %.3f ms, %.3f ops/sec%n", - operation, + operation + " (" + mode + ")", " ", operations, totalTime, @@ -268,7 +268,7 @@ private static void printRSAResults(int operations, double totalTime, String ope opsPerSec); /* Store results for delta table */ - String fullOperation = String.format("%s (%s)", operation, mode); + String fullOperation = operation; results.add(new BenchmarkResult(providerName, fullOperation, opsPerSec)); } @@ -383,9 +383,7 @@ public static void main(String[] args) { System.out.println("-----------------------------------------------------------------------------"); System.out.println(" Symmetric Cipher Benchmark"); - System.out.println("-----------------------------------------------------------------------------"); - System.out.println("| Operation | Size MiB | ms | MiB/s |"); - System.out.println("|------------------------------------------|----------|----------|----------|"); + System.out.println("-----------------------------------------------------------------------------\n"); /* Run symmetric benchmarks */ for (int i = 0; i < providers.length; i++) { @@ -399,17 +397,13 @@ public static void main(String[] args) { runEncDecBenchmark("DESede", "CBC", "NoPadding", providerNames[i]); } - if (i < providers.length - 1) { - System.out.println("|------------------------------------------|----------|----------|----------|"); - } - Security.removeProvider(providers[i].getName()); } - System.out.println("-----------------------------------------------------------------------------"); /* Run RSA benchmarks */ - System.out.println("\nRSA Benchmark Results"); + System.out.println("\n-----------------------------------------------------------------------------"); + System.out.println("RSA Benchmark Results"); System.out.println("-----------------------------------------------------------------------------"); for (Provider provider : providers) { From 6f87879760947430fb83142c09564b95a858c3d4 Mon Sep 17 00:00:00 2001 From: Jack Tjaden Date: Fri, 31 Jan 2025 15:02:09 -0700 Subject: [PATCH 4/5] Add SunRsaSign to RSA benchmark test --- examples/provider/CryptoBenchmark.java | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/provider/CryptoBenchmark.java b/examples/provider/CryptoBenchmark.java index 23e0b3f..e3308ab 100644 --- a/examples/provider/CryptoBenchmark.java +++ b/examples/provider/CryptoBenchmark.java @@ -85,10 +85,10 @@ private static void printDeltaTable() { double deltaPercent; System.out.println("\nPerformance Delta (compared to wolfJCE)"); - System.out.println("-----------------------------------------------------------------------------"); - System.out.println("| Operation | Provider | Delta | Delta |"); - System.out.println("| | | Value* | (%) |"); - System.out.println("|------------------------------------------|----------|----------|----------|"); + System.out.println("--------------------------------------------------------------------------------"); + System.out.println("| Operation | Provider | Delta | Delta |"); + System.out.println("| | | Value* | (%) |"); + System.out.println("|------------------------------------------|--------------|----------|----------|"); /* Group results by operation */ groupedResults = new HashMap<>(); @@ -126,16 +126,15 @@ private static void printDeltaTable() { deltaValue = wolfSpeed - otherSpeed; deltaPercent = ((wolfSpeed / otherSpeed) - 1.0) * 100; } - System.out.printf("| %-40s | %-8s | %+8.2f | %+8.1f |%n", + System.out.printf("| %-40s | %-12s | %+8.2f | %+8.1f |%n", operation.replace("RSA", "RSA/ECB/PKCS1Padding RSA"), provider, deltaValue, deltaPercent); - } + } } } - - System.out.println("-----------------------------------------------------------------------------"); + System.out.println("--------------------------------------------------------------------------------"); System.out.println("* Delta Value: MiB/s for symmetric ciphers, operations/second for RSA"); } @@ -289,8 +288,14 @@ private static void runRSABenchmark(String providerName, int keySize) throws Exc String cipherMode = "RSA/ECB/PKCS1Padding"; /* Initialize key generator and cipher */ - keyGen = KeyPairGenerator.getInstance("RSA", providerName); - cipher = Cipher.getInstance(cipherMode, providerName); + if (providerName.equals("SunJCE")) { + keyGen = KeyPairGenerator.getInstance("RSA", "SunRsaSign"); + cipher = Cipher.getInstance(cipherMode, "SunJCE"); + providerName = "SunRsaSign"; + } else { + keyGen = KeyPairGenerator.getInstance("RSA", providerName); + cipher = Cipher.getInstance(cipherMode, providerName); + } testData = generateTestData(SMALL_MESSAGE_SIZE); /* Key Generation benchmark */ @@ -407,18 +412,13 @@ public static void main(String[] args) { System.out.println("-----------------------------------------------------------------------------"); for (Provider provider : providers) { - if (!provider.getName().equals("SunJCE")) { - Security.insertProviderAt(provider, 1); - System.out.println("\n" + provider.getName() + ":"); - - for (int keySize : RSA_KEY_SIZES) { - runRSABenchmark(provider.getName(), keySize); - } - - Security.removeProvider(provider.getName()); + Security.insertProviderAt(provider, 1); + System.out.println("\n" + (provider.getName().equals("SunJCE") ? "SunJCE / SunRsaSign" : provider.getName()) + ":"); + for (int keySize : RSA_KEY_SIZES) { + runRSABenchmark(provider.getName(), keySize); } + Security.removeProvider(provider.getName()); } - System.out.println("-----------------------------------------------------------------------------"); /* Print delta table */ From 4839bcca32dc1e4da4dc1bc4f4486743d24d3fb3 Mon Sep 17 00:00:00 2001 From: Jack Tjaden Date: Fri, 31 Jan 2025 17:14:49 -0700 Subject: [PATCH 5/5] Fix to delta table for private/public SunJCE RSA test --- examples/provider/CryptoBenchmark.java | 46 ++++++++++++++++++-------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/examples/provider/CryptoBenchmark.java b/examples/provider/CryptoBenchmark.java index e3308ab..3a6d054 100644 --- a/examples/provider/CryptoBenchmark.java +++ b/examples/provider/CryptoBenchmark.java @@ -56,7 +56,7 @@ private static class BenchmarkResult { (byte)0xf4, (byte)0xf5, (byte)0xf6, (byte)0xf7 }; - /* Static DESede (Triple DES) key buffer */ + /* Static DESede (Triple DES) key buffer */ private static final byte[] STATIC_DES3_KEY = new byte[] { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, @@ -94,19 +94,19 @@ private static void printDeltaTable() { groupedResults = new HashMap<>(); for (BenchmarkResult result : results) { groupedResults - .computeIfAbsent(result.operation, k -> new HashMap<>()) - .put(result.provider, result.throughput); + .computeIfAbsent(result.operation, k -> new HashMap<>()) + .put(result.provider, result.throughput); } /* Sort operations to group RSA operations together */ List sortedOperations = new ArrayList<>(groupedResults.keySet()); Collections.sort(sortedOperations, (a, b) -> { - boolean aIsRSA = a.startsWith("RSA"); - boolean bIsRSA = b.startsWith("RSA"); + boolean aIsRSA = a.startsWith("RSA"); + boolean bIsRSA = b.startsWith("RSA"); - if (aIsRSA && !bIsRSA) return -1; - if (!aIsRSA && bIsRSA) return 1; - return a.compareTo(b); + if (aIsRSA && !bIsRSA) return -1; + if (!aIsRSA && bIsRSA) return 1; + return a.compareTo(b); }); /* Calculate and print deltas */ @@ -119,6 +119,17 @@ private static void printDeltaTable() { provider = providerEntry.getKey(); if (!provider.equals("wolfJCE")) { otherSpeed = providerEntry.getValue(); + + /* Adjust provider name for RSA operations */ + String displayProvider = provider; + if (isRSAOperation) { + if (operation.contains("key gen")) { + displayProvider = "SunRsaSign"; // Key generation uses SunRsaSign + } else { + displayProvider = "SunJCE"; // Public/private operations use SunJCE + } + } + if (isRSAOperation) { deltaValue = wolfSpeed - otherSpeed; deltaPercent = ((wolfSpeed / otherSpeed) - 1.0) * 100; @@ -126,11 +137,19 @@ private static void printDeltaTable() { deltaValue = wolfSpeed - otherSpeed; deltaPercent = ((wolfSpeed / otherSpeed) - 1.0) * 100; } - System.out.printf("| %-40s | %-12s | %+8.2f | %+8.1f |%n", - operation.replace("RSA", "RSA/ECB/PKCS1Padding RSA"), - provider, - deltaValue, - deltaPercent); + + /* Ensure unique operation-provider combination */ + String uniqueKey = operation + "|" + displayProvider; + if (!groupedResults.containsKey(uniqueKey)) { + System.out.printf("| %-40s | %-12s | %+8.2f | %+8.1f |%n", + operation.replace("RSA", "RSA/ECB/PKCS1Padding RSA"), + displayProvider, + deltaValue, + deltaPercent); + + /* Mark this combination as processed */ + groupedResults.put(uniqueKey, null); + } } } } @@ -138,6 +157,7 @@ private static void printDeltaTable() { System.out.println("* Delta Value: MiB/s for symmetric ciphers, operations/second for RSA"); } + /* Run symmetric encryption/decryption benchmarks */ private static void runEncDecBenchmark(String algorithm, String mode, String padding, String providerName) throws Exception { SecretKey key;