Skip to content

Commit

Permalink
Merge pull request #93 from jackctj117/wolfJSSE_Benchmark
Browse files Browse the repository at this point in the history
JCE: Implements Bouncy Castle and SunJCE tests with random IV
  • Loading branch information
cconlon authored Jan 7, 2025
2 parents 6ffb703 + f997661 commit 9eb95a6
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 30 deletions.
23 changes: 23 additions & 0 deletions README_JCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
---------

Expand Down
115 changes: 86 additions & 29 deletions examples/provider/CryptoBenchmark.java
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;

Expand Down Expand Up @@ -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 {
Expand All @@ -84,21 +80,32 @@ 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);
}

/* 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);
}
Expand All @@ -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);
}
Expand All @@ -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<Provider> providerList = new java.util.ArrayList<>();
java.util.List<String> 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());
Expand Down
90 changes: 89 additions & 1 deletion examples/provider/CryptoBenchmark.sh
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 9eb95a6

Please sign in to comment.