wolfcrypt/src/coding.c, wolfssl/wolfcrypt/coding.h, wolfcrypt/src/asn.c,

wolfcrypt/test/test.c: refactor Base64_Decode() with separate always-CT
  Base64_Decode() and never-CT Base64_Decode_nonCT(), and use the latter only to
  decode known-public PEM objects, otherwise use always-CT Base64_Decode().
This commit is contained in:
Daniel Pouzzner
2025-04-01 15:04:14 -05:00
parent 9bcb3f71d0
commit 51c6848340
5 changed files with 236 additions and 54 deletions

View File

@@ -526,6 +526,7 @@ WC_ASYNC_ENABLE_SHA384
WC_ASYNC_ENABLE_SHA512
WC_ASYNC_NO_CRYPT
WC_ASYNC_NO_HASH
WC_CACHE_RESISTANT_BASE64_TABLE
WC_DILITHIUM_CACHE_PRIV_VECTORS
WC_DILITHIUM_CACHE_PUB_VECTORS
WC_DILITHIUM_FIXED_ARRAY

View File

@@ -26556,10 +26556,27 @@ int PemToDer(const unsigned char* buff, long longSz, int type,
}
der = *pDer;
if (Base64_Decode((byte*)headerEnd, (word32)neededSz,
der->buffer, &der->length) < 0) {
WOLFSSL_ERROR(BUFFER_E);
return BUFFER_E;
switch (type) {
case PUBLICKEY_TYPE:
case ECC_PUBLICKEY_TYPE:
case RSA_PUBLICKEY_TYPE:
case CERT_TYPE:
case TRUSTED_CERT_TYPE:
case CRL_TYPE:
if (Base64_Decode_nonCT((byte*)headerEnd, (word32)neededSz,
der->buffer, &der->length) < 0)
{
WOLFSSL_ERROR(BUFFER_E);
return BUFFER_E;
}
break;
default:
if (Base64_Decode((byte*)headerEnd, (word32)neededSz,
der->buffer, &der->length) < 0) {
WOLFSSL_ERROR(BUFFER_E);
return BUFFER_E;
}
break;
}
if ((header == BEGIN_PRIV_KEY

View File

@@ -59,20 +59,19 @@ enum {
#ifdef WOLFSSL_BASE64_DECODE
#ifdef BASE64_NO_TABLE
static WC_INLINE byte Base64_Char2Val(byte c)
static WC_INLINE byte Base64_Char2Val_CT(byte c)
{
word16 v;
sword16 smallEnd = (sword16)c - 0x7b;
sword16 smallStart = (sword16)c - 0x61;
sword16 bigEnd = (sword16)c - 0x5b;
sword16 bigStart = (sword16)c - 0x41;
sword16 numEnd = (sword16)c - 0x3a;
sword16 numStart = (sword16)c - 0x30;
sword16 slashEnd = (sword16)c - 0x30;
sword16 slashStart = (sword16)c - 0x2f;
sword16 plusEnd = (sword16)c - 0x2c;
sword16 plusStart = (sword16)c - 0x2b;
int v;
int smallEnd = (int)c - 0x7b;
int smallStart = (int)c - 0x61;
int bigEnd = (int)c - 0x5b;
int bigStart = (int)c - 0x41;
int numEnd = (int)c - 0x3a;
int numStart = (int)c - 0x30;
int slashEnd = (int)c - 0x30;
int slashStart = (int)c - 0x2f;
int plusEnd = (int)c - 0x2c;
int plusStart = (int)c - 0x2b;
v = ((smallStart >> 8) ^ (smallEnd >> 8)) & (smallStart + 26 + 1);
v |= ((bigStart >> 8) ^ (bigEnd >> 8)) & (bigStart + 0 + 1);
@@ -82,9 +81,9 @@ static WC_INLINE byte Base64_Char2Val(byte c)
return (byte)(v - 1);
}
#else
static
ALIGN64 const byte base64Decode[] = { /* + starts at 0x2B */
ALIGN64 const byte base64Decode_table[] = { /* + starts at 0x2B */
/* 0x28: + , - . / */ 62, BAD, BAD, BAD, 63,
/* 0x30: 0 1 2 3 4 5 6 7 */ 52, 53, 54, 55, 56, 57, 58, 59,
/* 0x38: 8 9 : ; < = > ? */ 60, 61, BAD, BAD, BAD, BAD, BAD, BAD,
@@ -97,11 +96,11 @@ ALIGN64 const byte base64Decode[] = { /* + starts at 0x2B */
/* 0x70: p q r s t u v w */ 41, 42, 43, 44, 45, 46, 47, 48,
/* 0x78: x y z */ 49, 50, 51
};
#define BASE64DECODE_SZ (byte)(sizeof(base64Decode))
#define BASE64DECODE_TABLE_SZ (byte)(sizeof(base64Decode_table))
static WC_INLINE byte Base64_Char2Val(byte c)
static WC_INLINE byte Base64_Char2Val_by_table(byte c)
{
#ifndef WC_NO_CACHE_RESISTANT
#ifdef WC_CACHE_RESISTANT_BASE64_TABLE
/* 80 characters in table.
* 64 bytes in a cache line - first line has 64, second has 16
*/
@@ -111,16 +110,15 @@ static WC_INLINE byte Base64_Char2Val(byte c)
c = (byte)(c - BASE64_MIN);
mask = (byte)((((byte)(0x3f - c)) >> 7) - 1);
/* Load a value from the first cache line and use when mask set. */
v = (byte)(base64Decode[ c & 0x3f ] & mask);
v = (byte)(base64Decode_table[ c & 0x3f ] & mask);
/* Load a value from the second cache line and use when mask not set. */
v |= (byte)(base64Decode[(c & 0x0f) | 0x40] & (~mask));
v |= (byte)(base64Decode_table[(c & 0x0f) | 0x40] & (~mask));
return v;
#else
return base64Decode[c - BASE64_MIN];
return base64Decode_table[c - BASE64_MIN];
#endif
}
#endif
int Base64_SkipNewline(const byte* in, word32 *inLen,
word32 *outJ)
@@ -170,15 +168,13 @@ int Base64_SkipNewline(const byte* in, word32 *inLen,
return 0;
}
int Base64_Decode(const byte* in, word32 inLen, byte* out, word32* outLen)
int Base64_Decode_nonCT(const byte* in, word32 inLen, byte* out, word32* outLen)
{
word32 i = 0;
word32 j = 0;
word32 plainSz = inLen - ((inLen + (BASE64_LINE_SZ - 1)) / BASE64_LINE_SZ );
int ret;
#ifndef BASE64_NO_TABLE
const byte maxIdx = BASE64DECODE_SZ + BASE64_MIN - 1;
#endif
const byte maxIdx = BASE64DECODE_TABLE_SZ + BASE64_MIN - 1;
plainSz = (plainSz * 3 + 3) / 4;
if (plainSz > *outLen) return BAD_FUNC_ARG;
@@ -225,7 +221,6 @@ int Base64_Decode(const byte* in, word32 inLen, byte* out, word32* outLen)
if (pad3 && !pad4)
return ASN_INPUT_E;
#ifndef BASE64_NO_TABLE
if (e1 < BASE64_MIN || e2 < BASE64_MIN || e3 < BASE64_MIN ||
e4 < BASE64_MIN) {
WOLFSSL_MSG("Bad Base64 Decode data, too small");
@@ -236,17 +231,104 @@ int Base64_Decode(const byte* in, word32 inLen, byte* out, word32* outLen)
WOLFSSL_MSG("Bad Base64 Decode data, too big");
return ASN_INPUT_E;
}
#endif
if (i + 1 + !pad3 + !pad4 > *outLen) {
WOLFSSL_MSG("Bad Base64 Decode out buffer, too small");
return BAD_FUNC_ARG;
}
e1 = Base64_Char2Val(e1);
e2 = Base64_Char2Val(e2);
e3 = (byte)((e3 == PAD) ? 0 : Base64_Char2Val(e3));
e4 = (byte)((e4 == PAD) ? 0 : Base64_Char2Val(e4));
e1 = Base64_Char2Val_by_table(e1);
e2 = Base64_Char2Val_by_table(e2);
e3 = (byte)((e3 == PAD) ? 0 : Base64_Char2Val_by_table(e3));
e4 = (byte)((e4 == PAD) ? 0 : Base64_Char2Val_by_table(e4));
if (e1 == BAD || e2 == BAD || e3 == BAD || e4 == BAD) {
WOLFSSL_MSG("Bad Base64 Decode bad character");
return ASN_INPUT_E;
}
b1 = (byte)((e1 << 2) | (e2 >> 4));
b2 = (byte)(((e2 & 0xF) << 4) | (e3 >> 2));
b3 = (byte)(((e3 & 0x3) << 6) | e4);
out[i++] = b1;
if (!pad3)
out[i++] = b2;
if (!pad4)
out[i++] = b3;
else
break;
}
/* If the output buffer has a room for an extra byte, add a null terminator */
if (out && *outLen > i)
out[i]= '\0';
*outLen = i;
return 0;
}
int Base64_Decode(const byte* in, word32 inLen, byte* out, word32* outLen)
{
word32 i = 0;
word32 j = 0;
word32 plainSz = inLen - ((inLen + (BASE64_LINE_SZ - 1)) / BASE64_LINE_SZ );
int ret;
plainSz = (plainSz * 3 + 3) / 4;
if (plainSz > *outLen) return BAD_FUNC_ARG;
while (inLen > 3) {
int pad3 = 0;
int pad4 = 0;
byte b1, b2, b3;
byte e1, e2, e3, e4;
if ((ret = Base64_SkipNewline(in, &inLen, &j)) != 0) {
if (ret == WC_NO_ERR_TRACE(BUFFER_E)) {
/* Running out of buffer here is not an error */
break;
}
return ret;
}
e1 = in[j++];
if (e1 == '\0') {
break;
}
inLen--;
if ((ret = Base64_SkipNewline(in, &inLen, &j)) != 0) {
return ret;
}
e2 = in[j++];
inLen--;
if ((ret = Base64_SkipNewline(in, &inLen, &j)) != 0) {
return ret;
}
e3 = in[j++];
inLen--;
if ((ret = Base64_SkipNewline(in, &inLen, &j)) != 0) {
return ret;
}
e4 = in[j++];
inLen--;
if (e3 == PAD)
pad3 = 1;
if (e4 == PAD)
pad4 = 1;
if (pad3 && !pad4)
return ASN_INPUT_E;
if (i + 1 + !pad3 + !pad4 > *outLen) {
WOLFSSL_MSG("Bad Base64 Decode out buffer, too small");
return BAD_FUNC_ARG;
}
e1 = Base64_Char2Val_CT(e1);
e2 = Base64_Char2Val_CT(e2);
e3 = (byte)((e3 == PAD) ? 0 : Base64_Char2Val_CT(e3));
e4 = (byte)((e4 == PAD) ? 0 : Base64_Char2Val_CT(e4));
if (e1 == BAD || e2 == BAD || e3 == BAD || e4 == BAD) {
WOLFSSL_MSG("Bad Base64 Decode bad character");

View File

@@ -2871,27 +2871,27 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t error_test(void)
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t base64_test(void)
{
wc_test_ret_t ret;
WOLFSSL_SMALL_STACK_STATIC const byte good[] = "A+Gd\0\0\0";
WOLFSSL_SMALL_STACK_STATIC const byte goodEnd[] = "A+Gd \r\n";
WOLFSSL_SMALL_STACK_STATIC const byte good_spaces[] = " A + G d \0";
static const byte good[] = "A+Gd\0\0\0";
static const byte goodEnd[] = "A+Gd \r\n";
static const byte good_spaces[] = " A + G d \0";
byte out[128];
word32 outLen;
#ifdef WOLFSSL_BASE64_ENCODE
byte data[3];
word32 dataLen;
byte longData[79] = { 0 };
WOLFSSL_SMALL_STACK_STATIC const byte symbols[] = "+/A=";
static const byte symbols[] = "+/A=";
#endif
WOLFSSL_SMALL_STACK_STATIC const byte badSmall[] = "AAA!Gdj=";
WOLFSSL_SMALL_STACK_STATIC const byte badLarge[] = "AAA~Gdj=";
WOLFSSL_SMALL_STACK_STATIC const byte badEOL[] = "A+Gd!AA";
WOLFSSL_SMALL_STACK_STATIC const byte badPadding[] = "AA=A";
WOLFSSL_SMALL_STACK_STATIC const byte badChar[] = ",-.:;<=>?@[\\]^_`";
byte goodChar[] =
static const byte badSmall[] = "AAA!Gdj=";
static const byte badLarge[] = "AAA~Gdj=";
static const byte badEOL[] = "A+Gd!AA";
static const byte badPadding[] = "AA=A";
static const byte badChar[] = ",-.:;<=>?@[\\]^_`";
static const byte goodChar[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/;";
byte charTest[] = "A+Gd\0\0\0";
static const byte charTest[] = "A+Gd\0\0\0";
int i;
WOLFSSL_ENTER("base64_test");
@@ -2905,7 +2905,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t base64_test(void)
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
outLen = sizeof(goodChar);
ret = Base64_Decode(goodChar, sizeof(goodChar), goodChar, &outLen);
XMEMCPY(out, goodChar, sizeof(goodChar));
ret = Base64_Decode(out, sizeof(goodChar), out, &outLen);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (outLen != 64 / 4 * 3)
@@ -2942,24 +2943,102 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t base64_test(void)
/* Invalid character less than 0x2b */
for (i = 1; i < 0x2b; i++) {
outLen = sizeof(out);
charTest[0] = (byte)i;
ret = Base64_Decode(charTest, sizeof(charTest), out, &outLen);
XMEMCPY(out, charTest, sizeof(charTest));
out[0] = (byte)i;
ret = Base64_Decode(out, sizeof(charTest), out, &outLen);
if (ret != WC_NO_ERR_TRACE(ASN_INPUT_E))
return WC_TEST_RET_ENC_I(i);
}
/* Bad characters in range 0x2b - 0x7a. */
for (i = 0; i < (int)sizeof(badChar) - 1; i++) {
outLen = sizeof(out);
charTest[0] = badChar[i];
ret = Base64_Decode(charTest, sizeof(charTest), out, &outLen);
XMEMCPY(out, charTest, sizeof(charTest));
out[0] = badChar[i];
ret = Base64_Decode(out, sizeof(charTest), out, &outLen);
if (ret != WC_NO_ERR_TRACE(ASN_INPUT_E))
return WC_TEST_RET_ENC_I(i);
}
/* Invalid character greater than 0x7a */
for (i = 0x7b; i < 0x100; i++) {
outLen = sizeof(out);
charTest[0] = (byte)i;
ret = Base64_Decode(charTest, sizeof(charTest), out, &outLen);
XMEMCPY(out, charTest, sizeof(charTest));
out[0] = (byte)i;
ret = Base64_Decode(out, sizeof(charTest), out, &outLen);
if (ret != WC_NO_ERR_TRACE(ASN_INPUT_E))
return WC_TEST_RET_ENC_I(i);
}
/* Same tests again, using Base64_Decode_nonCT() */
/* Good Base64 encodings. */
outLen = sizeof(out);
ret = Base64_Decode_nonCT(good, sizeof(good), out, &outLen);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
outLen = sizeof(out);
ret = Base64_Decode_nonCT(goodEnd, sizeof(goodEnd), out, &outLen);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
outLen = sizeof(goodChar);
XMEMCPY(out, goodChar, sizeof(goodChar));
ret = Base64_Decode_nonCT(out, sizeof(goodChar), out, &outLen);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (outLen != 64 / 4 * 3)
return WC_TEST_RET_ENC_NC;
outLen = sizeof(out);
ret = Base64_Decode_nonCT(good_spaces, sizeof(good_spaces), out, &outLen);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
/* Bad parameters. */
outLen = 1;
ret = Base64_Decode_nonCT(good, sizeof(good), out, &outLen);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_EC(ret);
outLen = sizeof(out);
ret = Base64_Decode_nonCT(badEOL, sizeof(badEOL), out, &outLen);
if (ret != WC_NO_ERR_TRACE(ASN_INPUT_E))
return WC_TEST_RET_ENC_EC(ret);
outLen = sizeof(out);
ret = Base64_Decode_nonCT(badPadding, sizeof(badPadding), out, &outLen);
if (ret != WC_NO_ERR_TRACE(ASN_INPUT_E))
return WC_TEST_RET_ENC_EC(ret);
/* Bad character at each offset 0-3. */
for (i = 0; i < 4; i++) {
outLen = sizeof(out);
ret = Base64_Decode_nonCT(badSmall + i, 4, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ASN_INPUT_E))
return WC_TEST_RET_ENC_I(i);
ret = Base64_Decode_nonCT(badLarge + i, 4, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ASN_INPUT_E))
return WC_TEST_RET_ENC_I(i);
}
/* Invalid character less than 0x2b */
for (i = 1; i < 0x2b; i++) {
outLen = sizeof(out);
XMEMCPY(out, charTest, sizeof(charTest));
out[0] = (byte)i;
ret = Base64_Decode_nonCT(out, sizeof(charTest), out, &outLen);
if (ret != WC_NO_ERR_TRACE(ASN_INPUT_E))
return WC_TEST_RET_ENC_I(i);
}
/* Bad characters in range 0x2b - 0x7a. */
for (i = 0; i < (int)sizeof(badChar) - 1; i++) {
outLen = sizeof(out);
XMEMCPY(out, charTest, sizeof(charTest));
out[0] = badChar[i];
ret = Base64_Decode_nonCT(out, sizeof(charTest), out, &outLen);
if (ret != WC_NO_ERR_TRACE(ASN_INPUT_E))
return WC_TEST_RET_ENC_I(i);
}
/* Invalid character greater than 0x7a */
for (i = 0x7b; i < 0x100; i++) {
outLen = sizeof(out);
XMEMCPY(out, charTest, sizeof(charTest));
out[0] = (byte)i;
ret = Base64_Decode_nonCT(out, sizeof(charTest), out, &outLen);
if (ret != WC_NO_ERR_TRACE(ASN_INPUT_E))
return WC_TEST_RET_ENC_I(i);
}

View File

@@ -36,6 +36,9 @@
WOLFSSL_API int Base64_Decode(const byte* in, word32 inLen, byte* out,
word32* outLen);
WOLFSSL_API int Base64_Decode_nonCT(const byte* in, word32 inLen, byte* out,
word32* outLen);
#if defined(OPENSSL_EXTRA) || defined(SESSION_CERTS) || defined(WOLFSSL_KEY_GEN) \
|| defined(WOLFSSL_CERT_GEN) || defined(HAVE_WEBSERVER) || !defined(NO_DSA)
#ifndef WOLFSSL_BASE64_ENCODE