Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/qt5network5.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,12 @@ jobs:
# Run the QSSLSocket test, the make check takes too long
QTEST_ENVIRONMENT=ci ./tests/auto/network/ssl/qsslsocket/tst_qsslsocket 2>&1 | tee qsslsocket-test.log

# Check test results based on qt_ref
if grep -q "521 passed" qsslsocket-test.log; then
# Expected passes: non-FIPS also passes pskServer(WithoutProxy)
# (TLS 1.0 PSK-AES256-CBC-SHA); FIPS does not.
EXPECTED_PASS=${{ matrix.fips_ref == 'FIPS' && '521' || '522' }}
if grep -q "Totals: ${EXPECTED_PASS} passed" qsslsocket-test.log; then
TEST_RESULT=0
echo "SUCCESS: Found 521 passed tests as expected"
echo "SUCCESS: Found ${EXPECTED_PASS} passed tests as expected"
else
TEST_RESULT=1
echo "Tests failed unexpectedly for 'v5.15.8-lts-lgpl' branch."
Expand Down
42 changes: 30 additions & 12 deletions src/wp_aes_block.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,11 +509,12 @@ static int wp_aes_block_doit(wp_AesBlockCtx *ctx, unsigned char *out,
}

/**
* TLS 1.2 CBC decryption record post-processing.
* TLS CBC decryption record post-processing.
*
* Performs constant-time padding validation and MAC extraction after
* CBC decryption. For ETM (Encrypt-then-MAC) or no-MAC modes, strips the
* explicit IV and validates/removes padding. For MtE (MAC-then-Encrypt),
* explicit IV of TLS 1.1+ and DTLS records and validates/removes padding.
* TLS 1.0 records have no explicit IV. For MtE (MAC-then-Encrypt),
* also extracts the MAC using a constant-time rotation pattern.
*
* @param [in] ctx AES block context object.
Expand All @@ -531,8 +532,8 @@ static int wp_aes_block_tls_dec_record(wp_AesBlockCtx *ctx,
WOLFPROV_ENTER(WP_LOG_COMP_AES, "wp_aes_block_tls_dec_record");

/*
* TLS 1.2 CBC padding removal and MAC extraction.
* Buffer: [explicit_IV(BS)][payload][MAC(macsize)][padding(pad+1)]
* TLS CBC padding removal and MAC extraction.
* Buffer: [explicit_IV(ivLen)][payload][MAC(macsize)][padding(pad+1)]
*
* Constant-time padding validation based on OpenSSL's
* tls1_cbc_remove_padding_and_mac() (ssl/record/methods/tls_pad.c)
Expand All @@ -550,6 +551,7 @@ static int wp_aes_block_tls_dec_record(wp_AesBlockCtx *ctx,
size_t good;
size_t i, j;
size_t macSize = ctx->tlsmacsize;
size_t ivLen = 0;

/* Free any previously allocated MAC */
if (ctx->tlsmacAlloced) {
Expand All @@ -558,18 +560,34 @@ static int wp_aes_block_tls_dec_record(wp_AesBlockCtx *ctx,
ctx->tlsmac = NULL;
}

if (macSize > EVP_MAX_MD_SIZE ||
oLen < AES_BLOCK_SIZE + macSize + 1) {
switch (ctx->tls_version) {
case TLS1_2_VERSION:
case DTLS1_2_VERSION:
case TLS1_1_VERSION:
case DTLS1_VERSION:
case DTLS1_BAD_VER:
ivLen = AES_BLOCK_SIZE;
break;
case TLS1_VERSION:
ivLen = 0;
break;
default:
ok = 0;
break;
}

if (ok && (macSize > EVP_MAX_MD_SIZE ||
oLen < ivLen + macSize + 1)) {
ok = 0;
}

if (ok && macSize == 0) {
/* ETM (Encrypt-then-MAC) or no MAC: the record layer already
* handled the MAC. We only need to strip the explicit IV and
* handled the MAC. We only need to strip any explicit IV and
* validate+remove padding (same as OpenSSL ssl3_cbc_copy_mac
* returning early when mac_size == 0). */
unsigned char *ivRec = out + AES_BLOCK_SIZE;
size_t ivRecLen = oLen - AES_BLOCK_SIZE;
unsigned char *ivRec = out + ivLen;
size_t ivRecLen = oLen - ivLen;
unsigned char padV = ivRec[ivRecLen - 1];
size_t gd = (size_t)0 - ((size_t)(
wp_ct_int_mask_gte((int)ivRecLen, (int)padV + 1) & 1));
Expand Down Expand Up @@ -621,9 +639,9 @@ static int wp_aes_block_tls_dec_record(wp_AesBlockCtx *ctx,
rotatedMac = rotatedMacBuf +
((0 - (size_t)rotatedMacBuf) & 63);

/* For TLS 1.1+/DTLS: skip explicit IV */
rec = out + AES_BLOCK_SIZE;
recLen = oLen - AES_BLOCK_SIZE;
/* Skip the explicit IV, if any */
rec = out + ivLen;
recLen = oLen - ivLen;
origRecLen = recLen;

padVal = rec[recLen - 1];
Expand Down
158 changes: 98 additions & 60 deletions test/test_tls_cbc.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
defined(WP_HAVE_ECDH) && defined(WP_HAVE_SHA384)

/*
* Direct EVP-level test for TLS 1.2 CBC OSSL_CIPHER_PARAM_TLS_MAC handling.
* Direct EVP-level test for TLS CBC OSSL_CIPHER_PARAM_TLS_MAC handling.
* Exercises the same EVP calls the TLS record layer makes for provided CBC
* ciphers: set TLS_VERSION + TLS_MAC_SIZE, encrypt/decrypt in-place, then
* retrieve the MAC via TLS_MAC get_params.
Expand All @@ -35,22 +35,29 @@ static const unsigned char testPlain[] = "Hello TLS 1.2 CBC test from wolfProv!"

#define BS AES_BLOCK_SIZE

/* Encrypt a TLS 1.2 CBC record in-place. Returns 0 on success. */
static int test_tls_cbc_enc(EVP_CIPHER *cipher, const unsigned char *key,
const unsigned char *iv, const unsigned char *pt, int ptLen,
const unsigned char *mac, int macSize, unsigned char *buf, int *outLen)
/* Explicit IV length of a CBC record for the TLS version. */
static int test_tls_cbc_iv_len(unsigned int tlsVer)
{
return (tlsVer == TLS1_VERSION) ? 0 : BS;
}

/* Encrypt a TLS CBC record in-place. Returns 0 on success. */
static int test_tls_cbc_enc(EVP_CIPHER *cipher, unsigned int tlsVer,
const unsigned char *key, const unsigned char *iv,
const unsigned char *pt, int ptLen, const unsigned char *mac, int macSize,
unsigned char *buf, int *outLen)
{
int err = 0;
EVP_CIPHER_CTX *ctx = NULL;
OSSL_PARAM params[3];
unsigned int tlsVer = TLS1_2_VERSION;
size_t macSz = (size_t)macSize;
int inLen = BS + ptLen + macSize;
int ivLen = test_tls_cbc_iv_len(tlsVer);
int inLen = ivLen + ptLen + macSize;

/* Build in-place buffer: [explicit_IV][plaintext][MAC] */
memcpy(buf, iv, BS);
memcpy(buf + BS, pt, ptLen);
memcpy(buf + BS + ptLen, mac, macSize);
memcpy(buf, iv, ivLen);
memcpy(buf + ivLen, pt, ptLen);
memcpy(buf + ivLen + ptLen, mac, macSize);

ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL) {
Expand All @@ -76,21 +83,22 @@ static int test_tls_cbc_enc(EVP_CIPHER *cipher, const unsigned char *key,
}

/*
* Decrypt a TLS 1.2 CBC record in-place and verify:
* Decrypt a TLS CBC record in-place and verify:
* - outLen == ptLen
* - plaintext at buf+BS matches original
* - TLS_MAC get_params returns non-NULL pointer matching original MAC
* - plaintext after the explicit IV matches original
* - with a MAC (MtE), TLS_MAC get_params returns the original MAC
*/
static int test_tls_cbc_dec(EVP_CIPHER *cipher, const unsigned char *key,
const unsigned char *iv, const unsigned char *origPt, int ptLen,
const unsigned char *origMac, int macSize, unsigned char *buf, int encLen)
static int test_tls_cbc_dec(EVP_CIPHER *cipher, unsigned int tlsVer,
const unsigned char *key, const unsigned char *iv,
const unsigned char *origPt, int ptLen, const unsigned char *origMac,
int macSize, unsigned char *buf, int encLen)
{
int err = 0;
EVP_CIPHER_CTX *ctx = NULL;
OSSL_PARAM params[3];
OSSL_PARAM getParams[2];
unsigned int tlsVer = TLS1_2_VERSION;
size_t macSz = (size_t)macSize;
int ivLen = test_tls_cbc_iv_len(tlsVer);
int outLen = 0;
unsigned char *tlsMac = NULL;

Expand Down Expand Up @@ -120,20 +128,21 @@ static int test_tls_cbc_dec(EVP_CIPHER *cipher, const unsigned char *key,
err = 1;
}

/* Verify plaintext at buf+BS (past explicit IV) */
if (err == 0 && memcmp(buf + BS, origPt, ptLen) != 0) {
/* Verify plaintext past the explicit IV */
if (err == 0 && memcmp(buf + ivLen, origPt, ptLen) != 0) {
PRINT_ERR_MSG("dec plaintext mismatch");
err = 1;
}

/* Retrieve and verify TLS_MAC */
if (err == 0) {
/* Retrieve and verify TLS_MAC; with no MAC (ETM) there is none */
if (err == 0 && macSize > 0) {
getParams[0] = OSSL_PARAM_construct_octet_ptr(
OSSL_CIPHER_PARAM_TLS_MAC, (void **)&tlsMac, macSize);
getParams[1] = OSSL_PARAM_construct_end();
err = EVP_CIPHER_CTX_get_params(ctx, getParams) != 1;
}
if (err == 0 && (tlsMac == NULL || memcmp(tlsMac, origMac, macSize) != 0)) {
if (err == 0 && macSize > 0 &&
(tlsMac == NULL || memcmp(tlsMac, origMac, macSize) != 0)) {
PRINT_ERR_MSG("dec MAC mismatch or NULL");
err = 1;
}
Expand All @@ -144,7 +153,7 @@ static int test_tls_cbc_dec(EVP_CIPHER *cipher, const unsigned char *key,

/* Encrypt with encCtx provider, decrypt with decCtx provider. */
static int test_tls_cbc_interop(OSSL_LIB_CTX *encCtx, OSSL_LIB_CTX *decCtx,
const char *cipherName, int keyLen, int macSize)
unsigned int tlsVer, const char *cipherName, int keyLen, int macSize)
{
int err = 0;
EVP_CIPHER *encCipher = NULL;
Expand All @@ -167,11 +176,11 @@ static int test_tls_cbc_interop(OSSL_LIB_CTX *encCtx, OSSL_LIB_CTX *decCtx,
memset(mac, 0xCC, macSize);
}
if (err == 0) {
err = test_tls_cbc_enc(encCipher, key, iv, testPlain,
err = test_tls_cbc_enc(encCipher, tlsVer, key, iv, testPlain,
sizeof(testPlain), mac, macSize, buf, &encLen);
}
if (err == 0) {
err = test_tls_cbc_dec(decCipher, key, iv, testPlain,
err = test_tls_cbc_dec(decCipher, tlsVer, key, iv, testPlain,
sizeof(testPlain), mac, macSize, buf, encLen);
}

Expand All @@ -180,61 +189,90 @@ static int test_tls_cbc_interop(OSSL_LIB_CTX *encCtx, OSSL_LIB_CTX *decCtx,
return err;
}

static const struct {
typedef struct TlsCbcTest {
const char *cipher;
int keyLen;
int macSize;
} tlsCbcTests[] = {
} TlsCbcTest;

static const TlsCbcTest tls12CbcTests[] = {
{ "AES-256-CBC", 32, 48 }, /* ECDHE-RSA-AES256-SHA384 */
{ "AES-128-CBC", 16, 32 }, /* ECDHE-RSA-AES128-SHA256 */
};
#define TLS_CBC_TEST_CNT \
(int)(sizeof(tlsCbcTests) / sizeof(tlsCbcTests[0]))
#define TLS12_CBC_TEST_CNT \
(int)(sizeof(tls12CbcTests) / sizeof(tls12CbcTests[0]))

int test_tls12_cbc(void *data)
static const TlsCbcTest tlsSha1CbcTests[] = {
{ "AES-128-CBC", 16, 20 }, /* AES128-SHA */
{ "AES-128-CBC", 16, 0 }, /* AES128-SHA with Encrypt-then-MAC */
};
#define TLS_SHA1_CBC_TEST_CNT \
(int)(sizeof(tlsSha1CbcTests) / sizeof(tlsSha1CbcTests[0]))

/* Run the TLS CBC records through OpenSSL only, or across both providers. */
static int test_tls_cbc_ver(unsigned int tlsVer, const char *verName,
const TlsCbcTest *tests, int cnt, int baseline)
{
int err = 0;
int i;

(void)data;

for (i = 0; i < TLS_CBC_TEST_CNT && err == 0; i++) {
PRINT_MSG("TLS 1.2 CBC (OpenSSL -> wolfProvider): %s mac=%d",
tlsCbcTests[i].cipher, tlsCbcTests[i].macSize);
err = test_tls_cbc_interop(osslLibCtx, wpLibCtx,
tlsCbcTests[i].cipher,
tlsCbcTests[i].keyLen,
tlsCbcTests[i].macSize);
if (err == 0) {
PRINT_MSG("TLS 1.2 CBC (wolfProvider -> OpenSSL): %s mac=%d",
tlsCbcTests[i].cipher, tlsCbcTests[i].macSize);
err = test_tls_cbc_interop(wpLibCtx, osslLibCtx,
tlsCbcTests[i].cipher,
tlsCbcTests[i].keyLen,
tlsCbcTests[i].macSize);
for (i = 0; i < cnt && err == 0; i++) {
if (baseline) {
PRINT_MSG("%s CBC (OpenSSL baseline): %s mac=%d", verName,
tests[i].cipher, tests[i].macSize);
err = test_tls_cbc_interop(osslLibCtx, osslLibCtx, tlsVer,
tests[i].cipher, tests[i].keyLen,
tests[i].macSize);
}
else {
PRINT_MSG("%s CBC (OpenSSL -> wolfProvider): %s mac=%d", verName,
tests[i].cipher, tests[i].macSize);
err = test_tls_cbc_interop(osslLibCtx, wpLibCtx, tlsVer,
tests[i].cipher, tests[i].keyLen,
tests[i].macSize);
if (err == 0) {
PRINT_MSG("%s CBC (wolfProvider -> OpenSSL): %s mac=%d",
verName, tests[i].cipher, tests[i].macSize);
err = test_tls_cbc_interop(wpLibCtx, osslLibCtx, tlsVer,
tests[i].cipher, tests[i].keyLen,
tests[i].macSize);
}
}
}

return err;
}

int test_tls12_cbc(void *data)
{
(void)data;

return test_tls_cbc_ver(TLS1_2_VERSION, "TLS 1.2", tls12CbcTests,
TLS12_CBC_TEST_CNT, 0);
}

int test_tls12_cbc_ossl(void *data)
{
int err = 0;
int i;
(void)data;

return test_tls_cbc_ver(TLS1_2_VERSION, "TLS 1.2", tls12CbcTests,
TLS12_CBC_TEST_CNT, 1);
}

int test_tls10_cbc(void *data)
{
(void)data;

for (i = 0; i < TLS_CBC_TEST_CNT && err == 0; i++) {
PRINT_MSG("TLS 1.2 CBC (OpenSSL baseline): %s mac=%d",
tlsCbcTests[i].cipher, tlsCbcTests[i].macSize);
err = test_tls_cbc_interop(osslLibCtx, osslLibCtx,
tlsCbcTests[i].cipher,
tlsCbcTests[i].keyLen,
tlsCbcTests[i].macSize);
}
return test_tls_cbc_ver(TLS1_VERSION, "TLS 1.0", tlsSha1CbcTests,
TLS_SHA1_CBC_TEST_CNT, 0);
}

return err;
int test_dtls12_cbc(void *data)
{
(void)data;

return test_tls_cbc_ver(DTLS1_2_VERSION, "DTLS 1.2", tlsSha1CbcTests,
TLS_SHA1_CBC_TEST_CNT, 0);
}

/*
Expand Down Expand Up @@ -274,7 +312,7 @@ static int test_aes_tls_cbc_bad_pad_helper(OSSL_LIB_CTX *libCtx,

/* Encrypt a valid TLS record. */
if (err == 0) {
err = test_tls_cbc_enc(cipher, key, iv, testPlain, ptLen,
err = test_tls_cbc_enc(cipher, tlsVer, key, iv, testPlain, ptLen,
mac, macSize, buf, &encLen);
}

Expand Down Expand Up @@ -360,8 +398,8 @@ static int test_aes_tls_cbc_split_helper(OSSL_LIB_CTX *libCtx,
err = 1;
}
if (err == 0) {
err = test_tls_cbc_enc(cipher, key, iv, testPlain, sizeof(testPlain),
mac, macSize, buf, &encLen);
err = test_tls_cbc_enc(cipher, tlsVer, key, iv, testPlain,
sizeof(testPlain), mac, macSize, buf, &encLen);
}
/* Output buffer sized to the produced plaintext so an overread past the
* written region is caught. */
Expand Down
2 changes: 2 additions & 0 deletions test/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,8 @@ TEST_CASE test_case[] = {
defined(WP_HAVE_ECDH) && defined(WP_HAVE_SHA384)
TEST_DECL(test_tls12_cbc_ossl, NULL),
TEST_DECL(test_tls12_cbc, NULL),
TEST_DECL(test_tls10_cbc, NULL),
TEST_DECL(test_dtls12_cbc, NULL),
TEST_DECL(test_aes_tls_cbc_bad_pad, NULL),
TEST_DECL(test_aes_tls_cbc_split, NULL),
#endif
Expand Down
2 changes: 2 additions & 0 deletions test/unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,8 @@ int test_x509_cert(void *data);
defined(WP_HAVE_ECDH) && defined(WP_HAVE_SHA384)
int test_tls12_cbc(void *data);
int test_tls12_cbc_ossl(void *data);
int test_tls10_cbc(void *data);
int test_dtls12_cbc(void *data);
int test_aes_tls_cbc_bad_pad(void *data);
int test_aes_tls_cbc_split(void *data);
#endif
Expand Down
Loading