Skip to content

Commit 53b3f85

Browse files
authored
Fix error in CT compare, add negative tests for TLS pad checks (#385)
* Fix error in CT compare, add negative tests for TLS pad checks * Update to cleaner CT check, add CT unit tests * Fix compile warning * Fix test_ct.c build failures on gcc-9 and FIPS configs
1 parent 53ee73e commit 53b3f85

File tree

7 files changed

+531
-4
lines changed

7 files changed

+531
-4
lines changed

src/wp_internal.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,7 +1152,7 @@ BIO* wp_corebio_get_bio(WOLFPROV_CTX* provCtx, OSSL_CORE_BIO *coreBio)
11521152
/**
11531153
* Constant time, set mask when first value is equal to second.
11541154
*
1155-
* @param [in] a First valuue.
1155+
* @param [in] a First value.
11561156
* @param [in] b Second value.
11571157
* @return All bits set when true.
11581158
* @return 0 when false.
@@ -1165,20 +1165,20 @@ byte wp_ct_byte_mask_eq(byte a, byte b)
11651165
/**
11661166
* Constant time, set mask when first value is not equal to second.
11671167
*
1168-
* @param [in] a First valuue.
1168+
* @param [in] a First value.
11691169
* @param [in] b Second value.
11701170
* @return All bits set when true.
11711171
* @return 0 when false.
11721172
*/
11731173
byte wp_ct_byte_mask_ne(byte a, byte b)
11741174
{
1175-
return (((int32_t)b - a) >> 31) & (((int32_t)a - b) >> 31);
1175+
return ~wp_ct_byte_mask_eq(a, b);
11761176
}
11771177

11781178
/**
11791179
* Constant time, set mask when first value is greater than or equal second.
11801180
*
1181-
* @param [in] a First valuue.
1181+
* @param [in] a First value.
11821182
* @param [in] b Second value.
11831183
* @return All bits set when true.
11841184
* @return 0 when false.

test/include.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ test_unit_test_SOURCES = \
1515
test/test_aestag.c \
1616
test/test_cipher.c \
1717
test/test_cmac.c \
18+
test/test_ct.c \
1819
test/test_dh.c \
1920
test/test_digest.c \
2021
test/test_rand_seed.c \

test/test_cipher.c

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,86 @@ int test_des3_cbc_stream(void *data)
457457
return err;
458458
}
459459

460+
/*
461+
* Negative PKCS#7 padding test for DES3-CBC.
462+
* Encrypt block-aligned plaintext (produces a full padding block), corrupt a
463+
* ciphertext byte so the padding block is garbled, verify DecryptFinal rejects.
464+
*/
465+
int test_des3_cbc_bad_pad(void *data)
466+
{
467+
int err = 0;
468+
EVP_CIPHER *cipher = NULL;
469+
EVP_CIPHER_CTX *ctx = NULL;
470+
unsigned char key[24];
471+
unsigned char iv[8];
472+
unsigned char pt[8]; /* block-aligned: PKCS#7 adds full 8-byte pad block */
473+
unsigned char ct[24]; /* 8 pt + 8 pad = 16, but EncryptFinal may need room */
474+
unsigned char dec[24];
475+
int outLen = 0, fLen = 0;
476+
477+
(void)data;
478+
479+
PRINT_MSG("DES3-CBC negative PKCS#7 padding");
480+
481+
memset(key, 0xAA, sizeof(key));
482+
memset(iv, 0xBB, sizeof(iv));
483+
memset(pt, 0x42, sizeof(pt));
484+
485+
cipher = EVP_CIPHER_fetch(wpLibCtx, "DES-EDE3-CBC", "");
486+
if (cipher == NULL) {
487+
err = 1;
488+
}
489+
490+
/* Encrypt with padding. */
491+
if (err == 0) {
492+
ctx = EVP_CIPHER_CTX_new();
493+
if (ctx == NULL)
494+
err = 1;
495+
}
496+
if (err == 0) {
497+
err = EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv) != 1;
498+
}
499+
if (err == 0) {
500+
err = EVP_EncryptUpdate(ctx, ct, &outLen, pt, (int)sizeof(pt)) != 1;
501+
}
502+
if (err == 0) {
503+
err = EVP_EncryptFinal_ex(ctx, ct + outLen, &fLen) != 1;
504+
outLen += fLen;
505+
}
506+
EVP_CIPHER_CTX_free(ctx);
507+
ctx = NULL;
508+
509+
/* Corrupt first ciphertext block -- CBC garbles the padding block. */
510+
if (err == 0) {
511+
ct[0] ^= 0x01;
512+
}
513+
514+
/* Decrypt -- DecryptFinal should fail due to garbled padding. */
515+
if (err == 0) {
516+
ctx = EVP_CIPHER_CTX_new();
517+
if (ctx == NULL)
518+
err = 1;
519+
}
520+
if (err == 0) {
521+
err = EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv) != 1;
522+
}
523+
if (err == 0) {
524+
fLen = 0;
525+
err = EVP_DecryptUpdate(ctx, dec, &fLen, ct, outLen) != 1;
526+
}
527+
if (err == 0) {
528+
int ret = EVP_DecryptFinal_ex(ctx, dec + fLen, &fLen);
529+
if (ret == 1) {
530+
PRINT_ERR_MSG("DES3-CBC bad-pad: DecryptFinal should have failed");
531+
err = 1;
532+
}
533+
}
534+
535+
EVP_CIPHER_CTX_free(ctx);
536+
EVP_CIPHER_free(cipher);
537+
return err;
538+
}
539+
460540
#endif /* WP_HAVE_DES3CBC */
461541

462542
/******************************************************************************/
@@ -1326,4 +1406,86 @@ int test_aes256_cbc_multiple(void *data)
13261406

13271407
return err;
13281408
}
1409+
1410+
/*
1411+
* Negative PKCS#7 padding test for AES-256-CBC.
1412+
* Encrypt block-aligned plaintext (produces a full padding block), corrupt a
1413+
* ciphertext byte so the padding block is garbled, verify DecryptFinal rejects.
1414+
*/
1415+
int test_aes256_cbc_bad_pad(void *data)
1416+
{
1417+
int err = 0;
1418+
EVP_CIPHER *cipher = NULL;
1419+
EVP_CIPHER_CTX *ctx = NULL;
1420+
unsigned char key[32];
1421+
unsigned char iv[16];
1422+
unsigned char pt[16]; /* block-aligned: PKCS#7 adds full 16-byte pad block */
1423+
unsigned char ct[48];
1424+
unsigned char dec[48];
1425+
int outLen = 0, fLen = 0;
1426+
1427+
(void)data;
1428+
1429+
PRINT_MSG("AES-256-CBC negative PKCS#7 padding");
1430+
1431+
memset(key, 0xAA, sizeof(key));
1432+
memset(iv, 0xBB, sizeof(iv));
1433+
memset(pt, 0x42, sizeof(pt));
1434+
1435+
cipher = EVP_CIPHER_fetch(wpLibCtx, "AES-256-CBC", "");
1436+
if (cipher == NULL) {
1437+
err = 1;
1438+
}
1439+
1440+
/* Encrypt with padding. */
1441+
if (err == 0) {
1442+
ctx = EVP_CIPHER_CTX_new();
1443+
if (ctx == NULL)
1444+
err = 1;
1445+
}
1446+
if (err == 0) {
1447+
err = EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv) != 1;
1448+
}
1449+
if (err == 0) {
1450+
err = EVP_EncryptUpdate(ctx, ct, &outLen, pt, (int)sizeof(pt)) != 1;
1451+
}
1452+
if (err == 0) {
1453+
err = EVP_EncryptFinal_ex(ctx, ct + outLen, &fLen) != 1;
1454+
outLen += fLen;
1455+
}
1456+
EVP_CIPHER_CTX_free(ctx);
1457+
ctx = NULL;
1458+
1459+
/* Corrupt first ciphertext block -- CBC garbles the padding block. */
1460+
if (err == 0) {
1461+
ct[0] ^= 0x01;
1462+
}
1463+
1464+
/* Decrypt -- DecryptFinal should fail due to garbled padding. */
1465+
if (err == 0) {
1466+
ctx = EVP_CIPHER_CTX_new();
1467+
if (ctx == NULL)
1468+
err = 1;
1469+
}
1470+
if (err == 0) {
1471+
err = EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv) != 1;
1472+
}
1473+
if (err == 0) {
1474+
fLen = 0;
1475+
err = EVP_DecryptUpdate(ctx, dec, &fLen, ct, outLen) != 1;
1476+
}
1477+
if (err == 0) {
1478+
int ret = EVP_DecryptFinal_ex(ctx, dec + fLen, &fLen);
1479+
if (ret == 1) {
1480+
PRINT_ERR_MSG("AES-256-CBC bad-pad: DecryptFinal should have "
1481+
"failed");
1482+
err = 1;
1483+
}
1484+
}
1485+
1486+
EVP_CIPHER_CTX_free(ctx);
1487+
EVP_CIPHER_free(cipher);
1488+
return err;
1489+
}
1490+
13291491
#endif /* WP_HAVE_AESCBC */

test/test_ct.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/* test_ct.c
2+
*
3+
* Copyright (C) 2006-2025 wolfSSL Inc.
4+
*
5+
* This file is part of wolfProvider.
6+
*
7+
* wolfProvider is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation; either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* wolfProvider is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with wolfProvider. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
#include <wolfprovider/internal.h>
22+
#include "unit.h"
23+
24+
int test_ct_masks(void *data)
25+
{
26+
int err = 0;
27+
int a, b;
28+
byte res;
29+
30+
(void)data;
31+
32+
PRINT_MSG("Testing CT byte mask functions (exhaustive)");
33+
34+
/* Exhaustive test of all 65536 byte pairs for eq, ne, and their
35+
* relationship: ne(a,b) == (byte)~eq(a,b). */
36+
for (a = 0; a <= 255 && err == 0; a++) {
37+
for (b = 0; b <= 255 && err == 0; b++) {
38+
byte eqRes = wp_ct_byte_mask_eq((byte)a, (byte)b);
39+
byte neRes = wp_ct_byte_mask_ne((byte)a, (byte)b);
40+
byte expEq = (a == b) ? 0xFF : 0x00;
41+
byte expNe = (a != b) ? 0xFF : 0x00;
42+
byte eqNeg;
43+
44+
if (eqRes != expEq) {
45+
PRINT_ERR_MSG("ct_byte_mask_eq(%d, %d) = 0x%02x, expected "
46+
"0x%02x", a, b, eqRes, expEq);
47+
err = 1;
48+
}
49+
if (neRes != expNe) {
50+
PRINT_ERR_MSG("ct_byte_mask_ne(%d, %d) = 0x%02x, expected "
51+
"0x%02x", a, b, neRes, expNe);
52+
err = 1;
53+
}
54+
eqNeg = (byte)(eqRes ^ (byte)0xFF);
55+
if (eqNeg != neRes) {
56+
PRINT_ERR_MSG("ct_byte_mask ne/eq mismatch at (%d, %d): "
57+
"~eq=0x%02x ne=0x%02x", a, b,
58+
eqNeg, neRes);
59+
err = 1;
60+
}
61+
}
62+
}
63+
64+
PRINT_MSG("Testing CT int mask functions (boundary values)");
65+
66+
/* Test int comparison functions over a set of boundary values that cover
67+
* the actual usage domain (padding indices, record lengths, versions). */
68+
{
69+
static const int vals[] = {0, 1, 2, 127, 128, 254, 255, 256, 1000};
70+
int nvals = (int)(sizeof(vals) / sizeof(vals[0]));
71+
int i, j;
72+
73+
for (i = 0; i < nvals && err == 0; i++) {
74+
for (j = 0; j < nvals && err == 0; j++) {
75+
a = vals[i];
76+
b = vals[j];
77+
78+
res = wp_ct_int_mask_gte(a, b);
79+
if (res != ((a >= b) ? 0xFF : 0x00)) {
80+
PRINT_ERR_MSG("ct_int_mask_gte(%d, %d) = 0x%02x, expected "
81+
"0x%02x", a, b, res,
82+
(a >= b) ? 0xFF : 0x00);
83+
err = 1;
84+
}
85+
86+
res = wp_ct_int_mask_eq(a, b);
87+
if (res != ((a == b) ? 0xFF : 0x00)) {
88+
PRINT_ERR_MSG("ct_int_mask_eq(%d, %d) = 0x%02x, expected "
89+
"0x%02x", a, b, res,
90+
(a == b) ? 0xFF : 0x00);
91+
err = 1;
92+
}
93+
94+
res = wp_ct_int_mask_lt(a, b);
95+
if (res != ((a < b) ? 0xFF : 0x00)) {
96+
PRINT_ERR_MSG("ct_int_mask_lt(%d, %d) = 0x%02x, expected "
97+
"0x%02x", a, b, res,
98+
(a < b) ? 0xFF : 0x00);
99+
err = 1;
100+
}
101+
}
102+
}
103+
}
104+
105+
PRINT_MSG("Testing CT byte mask sel");
106+
107+
/* Selection: mask=0xFF picks a, mask=0x00 picks b. */
108+
res = wp_ct_byte_mask_sel(0xFF, 0xAB, 0xCD);
109+
if (res != 0xAB) {
110+
PRINT_ERR_MSG("ct_byte_mask_sel(0xFF, 0xAB, 0xCD) = 0x%02x", res);
111+
err = 1;
112+
}
113+
res = wp_ct_byte_mask_sel(0x00, 0xAB, 0xCD);
114+
if (res != 0xCD) {
115+
PRINT_ERR_MSG("ct_byte_mask_sel(0x00, 0xAB, 0xCD) = 0x%02x", res);
116+
err = 1;
117+
}
118+
119+
/* Selection driven by eq/ne masks. */
120+
res = wp_ct_byte_mask_sel(wp_ct_byte_mask_eq(5, 5), 0x11, 0x22);
121+
if (res != 0x11) {
122+
PRINT_ERR_MSG("ct_byte_mask_sel(eq(5,5), 0x11, 0x22) = 0x%02x", res);
123+
err = 1;
124+
}
125+
res = wp_ct_byte_mask_sel(wp_ct_byte_mask_eq(5, 6), 0x11, 0x22);
126+
if (res != 0x22) {
127+
PRINT_ERR_MSG("ct_byte_mask_sel(eq(5,6), 0x11, 0x22) = 0x%02x", res);
128+
err = 1;
129+
}
130+
131+
return err;
132+
}

0 commit comments

Comments
 (0)