add FFDH to speed command
authorHubert Kario <hubert@kario.pl>
Sat, 18 Jan 2020 18:13:02 +0000 (19:13 +0100)
committerTomas Mraz <tmraz@fedoraproject.org>
Mon, 1 Jun 2020 07:01:23 +0000 (09:01 +0200)
the openssl speed command could not benchmark FFDH speed, but it could
benchmark ECDH, making comparisons between the two hard

this commit adds this feature

fixes #9475

Signed-off-by: Hubert Kario <hubert@kario.pl>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
(Merged from https://github.com/openssl/openssl/pull/10887)

apps/speed.c

index bd05631..f481b6b 100644 (file)
@@ -16,6 +16,7 @@
 #define ECDH_SECONDS    10
 #define EdDSA_SECONDS   10
 #define SM2_SECONDS     10
+#define FFDH_SECONDS    10
 
 /* We need to use some deprecated APIs */
 #define OPENSSL_SUPPRESS_DEPRECATED
@@ -98,6 +99,9 @@
 # include <openssl/rsa.h>
 # include "./testrsa.h"
 #endif
+#ifndef OPENSSL_NO_DH
+# include <openssl/dh.h>
+#endif
 #include <openssl/x509.h>
 #if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0)
 # include <openssl/dsa.h>
 #define MAX_MISALIGNMENT 63
 #define MAX_ECDH_SIZE   256
 #define MISALIGN        64
+#define MAX_FFDH_SIZE 1024
 
 typedef struct openssl_speed_sec_st {
     int sym;
@@ -134,6 +139,7 @@ typedef struct openssl_speed_sec_st {
     int ecdh;
     int eddsa;
     int sm2;
+    int ffdh;
 } openssl_speed_sec_t;
 
 static volatile int run = 0;
@@ -435,6 +441,22 @@ static const OPT_PAIR rsa_choices[RSA_NUM] = {
 static double rsa_results[RSA_NUM][2];  /* 2 ops: sign then verify */
 #endif /* OPENSSL_NO_RSA */
 
+#ifndef OPENSSL_NO_DH
+enum ff_params_t {
+    R_FFDH_2048, R_FFDH_3072, R_FFDH_4096, R_FFDH_6144, R_FFDH_8192, FFDH_NUM
+};
+
+static const OPT_PAIR ffdh_choices[FFDH_NUM] = {
+    {"ffdh2048", R_FFDH_2048},
+    {"ffdh3072", R_FFDH_3072},
+    {"ffdh4096", R_FFDH_4096},
+    {"ffdh6144", R_FFDH_6144},
+    {"ffdh8192", R_FFDH_8192},
+};
+
+static double ffdh_results[FFDH_NUM][1];  /* 1 op: derivation */
+#endif /* OPENSSL_NO_DH */
+
 #ifndef OPENSSL_NO_EC
 enum ec_curves_t {
     R_EC_P160, R_EC_P192, R_EC_P224, R_EC_P256, R_EC_P384, R_EC_P521,
@@ -561,6 +583,11 @@ typedef struct loopargs_st {
     unsigned char *secret_a;
     unsigned char *secret_b;
     size_t outlen[EC_NUM];
+#endif
+#ifndef OPENSSL_NO_DH
+    EVP_PKEY_CTX *ffdh_ctx[FFDH_NUM];
+    unsigned char *secret_ff_a;
+    unsigned char *secret_ff_b;
 #endif
     EVP_CIPHER_CTX *ctx;
 #ifndef OPENSSL_NO_DEPRECATED_3_0
@@ -1067,6 +1094,24 @@ static int RSA_verify_loop(void *args)
 }
 #endif
 
+#ifndef OPENSSL_NO_DH
+static long ffdh_c[FFDH_NUM][1];
+
+static int FFDH_derive_key_loop(void *args)
+{
+        loopargs_t *tempargs = *(loopargs_t **) args;
+        EVP_PKEY_CTX *ffdh_ctx = tempargs->ffdh_ctx[testnum];
+        unsigned char *derived_secret = tempargs->secret_ff_a;
+        size_t outlen = MAX_FFDH_SIZE;
+        int count;
+
+        for (count = 0; COND(ffdh_c[testnum][0]); count++)
+            EVP_PKEY_derive(ffdh_ctx, derived_secret, &outlen);
+
+        return count;
+}
+#endif /* OPENSSL_NO_DH */
+
 #if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0)
 static long dsa_c[DSA_NUM][2];
 static int DSA_sign_loop(void *args)
@@ -1463,7 +1508,8 @@ int speed_main(int argc, char **argv)
 #endif
     openssl_speed_sec_t seconds = { SECONDS, RSA_SECONDS, DSA_SECONDS,
                                     ECDSA_SECONDS, ECDH_SECONDS,
-                                    EdDSA_SECONDS, SM2_SECONDS };
+                                    EdDSA_SECONDS, SM2_SECONDS,
+                                    FFDH_SECONDS };
 
     /* What follows are the buffers and key material. */
 #if !defined(OPENSSL_NO_RC5) && !defined(OPENSSL_NO_DEPRECATED_3_0)
@@ -1521,6 +1567,23 @@ int speed_main(int argc, char **argv)
     uint8_t rsa_doit[RSA_NUM] = { 0 };
     int primes = RSA_DEFAULT_PRIME_NUM;
 #endif
+#ifndef OPENSSL_NO_DH
+    typedef struct ffdh_params_st {
+        const char *name;
+        unsigned int nid;
+        unsigned int bits;
+    } FFDH_PARAMS;
+
+    static const FFDH_PARAMS ffdh_params[FFDH_NUM] = {
+        {"ffdh2048", NID_ffdhe2048, 2048},
+        {"ffdh3072", NID_ffdhe3072, 3072},
+        {"ffdh4096", NID_ffdhe4096, 4096},
+        {"ffdh6144", NID_ffdhe6144, 6144},
+        {"ffdh8192", NID_ffdhe8192, 8192}
+    };
+    uint8_t ffdh_doit[FFDH_NUM] = { 0 };
+
+#endif /* OPENSSL_NO_DH */
 #if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0)
     static const unsigned int dsa_bits[DSA_NUM] = { 512, 1024, 2048 };
     uint8_t dsa_doit[DSA_NUM] = { 0 };
@@ -1720,7 +1783,7 @@ int speed_main(int argc, char **argv)
         case OPT_SECONDS:
             seconds.sym = seconds.rsa = seconds.dsa = seconds.ecdsa
                         = seconds.ecdh = seconds.eddsa
-                        = seconds.sm2 = atoi(opt_arg());
+                        = seconds.sm2 = seconds.ffdh = atoi(opt_arg());
             break;
         case OPT_BYTES:
             lengths_single = atoi(opt_arg());
@@ -1767,6 +1830,18 @@ int speed_main(int argc, char **argv)
             }
         }
 #endif
+#ifndef OPENSSL_NO_DH
+        if (strncmp(algo, "ffdh", 4) == 0) {
+            if (algo[4] == '\0') {
+                memset(ffdh_doit, 1, sizeof(ffdh_doit));
+                continue;
+            }
+            if (opt_found(algo, ffdh_choices, &i)) {
+                ffdh_doit[i] = 2;
+                continue;
+            }
+        }
+#endif
 #if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0)
         if (strncmp(algo, "dsa", 3) == 0) {
             if (algo[3] == '\0') {
@@ -1901,6 +1976,10 @@ int speed_main(int argc, char **argv)
 #ifndef OPENSSL_NO_EC
         loopargs[i].secret_a = app_malloc(MAX_ECDH_SIZE, "ECDH secret a");
         loopargs[i].secret_b = app_malloc(MAX_ECDH_SIZE, "ECDH secret b");
+#endif
+#ifndef OPENSSL_NO_DH
+        loopargs[i].secret_ff_a = app_malloc(MAX_FFDH_SIZE, "FFDH secret a");
+        loopargs[i].secret_ff_b = app_malloc(MAX_FFDH_SIZE, "FFDH secret b");
 #endif
     }
 
@@ -1919,6 +1998,9 @@ int speed_main(int argc, char **argv)
 #if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_NO_DEPRECATED_3_0)
         memset(rsa_doit, 1, sizeof(rsa_doit));
 #endif
+#ifndef OPENSSL_NO_DH
+        memset(ffdh_doit, 1, sizeof(ffdh_doit));
+#endif
 #if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0)
         memset(dsa_doit, 1, sizeof(dsa_doit));
 #endif
@@ -2110,7 +2192,7 @@ int speed_main(int argc, char **argv)
         c[D_IGE_256_AES][i] = c[D_IGE_256_AES][i - 1] * l0 / l1;
     }
 
-#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+#  if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_NO_DEPRECATED_3_0)
     rsa_c[R_RSA_512][0] = count / 2000;
     rsa_c[R_RSA_512][1] = count / 400;
     for (i = 1; i < RSA_NUM; i++) {
@@ -2260,6 +2342,19 @@ int speed_main(int argc, char **argv)
 #   endif
 #  endif                          /* OPENSSL_NO_EC */
 
+#  ifndef OPENSSL_NO_DH
+    ffdh_c[R_FFDH_2048][0] = count / 1000;
+    for (i = R_FFDH_3072; i <= R_FFDH_8192; i++) {
+        ffdh_c[i][0] = ffdh_c[i - 1][0] / 2;
+        if (ffdh_doit[i] <= 1 && ffdh_c[i][0] == 0) {
+            ffdh_doit[i] = 0;
+        } else {
+            if (ffdh_c[i][0] == 0)
+                ffdh_c[i][0] = 1;
+        }
+    }
+#  endif /* OPENSSL_NO_DH */
+
 # else
 /* not worth fixing */
 #  error "You cannot disable DES on systems without SIGALRM."
@@ -3510,8 +3605,188 @@ int speed_main(int argc, char **argv)
         }
     }
 # endif                         /* OPENSSL_NO_SM2 */
-
 #endif                          /* OPENSSL_NO_EC */
+
+#ifndef OPENSSL_NO_DH
+    for (testnum = 0; testnum < FFDH_NUM; testnum++) {
+        int ffdh_checks = 1;
+
+        if (!ffdh_doit[testnum])
+            continue;
+
+        for (i = 0; i < loopargs_len; i++) {
+            EVP_PKEY *pkey_A = NULL;
+            EVP_PKEY *pkey_B = NULL;
+            EVP_PKEY_CTX *ffdh_ctx = NULL;
+            EVP_PKEY_CTX *test_ctx = NULL;
+            size_t secret_size;
+            size_t test_out;
+
+            /* Ensure that the error queue is empty */
+            if (ERR_peek_error()) {
+                BIO_printf(bio_err,
+                           "WARNING: the error queue contains previous unhandled errors.\n");
+                ERR_print_errors(bio_err);
+            }
+
+            pkey_A = EVP_PKEY_new();
+            if (!pkey_A) {
+                BIO_printf(bio_err, "Error while initialising EVP_PKEY (out of memory?).\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+            pkey_B = EVP_PKEY_new();
+            if (!pkey_B) {
+                BIO_printf(bio_err, "Error while initialising EVP_PKEY (out of memory?).\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+
+            ffdh_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DH, NULL);
+            if (!ffdh_ctx) {
+                BIO_printf(bio_err, "Error while allocating EVP_PKEY_CTX.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+
+            if (EVP_PKEY_keygen_init(ffdh_ctx) <= 0) {
+                BIO_printf(bio_err, "Error while initialising EVP_PKEY_CTX.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+            if (EVP_PKEY_CTX_set_dh_nid(ffdh_ctx, ffdh_params[testnum].nid) <= 0) {
+                BIO_printf(bio_err, "Error setting DH key size for keygen.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+
+            if (EVP_PKEY_keygen(ffdh_ctx, &pkey_A) <= 0 ||
+                EVP_PKEY_keygen(ffdh_ctx, &pkey_B) <= 0) {
+                BIO_printf(bio_err, "FFDH key generation failure.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+
+            EVP_PKEY_CTX_free(ffdh_ctx);
+
+            /* check if the derivation works correctly both ways so that
+             * we know if future derive calls will fail, and we can skip
+             * error checking in benchmarked code */
+            ffdh_ctx = EVP_PKEY_CTX_new(pkey_A, NULL);
+            if (!ffdh_ctx) {
+                BIO_printf(bio_err, "Error while allocating EVP_PKEY_CTX.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+            if (EVP_PKEY_derive_init(ffdh_ctx) <= 0) {
+                BIO_printf(bio_err, "FFDH derivation context init failure.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+            if (EVP_PKEY_derive_set_peer(ffdh_ctx, pkey_B) <= 0) {
+                BIO_printf(bio_err, "Assigning peer key for derivation failed.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+            if (EVP_PKEY_derive(ffdh_ctx, NULL, &secret_size) <= 0) {
+                BIO_printf(bio_err, "Checking size of shared secret failed.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+            if (secret_size > MAX_FFDH_SIZE) {
+                BIO_printf(bio_err, "Assertion failure: shared secret too large.\n");
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+            if (EVP_PKEY_derive(ffdh_ctx,
+                                loopargs[i].secret_ff_a,
+                                &secret_size) <= 0) {
+                BIO_printf(bio_err, "Shared secret derive failure.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+            /* Now check from side B */
+            test_ctx = EVP_PKEY_CTX_new(pkey_B, NULL);
+            if (!test_ctx) {
+                BIO_printf(bio_err, "Error while allocating EVP_PKEY_CTX.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+            if (!EVP_PKEY_derive_init(test_ctx) ||
+                !EVP_PKEY_derive_set_peer(test_ctx, pkey_A) ||
+                !EVP_PKEY_derive(test_ctx, NULL, &test_out) ||
+                !EVP_PKEY_derive(test_ctx, loopargs[i].secret_ff_b, &test_out) ||
+                test_out != secret_size) {
+                BIO_printf(bio_err, "FFDH computation failure.\n");
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+
+            /* compare the computed secrets */
+            if (CRYPTO_memcmp(loopargs[i].secret_ff_a,
+                              loopargs[i].secret_ff_b, secret_size)) {
+                BIO_printf(bio_err, "FFDH computations don't match.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+                ffdh_checks = 0;
+                break;
+            }
+
+            loopargs[i].ffdh_ctx[testnum] = ffdh_ctx;
+
+            EVP_PKEY_free(pkey_A);
+            pkey_A = NULL;
+            EVP_PKEY_free(pkey_B);
+            pkey_B = NULL;
+            EVP_PKEY_CTX_free(test_ctx);
+            test_ctx = NULL;
+        }
+        if (ffdh_checks != 0) {
+            pkey_print_message("", "ffdh", ffdh_c[testnum][0],
+                               ffdh_params[testnum].bits, seconds.ffdh);
+            Time_F(START);
+            count =
+                run_benchmark(async_jobs, FFDH_derive_key_loop, loopargs);
+            d = Time_F(STOP);
+            BIO_printf(bio_err,
+                       mr ? "+R12:%ld:%d:%.2f\n" :
+                       "%ld %u-bits FFDH ops in %.2fs\n", count,
+                       ffdh_params[testnum].bits, d);
+            ffdh_results[testnum][0] = (double)count / d;
+            rsa_count = count;
+        };
+        if (rsa_count <= 1) {
+            /* if longer than 10s, don't do any more */
+            stop_it(ffdh_doit, testnum);
+        }
+    }
+#endif  /* OPENSSL_NO_DH */
 #ifndef NO_FORK
  show_res:
 #endif
@@ -3688,6 +3963,26 @@ int speed_main(int argc, char **argv)
     }
 # endif
 #endif                          /* OPENSSL_NO_EC */
+#ifndef OPENSSL_NO_DH
+    testnum = 1;
+    for (k = 0; k < FFDH_NUM; k++) {
+        if (!ffdh_doit[k])
+            continue;
+        if (testnum && !mr) {
+            printf("%23sop     op/s\n", " ");
+            testnum = 0;
+        }
+        if (mr)
+            printf("+F8:%u:%u:%f:%f\n",
+                   k, ffdh_params[k].bits,
+                   ffdh_results[k][0], 1.0 / ffdh_results[k][0]);
+
+        else
+            printf("%4u bits ffdh %8.4fs %8.1f\n",
+                   ffdh_params[k].bits,
+                   1.0 / ffdh_results[k][0], ffdh_results[k][0]);
+    }
+#endif /* OPENSSL_NO_DH */
 
     ret = 0;
 
@@ -3701,6 +3996,13 @@ int speed_main(int argc, char **argv)
         for (k = 0; k < RSA_NUM; k++)
             RSA_free(loopargs[i].rsa_key[k]);
 #endif
+#ifndef OPENSSL_NO_DH
+        OPENSSL_free(loopargs[i].secret_ff_a);
+        OPENSSL_free(loopargs[i].secret_ff_b);
+        for (k = 0; k < FFDH_NUM; k++) {
+            EVP_PKEY_CTX_free(loopargs[i].ffdh_ctx[k]);
+        }
+#endif
 #if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0)
         for (k = 0; k < DSA_NUM; k++)
             DSA_free(loopargs[i].dsa_key[k]);
@@ -3982,7 +4284,20 @@ static int do_multi(int multi, int size_num)
                 sm2_results[k][1] += d;
             }
 #  endif /* OPENSSL_NO_SM2 */
-# endif
+# endif /* OPENSSL_NO_EC */
+# ifndef OPENSSL_NO_DH
+            else if (strncmp(buf, "+F8:", 4) == 0) {
+                int k;
+                double d;
+
+                p = buf + 4;
+                k = atoi(sstrsep(&p, sep));
+                sstrsep(&p, sep);
+
+                d = atof(sstrsep(&p, sep));
+                ffdh_results[k][0] += d;
+            }
+# endif /* OPENSSL_NO_DH */
 
             else if (strncmp(buf, "+H:", 3) == 0) {
                 ;