Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JCE: Implements DES algorithm, results comparison table and provider version information #94

Merged
merged 1 commit into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 135 additions & 51 deletions examples/provider/CryptoBenchmark.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<BenchmarkResult> 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,
Expand All @@ -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<String, Map<String, Double>> groupedResults;
String operation;
Map<String, Double> 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<String, Map<String, Double>> entry : groupedResults.entrySet()) {
operation = entry.getKey();
providerResults = entry.getValue();
wolfSpeed = providerResults.getOrDefault("wolfJCE", 0.0);

for (Map.Entry<String, Double> 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;
Expand All @@ -63,45 +139,54 @@ 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);
} else {
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);
}
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);
Expand All @@ -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) {
Expand All @@ -158,57 +236,63 @@ 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<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]);

/* 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 */
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]);
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();
Expand Down
6 changes: 3 additions & 3 deletions examples/provider/CryptoBenchmark.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading