Implemented algorithm benchmarking during registration
authorMartin Willi <martin@revosec.ch>
Mon, 16 Aug 2010 13:21:22 +0000 (15:21 +0200)
committerMartin Willi <martin@revosec.ch>
Mon, 16 Aug 2010 15:06:28 +0000 (17:06 +0200)
src/libstrongswan/crypto/crypto_factory.c
src/libstrongswan/crypto/crypto_tester.c
src/libstrongswan/crypto/crypto_tester.h

index 200fbbf..8089a1e 100644 (file)
 
 typedef struct entry_t entry_t;
 struct entry_t {
-       /** algorithm */
+       /* algorithm */
        u_int algo;
+       /* benchmarked speed */
+       u_int speed;
        /* constructor */
        union {
                crypter_constructor_t create_crypter;
@@ -32,6 +34,7 @@ struct entry_t {
                prf_constructor_t create_prf;
                rng_constructor_t create_rng;
                dh_constructor_t create_dh;
+               void *create;
        };
 };
 
@@ -93,6 +96,11 @@ struct private_crypto_factory_t {
        bool test_on_create;
 
        /**
+        * run algorithm benchmark during registration
+        */
+       bool bench;
+
+       /**
         * rwlock to lock access to modules
         */
        rwlock_t *lock;
@@ -114,7 +122,7 @@ METHOD(crypto_factory_t, create_crypter, crypter_t*,
                {
                        if (this->test_on_create &&
                                !this->tester->test_crypter(this->tester, algo, key_size,
-                                                                                       entry->create_crypter))
+                                                                                       entry->create_crypter, NULL))
                        {
                                continue;
                        }
@@ -145,7 +153,7 @@ METHOD(crypto_factory_t, create_signer, signer_t*,
                {
                        if (this->test_on_create &&
                                !this->tester->test_signer(this->tester, algo,
-                                                                                  entry->create_signer))
+                                                                                  entry->create_signer, NULL))
                        {
                                continue;
                        }
@@ -177,7 +185,7 @@ METHOD(crypto_factory_t, create_hasher, hasher_t*,
                {
                        if (this->test_on_create && algo != HASH_PREFERRED &&
                                !this->tester->test_hasher(this->tester, algo,
-                                                                                  entry->create_hasher))
+                                                                                  entry->create_hasher, NULL))
                        {
                                continue;
                        }
@@ -207,7 +215,8 @@ METHOD(crypto_factory_t, create_prf, prf_t*,
                if (entry->algo == algo)
                {
                        if (this->test_on_create &&
-                               !this->tester->test_prf(this->tester, algo, entry->create_prf))
+                               !this->tester->test_prf(this->tester, algo,
+                                                                               entry->create_prf, NULL))
                        {
                                continue;
                        }
@@ -238,7 +247,8 @@ METHOD(crypto_factory_t, create_rng, rng_t*,
                if (entry->algo >= quality && diff > entry->algo - quality)
                {
                        if (this->test_on_create &&
-                               !this->tester->test_rng(this->tester, quality, entry->create_rng))
+                               !this->tester->test_rng(this->tester, quality,
+                                                                               entry->create_rng, NULL))
                        {
                                continue;
                        }
@@ -284,20 +294,61 @@ METHOD(crypto_factory_t, create_dh, diffie_hellman_t*,
        return diffie_hellman;
 }
 
+/**
+ * Insert an algorithm entry to a list
+ */
+static void add_entry(private_crypto_factory_t *this, linked_list_t *list,
+                                         int algo, u_int speed, void *create)
+{
+       entry_t *entry, *current;
+       linked_list_t *tmp;
+       bool inserted = FALSE;
+
+       INIT(entry,
+               .algo = algo,
+               .speed = speed,
+       );
+       entry->create = create;
+
+       this->lock->write_lock(this->lock);
+       if (speed)
+       {       /* insert sorted by speed using a temporary list */
+               tmp = linked_list_create();
+               while (list->remove_first(list, (void**)&current) == SUCCESS)
+               {
+                       tmp->insert_last(tmp, current);
+               }
+               while (tmp->remove_first(tmp, (void**)&current) == SUCCESS)
+               {
+                       if (!inserted &&
+                               current->algo == algo &&
+                               current->speed < speed)
+                       {
+                               list->insert_last(list, entry);
+                               inserted = TRUE;
+                       }
+                       list->insert_last(list, current);
+               }
+               tmp->destroy(tmp);
+       }
+       if (!inserted)
+       {
+               list->insert_last(list, entry);
+       }
+       this->lock->unlock(this->lock);
+}
+
 METHOD(crypto_factory_t, add_crypter, void,
        private_crypto_factory_t *this, encryption_algorithm_t algo,
        crypter_constructor_t create)
 {
+       u_int speed = 0;
+
        if (!this->test_on_add ||
-               this->tester->test_crypter(this->tester, algo, 0, create))
+               this->tester->test_crypter(this->tester, algo, 0, create,
+                                                                  this->bench ? &speed : NULL))
        {
-               entry_t *entry = malloc_thing(entry_t);
-
-               entry->algo = algo;
-               entry->create_crypter = create;
-               this->lock->write_lock(this->lock);
-               this->crypters->insert_last(this->crypters, entry);
-               this->lock->unlock(this->lock);
+               add_entry(this, this->crypters, algo, speed, create);
        }
 }
 
@@ -325,16 +376,13 @@ METHOD(crypto_factory_t, add_signer, void,
        private_crypto_factory_t *this, integrity_algorithm_t algo,
        signer_constructor_t create)
 {
+       u_int speed = 0;
+
        if (!this->test_on_add ||
-               this->tester->test_signer(this->tester, algo, create))
+               this->tester->test_signer(this->tester, algo, create,
+                                                                 this->bench ? &speed : NULL))
        {
-               entry_t *entry = malloc_thing(entry_t);
-
-               entry->algo = algo;
-               entry->create_signer = create;
-               this->lock->write_lock(this->lock);
-               this->signers->insert_last(this->signers, entry);
-               this->lock->unlock(this->lock);
+               add_entry(this, this->signers, algo, speed, create);
        }
 }
 
@@ -362,16 +410,13 @@ METHOD(crypto_factory_t, add_hasher, void,
        private_crypto_factory_t *this, hash_algorithm_t algo,
        hasher_constructor_t create)
 {
+       u_int speed = 0;
+
        if (!this->test_on_add ||
-               this->tester->test_hasher(this->tester, algo, create))
+               this->tester->test_hasher(this->tester, algo, create,
+                                                                 this->bench ? &speed : NULL))
        {
-               entry_t *entry = malloc_thing(entry_t);
-
-               entry->algo = algo;
-               entry->create_hasher = create;
-               this->lock->write_lock(this->lock);
-               this->hashers->insert_last(this->hashers, entry);
-               this->lock->unlock(this->lock);
+               add_entry(this, this->hashers, algo, speed, create);
        }
 }
 
@@ -399,16 +444,13 @@ METHOD(crypto_factory_t, add_prf, void,
        private_crypto_factory_t *this, pseudo_random_function_t algo,
        prf_constructor_t create)
 {
+       u_int speed = 0;
+
        if (!this->test_on_add ||
-               this->tester->test_prf(this->tester, algo, create))
+               this->tester->test_prf(this->tester, algo, create,
+                                                          this->bench ? &speed : NULL))
        {
-               entry_t *entry = malloc_thing(entry_t);
-
-               entry->algo = algo;
-               entry->create_prf = create;
-               this->lock->write_lock(this->lock);
-               this->prfs->insert_last(this->prfs, entry);
-               this->lock->unlock(this->lock);
+               add_entry(this, this->prfs, algo, speed, create);
        }
 }
 
@@ -436,16 +478,13 @@ METHOD(crypto_factory_t, add_rng, void,
        private_crypto_factory_t *this, rng_quality_t quality,
        rng_constructor_t create)
 {
+       u_int speed = 0;
+
        if (!this->test_on_add ||
-               this->tester->test_rng(this->tester, quality, create))
+               this->tester->test_rng(this->tester, quality, create,
+                                                          this->bench ? &speed : NULL))
        {
-               entry_t *entry = malloc_thing(entry_t);
-
-               entry->algo = quality;
-               entry->create_rng = create;
-               this->lock->write_lock(this->lock);
-               this->rngs->insert_last(this->rngs, entry);
-               this->lock->unlock(this->lock);
+               add_entry(this, this->rngs, quality, speed, create);
        }
 }
 
@@ -473,13 +512,7 @@ METHOD(crypto_factory_t, add_dh, void,
        private_crypto_factory_t *this, diffie_hellman_group_t group,
        dh_constructor_t create)
 {
-       entry_t *entry = malloc_thing(entry_t);
-
-       entry->algo = group;
-       entry->create_dh = create;
-       this->lock->write_lock(this->lock);
-       this->dhs->insert_last(this->dhs, entry);
-       this->lock->unlock(this->lock);
+       add_entry(this, this->dhs, group, 0, create);
 }
 
 METHOD(crypto_factory_t, remove_dh, void,
@@ -695,6 +728,8 @@ crypto_factory_t *crypto_factory_create()
                                                                "libstrongswan.crypto_test.on_add", FALSE),
                .test_on_create = lib->settings->get_bool(lib->settings,
                                                                "libstrongswan.crypto_test.on_create", FALSE),
+               .bench = lib->settings->get_bool(lib->settings,
+                                                               "libstrongswan.crypto_test.bench", FALSE),
        );
 
        return &this->public;
index 2a6b904..13e186a 100644 (file)
@@ -1,6 +1,7 @@
 /*
- * Copyright (C) 2009 Martin Willi
+ * Copyright (C) 2009-2010 Martin Willi
  * Hochschule fuer Technik Rapperswil
+ * Copyright (C) 2010 revosec AG
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -15,6 +16,7 @@
 
 #define _GNU_SOURCE
 #include <dlfcn.h>
+#include <time.h>
 
 #include "crypto_tester.h"
 
@@ -67,6 +69,16 @@ struct private_crypto_tester_t {
         * should we run RNG_TRUE tests? Enough entropy?
         */
        bool rng_true;
+
+       /**
+        * time we test each algorithm
+        */
+       int bench_time;
+
+       /**
+        * size of buffer we use for benchmarking
+        */
+       int bench_size;
 };
 
 /**
@@ -85,9 +97,69 @@ static const char* get_name(void *sym)
        return "unknown";
 }
 
+/**
+ * Start a benchmark timer
+ */
+static void start_timing(struct timespec *start)
+{
+       clock_gettime(CLOCK_THREAD_CPUTIME_ID, start);
+}
+
+/**
+ * End a benchmark timer, return ms
+ */
+static u_int end_timing(struct timespec *start)
+{
+       struct timespec end;
+
+       clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
+       return (end.tv_nsec - start->tv_nsec) / 1000000 +
+                       (end.tv_sec - start->tv_sec) * 1000;
+}
+
+/**
+ * Benchmark a crypter
+ */
+static u_int bench_crypter(private_crypto_tester_t *this,
+       encryption_algorithm_t alg, crypter_constructor_t create)
+{
+       crypter_t *crypter;
+
+       crypter = create(alg, 0);
+       if (crypter)
+       {
+               char iv[crypter->get_iv_size(crypter)];
+               char key[crypter->get_key_size(crypter)];
+               chunk_t buf;
+               struct timespec start;
+               u_int runs;
+
+               memset(iv, 0x56, sizeof(iv));
+               memset(key, 0x12, sizeof(key));
+               crypter->set_key(crypter, chunk_from_thing(key));
+
+               buf = chunk_alloc(this->bench_size);
+               memset(buf.ptr, 0x34, buf.len);
+
+               runs = 0;
+               start_timing(&start);
+               while (end_timing(&start) < this->bench_time)
+               {
+                       crypter->encrypt(crypter, buf, chunk_from_thing(iv), NULL);
+                       crypter->decrypt(crypter, buf, chunk_from_thing(iv), NULL);
+                       runs++;
+               }
+               free(buf.ptr);
+               crypter->destroy(crypter);
+
+               return runs;
+       }
+       return 0;
+}
+
 METHOD(crypto_tester_t, test_crypter, bool,
        private_crypto_tester_t *this, encryption_algorithm_t alg, size_t key_size,
-       crypter_constructor_t create)
+       crypter_constructor_t create, u_int *speed)
 {
        enumerator_t *enumerator;
        crypter_test_vector_t *vector;
@@ -168,15 +240,63 @@ METHOD(crypto_tester_t, test_crypter, bool,
        }
        if (!failed)
        {
-               DBG1(DBG_LIB, "enabled  %N: passed %u test vectors",
-                        encryption_algorithm_names, alg, tested);
+               if (speed)
+               {
+                       *speed = bench_crypter(this, alg, create);
+                       DBG1(DBG_LIB, "enabled  %N: passed %u test vectors, %d points",
+                                encryption_algorithm_names, alg, tested, *speed);
+               }
+               else
+               {
+                       DBG1(DBG_LIB, "enabled  %N: passed %u test vectors",
+                                encryption_algorithm_names, alg, tested);
+               }
        }
        return !failed;
 }
 
+/**
+ * Benchmark a signer
+ */
+static u_int bench_signer(private_crypto_tester_t *this,
+       encryption_algorithm_t alg, signer_constructor_t create)
+{
+       signer_t *signer;
+
+       signer = create(alg);
+       if (signer)
+       {
+               char key[signer->get_key_size(signer)];
+               char mac[signer->get_block_size(signer)];
+               chunk_t buf;
+               struct timespec start;
+               u_int runs;
+
+               memset(key, 0x12, sizeof(key));
+               signer->set_key(signer, chunk_from_thing(key));
+
+               buf = chunk_alloc(this->bench_size);
+               memset(buf.ptr, 0x34, buf.len);
+
+               runs = 0;
+               start_timing(&start);
+               while (end_timing(&start) < this->bench_time)
+               {
+                       signer->get_signature(signer, buf, mac);
+                       signer->verify_signature(signer, buf, chunk_from_thing(mac));
+                       runs++;
+               }
+               free(buf.ptr);
+               signer->destroy(signer);
+
+               return runs;
+       }
+       return 0;
+}
+
 METHOD(crypto_tester_t, test_signer, bool,
        private_crypto_tester_t *this, integrity_algorithm_t alg,
-       signer_constructor_t create)
+       signer_constructor_t create, u_int *speed)
 {
        enumerator_t *enumerator;
        signer_test_vector_t *vector;
@@ -270,15 +390,58 @@ METHOD(crypto_tester_t, test_signer, bool,
        }
        if (!failed)
        {
-               DBG1(DBG_LIB, "enabled  %N: passed %u test vectors",
-                        integrity_algorithm_names, alg, tested);
+               if (speed)
+               {
+                       *speed = bench_signer(this, alg, create);
+                       DBG1(DBG_LIB, "enabled  %N: passed %u test vectors, %d points",
+                                integrity_algorithm_names, alg, tested, *speed);
+               }
+               else
+               {
+                       DBG1(DBG_LIB, "enabled  %N: passed %u test vectors",
+                                integrity_algorithm_names, alg, tested);
+               }
        }
        return !failed;
 }
 
+/**
+ * Benchmark a hasher
+ */
+static u_int bench_hasher(private_crypto_tester_t *this,
+       hash_algorithm_t alg, hasher_constructor_t create)
+{
+       hasher_t *hasher;
+
+       hasher = create(alg);
+       if (hasher)
+       {
+               char hash[hasher->get_hash_size(hasher)];
+               chunk_t buf;
+               struct timespec start;
+               u_int runs;
+
+               buf = chunk_alloc(this->bench_size);
+               memset(buf.ptr, 0x34, buf.len);
+
+               runs = 0;
+               start_timing(&start);
+               while (end_timing(&start) < this->bench_time)
+               {
+                       hasher->get_hash(hasher, buf, hash);
+                       runs++;
+               }
+               free(buf.ptr);
+               hasher->destroy(hasher);
+
+               return runs;
+       }
+       return 0;
+}
+
 METHOD(crypto_tester_t, test_hasher, bool,
        private_crypto_tester_t *this, hash_algorithm_t alg,
-       hasher_constructor_t create)
+       hasher_constructor_t create, u_int *speed)
 {
        enumerator_t *enumerator;
        hasher_test_vector_t *vector;
@@ -358,15 +521,58 @@ METHOD(crypto_tester_t, test_hasher, bool,
        }
        if (!failed)
        {
-               DBG1(DBG_LIB, "enabled  %N: passed %u test vectors",
-                        hash_algorithm_names, alg, tested);
+               if (speed)
+               {
+                       *speed = bench_hasher(this, alg, create);
+                       DBG1(DBG_LIB, "enabled  %N: passed %u test vectors, %d points",
+                                hash_algorithm_names, alg, tested, *speed);
+               }
+               else
+               {
+                       DBG1(DBG_LIB, "enabled  %N: passed %u test vectors",
+                                hash_algorithm_names, alg, tested);
+               }
        }
        return !failed;
 }
 
+/**
+ * Benchmark a PRF
+ */
+static u_int bench_prf(private_crypto_tester_t *this,
+                                          pseudo_random_function_t alg, prf_constructor_t create)
+{
+       prf_t *prf;
+
+       prf = create(alg);
+       if (prf)
+       {
+               char bytes[prf->get_block_size(prf)];
+               chunk_t buf;
+               struct timespec start;
+               u_int runs;
+
+               buf = chunk_alloc(this->bench_size);
+               memset(buf.ptr, 0x34, buf.len);
+
+               runs = 0;
+               start_timing(&start);
+               while (end_timing(&start) < this->bench_time)
+               {
+                       prf->get_bytes(prf, buf, bytes);
+                       runs++;
+               }
+               free(buf.ptr);
+               prf->destroy(prf);
+
+               return runs;
+       }
+       return 0;
+}
+
 METHOD(crypto_tester_t, test_prf, bool,
        private_crypto_tester_t *this, pseudo_random_function_t alg,
-       prf_constructor_t create)
+       prf_constructor_t create, u_int *speed)
 {
        enumerator_t *enumerator;
        prf_test_vector_t *vector;
@@ -457,15 +663,55 @@ METHOD(crypto_tester_t, test_prf, bool,
        }
        if (!failed)
        {
-               DBG1(DBG_LIB, "enabled  %N: passed %u test vectors",
-                        pseudo_random_function_names, alg, tested);
+               if (speed)
+               {
+                       *speed = bench_prf(this, alg, create);
+                       DBG1(DBG_LIB, "enabled  %N: passed %u test vectors, %d points",
+                                pseudo_random_function_names, alg, tested, *speed);
+               }
+               else
+               {
+                       DBG1(DBG_LIB, "enabled  %N: passed %u test vectors",
+                                pseudo_random_function_names, alg, tested);
+               }
        }
        return !failed;
 }
 
+/**
+ * Benchmark a RNG
+ */
+static u_int bench_rng(private_crypto_tester_t *this,
+                                          rng_quality_t quality, rng_constructor_t create)
+{
+       rng_t *rng;
+
+       rng = create(quality);
+       if (rng)
+       {
+               struct timespec start;
+               chunk_t buf;
+               u_int runs;
+
+               runs = 0;
+               buf = chunk_alloc(this->bench_size);
+               start_timing(&start);
+               while (end_timing(&start) < this->bench_time)
+               {
+                       rng->get_bytes(rng, buf.len, buf.ptr);
+                       runs++;
+               }
+               free(buf.ptr);
+               rng->destroy(rng);
+
+               return runs;
+       }
+       return 0;
+}
+
 METHOD(crypto_tester_t, test_rng, bool,
        private_crypto_tester_t *this, rng_quality_t quality,
-       rng_constructor_t create)
+       rng_constructor_t create, u_int *speed)
 {
        enumerator_t *enumerator;
        rng_test_vector_t *vector;
@@ -539,8 +785,17 @@ METHOD(crypto_tester_t, test_rng, bool,
        }
        if (!failed)
        {
-               DBG1(DBG_LIB, "enabled  %N: passed %u test vectors",
-                        rng_quality_names, quality, tested);
+               if (speed)
+               {
+                       *speed = bench_rng(this, quality, create);
+                       DBG1(DBG_LIB, "enabled  %N: passed %u test vectors, %d points",
+                                rng_quality_names, quality, tested, *speed);
+               }
+               else
+               {
+                       DBG1(DBG_LIB, "enabled  %N: passed %u test vectors",
+                                rng_quality_names, quality, tested);
+               }
        }
        return !failed;
 }
@@ -617,8 +872,15 @@ crypto_tester_t *crypto_tester_create()
                                                                "libstrongswan.crypto_test.required", FALSE),
                .rng_true = lib->settings->get_bool(lib->settings,
                                                                "libstrongswan.crypto_test.rng_true", FALSE),
+               .bench_time = lib->settings->get_int(lib->settings,
+                                                               "libstrongswan.crypto_test.bench_time", 50),
+               .bench_size = lib->settings->get_int(lib->settings,
+                                                               "libstrongswan.crypto_test.bench_size", 1024),
        );
 
+       /* enforce a block size of 16, should be fine for all algorithms */
+       this->bench_size = this->bench_size / 16 * 16;
+
        return &this->public;
 }
 
index ddcc2da..a670931 100644 (file)
@@ -116,46 +116,52 @@ struct crypto_tester_t {
         * @param alg                   algorithm to test
         * @param key_size              key size to test, 0 for all
         * @param create                constructor function for the crypter
+        * @param speed                 speed test result, NULL to omit
         * @return                              TRUE if test passed
         */
        bool (*test_crypter)(crypto_tester_t *this, encryption_algorithm_t alg,
-                                                size_t key_size, crypter_constructor_t create);
+                                                size_t key_size, crypter_constructor_t create,
+                                                u_int *speed);
        /**
         * Test a signer algorithm.
         *
         * @param alg                   algorithm to test
         * @param create                constructor function for the signer
+        * @param speed                 speed test result, NULL to omit
         * @return                              TRUE if test passed
         */
        bool (*test_signer)(crypto_tester_t *this, integrity_algorithm_t alg,
-                                               signer_constructor_t create);
+                                               signer_constructor_t create, u_int *speed);
        /**
         * Test a hasher algorithm.
         *
         * @param alg                   algorithm to test
         * @param create                constructor function for the hasher
+        * @param speed                 speed test result, NULL to omit
         * @return                              TRUE if test passed
         */
        bool (*test_hasher)(crypto_tester_t *this, hash_algorithm_t alg,
-                                               hasher_constructor_t create);
+                                               hasher_constructor_t create, u_int *speed);
        /**
         * Test a PRF algorithm.
         *
         * @param alg                   algorithm to test
         * @param create                constructor function for the PRF
+        * @param speed                 speed test result, NULL to omit
         * @return                              TRUE if test passed
         */
        bool (*test_prf)(crypto_tester_t *this, pseudo_random_function_t alg,
-                                        prf_constructor_t create);
+                                        prf_constructor_t create, u_int *speed);
        /**
         * Test a RNG implementation.
         *
         * @param alg                   algorithm to test
         * @param create                constructor function for the RNG
+        * @param speed                 speed test result, NULL to omit
         * @return                              TRUE if test passed
         */
        bool (*test_rng)(crypto_tester_t *this, rng_quality_t quality,
-                                        rng_constructor_t create);
+                                        rng_constructor_t create, u_int *speed);
        /**
         * Add a test vector to test a crypter.
         *