apps/speed.c: Fix the benchmarking for AEAD ciphers

Fixed the benchmarking for the evp aead interface for ccm, gcm, ocb, and siv,
where decryption fails when executing
`openssl speed -evp aes-128-ccm -decrypt` and
`openssl speed -evp aes-128-gcm -decrypt`.

Related issues are [24686](https://github.com/openssl/openssl/issues/24686)
and [24250](https://github.com/openssl/openssl/issues/24250).
Now both encryption and decryption, with or without AAD, executes correctly
without issues.

Reviewed-by: Tom Cosgrove <tom.cosgrove@arm.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25823)
This commit is contained in:
Mohammed Alhabib 2024-10-29 12:00:54 +03:00 committed by Tomas Mraz
parent 539b17b658
commit 607a46d003

View File

@ -515,6 +515,14 @@ static double sigs_results[MAX_SIG_NUM][3]; /* keygen, sign, verify */
#define COND(unused_cond) (run && count < (testmode ? 1 : INT_MAX))
#define COUNT(d) (count)
#define TAG_LEN 16
static unsigned int mode_op; /* AE Mode of operation */
static unsigned int aead = 0; /* AEAD flag */
static unsigned char aead_iv[12]; /* For AEAD modes */
static unsigned char aad[EVP_AEAD_TLS1_AAD_LEN] = { 0xcc };
static int aead_ivlen = sizeof(aead_iv);
typedef struct loopargs_st {
ASYNC_JOB *inprogress_job;
ASYNC_WAIT_CTX *wait_ctx;
@ -523,6 +531,7 @@ typedef struct loopargs_st {
unsigned char *buf_malloc;
unsigned char *buf2_malloc;
unsigned char *key;
unsigned char tag[TAG_LEN];
size_t buflen;
size_t sigsize;
size_t encsize;
@ -875,12 +884,8 @@ static int EVP_Update_loop(void *args)
unsigned char *buf = tempargs->buf;
EVP_CIPHER_CTX *ctx = tempargs->ctx;
int outl, count, rc;
unsigned char faketag[16] = { 0xcc };
if (decrypt) {
if (EVP_CIPHER_get_flags(EVP_CIPHER_CTX_get0_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) {
(void)EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, sizeof(faketag), faketag);
}
for (count = 0; COND(c[D_EVP][testnum]); count++) {
rc = EVP_DecryptUpdate(ctx, buf, &outl, buf, lengths[testnum]);
if (rc != 1) {
@ -908,44 +913,71 @@ static int EVP_Update_loop(void *args)
}
/*
* To make AEAD benchmarking more relevant perform TLS-like operations,
* 13-byte AAD followed by payload. But don't use TLS-formatted AAD, as
* payload length is not actually limited by 16KB...
* CCM does not support streaming. For the purpose of performance measurement,
* each message is encrypted using the same (key,iv)-pair. Do not use this
* code in your application.
*/
static int EVP_Update_loop_ccm(void *args)
static int EVP_Update_loop_aead_enc(void *args)
{
loopargs_t *tempargs = *(loopargs_t **) args;
unsigned char *buf = tempargs->buf;
unsigned char *key = tempargs->key;
EVP_CIPHER_CTX *ctx = tempargs->ctx;
int outl, count, realcount = 0, final;
unsigned char tag[12];
int outl, count, realcount = 0;
if (decrypt) {
for (count = 0; COND(c[D_EVP][testnum]); count++) {
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, sizeof(tag),
tag) > 0
/* reset iv */
&& EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv) > 0
/* counter is reset on every update */
&& EVP_DecryptUpdate(ctx, buf, &outl, buf, lengths[testnum]) > 0)
realcount++;
for (count = 0; COND(c[D_EVP][testnum]); count++) {
/* Set length of iv (Doesn't apply to SIV mode) */
if (mode_op != EVP_CIPH_SIV_MODE) {
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN,
aead_ivlen, NULL)) {
BIO_printf(bio_err, "\nFailed to set iv length\n");
dofail();
exit(1);
}
}
} else {
for (count = 0; COND(c[D_EVP][testnum]); count++) {
/* restore iv length field */
if (EVP_EncryptUpdate(ctx, NULL, &outl, NULL, lengths[testnum]) > 0
/* counter is reset on every update */
&& EVP_EncryptUpdate(ctx, buf, &outl, buf, lengths[testnum]) > 0)
realcount++;
/* Set tag_len (Not for GCM/SIV at encryption stage) */
if (mode_op != EVP_CIPH_GCM_MODE
&& mode_op != EVP_CIPH_SIV_MODE
&& mode_op != EVP_CIPH_GCM_SIV_MODE) {
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
TAG_LEN, NULL)) {
BIO_printf(bio_err, "\nFailed to set tag length\n");
dofail();
exit(1);
}
}
if (!EVP_CipherInit_ex(ctx, NULL, NULL, key, aead_iv, -1)) {
BIO_printf(bio_err, "\nFailed to set key and iv\n");
dofail();
exit(1);
}
/* Set total length of input. Only required for CCM */
if (mode_op == EVP_CIPH_CCM_MODE) {
if (!EVP_EncryptUpdate(ctx, NULL, &outl,
NULL, lengths[testnum])) {
BIO_printf(bio_err, "\nCouldn't set input text length\n");
dofail();
exit(1);
}
}
if (aead) {
if (!EVP_EncryptUpdate(ctx, NULL, &outl, aad, sizeof(aad))) {
BIO_printf(bio_err, "\nCouldn't insert AAD when encrypting\n");
dofail();
exit(1);
}
}
if (!EVP_EncryptUpdate(ctx, buf, &outl, buf, lengths[testnum])) {
BIO_printf(bio_err, "\nFailed to encrypt the data\n");
dofail();
exit(1);
}
if (EVP_EncryptFinal_ex(ctx, buf, &outl))
realcount++;
}
if (decrypt)
final = EVP_DecryptFinal_ex(ctx, buf, &outl);
else
final = EVP_EncryptFinal_ex(ctx, buf, &outl);
if (final == 0)
BIO_printf(bio_err, "Error finalizing ccm loop\n");
return realcount;
}
@ -953,34 +985,87 @@ static int EVP_Update_loop_ccm(void *args)
* To make AEAD benchmarking more relevant perform TLS-like operations,
* 13-byte AAD followed by payload. But don't use TLS-formatted AAD, as
* payload length is not actually limited by 16KB...
* CCM does not support streaming. For the purpose of performance measurement,
* each message is decrypted using the same (key,iv)-pair. Do not use this
* code in your application.
* For decryption, we will use buf2 to preserve the input text in buf.
*/
static int EVP_Update_loop_aead(void *args)
static int EVP_Update_loop_aead_dec(void *args)
{
loopargs_t *tempargs = *(loopargs_t **) args;
unsigned char *buf = tempargs->buf;
unsigned char *outbuf = tempargs->buf2;
unsigned char *key = tempargs->key;
unsigned char tag[TAG_LEN];
EVP_CIPHER_CTX *ctx = tempargs->ctx;
int outl, count, realcount = 0;
unsigned char aad[13] = { 0xcc };
unsigned char faketag[16] = { 0xcc };
if (decrypt) {
for (count = 0; COND(c[D_EVP][testnum]); count++) {
if (EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv) > 0
&& EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
sizeof(faketag), faketag) > 0
&& EVP_DecryptUpdate(ctx, NULL, &outl, aad, sizeof(aad)) > 0
&& EVP_DecryptUpdate(ctx, buf, &outl, buf, lengths[testnum]) > 0
&& EVP_DecryptFinal_ex(ctx, buf + outl, &outl) > 0)
realcount++;
for (count = 0; COND(c[D_EVP][testnum]); count++) {
/* Set the length of iv (Doesn't apply to SIV mode) */
if (mode_op != EVP_CIPH_SIV_MODE) {
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN,
aead_ivlen, NULL)) {
BIO_printf(bio_err, "\nFailed to set iv length\n");
dofail();
exit(1);
}
}
} else {
for (count = 0; COND(c[D_EVP][testnum]); count++) {
if (EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv) > 0
&& EVP_EncryptUpdate(ctx, NULL, &outl, aad, sizeof(aad)) > 0
&& EVP_EncryptUpdate(ctx, buf, &outl, buf, lengths[testnum]) > 0
&& EVP_EncryptFinal_ex(ctx, buf + outl, &outl) > 0)
realcount++;
/* Set the tag length (Doesn't apply to SIV mode) */
if (mode_op != EVP_CIPH_SIV_MODE
&& mode_op != EVP_CIPH_GCM_MODE
&& mode_op != EVP_CIPH_GCM_SIV_MODE) {
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
TAG_LEN, NULL)) {
BIO_printf(bio_err, "\nFailed to set tag length\n");
dofail();
exit(1);
}
}
if (!EVP_CipherInit_ex(ctx, NULL, NULL, key, aead_iv, -1)) {
BIO_printf(bio_err, "\nFailed to set key and iv\n");
dofail();
exit(1);
}
/* Set iv before decryption (Doesn't apply to SIV mode) */
if (mode_op != EVP_CIPH_SIV_MODE) {
if (!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, aead_iv)) {
BIO_printf(bio_err, "\nFailed to set iv\n");
dofail();
exit(1);
}
}
memcpy(tag, tempargs->tag, TAG_LEN);
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
TAG_LEN, tag)) {
BIO_printf(bio_err, "\nFailed to set tag\n");
dofail();
exit(1);
}
/* Set the total length of cipher text. Only required for CCM */
if (mode_op == EVP_CIPH_CCM_MODE) {
if (!EVP_DecryptUpdate(ctx, NULL, &outl,
NULL, lengths[testnum])) {
BIO_printf(bio_err, "\nCouldn't set cipher text length\n");
dofail();
exit(1);
}
}
if (aead) {
if (!EVP_DecryptUpdate(ctx, NULL, &outl, aad, sizeof(aad))) {
BIO_printf(bio_err, "\nCouldn't insert AAD when decrypting\n");
dofail();
exit(1);
}
}
if (!EVP_DecryptUpdate(ctx, outbuf, &outl, buf, lengths[testnum])) {
BIO_printf(bio_err, "\nFailed to decrypt the data\n");
dofail();
exit(1);
}
if (EVP_DecryptFinal_ex(ctx, outbuf, &outl))
realcount++;
}
return realcount;
}
@ -1803,14 +1888,14 @@ int speed_main(int argc, char **argv)
OPTION_CHOICE o;
int async_init = 0, multiblock = 0, pr_header = 0;
uint8_t doit[ALGOR_NUM] = { 0 };
int ret = 1, misalign = 0, lengths_single = 0, aead = 0;
int ret = 1, misalign = 0, lengths_single = 0;
STACK_OF(EVP_KEM) *kem_stack = NULL;
STACK_OF(EVP_SIGNATURE) *sig_stack = NULL;
long count = 0;
unsigned int size_num = SIZE_NUM;
unsigned int i, k, loopargs_len = 0, async_jobs = 0;
unsigned int idx;
int keylen;
int keylen = 0;
int buflen;
size_t declen;
BIGNUM *bn = NULL;
@ -2839,12 +2924,20 @@ int speed_main(int argc, char **argv)
}
}
/*-
* There are three scenarios for D_EVP:
* 1- Using authenticated encryption (AE) e.g. CCM, GCM, OCB etc.
* 2- Using AE + associated data (AD) i.e. AEAD using CCM, GCM, OCB etc.
* 3- Not using AE or AD e.g. ECB, CBC, CFB etc.
*/
if (doit[D_EVP]) {
if (evp_cipher != NULL) {
int (*loopfunc) (void *) = EVP_Update_loop;
int (*loopfunc) (void *);
int outlen = 0;
unsigned int ae_mode = 0;
if (multiblock && (EVP_CIPHER_get_flags(evp_cipher) &
EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK)) {
if (multiblock && (EVP_CIPHER_get_flags(evp_cipher)
& EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK)) {
multiblock_speed(evp_cipher, lengths_single, &seconds);
ret = 0;
goto end;
@ -2852,16 +2945,27 @@ int speed_main(int argc, char **argv)
names[D_EVP] = EVP_CIPHER_get0_name(evp_cipher);
if (EVP_CIPHER_get_mode(evp_cipher) == EVP_CIPH_CCM_MODE) {
loopfunc = EVP_Update_loop_ccm;
} else if (aead && (EVP_CIPHER_get_flags(evp_cipher) &
EVP_CIPH_FLAG_AEAD_CIPHER)) {
loopfunc = EVP_Update_loop_aead;
mode_op = EVP_CIPHER_get_mode(evp_cipher);
if (aead) {
if (lengths == lengths_list) {
lengths = aead_lengths_list;
size_num = OSSL_NELEM(aead_lengths_list);
}
}
if (mode_op == EVP_CIPH_GCM_MODE
|| mode_op == EVP_CIPH_CCM_MODE
|| mode_op == EVP_CIPH_OCB_MODE
|| mode_op == EVP_CIPH_SIV_MODE
|| mode_op == EVP_CIPH_GCM_SIV_MODE) {
ae_mode = 1;
if (decrypt)
loopfunc = EVP_Update_loop_aead_dec;
else
loopfunc = EVP_Update_loop_aead_enc;
} else {
loopfunc = EVP_Update_loop;
}
for (testnum = 0; testnum < size_num; testnum++) {
print_message(names[D_EVP], lengths[testnum], seconds.sym);
@ -2872,38 +2976,145 @@ int speed_main(int argc, char **argv)
BIO_printf(bio_err, "\nEVP_CIPHER_CTX_new failure\n");
exit(1);
}
/*
* For AE modes, we must first encrypt the data to get
* a valid tag that enables us to decrypt. If we don't
* encrypt first, we won't have a valid tag that enables
* authenticity and hence decryption will fail.
*/
if (!EVP_CipherInit_ex(loopargs[k].ctx, evp_cipher, NULL,
NULL, iv, decrypt ? 0 : 1)) {
BIO_printf(bio_err, "\nEVP_CipherInit_ex failure\n");
NULL, NULL, ae_mode ? 1 : !decrypt)) {
BIO_printf(bio_err, "\nCouldn't init the context\n");
dofail();
exit(1);
}
/* Padding isn't needed */
EVP_CIPHER_CTX_set_padding(loopargs[k].ctx, 0);
keylen = EVP_CIPHER_CTX_get_key_length(loopargs[k].ctx);
loopargs[k].key = app_malloc(keylen, "evp_cipher key");
EVP_CIPHER_CTX_rand_key(loopargs[k].ctx, loopargs[k].key);
if (!EVP_CipherInit_ex(loopargs[k].ctx, NULL, NULL,
loopargs[k].key, NULL, -1)) {
BIO_printf(bio_err, "\nEVP_CipherInit_ex failure\n");
dofail();
exit(1);
}
OPENSSL_clear_free(loopargs[k].key, keylen);
/* GCM-SIV/SIV mode only allows for a single Update operation */
if (EVP_CIPHER_get_mode(evp_cipher) == EVP_CIPH_SIV_MODE
|| EVP_CIPHER_get_mode(evp_cipher) == EVP_CIPH_GCM_SIV_MODE)
(void)EVP_CIPHER_CTX_ctrl(loopargs[k].ctx,
EVP_CTRL_SET_SPEED, 1, NULL);
if (!ae_mode) {
if (!EVP_CipherInit_ex(loopargs[k].ctx, NULL, NULL,
loopargs[k].key, NULL, -1)) {
BIO_printf(bio_err, "\nFailed to set the key\n");
dofail();
exit(1);
}
} else if (mode_op == EVP_CIPH_SIV_MODE
|| mode_op == EVP_CIPH_GCM_SIV_MODE) {
EVP_CIPHER_CTX_ctrl(loopargs[k].ctx,
EVP_CTRL_SET_SPEED, 1, NULL);
}
if (ae_mode && decrypt) {
/* Set length of iv (Doesn't apply to SIV mode) */
if (mode_op != EVP_CIPH_SIV_MODE) {
if (!EVP_CIPHER_CTX_ctrl(loopargs[k].ctx,
EVP_CTRL_AEAD_SET_IVLEN,
aead_ivlen, NULL)) {
BIO_printf(bio_err, "\nFailed to set iv length\n");
dofail();
exit(1);
}
}
/* Set tag_len (Not for GCM/SIV at encryption stage) */
if (mode_op != EVP_CIPH_GCM_MODE
&& mode_op != EVP_CIPH_SIV_MODE
&& mode_op != EVP_CIPH_GCM_SIV_MODE) {
if (!EVP_CIPHER_CTX_ctrl(loopargs[k].ctx,
EVP_CTRL_AEAD_SET_TAG,
TAG_LEN, NULL)) {
BIO_printf(bio_err,
"\nFailed to set tag length\n");
dofail();
exit(1);
}
}
if (!EVP_CipherInit_ex(loopargs[k].ctx, NULL, NULL,
loopargs[k].key, aead_iv, -1)) {
BIO_printf(bio_err, "\nFailed to set the key\n");
dofail();
exit(1);
}
/* Set total length of input. Only required for CCM */
if (mode_op == EVP_CIPH_CCM_MODE) {
if (!EVP_EncryptUpdate(loopargs[k].ctx, NULL,
&outlen, NULL,
lengths[testnum])) {
BIO_printf(bio_err,
"\nCouldn't set input text length\n");
dofail();
exit(1);
}
}
if (aead) {
if (!EVP_EncryptUpdate(loopargs[k].ctx, NULL,
&outlen, aad, sizeof(aad))) {
BIO_printf(bio_err,
"\nCouldn't insert AAD when encrypting\n");
dofail();
exit(1);
}
}
if (!EVP_EncryptUpdate(loopargs[k].ctx, loopargs[k].buf,
&outlen, loopargs[k].buf,
lengths[testnum])) {
BIO_printf(bio_err,
"\nFailed to to encrypt the data\n");
dofail();
exit(1);
}
if (!EVP_EncryptFinal_ex(loopargs[k].ctx,
loopargs[k].buf, &outlen)) {
BIO_printf(bio_err,
"\nFailed finalize the encryption\n");
dofail();
exit(1);
}
if (!EVP_CIPHER_CTX_ctrl(loopargs[k].ctx, EVP_CTRL_AEAD_GET_TAG,
TAG_LEN, &loopargs[k].tag)) {
BIO_printf(bio_err, "\nFailed to get the tag\n");
dofail();
exit(1);
}
EVP_CIPHER_CTX_free(loopargs[k].ctx);
loopargs[k].ctx = EVP_CIPHER_CTX_new();
if (loopargs[k].ctx == NULL) {
BIO_printf(bio_err,
"\nEVP_CIPHER_CTX_new failure\n");
exit(1);
}
if (!EVP_CipherInit_ex(loopargs[k].ctx, evp_cipher,
NULL, NULL, NULL, 0)) {
BIO_printf(bio_err,
"\nFailed initializing the context\n");
dofail();
exit(1);
}
EVP_CIPHER_CTX_set_padding(loopargs[k].ctx, 0);
/* GCM-SIV/SIV only allows for a single Update operation */
if (mode_op == EVP_CIPH_SIV_MODE
|| mode_op == EVP_CIPH_GCM_SIV_MODE)
EVP_CIPHER_CTX_ctrl(loopargs[k].ctx,
EVP_CTRL_SET_SPEED, 1, NULL);
}
}
Time_F(START);
count = run_benchmark(async_jobs, loopfunc, loopargs);
d = Time_F(STOP);
for (k = 0; k < loopargs_len; k++)
for (k = 0; k < loopargs_len; k++) {
OPENSSL_clear_free(loopargs[k].key, keylen);
EVP_CIPHER_CTX_free(loopargs[k].ctx);
}
print_result(D_EVP, testnum, count, d);
}
} else if (evp_md_name != NULL) {
@ -4881,7 +5092,6 @@ static void multiblock_speed(const EVP_CIPHER *evp_cipher, int lengths_single,
print_message(alg_name, mblengths[j], seconds->sym);
Time_F(START);
for (count = 0; run && COND(count); count++) {
unsigned char aad[EVP_AEAD_TLS1_AAD_LEN];
EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM mb_param;
size_t len = mblengths[j];
int packlen;