From f997661a3c45e7998f9cafc4f4ddffc68733a16a Mon Sep 17 00:00:00 2001 From: Jack Tjaden Date: Mon, 6 Jan 2025 13:04:41 -0700 Subject: [PATCH] JCE: Implements Bouncy Castle and SunJCE tests with random IV --- README_JCE.md | 23 +++++ examples/provider/CryptoBenchmark.java | 115 ++++++++++++++++++------- examples/provider/CryptoBenchmark.sh | 90 ++++++++++++++++++- 3 files changed, 198 insertions(+), 30 deletions(-) diff --git a/README_JCE.md b/README_JCE.md index f2362b6..b7941a3 100644 --- a/README_JCE.md +++ b/README_JCE.md @@ -307,6 +307,29 @@ ant build-jce-release # Build JCE JAR ``` This script requires for JAVA_HOME to be set. +For Bouncy Castle comparison testing: + +CryptoBenchmark.sh will prompt with the following: + +``` +Would you like to download Bouncy Castle JARs? (y/n) +``` +If you respond with 'y', the script will download the Bouncy Castle JARs and run the benchmark with Bouncy Castle. At the end of the benchmark, the script will prompt whether or not to remove the Bouncy Castle JAR files. + +If you prefer to download the JARs manually, follow the instructions below: + +Visit [bouncy-castle-java](https://www.bouncycastle.org/download/bouncy-castle-java/) +Download: +``` +bcprov-jdk18on-1.79.jar # Bouncy Castle Provider +bctls-jdk18on-1.79.jar # Bouncy Castle DTLS/TLS API/JSSE Provider +``` +Copy jar files to wolfcrypt-jni/lib/: +``` +cp bcprov-jdk18on-1.79.jar wolfcrypt-jni/lib +cp bctls-jdk18on-1.79.jar wolfcrypt-jni/lib +``` + ### JAR Code Signing --------- diff --git a/examples/provider/CryptoBenchmark.java b/examples/provider/CryptoBenchmark.java index f189ab0..7ca41d3 100644 --- a/examples/provider/CryptoBenchmark.java +++ b/examples/provider/CryptoBenchmark.java @@ -21,7 +21,7 @@ public class CryptoBenchmark { private static final int GCM_TAG_LENGTH = 128; /* GCM auth tag length in bits */ private static final int AES_KEY_SIZE = 256; - /* Static key and IV buffers */ + /* Static key buffer */ private static final byte[] STATIC_KEY = new byte[] { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef, @@ -33,18 +33,11 @@ public class CryptoBenchmark { (byte)0xf4, (byte)0xf5, (byte)0xf6, (byte)0xf7 }; - private static final byte[] STATIC_IV = new byte[] { - (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78, - (byte)0x90, (byte)0xab, (byte)0xcd, (byte)0xef, - (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, - (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01 - }; - private static byte[] generateTestData(int size) { return new byte[size]; /* Creates array initialized with zeros */ } - private static void runBenchmark(String algorithm, String mode) throws Exception { + private static void runBenchmark(String algorithm, String mode, String providerName) throws Exception { /* Key generation variables */ SecretKey key; @@ -73,8 +66,11 @@ private static void runBenchmark(String algorithm, String mode) throws Exception /* Use static pre-made key */ key = new SecretKeySpec(STATIC_KEY, "AES"); - /* Use static pre-made IV */ - ivBytes = STATIC_IV; + /* Generate random IV */ + SecureRandom secureRandom = new SecureRandom(); + ivBytes = new byte[AES_BLOCK_SIZE]; + secureRandom.nextBytes(ivBytes); + if (mode.equals("GCM")) { params = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes); } else { @@ -84,14 +80,20 @@ private static void runBenchmark(String algorithm, String mode) throws Exception /* Generate test data filled with zeros */ testData = generateTestData(DATA_SIZE); - /* Initialize cipher */ - cipher = Cipher.getInstance(algorithm); + /* Initialize cipher with specific provider */ + cipher = Cipher.getInstance(algorithm, 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); + } 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); } @@ -99,6 +101,11 @@ private static void runBenchmark(String algorithm, String mode) throws Exception /* 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); + } cipher.init(Cipher.ENCRYPT_MODE, key, params); encryptedData = cipher.doFinal(testData); } @@ -114,14 +121,16 @@ private static void runBenchmark(String algorithm, String mode) throws Exception /* Calculate throughput using seconds for MiB/s */ encryptThroughput = (DATA_SIZE / (encryptTime / 1000000000.0)) / (1024.0 * 1024.0); - /* Print encryption results immediately */ - String testName = "AES-256-" + mode; - System.out.printf("%s-enc %4.2f MiB took %1.3f ms, %8.3f MiB/s%n", - testName, dataSizeMiB, encryptTimeMS, encryptThroughput); + /* Store encryption results */ + String testName = String.format("AES-256-%s (%s)", mode, 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 */ 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); } @@ -134,23 +143,71 @@ private static void runBenchmark(String algorithm, String mode) throws Exception /* Calculate throughput using seconds for MiB/s */ decryptThroughput = (DATA_SIZE / (decryptTime / 1000000000.0)) / (1024.0 * 1024.0); - /* Print decryption results immediately */ - System.out.printf("%s-dec %4.2f MiB took %1.3f ms, %8.3f MiB/s%n", - testName, dataSizeMiB, decryptTimeMS, decryptThroughput); + /* Store decryption results */ + System.out.printf("| %-40s | %8.3f | %8.3f | %8.3f |%n", + testName + " dec", dataSizeMiB, decryptTimeMS, decryptThroughput); } public static void main(String[] args) { try { - /* Register wolfJCE as the default provider */ - Security.insertProviderAt(new WolfCryptProvider(), 1); - - System.out.println("------------------------------------------------------------------------------"); - System.out.println(" JCE Crypto Benchmark"); - System.out.println("------------------------------------------------------------------------------"); + /* 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<>(); + + /* 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]); - /* Run benchmarks for different algorithms */ - runBenchmark("AES/CBC/PKCS5Padding", "CBC"); - runBenchmark("AES/GCM/NoPadding", "GCM"); + 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("|------------------------------------------|----------|----------|----------|"); + + /* Test each provider */ + for (int i = 0; i < providers.length; i++) { + Security.insertProviderAt(providers[i], 1); + + /* Run benchmarks for different algorithms */ + runBenchmark("AES/CBC/PKCS5Padding", "CBC", providerNames[i]); + runBenchmark("AES/GCM/NoPadding", "GCM", 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("-----------------------------------------------------------------------------"); } catch (Exception e) { System.err.println("Benchmark failed: " + e.getMessage()); diff --git a/examples/provider/CryptoBenchmark.sh b/examples/provider/CryptoBenchmark.sh index 8d5bcb1..6280aa8 100755 --- a/examples/provider/CryptoBenchmark.sh +++ b/examples/provider/CryptoBenchmark.sh @@ -1,5 +1,93 @@ #!/bin/bash +# Flag to track if we downloaded BC during this session +BC_DOWNLOADED=false + +# Function to download Bouncy Castle JARs +download_bc_jars() { + local bc_version="1.79" + local lib_dir="../../../lib" + local bc_url="https://downloads.bouncycastle.org/java" + + echo -n "Downloading Bouncy Castle JARs... " + + # Create lib directory if it doesn't exist + mkdir -p "$lib_dir" 2>/dev/null + + # Download both required JARs + if command -v wget >/dev/null; then + wget -q -P "$lib_dir" "$bc_url/bcprov-jdk18on-$bc_version.jar" 2>/dev/null && + wget -q -P "$lib_dir" "$bc_url/bctls-jdk18on-$bc_version.jar" 2>/dev/null || return 1 + elif command -v curl >/dev/null; then + curl -s -L -o "$lib_dir/bcprov-jdk18on-$bc_version.jar" "$bc_url/bcprov-jdk18on-$bc_version.jar" 2>/dev/null && + curl -s -L -o "$lib_dir/bctls-jdk18on-$bc_version.jar" "$bc_url/bctls-jdk18on-$bc_version.jar" 2>/dev/null || return 1 + else + echo "failed" + echo "Error: Neither wget nor curl is available. Please install either wget or curl." + return 1 + fi + + # Verify downloads were successful + if [ -f "$lib_dir/bcprov-jdk18on-$bc_version.jar" ] && [ -f "$lib_dir/bctls-jdk18on-$bc_version.jar" ]; then + echo "done" + BC_DOWNLOADED=true + return 0 + else + echo "failed" + return 1 + fi +} + +# Function to cleanup BC JARs +cleanup_bc_jars() { + local lib_dir="../../../lib" + echo -n "Removing Bouncy Castle JARs... " + rm -f "$lib_dir/bcprov-jdk18on-1.79.jar" "$lib_dir/bctls-jdk18on-1.79.jar" 2>/dev/null + if [ $? -eq 0 ]; then + echo "done" + return 0 + else + echo "failed" + return 1 + fi +} + cd ./examples/build/provider + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../../lib/:/usr/local/lib -java -classpath ../../../lib/wolfcrypt-jni.jar:./ -Dsun.boot.library.path=../../../lib/ CryptoBenchmark $@ + +CLASSPATH="../../../lib/wolfcrypt-jni.jar:." + +if [ -f "../../../lib/bcprov-jdk18on-1.79.jar" ] && [ -f "../../../lib/bctls-jdk18on-1.79.jar" ]; then + echo "Running crypto benchmark with Bouncy Castle" + CLASSPATH="$CLASSPATH:../../../lib/bcprov-jdk18on-1.79.jar:../../../lib/bctls-jdk18on-1.79.jar" +else + echo "Bouncy Castle JARs not found in lib directory" + read -p "Would you like to download Bouncy Castle JARs? (y/n) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + if download_bc_jars; then + echo "Running crypto benchmark with Bouncy Castle" + CLASSPATH="$CLASSPATH:../../../lib/bcprov-jdk18on-1.79.jar:../../../lib/bctls-jdk18on-1.79.jar" + else + echo "Running crypto benchmark without Bouncy Castle due to download failure" + fi + else + echo "Running crypto benchmark without Bouncy Castle" + fi +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 + echo + read -p "Would you like to remove the downloaded Bouncy Castle JARs? (y/n) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + cleanup_bc_jars + else + echo "Keeping Bouncy Castle JARs for future use" + fi +fi