From 45f14e5e7c53bf869879edb3fadcb3159b341f02 Mon Sep 17 00:00:00 2001
From: Jack Tjaden <jack@wolfssl.com>
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<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,
+      (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<String, Map<String, Double>> groupedResults;
+    String operation;
+    Map<String, Double> 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<String, Map<String, Double>> entry : groupedResults.entrySet()) {
+      operation = entry.getKey();
+      providerResults = entry.getValue();
+      wolfSpeed = providerResults.getOrDefault("wolfJCE", 0.0);
+      isRSAOperation = operation.startsWith("RSA");
+
+      for (Map.Entry<String, Double> 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<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,
-        (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<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("|------------------------------------------|----------|----------|----------|");
+    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<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;
+      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<Provider> providerList = new java.util.ArrayList<>();
+      java.util.List<String> 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<Provider> providerList = new java.util.ArrayList<>();
-            java.util.List<String> 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 <jack@wolfssl.com>
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<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,
-      (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<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,
+        (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<String, Map<String, Double>> groupedResults;
-    String operation;
-    Map<String, Double> 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<String, Map<String, Double>> entry : groupedResults.entrySet()) {
-      operation = entry.getKey();
-      providerResults = entry.getValue();
-      wolfSpeed = providerResults.getOrDefault("wolfJCE", 0.0);
-      isRSAOperation = operation.startsWith("RSA");
-
-      for (Map.Entry<String, Double> 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<String, Map<String, Double>> groupedResults;
+        Map<String, Double> 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<String> 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<String, Double> 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<Provider> providerList = new java.util.ArrayList<>();
-      java.util.List<String> 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<Provider> providerList = new java.util.ArrayList<>();
+            java.util.List<String> 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 <jack@wolfssl.com>
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 <jack@wolfssl.com>
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 <jack@wolfssl.com>
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<String> 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;