levitte@xxxxxxxxxxx said: > What does surprise me, though, is that direct EVP_MAC calls would be slower > than going through the PKEY bridge. I would very much like to see your code > to see what's going on. Over on an ntpsec list, Kurt Roeckx reported that he was still waiting... Richard's message said "I", so I sent him a copy off list. Correcting that...
/* Last modified on Sat Aug 28 14:30:11 PDT 1999 by murray */ /* Hack to time various implementations of CMAC. * * This is just the CMAC timing. * It doesn't include the copy or compare or finding the right key. * * Beware of overflows in the timing computations. * * Disable AES-NI (Intel hardware: NI == New Instruction) with: * OPENSSL_ia32cap="~0x200000200000000" * Check /proc/cpuinfo flags for "aes" to see if you have it. */ #define CMAC_VERSION_CUTOFF 0x10000003 #include <stdbool.h> #include <stdint.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <time.h> /* Silence warnings from CMAC routines in OpenSSL 3.0.0 */ #define OPENSSL_SUPPRESS_DEPRECATED 1 #include <openssl/opensslv.h> #include <openssl/err.h> #include <openssl/cmac.h> #include <openssl/ssl.h> #include <openssl/evp.h> #include <openssl/rand.h> #include <openssl/objects.h> #if OPENSSL_VERSION_NUMBER > 0x20000000L #include <openssl/params.h> #endif #define UNUSED_ARG(arg) ((void)(arg)) int NUM = 1000000; #define PACKET_LENGTH 48 #define MAX_KEY_LENGTH 64 CMAC_CTX *cmac; #if OPENSSL_VERSION_NUMBER > 0x20000000L EVP_MAC_CTX *evp; #endif unsigned char answer[EVP_MAX_MD_SIZE]; static void ssl_init(void) { #if OPENSSL_VERSION_NUMBER > 0x20000000L EVP_MAC *mac; #endif ERR_load_crypto_strings(); OpenSSL_add_all_digests(); OpenSSL_add_all_ciphers(); cmac = CMAC_CTX_new(); #if OPENSSL_VERSION_NUMBER > 0x20000000L mac = EVP_MAC_fetch(NULL, "cmac", NULL); if (NULL == mac) printf("## Oops, EVP_MAC_fetch() failed.\n"); evp = EVP_MAC_CTX_new(mac); if (NULL == evp) printf("## Oops, EVP_MAC_CTX_new() failed.\n"); #endif } static const EVP_CIPHER *CheckCipher(const char *name) { const EVP_CIPHER *cipher; char cbc[100]; snprintf(cbc, sizeof(cbc), "%s-CBC", name); cipher = EVP_get_cipherbyname(cbc); if (0 && NULL == cipher) { /* no error available */ unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops: EVP_get_cipherbyname() failed: %s\n %s\n", cbc, str); return NULL; } return cipher; } static void PrintHex(const unsigned char* bytes, int length) { printf(" "); for (int i=0; i<length; i++) { printf("%02x", bytes[i]); } } static size_t One_CMAC( const EVP_CIPHER *cipher, /* cipher algorithm */ uint8_t *key, /* key pointer */ int keylength, /* key size */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { size_t len; if (1 != CMAC_Init(cmac, key, keylength, cipher, NULL)) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, CMAC_Init() failed:\n %s.\n", str); return 0; } if (1 != CMAC_Update(cmac, pkt, pktlength)) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, CMAC_Update() failed:\n %s.\n", str); return 0; } if (1 != CMAC_Final(cmac, answer, &len)) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, CMAC_Final() failed:\n %s.\n", str); return 0; } return len; } static void DoCMAC( const char *name, /* name of cipher */ uint8_t *key, /* key pointer */ int keylength, /* key length */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { const EVP_CIPHER *cipher = CheckCipher(name); struct timespec start, stop; double fast; unsigned long digestlength = 0; if (NULL == cipher) { return; } clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < NUM; i++) { digestlength = One_CMAC(cipher, key, keylength, pkt, pktlength); if (0 == digestlength) break; } clock_gettime(CLOCK_MONOTONIC, &stop); fast = (stop.tv_sec-start.tv_sec)*1E9 + (stop.tv_nsec-start.tv_nsec); printf("%12s %2d %2d %2lu %6.0f %6.3f", name, keylength, pktlength, digestlength, fast/NUM, fast/1E9); PrintHex(answer, digestlength); printf("\n"); } #if OPENSSL_VERSION_NUMBER > 0x10101000L static size_t One_PKEY( EVP_PKEY *pkey, EVP_MD_CTX *ctx, /* context */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { size_t len = EVP_MAX_MD_SIZE; if (1 != EVP_DigestSignInit(ctx, NULL, NULL, NULL, pkey)) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, EVP_DigestSignInit() failed:\n %s.\n", str); return 0; } EVP_DigestSign(ctx, answer, &len, pkt, pktlength); return len; } static void DoPKEY( const char *name, /* name of cipher */ uint8_t *key, /* key pointer */ int keylength, /* key length */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { struct timespec start, stop; double fast; unsigned long digestlength = 0; const EVP_CIPHER *cipher = CheckCipher(name); EVP_PKEY *pkey; EVP_MD_CTX *ctx; if (NULL == cipher) { return; } pkey = EVP_PKEY_new_CMAC_key(NULL, key, keylength, cipher); if (NULL == pkey) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, EVP_PKEY_new_CMAC_key() failed: %s\n %s.\n", \ name, str); return; } ctx = EVP_MD_CTX_new(); if (NULL == ctx) { printf("## Oops, EVP_MD_CTX_new() failed.\n"); return; } clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < NUM; i++) { digestlength = One_PKEY(pkey, ctx, pkt, pktlength); } clock_gettime(CLOCK_MONOTONIC, &stop); fast = (stop.tv_sec-start.tv_sec)*1E9 + (stop.tv_nsec-start.tv_nsec); printf("%12s %2d %2d %2lu %6.0f %6.3f", name, keylength, pktlength, digestlength, fast/NUM, fast/1E9); PrintHex(answer, digestlength); printf("\n"); EVP_MD_CTX_free(ctx); EVP_PKEY_free(pkey); } #endif #if OPENSSL_VERSION_NUMBER > 0x20000000L static size_t One_EVP_MAC( EVP_MAC_CTX *ctx, /* context */ char *cipher, uint8_t *key, /* key pointer */ int keylength, /* key length */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { OSSL_PARAM params[3]; size_t len = EVP_MAX_MD_SIZE; params[0] = OSSL_PARAM_construct_utf8_string("cipher", cipher, 0); params[1] = OSSL_PARAM_construct_octet_string("key", key, keylength); params[2] = OSSL_PARAM_construct_end(); if (0 == EVP_MAC_CTX_set_params(ctx, params)) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, EVP_MAC_CTX_set_params() failed: %s.\n", str); return 0; } if (0 == EVP_MAC_init(ctx)) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, EVP_MAC_init() failed: %s.\n", str); return 0; } if (0 == EVP_MAC_update(ctx, pkt, pktlength)) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, EVP_MAC_init() failed: %s.\n", str); return 0; } if (0 == EVP_MAC_final(ctx, answer, &len, sizeof(answer))) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, EVP_MAC_init() failed: %s.\n", str); return 0; } return len; } static void Do_EVP_MAC( const char *name, /* name of cipher */ uint8_t *key, /* key pointer */ int keylength, /* key length */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { const EVP_CIPHER *cipher = CheckCipher(name); struct timespec start, stop; double fast; unsigned long digestlength = 0; char cbc[100]; if (NULL == cipher) { return; } snprintf(cbc, sizeof(cbc), "%s-CBC", name); clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < NUM; i++) { digestlength = One_EVP_MAC(evp, cbc, key, keylength, pkt, pktlength); if (0 == digestlength) break; } clock_gettime(CLOCK_MONOTONIC, &stop); fast = (stop.tv_sec-start.tv_sec)*1E9 + (stop.tv_nsec-start.tv_nsec); printf("%12s %2d %2d %2lu %6.0f %6.3f", name, keylength, pktlength, digestlength, fast/NUM, fast/1E9); PrintHex(answer, digestlength); printf("\n"); } static size_t One_EVP_MAC2( EVP_MAC_CTX *ctx, /* context */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { size_t len = EVP_MAX_MD_SIZE; if (0 == EVP_MAC_init(ctx)) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, EVP_MAC_init() failed: %s.\n", str); return 0; } if (0 == EVP_MAC_update(ctx, pkt, pktlength)) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, EVP_MAC_init() failed: %s.\n", str); return 0; } if (0 == EVP_MAC_final(ctx, answer, &len, sizeof(answer))) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, EVP_MAC_init() failed: %s.\n", str); return 0; } return len; } static void Do_EVP_MAC2( const char *name, /* name of cipher */ uint8_t *key, /* key pointer */ int keylength, /* key length */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { struct timespec start, stop; double fast; unsigned long digestlength = 0; char cbc[100]; const EVP_CIPHER *cipher = CheckCipher(name); OSSL_PARAM params[3]; if (NULL == cipher) { return; } snprintf(cbc, sizeof(cbc), "%s-CBC", name); params[0] = OSSL_PARAM_construct_utf8_string("cipher", cbc, 0); params[1] = OSSL_PARAM_construct_octet_string("key", key, keylength); params[2] = OSSL_PARAM_construct_end(); if (0 == EVP_MAC_CTX_set_params(evp, params)) { unsigned long err = ERR_get_error(); char * str = ERR_error_string(err, NULL); printf("## Oops, EVP_MAC_CTX_set_params() failed: %s.\n", str); return; } clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < NUM; i++) { digestlength = One_EVP_MAC2(evp, pkt, pktlength); if (0 == digestlength) break; } clock_gettime(CLOCK_MONOTONIC, &stop); fast = (stop.tv_sec-start.tv_sec)*1E9 + (stop.tv_nsec-start.tv_nsec); printf("%12s %2d %2d %2lu %6.0f %6.3f", name, keylength, pktlength, digestlength, fast/NUM, fast/1E9); PrintHex(answer, digestlength); printf("\n"); } #endif int main(int argc, char *argv[]) { uint8_t key[MAX_KEY_LENGTH]; uint8_t packet[PACKET_LENGTH]; UNUSED_ARG(argc); UNUSED_ARG(argv); setlinebuf(stdout); ssl_init(); RAND_bytes((unsigned char *)&key, MAX_KEY_LENGTH); RAND_bytes((unsigned char *)&packet, PACKET_LENGTH); for (int i=0; i< MAX_KEY_LENGTH; i++) key[i]=i*i+0x23; for (int i=0; i< PACKET_LENGTH; i++) packet[i]=i*i+0x31; printf("# %s\n", OPENSSL_VERSION_TEXT); printf("\n"); printf("# KL=key length, PL=packet length, CL=CMAC length\n"); printf("# CMAC KL PL CL ns/op sec/run\n"); #if OPENSSL_VERSION_NUMBER < 0x20000000L /* Hangs on 3.0.0 Checking OPENSSL_NO_DES doesn't work. */ DoCMAC("DES", key, 8, packet, PACKET_LENGTH); #endif DoCMAC("DES-EDE", key, 16, packet, PACKET_LENGTH); DoCMAC("DES-EDE3", key, 24, packet, PACKET_LENGTH); #ifndef OPENSSL_NO_SM4 DoCMAC("SM4", key, 16, packet, PACKET_LENGTH); #endif DoCMAC("AES-128", key, 16, packet, PACKET_LENGTH); DoCMAC("AES-192", key, 24, packet, PACKET_LENGTH); DoCMAC("AES-256", key, 32, packet, PACKET_LENGTH); DoCMAC("CAMELLIA-128", key, 16, packet, PACKET_LENGTH); DoCMAC("CAMELLIA-192", key, 24, packet, PACKET_LENGTH); DoCMAC("CAMELLIA-256", key, 32, packet, PACKET_LENGTH); DoCMAC("ARIA-128", key, 16, packet, PACKET_LENGTH); DoCMAC("ARIA-192", key, 24, packet, PACKET_LENGTH); DoCMAC("ARIA-256", key, 32, packet, PACKET_LENGTH); #if OPENSSL_VERSION_NUMBER > 0x10101000L printf("\n"); printf("# KL=key length, PL=packet length, CL=CMAC length\n"); printf("# PKEY KL PL CL ns/op sec/run\n"); #if OPENSSL_VERSION_NUMBER < 0x20000000L DoPKEY("DES", key, 8, packet, PACKET_LENGTH); #endif DoPKEY("DES-EDE", key, 16, packet, PACKET_LENGTH); DoPKEY("DES-EDE3", key, 24, packet, PACKET_LENGTH); #ifndef OPENSSL_NO_SM4 DoPKEY("SM4", key, 16, packet, PACKET_LENGTH); #endif DoPKEY("AES-128", key, 16, packet, PACKET_LENGTH); DoPKEY("AES-192", key, 24, packet, PACKET_LENGTH); DoPKEY("AES-256", key, 32, packet, PACKET_LENGTH); DoPKEY("CAMELLIA-128", key, 16, packet, PACKET_LENGTH); DoPKEY("CAMELLIA-192", key, 24, packet, PACKET_LENGTH); DoPKEY("CAMELLIA-256", key, 32, packet, PACKET_LENGTH); DoPKEY("ARIA-128", key, 16, packet, PACKET_LENGTH); DoPKEY("ARIA-192", key, 24, packet, PACKET_LENGTH); DoPKEY("ARIA-256", key, 32, packet, PACKET_LENGTH); #endif #if OPENSSL_VERSION_NUMBER > 0x20000000L printf("\n"); printf("# KL=key length, PL=packet length, CL=CMAC length\n"); printf("# EVP_MAC KL PL CL ns/op sec/run\n"); Do_EVP_MAC("DES-EDE", key, 16, packet, PACKET_LENGTH); Do_EVP_MAC("DES-EDE3", key, 24, packet, PACKET_LENGTH); #ifndef OPENSSL_NO_SM4 Do_EVP_MAC("SM4", key, 16, packet, PACKET_LENGTH); #endif Do_EVP_MAC("AES-128", key, 16, packet, PACKET_LENGTH); Do_EVP_MAC("AES-192", key, 24, packet, PACKET_LENGTH); Do_EVP_MAC("AES-256", key, 32, packet, PACKET_LENGTH); Do_EVP_MAC("CAMELLIA-128", key, 16, packet, PACKET_LENGTH); Do_EVP_MAC("CAMELLIA-192", key, 24, packet, PACKET_LENGTH); Do_EVP_MAC("CAMELLIA-256", key, 32, packet, PACKET_LENGTH); Do_EVP_MAC("ARIA-128", key, 16, packet, PACKET_LENGTH); Do_EVP_MAC("ARIA-192", key, 24, packet, PACKET_LENGTH); Do_EVP_MAC("ARIA-256", key, 32, packet, PACKET_LENGTH); printf("\n"); printf("Preload cipher and key.\n"); Do_EVP_MAC2("DES-EDE", key, 16, packet, PACKET_LENGTH); Do_EVP_MAC2("DES-EDE3", key, 24, packet, PACKET_LENGTH); #ifndef OPENSSL_NO_SM4 Do_EVP_MAC2("SM4", key, 16, packet, PACKET_LENGTH); #endif Do_EVP_MAC2("AES-128", key, 16, packet, PACKET_LENGTH); Do_EVP_MAC2("AES-192", key, 24, packet, PACKET_LENGTH); Do_EVP_MAC2("AES-256", key, 32, packet, PACKET_LENGTH); Do_EVP_MAC2("CAMELLIA-128", key, 16, packet, PACKET_LENGTH); Do_EVP_MAC2("CAMELLIA-192", key, 24, packet, PACKET_LENGTH); Do_EVP_MAC2("CAMELLIA-256", key, 32, packet, PACKET_LENGTH); Do_EVP_MAC2("ARIA-128", key, 16, packet, PACKET_LENGTH); Do_EVP_MAC2("ARIA-192", key, 24, packet, PACKET_LENGTH); Do_EVP_MAC2("ARIA-256", key, 32, packet, PACKET_LENGTH); #endif return 0; }
-- These are my opinions. I hate spam.