mirror of https://github.com/wolfSSL/wolfssl.git
1039 lines
36 KiB
C
1039 lines
36 KiB
C
/* test_async.c
|
|
*
|
|
* Copyright (C) 2006-2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#include <tests/unit.h>
|
|
|
|
#ifdef NO_INLINE
|
|
#include <wolfssl/wolfcrypt/misc.h>
|
|
#else
|
|
#define WOLFSSL_MISC_INCLUDED
|
|
#include <wolfcrypt/src/misc.c>
|
|
#endif
|
|
|
|
#include <wolfssl/wolfcrypt/settings.h>
|
|
#include <wolfssl/wolfcrypt/error-crypt.h>
|
|
#include <wolfssl/wolfcrypt/cryptocb.h>
|
|
#include <wolfssl/wolfcrypt/aes.h>
|
|
#include <wolfssl/wolfcrypt/des3.h>
|
|
#include <wolfssl/wolfcrypt/chacha20_poly1305.h>
|
|
#include <wolfssl/wolfcrypt/async.h>
|
|
#include <wolfssl/wolfcrypt/wolfevent.h>
|
|
#include <tests/api/api.h>
|
|
#include <tests/api/test_async.h>
|
|
#include <tests/utils.h>
|
|
|
|
/*
|
|
* Crypto callback async "poll to fill output" completion
|
|
* (WOLF_CRYPTO_CB_ASYNC_POLL).
|
|
*
|
|
* A crypto callback that returns WC_PENDING_E for a TLS record cipher is
|
|
* completed at poll time: wolfSSL keeps the async event queued and re-enters
|
|
* the callback with WC_ALGO_TYPE_ASYNC_POLL so it can finish the job and fill
|
|
* the output buffer. These tests exercise the completion mechanism directly
|
|
* with a fake HSM that offloads the record ciphers (AES-GCM, AES-CBC,
|
|
* AES-CCM, 3DES-CBC) and declines everything else.
|
|
*
|
|
* The tests only run when the feature is compiled in; otherwise they skip.
|
|
*/
|
|
|
|
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLF_CRYPTO_CB_ASYNC_POLL)
|
|
|
|
#define TEST_ASYNC_HSM_DEVID 0x00005150
|
|
#define TEST_ASYNC_HSM_DEVID2 0x00005151 /* second device, per-peer TLS test */
|
|
#define TEST_ASYNC_MAX_POLLS 16
|
|
|
|
/* Offloading tests run at pend depths 0..TEST_ASYNC_MAX_PEND. Depth N returns
|
|
* WC_PENDING_E on submit and N more times at poll before completing (N+1 polls
|
|
* total). Must stay below TEST_ASYNC_MAX_POLLS. */
|
|
#define TEST_ASYNC_MAX_PEND 5
|
|
|
|
/* One in-flight offloaded job. */
|
|
typedef struct TestHsmJob {
|
|
int active;
|
|
int type; /* wc_CipherType */
|
|
int dec; /* 0 = encrypt, 1 = decrypt */
|
|
int pendRemaining; /* polls still returning WC_PENDING_E */
|
|
void* obj; /* Aes* or Des3* */
|
|
byte* out;
|
|
const byte* in;
|
|
word32 sz;
|
|
const byte* iv;
|
|
word32 ivSz;
|
|
byte* authTag; /* encrypt: output tag */
|
|
const byte* authTagDec; /* decrypt: input tag to verify */
|
|
word32 authTagSz;
|
|
const byte* authIn;
|
|
word32 authInSz;
|
|
} TestHsmJob;
|
|
|
|
typedef struct TestHsm {
|
|
TestHsmJob job;
|
|
int pendPolls; /* polls to stay in-flight before completing */
|
|
int offloadDec; /* also offload decrypt (single-op tests only) */
|
|
int invocations; /* callback entered at all (any algo/type) */
|
|
int submits; /* offloaded (pended) op count */
|
|
int decSubmits; /* offloaded decrypt op count */
|
|
int polls;
|
|
} TestHsm;
|
|
|
|
/* Run the software cipher into the saved output, with routing disabled so the
|
|
* completion does not re-enter this callback. */
|
|
static int test_hsm_complete(TestHsmJob* j)
|
|
{
|
|
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
|
|
switch (j->type) {
|
|
#ifdef HAVE_AESGCM
|
|
case WC_CIPHER_AES_GCM: {
|
|
Aes* aes = (Aes*)j->obj;
|
|
int save = aes->devId;
|
|
aes->devId = INVALID_DEVID;
|
|
if (j->dec)
|
|
ret = wc_AesGcmDecrypt(aes, j->out, j->in, j->sz, j->iv, j->ivSz,
|
|
j->authTagDec, j->authTagSz, j->authIn,
|
|
j->authInSz);
|
|
else
|
|
ret = wc_AesGcmEncrypt(aes, j->out, j->in, j->sz, j->iv, j->ivSz,
|
|
j->authTag, j->authTagSz, j->authIn,
|
|
j->authInSz);
|
|
aes->devId = save;
|
|
break;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_AESCCM
|
|
case WC_CIPHER_AES_CCM: {
|
|
Aes* aes = (Aes*)j->obj;
|
|
int save = aes->devId;
|
|
aes->devId = INVALID_DEVID;
|
|
if (j->dec)
|
|
ret = wc_AesCcmDecrypt(aes, j->out, j->in, j->sz, j->iv, j->ivSz,
|
|
j->authTagDec, j->authTagSz, j->authIn,
|
|
j->authInSz);
|
|
else
|
|
ret = wc_AesCcmEncrypt(aes, j->out, j->in, j->sz, j->iv, j->ivSz,
|
|
j->authTag, j->authTagSz, j->authIn,
|
|
j->authInSz);
|
|
aes->devId = save;
|
|
break;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_AES_CBC
|
|
case WC_CIPHER_AES_CBC: {
|
|
Aes* aes = (Aes*)j->obj;
|
|
int save = aes->devId;
|
|
aes->devId = INVALID_DEVID;
|
|
if (j->dec)
|
|
ret = wc_AesCbcDecrypt(aes, j->out, j->in, j->sz);
|
|
else
|
|
ret = wc_AesCbcEncrypt(aes, j->out, j->in, j->sz);
|
|
aes->devId = save;
|
|
break;
|
|
}
|
|
#endif
|
|
#ifndef NO_DES3
|
|
case WC_CIPHER_DES3: {
|
|
Des3* des = (Des3*)j->obj;
|
|
int save = des->devId;
|
|
des->devId = INVALID_DEVID;
|
|
if (j->dec)
|
|
ret = wc_Des3_CbcDecrypt(des, j->out, j->in, j->sz);
|
|
else
|
|
ret = wc_Des3_CbcEncrypt(des, j->out, j->in, j->sz);
|
|
des->devId = save;
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* Fake HSM: pend and poll-complete the record ciphers, decline everything
|
|
* else so it falls back to software. */
|
|
static int test_hsm_cb(int devId, wc_CryptoInfo* info, void* ctx)
|
|
{
|
|
TestHsm* hsm = (TestHsm*)ctx;
|
|
(void)devId;
|
|
|
|
if (info == NULL)
|
|
return WC_NO_ERR_TRACE(BAD_FUNC_ARG);
|
|
|
|
hsm->invocations++;
|
|
|
|
if (info->algo_type == WC_ALGO_TYPE_CIPHER) {
|
|
TestHsmJob* j = &hsm->job;
|
|
|
|
/* Offload decrypt only when asked. The single job slot cannot hold a
|
|
* client decrypt and a server encrypt at once, which collides in the
|
|
* TLS half-duplex flow; the direct single-op tests drive decrypt on
|
|
* its own instead. */
|
|
if (info->cipher.enc == 0 && !hsm->offloadDec)
|
|
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
|
|
XMEMSET(j, 0, sizeof(*j));
|
|
j->dec = (info->cipher.enc == 0);
|
|
|
|
switch (info->cipher.type) {
|
|
#ifdef HAVE_AESGCM
|
|
case WC_CIPHER_AES_GCM:
|
|
if (j->dec) {
|
|
wc_CryptoCb_AesAuthDec* e = &info->cipher.aesgcm_dec;
|
|
j->obj = e->aes; j->out = e->out; j->in = e->in; j->sz = e->sz;
|
|
j->iv = e->iv; j->ivSz = e->ivSz;
|
|
j->authTagDec = e->authTag; j->authTagSz = e->authTagSz;
|
|
j->authIn = e->authIn; j->authInSz = e->authInSz;
|
|
}
|
|
else {
|
|
wc_CryptoCb_AesAuthEnc* e = &info->cipher.aesgcm_enc;
|
|
j->obj = e->aes; j->out = e->out; j->in = e->in; j->sz = e->sz;
|
|
j->iv = e->iv; j->ivSz = e->ivSz;
|
|
j->authTag = e->authTag; j->authTagSz = e->authTagSz;
|
|
j->authIn = e->authIn; j->authInSz = e->authInSz;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_AESCCM
|
|
case WC_CIPHER_AES_CCM: /* CCM uses the nonce field */
|
|
if (j->dec) {
|
|
wc_CryptoCb_AesAuthDec* e = &info->cipher.aesccm_dec;
|
|
j->obj = e->aes; j->out = e->out; j->in = e->in; j->sz = e->sz;
|
|
j->iv = e->nonce; j->ivSz = e->nonceSz;
|
|
j->authTagDec = e->authTag; j->authTagSz = e->authTagSz;
|
|
j->authIn = e->authIn; j->authInSz = e->authInSz;
|
|
}
|
|
else {
|
|
wc_CryptoCb_AesAuthEnc* e = &info->cipher.aesccm_enc;
|
|
j->obj = e->aes; j->out = e->out; j->in = e->in; j->sz = e->sz;
|
|
j->iv = e->nonce; j->ivSz = e->nonceSz;
|
|
j->authTag = e->authTag; j->authTagSz = e->authTagSz;
|
|
j->authIn = e->authIn; j->authInSz = e->authInSz;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_AES_CBC
|
|
case WC_CIPHER_AES_CBC:
|
|
j->obj = info->cipher.aescbc.aes; j->out = info->cipher.aescbc.out;
|
|
j->in = info->cipher.aescbc.in; j->sz = info->cipher.aescbc.sz;
|
|
break;
|
|
#endif
|
|
#ifndef NO_DES3
|
|
case WC_CIPHER_DES3:
|
|
j->obj = info->cipher.des3.des; j->out = info->cipher.des3.out;
|
|
j->in = info->cipher.des3.in; j->sz = info->cipher.des3.sz;
|
|
break;
|
|
#endif
|
|
default:
|
|
/* not a record cipher this HSM offloads: run in software */
|
|
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
}
|
|
|
|
j->active = 1;
|
|
j->type = info->cipher.type;
|
|
j->pendRemaining = hsm->pendPolls;
|
|
hsm->submits++;
|
|
if (j->dec)
|
|
hsm->decSubmits++;
|
|
return WC_PENDING_E;
|
|
}
|
|
|
|
if (info->algo_type == WC_ALGO_TYPE_ASYNC_POLL) {
|
|
TestHsmJob* j = &hsm->job;
|
|
hsm->polls++;
|
|
if (!j->active)
|
|
return WC_NO_ERR_TRACE(WC_NO_PENDING_E);
|
|
if (j->pendRemaining > 0) {
|
|
j->pendRemaining--; /* still in-flight this poll */
|
|
return WC_PENDING_E;
|
|
}
|
|
j->active = 0;
|
|
return test_hsm_complete(j);
|
|
}
|
|
|
|
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
}
|
|
|
|
/* Reset the device counters and in-flight job for another run, and set how
|
|
* many polls the next op stays in-flight (pendPolls) before completing. */
|
|
static void test_hsm_reset(TestHsm* hsm, int pendPolls)
|
|
{
|
|
XMEMSET(&hsm->job, 0, sizeof(hsm->job));
|
|
hsm->pendPolls = pendPolls;
|
|
hsm->invocations = 0;
|
|
hsm->submits = 0;
|
|
hsm->decSubmits = 0;
|
|
hsm->polls = 0;
|
|
}
|
|
|
|
/* Init the event and drive the poll to completion, bounded so a missing poll
|
|
* path fails instead of hanging. Returns the event result. */
|
|
static int test_async_drive_poll(WC_ASYNC_DEV* dev, int ret)
|
|
{
|
|
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
|
|
WOLF_EVENT* ev = &dev->event;
|
|
int i;
|
|
(void)wolfAsync_EventInit(ev, WOLF_EVENT_TYPE_ASYNC_WOLFCRYPT, dev,
|
|
WC_ASYNC_FLAG_NONE);
|
|
for (i = 0; i < TEST_ASYNC_MAX_POLLS &&
|
|
ev->ret == WC_NO_ERR_TRACE(WC_PENDING_E); i++) {
|
|
(void)wolfAsync_EventPoll(ev, WOLF_POLL_FLAG_CHECK_HW);
|
|
}
|
|
ret = ev->ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static WC_INLINE const byte* test_async_key32(void)
|
|
{
|
|
static const byte k[32] = {
|
|
0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,
|
|
0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,
|
|
0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,
|
|
0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4
|
|
};
|
|
return k;
|
|
}
|
|
static WC_INLINE const byte* test_async_iv12(void)
|
|
{
|
|
static const byte v[12] = {
|
|
0xca,0xfe,0xba,0xbe,0xfa,0xce,0xdb,0xad,0xde,0xca,0xf8,0x88
|
|
};
|
|
return v;
|
|
}
|
|
static WC_INLINE const byte* test_async_iv16(void)
|
|
{
|
|
static const byte v[16] = {
|
|
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
|
|
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f
|
|
};
|
|
return v;
|
|
}
|
|
static WC_INLINE const byte* test_async_plain32(void)
|
|
{
|
|
static const byte p[32] = {
|
|
0xd9,0x31,0x32,0x25,0xf8,0x84,0x06,0xe5,
|
|
0xa5,0x59,0x09,0xc5,0xaf,0xf5,0x26,0x9a,
|
|
0x86,0xa7,0xa9,0x53,0x15,0x34,0xf7,0xda,
|
|
0x2e,0x4c,0x30,0x3d,0x8a,0x31,0x8a,0x72
|
|
};
|
|
return p;
|
|
}
|
|
static WC_INLINE const byte* test_async_aad12(void)
|
|
{
|
|
static const byte a[12] = {
|
|
0xfe,0xed,0xfa,0xce,0xde,0xad,0xbe,0xef,0xfe,0xed,0xfa,0xce
|
|
};
|
|
return a;
|
|
}
|
|
|
|
#endif /* feature macros */
|
|
|
|
/*
|
|
* AES-GCM record cipher: pends, poll-completes, and produces the same
|
|
* ciphertext and tag as a pure software encrypt.
|
|
*/
|
|
int test_wc_CryptoCb_AsyncPollAesGcm(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLF_CRYPTO_CB_ASYNC_POLL) && defined(HAVE_AESGCM)
|
|
TestHsm hsm;
|
|
Aes hw, sw;
|
|
byte out[32], ref[32], tag[16], rtag[16];
|
|
int registered = 0;
|
|
int pend;
|
|
|
|
XMEMSET(&hsm, 0, sizeof(hsm));
|
|
XMEMSET(&hw, 0, sizeof(hw));
|
|
XMEMSET(&sw, 0, sizeof(sw));
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_ASYNC_HSM_DEVID, test_hsm_cb,
|
|
&hsm), 0);
|
|
if (EXPECT_SUCCESS())
|
|
registered = 1;
|
|
|
|
ExpectIntEQ(wc_AesInit(&hw, NULL, TEST_ASYNC_HSM_DEVID), 0);
|
|
ExpectIntEQ(wc_AesInit(&sw, NULL, INVALID_DEVID), 0);
|
|
ExpectIntEQ(wc_AesGcmSetKey(&hw, test_async_key32(), 32), 0);
|
|
ExpectIntEQ(wc_AesGcmSetKey(&sw, test_async_key32(), 32), 0);
|
|
|
|
/* software reference */
|
|
ExpectIntEQ(wc_AesGcmEncrypt(&sw, ref, test_async_plain32(), 32,
|
|
test_async_iv12(), 12, rtag, 16, test_async_aad12(), 12), 0);
|
|
|
|
/* pend depth 0..TEST_ASYNC_MAX_PEND: op completes on poll number pend+1 */
|
|
for (pend = 0; pend <= TEST_ASYNC_MAX_PEND; pend++) {
|
|
test_hsm_reset(&hsm, pend);
|
|
ExpectIntEQ(test_async_drive_poll(&hw.asyncDev,
|
|
wc_AesGcmEncrypt(&hw, out, test_async_plain32(), 32,
|
|
test_async_iv12(), 12, tag, 16, test_async_aad12(), 12)), 0);
|
|
ExpectIntEQ(hsm.submits, 1);
|
|
ExpectIntEQ(hsm.polls, pend + 1);
|
|
ExpectBufEQ(out, ref, 32);
|
|
ExpectBufEQ(tag, rtag, 16);
|
|
}
|
|
|
|
/* decrypt side: poll-complete recovers the plaintext (same GCM key) */
|
|
hsm.offloadDec = 1;
|
|
for (pend = 0; pend <= TEST_ASYNC_MAX_PEND; pend++) {
|
|
test_hsm_reset(&hsm, pend);
|
|
ExpectIntEQ(test_async_drive_poll(&hw.asyncDev,
|
|
wc_AesGcmDecrypt(&hw, out, ref, 32,
|
|
test_async_iv12(), 12, rtag, 16, test_async_aad12(), 12)), 0);
|
|
ExpectIntEQ(hsm.submits, 1);
|
|
ExpectIntEQ(hsm.polls, pend + 1);
|
|
ExpectBufEQ(out, test_async_plain32(), 32);
|
|
}
|
|
|
|
wc_AesFree(&hw);
|
|
wc_AesFree(&sw);
|
|
if (registered)
|
|
wc_CryptoCb_UnRegisterDevice(TEST_ASYNC_HSM_DEVID);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* AES-CBC record cipher: pends, poll-completes, matches software.
|
|
*/
|
|
int test_wc_CryptoCb_AsyncPollAesCbc(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLF_CRYPTO_CB_ASYNC_POLL) && defined(HAVE_AES_CBC)
|
|
TestHsm hsm;
|
|
Aes hw, sw, hwd;
|
|
byte out[32], ref[32];
|
|
int registered = 0;
|
|
int pend;
|
|
|
|
XMEMSET(&hsm, 0, sizeof(hsm));
|
|
XMEMSET(&hw, 0, sizeof(hw));
|
|
XMEMSET(&sw, 0, sizeof(sw));
|
|
XMEMSET(&hwd, 0, sizeof(hwd));
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_ASYNC_HSM_DEVID, test_hsm_cb,
|
|
&hsm), 0);
|
|
if (EXPECT_SUCCESS())
|
|
registered = 1;
|
|
|
|
ExpectIntEQ(wc_AesInit(&hw, NULL, TEST_ASYNC_HSM_DEVID), 0);
|
|
ExpectIntEQ(wc_AesInit(&sw, NULL, INVALID_DEVID), 0);
|
|
ExpectIntEQ(wc_AesInit(&hwd, NULL, TEST_ASYNC_HSM_DEVID), 0);
|
|
ExpectIntEQ(wc_AesSetKey(&hw, test_async_key32(), 32, test_async_iv16(),
|
|
AES_ENCRYPTION), 0);
|
|
ExpectIntEQ(wc_AesSetKey(&sw, test_async_key32(), 32, test_async_iv16(),
|
|
AES_ENCRYPTION), 0);
|
|
ExpectIntEQ(wc_AesSetKey(&hwd, test_async_key32(), 32, test_async_iv16(),
|
|
AES_DECRYPTION), 0);
|
|
|
|
ExpectIntEQ(wc_AesCbcEncrypt(&sw, ref, test_async_plain32(), 32), 0);
|
|
|
|
for (pend = 0; pend <= TEST_ASYNC_MAX_PEND; pend++) {
|
|
test_hsm_reset(&hsm, pend);
|
|
/* CBC chains, so re-set the IV before each encrypt */
|
|
ExpectIntEQ(wc_AesSetIV(&hw, test_async_iv16()), 0);
|
|
ExpectIntEQ(test_async_drive_poll(&hw.asyncDev,
|
|
wc_AesCbcEncrypt(&hw, out, test_async_plain32(), 32)), 0);
|
|
ExpectIntEQ(hsm.submits, 1);
|
|
ExpectIntEQ(hsm.polls, pend + 1);
|
|
ExpectBufEQ(out, ref, 32);
|
|
}
|
|
|
|
/* decrypt side: poll-complete recovers the plaintext from ref */
|
|
hsm.offloadDec = 1;
|
|
for (pend = 0; pend <= TEST_ASYNC_MAX_PEND; pend++) {
|
|
test_hsm_reset(&hsm, pend);
|
|
ExpectIntEQ(wc_AesSetIV(&hwd, test_async_iv16()), 0);
|
|
ExpectIntEQ(test_async_drive_poll(&hwd.asyncDev,
|
|
wc_AesCbcDecrypt(&hwd, out, ref, 32)), 0);
|
|
ExpectIntEQ(hsm.submits, 1);
|
|
ExpectIntEQ(hsm.polls, pend + 1);
|
|
ExpectBufEQ(out, test_async_plain32(), 32);
|
|
}
|
|
|
|
wc_AesFree(&hw);
|
|
wc_AesFree(&sw);
|
|
wc_AesFree(&hwd);
|
|
if (registered)
|
|
wc_CryptoCb_UnRegisterDevice(TEST_ASYNC_HSM_DEVID);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* AES-CCM record cipher: pends, poll-completes, matches software.
|
|
*/
|
|
int test_wc_CryptoCb_AsyncPollAesCcm(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLF_CRYPTO_CB_ASYNC_POLL) && defined(HAVE_AESCCM)
|
|
TestHsm hsm;
|
|
Aes hw, sw;
|
|
byte out[32], ref[32], tag[16], rtag[16];
|
|
int registered = 0;
|
|
int pend;
|
|
|
|
XMEMSET(&hsm, 0, sizeof(hsm));
|
|
XMEMSET(&hw, 0, sizeof(hw));
|
|
XMEMSET(&sw, 0, sizeof(sw));
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_ASYNC_HSM_DEVID, test_hsm_cb,
|
|
&hsm), 0);
|
|
if (EXPECT_SUCCESS())
|
|
registered = 1;
|
|
|
|
ExpectIntEQ(wc_AesInit(&hw, NULL, TEST_ASYNC_HSM_DEVID), 0);
|
|
ExpectIntEQ(wc_AesInit(&sw, NULL, INVALID_DEVID), 0);
|
|
ExpectIntEQ(wc_AesCcmSetKey(&hw, test_async_key32(), 16), 0);
|
|
ExpectIntEQ(wc_AesCcmSetKey(&sw, test_async_key32(), 16), 0);
|
|
|
|
ExpectIntEQ(wc_AesCcmEncrypt(&sw, ref, test_async_plain32(), 32,
|
|
test_async_iv12(), 12, rtag, 16, test_async_aad12(), 12), 0);
|
|
|
|
for (pend = 0; pend <= TEST_ASYNC_MAX_PEND; pend++) {
|
|
test_hsm_reset(&hsm, pend);
|
|
ExpectIntEQ(test_async_drive_poll(&hw.asyncDev,
|
|
wc_AesCcmEncrypt(&hw, out, test_async_plain32(), 32,
|
|
test_async_iv12(), 12, tag, 16, test_async_aad12(), 12)), 0);
|
|
ExpectIntEQ(hsm.submits, 1);
|
|
ExpectIntEQ(hsm.polls, pend + 1);
|
|
ExpectBufEQ(out, ref, 32);
|
|
ExpectBufEQ(tag, rtag, 16);
|
|
}
|
|
|
|
/* decrypt side: poll-complete recovers the plaintext (same CCM key) */
|
|
hsm.offloadDec = 1;
|
|
for (pend = 0; pend <= TEST_ASYNC_MAX_PEND; pend++) {
|
|
test_hsm_reset(&hsm, pend);
|
|
ExpectIntEQ(test_async_drive_poll(&hw.asyncDev,
|
|
wc_AesCcmDecrypt(&hw, out, ref, 32,
|
|
test_async_iv12(), 12, rtag, 16, test_async_aad12(), 12)), 0);
|
|
ExpectIntEQ(hsm.submits, 1);
|
|
ExpectIntEQ(hsm.polls, pend + 1);
|
|
ExpectBufEQ(out, test_async_plain32(), 32);
|
|
}
|
|
|
|
wc_AesFree(&hw);
|
|
wc_AesFree(&sw);
|
|
if (registered)
|
|
wc_CryptoCb_UnRegisterDevice(TEST_ASYNC_HSM_DEVID);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* 3DES-CBC record cipher: pends, poll-completes, matches software. Covers the
|
|
* WOLFSSL_ASYNC_MARKER_3DES routing (not just AES).
|
|
*/
|
|
int test_wc_CryptoCb_AsyncPollDes3(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLF_CRYPTO_CB_ASYNC_POLL) && !defined(NO_DES3)
|
|
TestHsm hsm;
|
|
Des3 hw, sw, hwd;
|
|
byte out[32], ref[32];
|
|
static const byte key24[24] = {
|
|
0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
|
|
0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0x01,
|
|
0x45,0x67,0x89,0xab,0xcd,0xef,0x01,0x23
|
|
};
|
|
static const byte iv8[8] = {
|
|
0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef
|
|
};
|
|
int registered = 0;
|
|
int pend;
|
|
|
|
XMEMSET(&hsm, 0, sizeof(hsm));
|
|
XMEMSET(&hw, 0, sizeof(hw));
|
|
XMEMSET(&sw, 0, sizeof(sw));
|
|
XMEMSET(&hwd, 0, sizeof(hwd));
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_ASYNC_HSM_DEVID, test_hsm_cb,
|
|
&hsm), 0);
|
|
if (EXPECT_SUCCESS())
|
|
registered = 1;
|
|
|
|
ExpectIntEQ(wc_Des3Init(&hw, NULL, TEST_ASYNC_HSM_DEVID), 0);
|
|
ExpectIntEQ(wc_Des3Init(&sw, NULL, INVALID_DEVID), 0);
|
|
ExpectIntEQ(wc_Des3Init(&hwd, NULL, TEST_ASYNC_HSM_DEVID), 0);
|
|
ExpectIntEQ(wc_Des3_SetKey(&hw, key24, iv8, DES_ENCRYPTION), 0);
|
|
ExpectIntEQ(wc_Des3_SetKey(&sw, key24, iv8, DES_ENCRYPTION), 0);
|
|
ExpectIntEQ(wc_Des3_SetKey(&hwd, key24, iv8, DES_DECRYPTION), 0);
|
|
|
|
ExpectIntEQ(wc_Des3_CbcEncrypt(&sw, ref, test_async_plain32(), 32), 0);
|
|
|
|
for (pend = 0; pend <= TEST_ASYNC_MAX_PEND; pend++) {
|
|
test_hsm_reset(&hsm, pend);
|
|
/* CBC chains, so re-set the IV before each encrypt */
|
|
ExpectIntEQ(wc_Des3_SetIV(&hw, iv8), 0);
|
|
ExpectIntEQ(test_async_drive_poll(&hw.asyncDev,
|
|
wc_Des3_CbcEncrypt(&hw, out, test_async_plain32(), 32)), 0);
|
|
ExpectIntEQ(hsm.submits, 1);
|
|
ExpectIntEQ(hsm.polls, pend + 1);
|
|
ExpectBufEQ(out, ref, 32);
|
|
}
|
|
|
|
/* decrypt side: poll-complete recovers the plaintext from ref */
|
|
hsm.offloadDec = 1;
|
|
for (pend = 0; pend <= TEST_ASYNC_MAX_PEND; pend++) {
|
|
test_hsm_reset(&hsm, pend);
|
|
ExpectIntEQ(wc_Des3_SetIV(&hwd, iv8), 0);
|
|
ExpectIntEQ(test_async_drive_poll(&hwd.asyncDev,
|
|
wc_Des3_CbcDecrypt(&hwd, out, ref, 32)), 0);
|
|
ExpectIntEQ(hsm.submits, 1);
|
|
ExpectIntEQ(hsm.polls, pend + 1);
|
|
ExpectBufEQ(out, test_async_plain32(), 32);
|
|
}
|
|
|
|
wc_Des3Free(&hw);
|
|
wc_Des3Free(&sw);
|
|
wc_Des3Free(&hwd);
|
|
if (registered)
|
|
wc_CryptoCb_UnRegisterDevice(TEST_ASYNC_HSM_DEVID);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* A cipher with a crypto callback dispatch that the HSM declines (AES-CTR):
|
|
* the callback IS entered (it has a dispatch) but returns unavailable, so the
|
|
* op falls back to software with no pend and no poll. Contrast with the
|
|
* unimplemented ciphers below, where the callback is never entered.
|
|
*/
|
|
int test_wc_CryptoCb_AsyncPollUnsupported(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLF_CRYPTO_CB_ASYNC_POLL) && defined(WOLFSSL_AES_COUNTER)
|
|
TestHsm hsm;
|
|
Aes hw, sw;
|
|
byte out[32], ref[32];
|
|
int registered = 0;
|
|
|
|
XMEMSET(&hsm, 0, sizeof(hsm));
|
|
XMEMSET(&hw, 0, sizeof(hw));
|
|
XMEMSET(&sw, 0, sizeof(sw));
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_ASYNC_HSM_DEVID, test_hsm_cb,
|
|
&hsm), 0);
|
|
if (EXPECT_SUCCESS())
|
|
registered = 1;
|
|
|
|
ExpectIntEQ(wc_AesInit(&hw, NULL, TEST_ASYNC_HSM_DEVID), 0);
|
|
ExpectIntEQ(wc_AesInit(&sw, NULL, INVALID_DEVID), 0);
|
|
ExpectIntEQ(wc_AesSetKey(&hw, test_async_key32(), 32, test_async_iv16(),
|
|
AES_ENCRYPTION), 0);
|
|
ExpectIntEQ(wc_AesSetKey(&sw, test_async_key32(), 32, test_async_iv16(),
|
|
AES_ENCRYPTION), 0);
|
|
|
|
/* declined by the HSM -> software fallback, completes synchronously */
|
|
ExpectIntEQ(wc_AesCtrEncrypt(&hw, out, test_async_plain32(), 32), 0);
|
|
ExpectIntGE(hsm.invocations, 1); /* dispatch exists: callback was entered */
|
|
ExpectIntEQ(hsm.submits, 0); /* but declined: not offloaded */
|
|
ExpectIntEQ(hsm.polls, 0);
|
|
|
|
ExpectIntEQ(wc_AesCtrEncrypt(&sw, ref, test_async_plain32(), 32), 0);
|
|
ExpectBufEQ(out, ref, 32);
|
|
|
|
wc_AesFree(&hw);
|
|
wc_AesFree(&sw);
|
|
if (registered)
|
|
wc_CryptoCb_UnRegisterDevice(TEST_ASYNC_HSM_DEVID);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Negative test: ChaCha20-Poly1305 is named in the crypto callback cipher type
|
|
* table (WC_CIPHER_CHACHA) but has no dispatch, so it is NOT offloadable. With
|
|
* a device registered, a ChaCha20-Poly1305 encrypt must never enter the
|
|
* callback and must run entirely in software. If crypto callback ChaCha
|
|
* support is ever added, this test will fail and must be updated.
|
|
*/
|
|
int test_wc_CryptoCb_AsyncPollChachaUnimpl(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLF_CRYPTO_CB_ASYNC_POLL) && defined(HAVE_CHACHA) && \
|
|
defined(HAVE_POLY1305)
|
|
TestHsm hsm;
|
|
byte out[32], tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
static const byte key[CHACHA20_POLY1305_AEAD_KEYSIZE] = {
|
|
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
|
|
0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
|
|
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
|
|
0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f
|
|
};
|
|
static const byte iv[CHACHA20_POLY1305_AEAD_IV_SIZE] = {
|
|
0x07,0x00,0x00,0x00,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47
|
|
};
|
|
int registered = 0;
|
|
|
|
XMEMSET(&hsm, 0, sizeof(hsm));
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_ASYNC_HSM_DEVID, test_hsm_cb,
|
|
&hsm), 0);
|
|
if (EXPECT_SUCCESS())
|
|
registered = 1;
|
|
|
|
/* no crypto callback dispatch for ChaCha: runs in software, cb untouched */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv,
|
|
test_async_aad12(), 12, test_async_plain32(), 32, out, tag), 0);
|
|
ExpectIntEQ(hsm.invocations, 0);
|
|
ExpectIntEQ(hsm.submits, 0);
|
|
ExpectIntEQ(hsm.polls, 0);
|
|
|
|
if (registered)
|
|
wc_CryptoCb_UnRegisterDevice(TEST_ASYNC_HSM_DEVID);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Negative test: single DES is named in the crypto callback cipher type table
|
|
* (WC_CIPHER_DES) but has no dispatch, so a DES-CBC encrypt with a device
|
|
* registered must never enter the callback and run entirely in software.
|
|
*/
|
|
int test_wc_CryptoCb_AsyncPollDesUnimpl(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLF_CRYPTO_CB_ASYNC_POLL) && !defined(NO_DES3)
|
|
TestHsm hsm;
|
|
Des des;
|
|
byte out[32];
|
|
static const byte key8[8] = {
|
|
0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef
|
|
};
|
|
static const byte iv8[8] = {
|
|
0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef
|
|
};
|
|
int registered = 0;
|
|
|
|
XMEMSET(&hsm, 0, sizeof(hsm));
|
|
XMEMSET(&des, 0, sizeof(des));
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_ASYNC_HSM_DEVID, test_hsm_cb,
|
|
&hsm), 0);
|
|
if (EXPECT_SUCCESS())
|
|
registered = 1;
|
|
|
|
ExpectIntEQ(wc_Des_SetKey(&des, key8, iv8, DES_ENCRYPTION), 0);
|
|
/* no crypto callback dispatch for single DES: cb untouched */
|
|
ExpectIntEQ(wc_Des_CbcEncrypt(&des, out, test_async_plain32(), 32), 0);
|
|
ExpectIntEQ(hsm.invocations, 0);
|
|
ExpectIntEQ(hsm.submits, 0);
|
|
ExpectIntEQ(hsm.polls, 0);
|
|
|
|
if (registered)
|
|
wc_CryptoCb_UnRegisterDevice(TEST_ASYNC_HSM_DEVID);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Full-stack TLS 1.3 tests. One generic harness drives an in-memory
|
|
* client/server handshake and an application-data echo through the crypto
|
|
* callback record cipher; only the callback implementation and the pinned
|
|
* ciphersuite are swapped per test.
|
|
*/
|
|
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLF_CRYPTO_CB_ASYNC_POLL) && defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_AESGCM) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
|
|
#define TEST_ASYNC_TLS
|
|
|
|
/* A device that offloads the AES-GCM record cipher but does NOT implement the
|
|
* poll completion (returns unavailable for WC_ALGO_TYPE_ASYNC_POLL). The op
|
|
* pends and can never finish, so the record layer must fail rather than ship
|
|
* an unencrypted record. */
|
|
static int test_hsm_nopoll_cb(int devId, wc_CryptoInfo* info, void* ctx)
|
|
{
|
|
TestHsm* hsm = (TestHsm*)ctx;
|
|
(void)devId;
|
|
|
|
if (info == NULL)
|
|
return WC_NO_ERR_TRACE(BAD_FUNC_ARG);
|
|
|
|
hsm->invocations++;
|
|
|
|
if (info->algo_type == WC_ALGO_TYPE_CIPHER && info->cipher.enc == 1 &&
|
|
info->cipher.type == WC_CIPHER_AES_GCM) {
|
|
hsm->submits++;
|
|
return WC_PENDING_E; /* pended, but no poll support */
|
|
}
|
|
if (info->algo_type == WC_ALGO_TYPE_ASYNC_POLL) {
|
|
hsm->polls++;
|
|
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
}
|
|
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
}
|
|
|
|
/* Drive one wolfSSL_write/read through, servicing async pends. Returns 1 if
|
|
* the message round-trips, 0 on failure. */
|
|
static int test_async_tls_echo(WOLFSSL* ssl_c, WOLFSSL* ssl_s)
|
|
{
|
|
const char msg[] = "hello async record layer";
|
|
byte buf[64];
|
|
int ret, err, i;
|
|
|
|
for (i = 0, ret = -1; i < 40; i++) {
|
|
ret = wolfSSL_write(ssl_c, msg, (int)sizeof(msg));
|
|
if (ret == (int)sizeof(msg))
|
|
break;
|
|
err = wolfSSL_get_error(ssl_c, ret);
|
|
if (err == WC_NO_ERR_TRACE(WC_PENDING_E))
|
|
(void)wolfSSL_AsyncPoll(ssl_c, WOLF_POLL_FLAG_CHECK_HW);
|
|
else if (err != WOLFSSL_ERROR_WANT_READ &&
|
|
err != WOLFSSL_ERROR_WANT_WRITE)
|
|
return 0;
|
|
}
|
|
if (ret != (int)sizeof(msg))
|
|
return 0;
|
|
|
|
for (i = 0, ret = -1; i < 40; i++) {
|
|
ret = wolfSSL_read(ssl_s, buf, (int)sizeof(buf));
|
|
if (ret > 0)
|
|
break;
|
|
err = wolfSSL_get_error(ssl_s, ret);
|
|
if (err == WC_NO_ERR_TRACE(WC_PENDING_E))
|
|
(void)wolfSSL_AsyncPoll(ssl_s, WOLF_POLL_FLAG_CHECK_HW);
|
|
else if (err != WOLFSSL_ERROR_WANT_READ &&
|
|
err != WOLFSSL_ERROR_WANT_WRITE)
|
|
return 0;
|
|
}
|
|
return (ret == (int)sizeof(msg) && XMEMCMP(buf, msg, sizeof(msg)) == 0);
|
|
}
|
|
|
|
typedef struct TlsAsyncResult {
|
|
int handshake; /* 0 = handshake completed */
|
|
int dataOk; /* 1 = echo round-tripped */
|
|
int cipherSubmits; /* record ciphers offloaded to the device */
|
|
} TlsAsyncResult;
|
|
|
|
/* Generic harness: register the given callback, run a TLS 1.3 handshake and
|
|
* echo over memio with the given ciphersuite pinned, and report the outcome
|
|
* plus how many record ciphers were offloaded. */
|
|
static void test_async_tls_run(CryptoDevCallbackFunc cb, const char* cipherList,
|
|
int pendPolls, TlsAsyncResult* res)
|
|
{
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
TestHsm hsm;
|
|
int registered = 0;
|
|
|
|
res->handshake = -1;
|
|
res->dataOk = 0;
|
|
res->cipherSubmits = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&hsm, 0, sizeof(hsm));
|
|
hsm.pendPolls = pendPolls; /* polls each record cipher stays in-flight */
|
|
|
|
if (wc_CryptoCb_RegisterDevice(TEST_ASYNC_HSM_DEVID, cb, &hsm) != 0)
|
|
return;
|
|
registered = 1;
|
|
|
|
if (test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method) != 0)
|
|
goto cleanup;
|
|
|
|
(void)wolfSSL_CTX_SetDevId(ctx_c, TEST_ASYNC_HSM_DEVID);
|
|
(void)wolfSSL_CTX_SetDevId(ctx_s, TEST_ASYNC_HSM_DEVID);
|
|
(void)wolfSSL_SetDevId(ssl_c, TEST_ASYNC_HSM_DEVID);
|
|
(void)wolfSSL_SetDevId(ssl_s, TEST_ASYNC_HSM_DEVID);
|
|
|
|
if (cipherList != NULL) {
|
|
if (wolfSSL_set_cipher_list(ssl_c, cipherList) != WOLFSSL_SUCCESS ||
|
|
wolfSSL_set_cipher_list(ssl_s, cipherList) != WOLFSSL_SUCCESS)
|
|
goto cleanup;
|
|
}
|
|
|
|
res->handshake = test_memio_do_handshake(ssl_c, ssl_s, 10, NULL);
|
|
if (res->handshake == 0)
|
|
res->dataOk = test_async_tls_echo(ssl_c, ssl_s);
|
|
res->cipherSubmits = hsm.submits;
|
|
|
|
cleanup:
|
|
if (ssl_c != NULL)
|
|
wolfSSL_free(ssl_c);
|
|
if (ssl_s != NULL)
|
|
wolfSSL_free(ssl_s);
|
|
if (ctx_c != NULL)
|
|
wolfSSL_CTX_free(ctx_c);
|
|
if (ctx_s != NULL)
|
|
wolfSSL_CTX_free(ctx_s);
|
|
if (registered)
|
|
wc_CryptoCb_UnRegisterDevice(TEST_ASYNC_HSM_DEVID);
|
|
}
|
|
|
|
#endif /* TEST_ASYNC_TLS */
|
|
|
|
/*
|
|
* Full TLS 1.3 with AES-GCM offloaded to a poll-completing device: the
|
|
* handshake and an application-data echo both succeed, and the record cipher
|
|
* is offloaded (poll completion drives the encrypted records).
|
|
*/
|
|
int test_wc_CryptoCb_AsyncPollTlsAesGcm(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_ASYNC_TLS
|
|
TlsAsyncResult res;
|
|
int pend;
|
|
|
|
/* pend depth 0..TEST_ASYNC_MAX_PEND: each record cipher stays in-flight for
|
|
* that many extra polls before completing. */
|
|
for (pend = 0; pend <= TEST_ASYNC_MAX_PEND; pend++) {
|
|
test_async_tls_run(test_hsm_cb,
|
|
"TLS13-AES128-GCM-SHA256:TLS13-AES256-GCM-SHA384", pend, &res);
|
|
|
|
ExpectIntEQ(res.handshake, 0);
|
|
ExpectIntEQ(res.dataOk, 1);
|
|
ExpectIntGT(res.cipherSubmits, 0);
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Full TLS 1.3 with ChaCha20-Poly1305: ChaCha has no crypto callback dispatch,
|
|
* so the record cipher runs in software. The handshake and echo still succeed
|
|
* and the device is never asked to offload a record cipher.
|
|
*/
|
|
int test_wc_CryptoCb_AsyncPollTlsChachaNotOffloaded(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(TEST_ASYNC_TLS) && defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
|
TlsAsyncResult res;
|
|
|
|
test_async_tls_run(test_hsm_cb, "TLS13-CHACHA20-POLY1305-SHA256", 1, &res);
|
|
|
|
ExpectIntEQ(res.handshake, 0);
|
|
ExpectIntEQ(res.dataOk, 1);
|
|
ExpectIntEQ(res.cipherSubmits, 0); /* ChaCha not dispatched to the device */
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Full TLS 1.3 with AES-GCM offloaded to a device that pends the record cipher
|
|
* but does NOT implement poll completion: the handshake must fail rather than
|
|
* transmit an unencrypted record.
|
|
*/
|
|
int test_wc_CryptoCb_AsyncPollTlsNoPollFails(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_ASYNC_TLS
|
|
TlsAsyncResult res;
|
|
|
|
test_async_tls_run(test_hsm_nopoll_cb,
|
|
"TLS13-AES128-GCM-SHA256:TLS13-AES256-GCM-SHA384", 1, &res);
|
|
|
|
ExpectIntNE(res.handshake, 0); /* expected failure */
|
|
ExpectIntEQ(res.dataOk, 0);
|
|
ExpectIntGT(res.cipherSubmits, 0); /* it did pend a record cipher */
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Full TLS 1.3 with AES-GCM offloaded on BOTH the encrypt and decrypt record
|
|
* paths. Each peer uses its own poll-completing device, so a client decrypt and
|
|
* a server encrypt never share one job slot. The handshake and echo succeed and
|
|
* both peers offload at least one decrypt through poll completion, confirming
|
|
* the poll model drives the receive (decrypt) side, not just send.
|
|
*/
|
|
int test_wc_CryptoCb_AsyncPollTlsBothDirections(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_ASYNC_TLS
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
TestHsm hsm_c, hsm_s;
|
|
int reg_c = 0, reg_s = 0;
|
|
int handshake = -1, dataOk = 0;
|
|
const char* ciphers = "TLS13-AES128-GCM-SHA256:TLS13-AES256-GCM-SHA384";
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&hsm_c, 0, sizeof(hsm_c));
|
|
XMEMSET(&hsm_s, 0, sizeof(hsm_s));
|
|
hsm_c.pendPolls = 1; hsm_c.offloadDec = 1;
|
|
hsm_s.pendPolls = 1; hsm_s.offloadDec = 1;
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_ASYNC_HSM_DEVID, test_hsm_cb,
|
|
&hsm_c), 0);
|
|
if (EXPECT_SUCCESS())
|
|
reg_c = 1;
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_ASYNC_HSM_DEVID2, test_hsm_cb,
|
|
&hsm_s), 0);
|
|
if (EXPECT_SUCCESS())
|
|
reg_s = 1;
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
if (ssl_c != NULL && ssl_s != NULL) {
|
|
(void)wolfSSL_CTX_SetDevId(ctx_c, TEST_ASYNC_HSM_DEVID);
|
|
(void)wolfSSL_CTX_SetDevId(ctx_s, TEST_ASYNC_HSM_DEVID2);
|
|
(void)wolfSSL_SetDevId(ssl_c, TEST_ASYNC_HSM_DEVID);
|
|
(void)wolfSSL_SetDevId(ssl_s, TEST_ASYNC_HSM_DEVID2);
|
|
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, ciphers), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, ciphers), WOLFSSL_SUCCESS);
|
|
|
|
/* Both directions offloaded on both peers means many more poll cycles
|
|
* than the encrypt-only tests, so allow more handshake rounds. */
|
|
handshake = test_memio_do_handshake(ssl_c, ssl_s, 64, NULL);
|
|
ExpectIntEQ(handshake, 0);
|
|
if (handshake == 0)
|
|
dataOk = test_async_tls_echo(ssl_c, ssl_s);
|
|
ExpectIntEQ(dataOk, 1);
|
|
|
|
/* both peers offloaded record ciphers, including >=1 decrypt each */
|
|
ExpectIntGT(hsm_c.submits, 0);
|
|
ExpectIntGT(hsm_s.submits, 0);
|
|
ExpectIntGT(hsm_c.decSubmits, 0);
|
|
ExpectIntGT(hsm_s.decSubmits, 0);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
if (reg_c)
|
|
wc_CryptoCb_UnRegisterDevice(TEST_ASYNC_HSM_DEVID);
|
|
if (reg_s)
|
|
wc_CryptoCb_UnRegisterDevice(TEST_ASYNC_HSM_DEVID2);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|