diff --git a/examples/provider/CryptoBenchmark.java b/examples/provider/CryptoBenchmark.java index 7ca41d3..1253450 100644 --- a/examples/provider/CryptoBenchmark.java +++ b/examples/provider/CryptoBenchmark.java @@ -1,5 +1,4 @@ import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.IvParameterSpec; @@ -8,21 +7,40 @@ import java.security.SecureRandom; import java.security.Security; import java.security.spec.AlgorithmParameterSpec; -import java.util.Arrays; +import java.util.*; 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; /* Number of iterations */ + 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 GCM_TAG_LENGTH = 128; /* GCM auth tag length in bits */ - private static final int AES_KEY_SIZE = 256; + private static final int DES3_BLOCK_SIZE = 8; + private static final int GCM_TAG_LENGTH = 128; - /* Static key buffer */ - private static final byte[] STATIC_KEY = new byte[] { + /* 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, @@ -33,25 +51,83 @@ public class CryptoBenchmark { (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]; /* Creates array initialized with zeros */ + return new byte[size]; + } + + private static void printProviderInfo(Provider provider) { + System.out.printf("%s version: %.1f%n", provider.getName(), provider.getVersion()); } - private static void runBenchmark(String algorithm, String mode, String providerName) throws Exception { - /* Key generation variables */ + 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("|------------------------------------------|----------|----------|----------|"); + + /* 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); + + 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; + + System.out.printf("| %-40s | %-8s | %+8.2f | %+8.1f |%n", + operation, + provider, + deltaMiBs, + deltaPercent); + } + } + } + System.out.println("-----------------------------------------------------------------------------"); + } + + private static void runBenchmark(String algorithm, String mode, String padding, + String providerName) throws Exception { SecretKey key; - - /* IV/Nonce generation variables */ byte[] ivBytes; - /* Using specific type instead of Object */ AlgorithmParameterSpec params; - /* Test data variables */ byte[] testData; byte[] encryptedData = null; double dataSizeMiB; - - /* Cipher variables */ Cipher cipher; + String cipherName = algorithm + "/" + mode + "/" + padding; /* Timing variables */ long startTime; @@ -63,13 +139,26 @@ private static void runBenchmark(String algorithm, String mode, String providerN double encryptTimeMS; double decryptTimeMS; - /* Use static pre-made key */ - key = new SecretKeySpec(STATIC_KEY, "AES"); - + /* 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); + } + /* Generate random IV */ SecureRandom secureRandom = new SecureRandom(); - ivBytes = new byte[AES_BLOCK_SIZE]; - secureRandom.nextBytes(ivBytes); + 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); + } if (mode.equals("GCM")) { params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); @@ -77,15 +166,13 @@ private static void runBenchmark(String algorithm, String mode, String providerN params = new IvParameterSpec(ivBytes); } - /* Generate test data filled with zeros */ testData = generateTestData(DATA_SIZE); /* Initialize cipher with specific provider */ - cipher = Cipher.getInstance(algorithm, providerName); + cipher = Cipher.getInstance(cipherName, providerName); /* Warm up phase */ for (int i = 0; i < WARMUP_ITERATIONS; i++) { - /* Generate fresh IV for each warmup iteration when using GCM */ if (mode.equals("GCM")) { secureRandom.nextBytes(ivBytes); params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); @@ -93,7 +180,6 @@ private static void runBenchmark(String algorithm, String mode, String providerN cipher.init(Cipher.ENCRYPT_MODE, key, params); encryptedData = cipher.doFinal(testData); - /* Use the same params for decryption since we're decrypting what we just encrypted */ cipher.init(Cipher.DECRYPT_MODE, key, params); cipher.doFinal(encryptedData); } @@ -101,7 +187,6 @@ private static void runBenchmark(String algorithm, String mode, String providerN /* Benchmark encryption */ startTime = System.nanoTime(); for (int i = 0; i < TEST_ITERATIONS; i++) { - /* Generate fresh IV for each iteration when using GCM */ if (mode.equals("GCM")) { secureRandom.nextBytes(ivBytes); params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); @@ -112,40 +197,33 @@ private static void runBenchmark(String algorithm, String mode, String providerN endTime = System.nanoTime(); encryptTime = (endTime - startTime) / TEST_ITERATIONS; - /* Calculate data size in MiB */ dataSizeMiB = (DATA_SIZE * TEST_ITERATIONS) / (1024.0 * 1024.0); - - /* Calculate time in milliseconds */ encryptTimeMS = encryptTime / 1000000.0; - - /* Calculate throughput using seconds for MiB/s */ encryptThroughput = (DATA_SIZE / (encryptTime / 1000000000.0)) / (1024.0 * 1024.0); - /* Store encryption results */ - String testName = String.format("AES-256-%s (%s)", mode, providerName); + String testName = String.format("%s (%s)", cipherName, providerName); System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", testName + " enc", dataSizeMiB, encryptTimeMS, encryptThroughput); - /* Benchmark decryption using the encrypted data from encryption benchmark */ + results.add(new BenchmarkResult(providerName, cipherName + " enc", encryptThroughput)); + + /* Benchmark decryption */ startTime = System.nanoTime(); for (int i = 0; i < TEST_ITERATIONS; i++) { - /* Note: For decryption, we use the last IV/params from encryption - since we're decrypting the last encrypted data */ cipher.init(Cipher.DECRYPT_MODE, key, params); cipher.doFinal(encryptedData); } endTime = System.nanoTime(); decryptTime = (endTime - startTime) / TEST_ITERATIONS; - /* Calculate time in milliseconds */ decryptTimeMS = decryptTime / 1000000.0; - - /* Calculate throughput using seconds for MiB/s */ decryptThroughput = (DATA_SIZE / (decryptTime / 1000000000.0)) / (1024.0 * 1024.0); - /* Store decryption results */ 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)); } public static void main(String[] args) { @@ -158,36 +236,37 @@ public static void main(String[] args) { bcProvider = (Provider) bcClass.getDeclaredConstructor().newInstance(); hasBouncyCastle = true; } catch (Exception e) { - // Bouncy Castle not available + /* 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<>(); - /* Always add wolfJCE first */ providerList.add(new WolfCryptProvider()); providerNameList.add("wolfJCE"); - /* Always add SunJCE second */ providerList.add(new com.sun.crypto.provider.SunJCE()); providerNameList.add("SunJCE"); - /* Add Bouncy Castle if available */ 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("-----------------------------------------------------------------------------"); - /* Print table header */ - System.out.println("| Operation | Size MiB | ms | MiB/s |"); + System.out.println("| Operation | Size MiB | ms | MiB/s |"); System.out.println("|------------------------------------------|----------|----------|----------|"); /* Test each provider */ @@ -195,20 +274,25 @@ public static void main(String[] args) { Security.insertProviderAt(providers[i], 1); /* Run benchmarks for different algorithms */ - runBenchmark("AES/CBC/PKCS5Padding", "CBC", providerNames[i]); - runBenchmark("AES/GCM/NoPadding", "GCM", providerNames[i]); + 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]); + } - /* Add separator between providers */ if (i < providers.length - 1) { System.out.println("|------------------------------------------|----------|----------|----------|"); } - /* Reset provider after each test */ Security.removeProvider(providers[i].getName()); } System.out.println("-----------------------------------------------------------------------------"); + printDeltaTable(); + } catch (Exception e) { System.err.println("Benchmark failed: " + e.getMessage()); e.printStackTrace(); diff --git a/examples/provider/CryptoBenchmark.sh b/examples/provider/CryptoBenchmark.sh index 6280aa8..aaa3c82 100755 --- a/examples/provider/CryptoBenchmark.sh +++ b/examples/provider/CryptoBenchmark.sh @@ -80,10 +80,10 @@ fi # Run the benchmark java -classpath $CLASSPATH -Dsun.boot.library.path=../../../lib/ CryptoBenchmark $@ -# After benchmark completion, ask about cleanup if we downloaded the files -if [ "$BC_DOWNLOADED" = true ]; then +# Always prompt for cleanup after benchmark completion if Bouncy Castle files exist +if [ -f "../../../lib/bcprov-jdk18on-1.79.jar" ] && [ -f "../../../lib/bctls-jdk18on-1.79.jar" ]; then echo - read -p "Would you like to remove the downloaded Bouncy Castle JARs? (y/n) " -n 1 -r + read -p "Would you like to remove the Bouncy Castle JARs? (y/n) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then cleanup_bc_jars