diff options
-rw-r--r-- | lib/softoken/pkcs11c.c | 110 |
1 files changed, 74 insertions, 36 deletions
diff --git a/lib/softoken/pkcs11c.c b/lib/softoken/pkcs11c.c index 3686b7f2b..ccf9a02ae 100644 --- a/lib/softoken/pkcs11c.c +++ b/lib/softoken/pkcs11c.c @@ -1605,6 +1605,68 @@ NSC_DecryptUpdate(CK_SESSION_HANDLE hSession, return CKR_OK; } +/* From ssl3con.c: Constant-time helper macro that copies the MSB of x to all + * other bits. */ +#define DUPLICATE_MSB_TO_ALL(x) ((unsigned int)((int)(x) >> (sizeof(int) * 8 - 1))) +/* CK_RVToMask returns, in constant time, a mask value of + * all ones if rv == CKR_OK. Otherwise it returns zero. */ +static unsigned int +CK_RVToMask(CK_RV rv) +{ + unsigned int good; + /* rv ^ CKR_OK is zero iff rv == CKR_OK. Subtracting one results + * in the MSB being set to one iff it was zero before. */ + good = rv ^ CKR_OK; + good--; + return DUPLICATE_MSB_TO_ALL(good); +} +/* Constant-time helper macro that selects l or r depending on all-1 or all-0 + * mask m */ +#define CT_SEL(m, l, r) (((m) & (l)) | (~(m) & (r))) +/* Constant-time helper macro that returns all-1s if x is not 0; and all-0s + * otherwise. */ +#define CT_NOT_ZERO(x) (DUPLICATE_MSB_TO_ALL(((x) | (0 - x)))) + +/* sftk_CheckCBCPadding checks, in constant time, the padding validity and + * accordingly sets the pad length. */ +static CK_RV +sftk_CheckCBCPadding(CK_BYTE_PTR pLastPart, + unsigned int blockSize, unsigned int *outPadSize) +{ + PORT_Assert(outPadSize); + + unsigned int padSize = (unsigned int)pLastPart[blockSize - 1]; + + /* If padSize <= blockSize, set goodPad to all-1s and all-0s otherwise.*/ + unsigned int goodPad = DUPLICATE_MSB_TO_ALL(~(blockSize - padSize)); + /* padSize should not be 0 */ + goodPad &= CT_NOT_ZERO(padSize); + + unsigned int i; + for (i = 0; i < blockSize; i++) { + /* If i < padSize, set loopMask to all-1s and all-0s otherwise.*/ + unsigned int loopMask = DUPLICATE_MSB_TO_ALL(~(padSize - 1 - i)); + /* Get the padding value (should be padSize) from buffer */ + unsigned int padVal = pLastPart[blockSize - 1 - i]; + /* Update goodPad only if i < padSize */ + goodPad &= CT_SEL(loopMask, ~(padVal ^ padSize), goodPad); + } + + /* If any of the final padding bytes had the wrong value, one or more + * of the lower eight bits of |goodPad| will be cleared. We AND the + * bottom 8 bits together and duplicate the result to all the bits. */ + goodPad &= goodPad >> 4; + goodPad &= goodPad >> 2; + goodPad &= goodPad >> 1; + goodPad <<= sizeof(goodPad) * 8 - 1; + goodPad = DUPLICATE_MSB_TO_ALL(goodPad); + + /* Set outPadSize to padSize or 0 */ + *outPadSize = CT_SEL(goodPad, padSize, 0); + /* Return OK if the pad is valid */ + return CT_SEL(goodPad, CKR_OK, CKR_ENCRYPTED_DATA_INVALID); +} + /* NSC_DecryptFinal finishes a multiple-part decryption operation. */ CK_RV NSC_DecryptFinal(CK_SESSION_HANDLE hSession, @@ -1643,24 +1705,10 @@ NSC_DecryptFinal(CK_SESSION_HANDLE hSession, if (rv != SECSuccess) { crv = sftk_MapDecryptError(PORT_GetError()); } else { - unsigned int padSize = - (unsigned int)pLastPart[context->blockSize - 1]; - if ((padSize > context->blockSize) || (padSize == 0)) { - crv = CKR_ENCRYPTED_DATA_INVALID; - } else { - unsigned int i; - unsigned int badPadding = 0; /* used as a boolean */ - for (i = 0; i < padSize; i++) { - badPadding |= - (unsigned int)pLastPart[context->blockSize - 1 - i] ^ - padSize; - } - if (badPadding) { - crv = CKR_ENCRYPTED_DATA_INVALID; - } else { - *pulLastPartLen = outlen - padSize; - } - } + unsigned int padSize = 0; + crv = sftk_CheckCBCPadding(&pLastPart[outlen - context->blockSize], context->blockSize, &padSize); + /* Update pulLastPartLen, in constant time, if crv is OK */ + *pulLastPartLen = CT_SEL(CK_RVToMask(crv), outlen - padSize, *pulLastPartLen); } } } @@ -1693,7 +1741,7 @@ NSC_Decrypt(CK_SESSION_HANDLE hSession, return crv; if (!pData) { - outlen = ulEncryptedDataLen + context->blockSize; + *pulDataLen = (CK_ULONG)(ulEncryptedDataLen + context->blockSize); goto done; } @@ -1721,29 +1769,19 @@ NSC_Decrypt(CK_SESSION_HANDLE hSession, pEncryptedData, ulEncryptedDataLen); /* XXX need to do MUCH better error mapping than this. */ crv = (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError()); - if (rv == SECSuccess && context->doPad) { - unsigned int padding = pData[outlen - 1]; - if (padding > context->blockSize || !padding) { - crv = CKR_ENCRYPTED_DATA_INVALID; + if (rv == SECSuccess) { + if (context->doPad) { + unsigned int padSize = 0; + crv = sftk_CheckCBCPadding(&pData[outlen - context->blockSize], context->blockSize, &padSize); + /* Update pulDataLen, in constant time, if crv is OK */ + *pulDataLen = CT_SEL(CK_RVToMask(crv), outlen - padSize, *pulDataLen); } else { - unsigned int i; - unsigned int badPadding = 0; /* used as a boolean */ - for (i = 0; i < padding; i++) { - badPadding |= (unsigned int)pData[outlen - 1 - i] ^ padding; - } - if (badPadding) { - crv = CKR_ENCRYPTED_DATA_INVALID; - } else { - outlen -= padding; - } + *pulDataLen = (CK_ULONG)outlen; } } sftk_TerminateOp(session, SFTK_DECRYPT, context); done: sftk_FreeSession(session); - if (crv == CKR_OK) { - *pulDataLen = (CK_ULONG)outlen; - } return crv; } |