Skip to content

Commit d2370b9

Browse files
committed
F-7260 - Make PKCS#1 v1.5 unpadding branch-free
1 parent ef72f93 commit d2370b9

2 files changed

Lines changed: 130 additions & 22 deletions

File tree

src/tpm2_asn.c

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -366,31 +366,65 @@ int TPM2_ASN_DecodeRsaPubKey(uint8_t* input, int inputSz,
366366
return rc;
367367
}
368368

369-
int TPM2_ASN_RsaUnpadPkcsv15(uint8_t** pSig, int* sigSz)
369+
/* Width-independent sign bit position of unsigned int */
370+
#define TPM2_ASN_CT_SHIFT ((unsigned int)(sizeof(unsigned int) * 8u - 1u))
371+
372+
/* 1 when v is zero, without a data-dependent branch */
373+
static int TPM2_ASN_CtIsZero(unsigned int v)
370374
{
371-
int rc = -1;
372-
uint8_t* sig = *pSig;
373-
int idx = 0;
375+
return (int)((((v | (0u - v)) >> TPM2_ASN_CT_SHIFT) & 1u) ^ 1u);
376+
}
374377

375-
if (*sigSz < 3)
376-
return rc;
378+
/* 1 when a is less than b, for values well below the unsigned range */
379+
static int TPM2_ASN_CtLt(unsigned int a, unsigned int b)
380+
{
381+
return (int)(((a - b) >> TPM2_ASN_CT_SHIFT) & 1u);
382+
}
377383

378-
if (sig[idx++] == 0x00 && sig[idx++] == 0x01) {
379-
while (idx < *sigSz) {
380-
if (sig[idx] != 0xFF)
381-
break;
382-
idx++;
383-
}
384-
/* Require the null separator to be in bounds (avoids a 1-byte
385-
* over-read) and at least 8 padding bytes per PKCS#1 v1.5 Sec.9.2
386-
* (also rejects the all-0xFF Bleichenbacher variant). */
387-
if (idx < *sigSz && (idx - 2) >= 8 && sig[idx++] == 0x00) {
388-
rc = 0;
389-
*pSig = &sig[idx];
390-
*sigSz -= idx;
391-
}
392-
}
393-
return rc;
384+
int TPM2_ASN_RsaUnpadPkcsv15(uint8_t** pSig, int* sigSz)
385+
{
386+
uint8_t* sig = *pSig;
387+
int len = *sigSz;
388+
int i;
389+
int isFF, notYet, isSep;
390+
int sepFound = 0;
391+
int sepIdx = 0;
392+
int dataIdx;
393+
volatile int bad = 0;
394+
395+
if (len < 3)
396+
return -1;
397+
398+
/* Block type 1: 0x00 0x01 FF..FF 0x00 data. Every check below folds
399+
* into an accumulator so no branch depends on the buffer contents. */
400+
bad |= sig[0];
401+
bad |= (sig[1] ^ 0x01);
402+
403+
/* Locate the first byte at or after index 2 that is not 0xFF, scanning
404+
* the whole buffer so the padding length is not revealed by timing. */
405+
for (i = 2; i < len; i++) {
406+
/* 1 when sig[i] is 0xFF, without branching on the byte value */
407+
isFF = TPM2_ASN_CtIsZero((unsigned int)(sig[i] ^ 0xFF));
408+
notYet = 1 - sepFound;
409+
isSep = notYet & (1 - isFF);
410+
sepIdx += isSep * i;
411+
sepFound |= isSep;
412+
}
413+
414+
/* A separator must exist, must be 0x00, and must follow at least 8
415+
* padding bytes per PKCS#1 v1.5 Sec.9.2 (this also rejects the
416+
* all-0xFF Bleichenbacher variant). */
417+
bad |= (1 - sepFound);
418+
bad |= sig[sepIdx];
419+
bad |= TPM2_ASN_CtLt((unsigned int)sepIdx, 10u);
420+
421+
if (bad != 0)
422+
return -1;
423+
424+
dataIdx = sepIdx + 1;
425+
*pSig = &sig[dataIdx];
426+
*sigSz -= dataIdx;
427+
return 0;
394428
}
395429

396430
#endif /* !WOLFTPM2_NO_ASN */

tests/unit_tests.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4947,6 +4947,79 @@ static void test_TPM2_ASN_DecodeX509Cert_Errors(void)
49474947
#endif
49484948
}
49494949

4950+
static void test_TPM2_ASN_RsaUnpadPkcsv15(void)
4951+
{
4952+
#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN)
4953+
byte blk[64];
4954+
byte* p;
4955+
int sz;
4956+
int i;
4957+
4958+
/* Well formed: 00 01 FF*8 00 then 2 data bytes */
4959+
XMEMSET(blk, 0xFF, sizeof(blk));
4960+
blk[0] = 0x00; blk[1] = 0x01; blk[10] = 0x00;
4961+
blk[11] = 0xAA; blk[12] = 0xBB;
4962+
p = blk; sz = 13;
4963+
AssertIntEQ(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
4964+
AssertIntEQ(sz, 2);
4965+
AssertIntEQ(p[0], 0xAA);
4966+
AssertIntEQ(p[1], 0xBB);
4967+
4968+
/* Exactly 8 pad bytes is the minimum accepted */
4969+
XMEMSET(blk, 0xFF, sizeof(blk));
4970+
blk[0] = 0x00; blk[1] = 0x01; blk[10] = 0x00; blk[11] = 0x5A;
4971+
p = blk; sz = 12;
4972+
AssertIntEQ(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
4973+
AssertIntEQ(sz, 1);
4974+
4975+
/* Seven pad bytes must be rejected */
4976+
XMEMSET(blk, 0xFF, sizeof(blk));
4977+
blk[0] = 0x00; blk[1] = 0x01; blk[9] = 0x00; blk[10] = 0x5A;
4978+
p = blk; sz = 11;
4979+
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
4980+
4981+
/* Wrong leading byte */
4982+
XMEMSET(blk, 0xFF, sizeof(blk));
4983+
blk[0] = 0x01; blk[1] = 0x01; blk[10] = 0x00;
4984+
p = blk; sz = 12;
4985+
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
4986+
4987+
/* Block type 2 must be rejected (this routine is type 1 only) */
4988+
XMEMSET(blk, 0xFF, sizeof(blk));
4989+
blk[0] = 0x00; blk[1] = 0x02; blk[10] = 0x00;
4990+
p = blk; sz = 12;
4991+
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
4992+
4993+
/* No separator at all (all 0xFF tail) */
4994+
XMEMSET(blk, 0xFF, sizeof(blk));
4995+
blk[0] = 0x00; blk[1] = 0x01;
4996+
p = blk; sz = 16;
4997+
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
4998+
4999+
/* Non-zero, non-FF byte where the separator belongs */
5000+
XMEMSET(blk, 0xFF, sizeof(blk));
5001+
blk[0] = 0x00; blk[1] = 0x01; blk[10] = 0x7E;
5002+
p = blk; sz = 16;
5003+
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
5004+
5005+
/* Too short to hold a block */
5006+
for (i = 0; i < 3; i++) {
5007+
XMEMSET(blk, 0x00, sizeof(blk));
5008+
p = blk; sz = i;
5009+
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
5010+
}
5011+
5012+
/* Separator as the final byte yields an empty payload */
5013+
XMEMSET(blk, 0xFF, sizeof(blk));
5014+
blk[0] = 0x00; blk[1] = 0x01; blk[11] = 0x00;
5015+
p = blk; sz = 12;
5016+
AssertIntEQ(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
5017+
AssertIntEQ(sz, 0);
5018+
5019+
printf("Test TPM Wrapper: %-40s Passed\n", "ASN RsaUnpadPkcsv15:");
5020+
#endif
5021+
}
5022+
49505023
#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN)
49515024
#include <examples/endorsement/trusted_certs_der.h>
49525025
#endif
@@ -7237,6 +7310,7 @@ int unit_tests(int argc, char *argv[])
72377310
test_wolfTPM2_CSR();
72387311
test_wolfTPM2_CryptoDevCb_EccVerifyOversizedRS();
72397312
test_TPM2_ASN_DecodeX509Cert_Errors();
7313+
test_TPM2_ASN_RsaUnpadPkcsv15();
72407314
test_TPM2_ASN_DecodeX509Cert_Valid();
72417315
test_TPM2_ASN_DecodeTag_Errors();
72427316
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFTPM2_PEM_DECODE) && \

0 commit comments

Comments
 (0)