diff --git a/.github/workflows/qt5network5.yml b/.github/workflows/qt5network5.yml index e1fff4b1..45ee0579 100644 --- a/.github/workflows/qt5network5.yml +++ b/.github/workflows/qt5network5.yml @@ -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." diff --git a/src/wp_aes_block.c b/src/wp_aes_block.c index 592662ad..5ce3f749 100644 --- a/src/wp_aes_block.c +++ b/src/wp_aes_block.c @@ -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. @@ -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) @@ -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) { @@ -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)); @@ -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]; diff --git a/test/test_tls_cbc.c b/test/test_tls_cbc.c index 14c98fb4..efeb74cd 100644 --- a/test/test_tls_cbc.c +++ b/test/test_tls_cbc.c @@ -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. @@ -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) { @@ -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; @@ -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; } @@ -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; @@ -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); } @@ -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); } /* @@ -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); } @@ -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. */ diff --git a/test/unit.c b/test/unit.c index 2e708704..f6f4effa 100644 --- a/test/unit.c +++ b/test/unit.c @@ -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 diff --git a/test/unit.h b/test/unit.h index 9d7c1f3b..f5f27fa5 100644 --- a/test/unit.h +++ b/test/unit.h @@ -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