Commit 60de8da9 authored by Robert Relyea's avatar Robert Relyea

Bug 1630721 Softoken Functions for FIPS missing r=mt

For FIPS we need the following:

 1. NIST official Key padding for AES Key Wrap.
 2. Combined Hash/Sign mechanisms for DSA and ECDSA.

In the first case our AES_KEY_WRAP_PAD function addes pkcs8 padding to the
normal AES_KEY_WRAP, which is a different algorithm then the padded key wrap
specified by NIST. PKCS #11 recognized this and created a special mechanism to
handle NIST padding. That is why we don't have industry test vectors for
CKM_NSS_AES_KEY_WRAP_PAD. This patch implements that NIST version (while
maintaining our own). Also PKCS #11 v3.0 specified PKCS #11 mechanism for
AES_KEY_WRAP which are compatible (semantically) with the NSS vendor specific
versions, but with non-vendor specific numbers. Softoken now accepts both
numbers.

This patch also updates softoken to handle DSA and ECDSA combined hash
algorithms other than just SHA1 (which is no longer validated).

Finally this patch uses the NIST KWP test vectors in new gtests for the
AES_KEY_WRAP_KWP wrapping algorithm.

As part of the AES_KEY_WRAP_KWP code, the Constant time macros have been
generalized and moved to secport. Old macros scattered throughout the code
have been deleted and existing contant time code has been updated to use
the new macros.

Differential Revision: https://phabricator.services.mozilla.com/D71225
parent 7d4b0ba0
This diff is collapsed.
......@@ -9,6 +9,7 @@ MODULE = nss
CPPSRCS = \
pk11_aes_gcm_unittest.cc \
pk11_aeskeywrap_unittest.cc \
pk11_aeskeywrapkwp_unittest.cc \
pk11_aeskeywrappad_unittest.cc \
pk11_cbc_unittest.cc \
pk11_chacha20poly1305_unittest.cc \
......
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <memory>
#include "nss.h"
#include "pk11pub.h"
#include "testvectors/kw-vectors.h"
#include "testvectors/kwp-vectors.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {
class Pkcs11AESKeyWrapKwpTest
: public ::testing::TestWithParam<keywrap_vector> {
protected:
CK_MECHANISM_TYPE mechanism = CKM_AES_KEY_WRAP_KWP;
void WrapUnwrap(unsigned char* kek_data, unsigned int kek_len,
unsigned char* key_data, unsigned int key_data_len,
unsigned char* expected_ciphertext,
unsigned int expected_ciphertext_len,
std::map<Action, Result> tests, uint32_t test_id) {
std::vector<unsigned char> wrapped_key(PR_MAX(1U, expected_ciphertext_len));
std::vector<unsigned char> unwrapped_key(PR_MAX(1U, key_data_len));
std::vector<unsigned char> zeros(PR_MAX(1U, expected_ciphertext_len), 0);
unsigned int wrapped_key_len = 0;
unsigned int unwrapped_key_len = 0;
SECStatus rv;
std::stringstream s;
s << "Test with original ID #" << test_id << " failed." << std::endl;
std::string msg = s.str();
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot) << msg;
// Import encryption key.
SECItem kek_item = {siBuffer, kek_data, kek_len};
ScopedPK11SymKey kek(PK11_ImportSymKeyWithFlags(
slot.get(), mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &kek_item,
CKF_DECRYPT, PR_FALSE, nullptr));
EXPECT_TRUE(!!kek) << msg;
// Wrap key
Action test = WRAP;
if (tests.count(test)) {
rv = PK11_Encrypt(kek.get(), mechanism, nullptr /* param */,
wrapped_key.data(), &wrapped_key_len,
wrapped_key.size(), key_data, key_data_len);
ASSERT_EQ(rv, tests[test].expect_rv) << msg;
// If we failed, check that output was not produced.
if (rv == SECFailure) {
EXPECT_TRUE(wrapped_key_len == 0);
EXPECT_TRUE(!memcmp(wrapped_key.data(), zeros.data(), wrapped_key_len));
}
if (tests[test].output_match) {
EXPECT_EQ(expected_ciphertext_len, wrapped_key_len) << msg;
EXPECT_TRUE(!memcmp(expected_ciphertext, wrapped_key.data(),
expected_ciphertext_len))
<< msg;
} else {
// If we produced output, verify that it doesn't match the vector
if (wrapped_key_len) {
EXPECT_FALSE(wrapped_key_len == expected_ciphertext_len &&
!memcmp(wrapped_key.data(), expected_ciphertext,
expected_ciphertext_len))
<< msg;
}
}
}
// Unwrap key
test = UNWRAP;
if (tests.count(test)) {
rv = PK11_Decrypt(kek.get(), mechanism, nullptr /* param */,
unwrapped_key.data(), &unwrapped_key_len,
unwrapped_key.size(), expected_ciphertext,
expected_ciphertext_len);
ASSERT_EQ(rv, tests[test].expect_rv) << msg;
// If we failed, check that output was not produced.
if (rv == SECFailure) {
EXPECT_TRUE(unwrapped_key_len == 0);
EXPECT_TRUE(
!memcmp(unwrapped_key.data(), zeros.data(), unwrapped_key_len));
}
if (tests[test].output_match) {
EXPECT_EQ(unwrapped_key_len, key_data_len) << msg;
EXPECT_TRUE(!memcmp(key_data, unwrapped_key.data(), key_data_len))
<< msg;
} else {
// If we produced output, verify that it doesn't match the vector
if (unwrapped_key_len) {
EXPECT_FALSE(
unwrapped_key_len == expected_ciphertext_len &&
!memcmp(unwrapped_key.data(), key_data, unwrapped_key_len))
<< msg;
}
}
}
}
void WrapUnwrap(keywrap_vector testvector) {
WrapUnwrap(testvector.key.data(), testvector.key.size(),
testvector.msg.data(), testvector.msg.size(),
testvector.ct.data(), testvector.ct.size(), testvector.tests,
testvector.test_id);
}
};
TEST_P(Pkcs11AESKeyWrapKwpTest, TestVectors) { WrapUnwrap(GetParam()); }
INSTANTIATE_TEST_CASE_P(Pkcs11NistAESKWPTest, Pkcs11AESKeyWrapKwpTest,
::testing::ValuesIn(kNistAesKWPVectors));
} /* nss_test */
......@@ -14,6 +14,7 @@
'pk11_aes_cmac_unittest.cc',
'pk11_aes_gcm_unittest.cc',
'pk11_aeskeywrap_unittest.cc',
'pk11_aeskeywrapkwp_unittest.cc',
'pk11_aeskeywrappad_unittest.cc',
'pk11_cbc_unittest.cc',
'pk11_chacha20poly1305_unittest.cc',
......
This diff is collapsed.
......@@ -944,6 +944,38 @@ AESKeyWrap_Decrypt(AESKeyWrapContext *cx, unsigned char *output,
unsigned int *outputLen, unsigned int maxOutputLen,
const unsigned char *input, unsigned int inputLen);
/*
** Perform AES padded key wrap.
** "cx" the context
** "output" the output buffer to store the encrypted data.
** "outputLen" how much data is stored in "output". Set by the routine
** after some data is stored in output.
** "maxOutputLen" the maximum amount of data that can ever be
** stored in "output"
** "input" the input data
** "inputLen" the amount of input data
*/
extern SECStatus
AESKeyWrap_EncryptKWP(AESKeyWrapContext *cx, unsigned char *output,
unsigned int *outputLen, unsigned int maxOutputLen,
const unsigned char *input, unsigned int inputLen);
/*
** Perform AES padded key unwrap.
** "cx" the context
** "output" the output buffer to store the decrypted data.
** "outputLen" how much data is stored in "output". Set by the routine
** after some data is stored in output.
** "maxOutputLen" the maximum amount of data that can ever be
** stored in "output"
** "input" the input data
** "inputLen" the amount of input data
*/
extern SECStatus
AESKeyWrap_DecryptKWP(AESKeyWrapContext *cx, unsigned char *output,
unsigned int *outputLen, unsigned int maxOutputLen,
const unsigned char *input, unsigned int inputLen);
/******************************************/
/*
** Camellia symmetric block cypher
......
......@@ -114,9 +114,9 @@ typedef int __BLAPI_DEPRECATED __attribute__((deprecated));
#define BLAKE2B_BLOCK_LENGTH 128 /* Bytes */
#define HASH_BLOCK_LENGTH_MAX SHA512_BLOCK_LENGTH
#define AES_KEY_WRAP_IV_BYTES 8
#define AES_KEY_WRAP_BLOCK_SIZE 8 /* bytes */
#define AES_BLOCK_SIZE 16 /* bytes */
#define AES_BLOCK_SIZE 16 /* bytes */
#define AES_KEY_WRAP_BLOCK_SIZE (AES_BLOCK_SIZE / 2)
#define AES_KEY_WRAP_IV_BYTES AES_KEY_WRAP_BLOCK_SIZE
#define AES_128_KEY_LENGTH 16 /* bytes */
#define AES_192_KEY_LENGTH 24 /* bytes */
......
......@@ -16,34 +16,19 @@
* field. (SHA-384/512 have 128-bit length.) */
#define MAX_HASH_BIT_COUNT_BYTES 16
/* Some utility functions are needed:
*
* These macros return the given value with the MSB copied to all the other
* bits. They use the fact that an arithmetic shift shifts-in the sign bit.
* However, this is not ensured by the C standard so you may need to replace
* them with something else on odd CPUs.
*
* Note: the argument to these macros must be an unsigned int.
* */
#define DUPLICATE_MSB_TO_ALL(x) ((unsigned int)((int)(x) >> (sizeof(int) * 8 - 1)))
#define DUPLICATE_MSB_TO_ALL_8(x) ((unsigned char)(DUPLICATE_MSB_TO_ALL(x)))
/* constantTimeGE returns 0xff if a>=b and 0x00 otherwise, where a, b <
* MAX_UINT/2. */
static unsigned char
constantTimeGE(unsigned int a, unsigned int b)
{
a -= b;
return DUPLICATE_MSB_TO_ALL(~a);
return PORT_CT_GE(a, b);
}
/* constantTimeEQ8 returns 0xff if a==b and 0x00 otherwise. */
static unsigned char
constantTimeEQ8(unsigned char a, unsigned char b)
constantTimeEQ(unsigned char a, unsigned char b)
{
unsigned int c = a ^ b;
c--;
return DUPLICATE_MSB_TO_ALL_8(c);
return PORT_CT_EQ(a, b);
}
/* MAC performs a constant time SSLv3/TLS MAC of |dataLen| bytes of |data|,
......@@ -223,8 +208,8 @@ MAC(unsigned char *mdOut,
* constant time, to |macOut|. */
for (i = numStartingBlocks; i <= numStartingBlocks + varianceBlocks; i++) {
unsigned char block[HASH_BLOCK_LENGTH_MAX];
unsigned char isBlockA = constantTimeEQ8(i, indexA);
unsigned char isBlockB = constantTimeEQ8(i, indexB);
unsigned char isBlockA = constantTimeEQ(i, indexA);
unsigned char isBlockB = constantTimeEQ(i, indexB);
for (j = 0; j < mdBlockSize; j++) {
unsigned char isPastC = isBlockA & constantTimeGE(j, c);
unsigned char isPastCPlus1 = isBlockA & constantTimeGE(j, c + 1);
......
......@@ -332,7 +332,9 @@ static const struct FREEBLVectorStr vector =
/* End of version 3.022 */
ChaCha20Poly1305_Encrypt,
ChaCha20Poly1305_Decrypt,
AES_AEAD
AES_AEAD,
AESKeyWrap_EncryptKWP,
AESKeyWrap_DecryptKWP
/* End of version 3.023 */
};
......
......@@ -1138,6 +1138,7 @@ AESKeyWrap_Encrypt(AESKeyWrapContext *cx, unsigned char *output,
return vector->p_AESKeyWrap_Encrypt(cx, output, outputLen, maxOutputLen,
input, inputLen);
}
SECStatus
AESKeyWrap_Decrypt(AESKeyWrapContext *cx, unsigned char *output,
unsigned int *outputLen, unsigned int maxOutputLen,
......@@ -1149,6 +1150,28 @@ AESKeyWrap_Decrypt(AESKeyWrapContext *cx, unsigned char *output,
input, inputLen);
}
SECStatus
AESKeyWrap_EncryptKWP(AESKeyWrapContext *cx, unsigned char *output,
unsigned int *outputLen, unsigned int maxOutputLen,
const unsigned char *input, unsigned int inputLen)
{
if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
return SECFailure;
return vector->p_AESKeyWrap_EncryptKWP(cx, output, outputLen, maxOutputLen,
input, inputLen);
}
SECStatus
AESKeyWrap_DecryptKWP(AESKeyWrapContext *cx, unsigned char *output,
unsigned int *outputLen, unsigned int maxOutputLen,
const unsigned char *input, unsigned int inputLen)
{
if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
return SECFailure;
return vector->p_AESKeyWrap_DecryptKWP(cx, output, outputLen, maxOutputLen,
input, inputLen);
}
PRBool
BLAPI_SHVerify(const char *name, PRFuncPtr addr)
{
......
......@@ -796,6 +796,20 @@ struct FREEBLVectorStr {
const unsigned char *input, unsigned int inputLen,
void *params, unsigned int paramsLen,
const unsigned char *aad, unsigned int aadLen);
SECStatus (*p_AESKeyWrap_EncryptKWP)(AESKeyWrapContext *cx,
unsigned char *output,
unsigned int *outputLen,
unsigned int maxOutputLen,
const unsigned char *input,
unsigned int inputLen);
SECStatus (*p_AESKeyWrap_DecryptKWP)(AESKeyWrapContext *cx,
unsigned char *output,
unsigned int *outputLen,
unsigned int maxOutputLen,
const unsigned char *input,
unsigned int inputLen);
/* Version 3.023 came to here */
/* Add new function pointers at the end of this struct and bump
......
......@@ -250,6 +250,8 @@ PK11_GetKeyType(CK_MECHANISM_TYPE type, unsigned long len)
case CKM_AES_KEY_GEN:
case CKM_NSS_AES_KEY_WRAP:
case CKM_NSS_AES_KEY_WRAP_PAD:
case CKM_AES_KEY_WRAP:
case CKM_AES_KEY_WRAP_KWP:
case CKM_AES_XCBC_MAC:
case CKM_AES_XCBC_MAC_96:
return CKK_AES;
......
......@@ -1237,8 +1237,8 @@ sec_pkcs5_des(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des,
/* remove padding */
if ((encrypt == PR_FALSE) && (rv == SECSuccess)) {
crv = sftk_CheckCBCPadding(dest->data, dest->len, DES_BLOCK_SIZE, &pad);
dest->len = CT_SEL(sftk_CKRVToMask(crv), dest->len - pad, dest->len);
PORT_SetError(CT_SEL(sftk_CKRVToMask(crv), error, SEC_ERROR_BAD_PASSWORD));
dest->len = PORT_CT_SEL(sftk_CKRVToMask(crv), dest->len - pad, dest->len);
PORT_SetError(PORT_CT_SEL(sftk_CKRVToMask(crv), error, SEC_ERROR_BAD_PASSWORD));
}
DES_DestroyContext(ctxt, PR_TRUE);
......@@ -1312,8 +1312,8 @@ sec_pkcs5_aes(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des,
/* remove padding */
if ((encrypt == PR_FALSE) && (rv == SECSuccess)) {
crv = sftk_CheckCBCPadding(dest->data, dest->len, AES_BLOCK_SIZE, &pad);
dest->len = CT_SEL(sftk_CKRVToMask(crv), dest->len - pad, dest->len);
PORT_SetError(CT_SEL(sftk_CKRVToMask(crv), error, SEC_ERROR_BAD_PASSWORD));
dest->len = PORT_CT_SEL(sftk_CKRVToMask(crv), dest->len - pad, dest->len);
PORT_SetError(PORT_CT_SEL(sftk_CKRVToMask(crv), error, SEC_ERROR_BAD_PASSWORD));
}
AES_DestroyContext(ctxt, PR_TRUE);
......@@ -1388,8 +1388,8 @@ sec_pkcs5_aes_key_wrap(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_de
/* remove padding */
if ((encrypt == PR_FALSE) && (rv == SECSuccess)) {
crv = sftk_CheckCBCPadding(dest->data, dest->len, AES_BLOCK_SIZE, &pad);
dest->len = CT_SEL(sftk_CKRVToMask(crv), dest->len - pad, dest->len);
PORT_SetError(CT_SEL(sftk_CKRVToMask(crv), error, SEC_ERROR_BAD_PASSWORD));
dest->len = PORT_CT_SEL(sftk_CKRVToMask(crv), dest->len - pad, dest->len);
PORT_SetError(PORT_CT_SEL(sftk_CKRVToMask(crv), error, SEC_ERROR_BAD_PASSWORD));
}
AESKeyWrap_DestroyContext(ctxt, PR_TRUE);
......
......@@ -323,6 +323,10 @@ static const struct mechanismList mechanisms[] = {
{ CKM_DSA, { DSA_MIN_P_BITS, DSA_MAX_P_BITS, CKF_SN_VR }, PR_TRUE },
{ CKM_DSA_PARAMETER_GEN, { DSA_MIN_P_BITS, DSA_MAX_P_BITS, CKF_GENERATE }, PR_TRUE },
{ CKM_DSA_SHA1, { DSA_MIN_P_BITS, DSA_MAX_P_BITS, CKF_SN_VR }, PR_TRUE },
{ CKM_DSA_SHA224, { DSA_MIN_P_BITS, DSA_MAX_P_BITS, CKF_SN_VR }, PR_TRUE },
{ CKM_DSA_SHA256, { DSA_MIN_P_BITS, DSA_MAX_P_BITS, CKF_SN_VR }, PR_TRUE },
{ CKM_DSA_SHA384, { DSA_MIN_P_BITS, DSA_MAX_P_BITS, CKF_SN_VR }, PR_TRUE },
{ CKM_DSA_SHA512, { DSA_MIN_P_BITS, DSA_MAX_P_BITS, CKF_SN_VR }, PR_TRUE },
/* -------------------- Diffie Hellman Operations --------------------- */
/* no diffie hellman yet */
{ CKM_DH_PKCS_KEY_PAIR_GEN, { DH_MIN_P_BITS, DH_MAX_P_BITS, CKF_GENERATE_KEY_PAIR }, PR_TRUE },
......@@ -332,6 +336,10 @@ static const struct mechanismList mechanisms[] = {
{ CKM_ECDH1_DERIVE, { EC_MIN_KEY_BITS, EC_MAX_KEY_BITS, CKF_DERIVE | CKF_EC_BPNU }, PR_TRUE },
{ CKM_ECDSA, { EC_MIN_KEY_BITS, EC_MAX_KEY_BITS, CKF_SN_VR | CKF_EC_BPNU }, PR_TRUE },
{ CKM_ECDSA_SHA1, { EC_MIN_KEY_BITS, EC_MAX_KEY_BITS, CKF_SN_VR | CKF_EC_BPNU }, PR_TRUE },
{ CKM_ECDSA_SHA224, { EC_MIN_KEY_BITS, EC_MAX_KEY_BITS, CKF_SN_VR | CKF_EC_BPNU }, PR_TRUE },
{ CKM_ECDSA_SHA256, { EC_MIN_KEY_BITS, EC_MAX_KEY_BITS, CKF_SN_VR | CKF_EC_BPNU }, PR_TRUE },
{ CKM_ECDSA_SHA384, { EC_MIN_KEY_BITS, EC_MAX_KEY_BITS, CKF_SN_VR | CKF_EC_BPNU }, PR_TRUE },
{ CKM_ECDSA_SHA512, { EC_MIN_KEY_BITS, EC_MAX_KEY_BITS, CKF_SN_VR | CKF_EC_BPNU }, PR_TRUE },
/* ------------------------- RC2 Operations --------------------------- */
{ CKM_RC2_KEY_GEN, { 1, 128, CKF_GENERATE }, PR_TRUE },
{ CKM_RC2_ECB, { 1, 128, CKF_EN_DE_WR_UN }, PR_TRUE },
......@@ -556,6 +564,9 @@ static const struct mechanismList mechanisms[] = {
/* ------------------ AES Key Wrap (also encrypt) ------------------- */
{ CKM_NSS_AES_KEY_WRAP, { 16, 32, CKF_EN_DE_WR_UN }, PR_TRUE },
{ CKM_NSS_AES_KEY_WRAP_PAD, { 16, 32, CKF_EN_DE_WR_UN }, PR_TRUE },
{ CKM_AES_KEY_WRAP, { 16, 32, CKF_EN_DE_WR_UN }, PR_TRUE },
{ CKM_AES_KEY_WRAP_PAD, { 16, 32, CKF_EN_DE_WR_UN }, PR_TRUE },
{ CKM_AES_KEY_WRAP_KWP, { 1, 32, CKF_EN_DE_WR_UN }, PR_TRUE },
/* --------------------------- J-PAKE -------------------------------- */
{ CKM_NSS_JPAKE_ROUND1_SHA1, { 0, 0, CKF_GENERATE }, PR_TRUE },
{ CKM_NSS_JPAKE_ROUND1_SHA256, { 0, 0, CKF_GENERATE }, PR_TRUE },
......
......@@ -1334,11 +1334,14 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
}
case CKM_NSS_AES_KEY_WRAP_PAD:
case CKM_AES_KEY_WRAP_PAD:
context->doPad = PR_TRUE;
/* fall thru */
case CKM_NSS_AES_KEY_WRAP:
context->multi = PR_FALSE;
case CKM_AES_KEY_WRAP:
context->blockSize = 8;
case CKM_AES_KEY_WRAP_KWP:
context->multi = PR_FALSE;
if (key_type != CKK_AES) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
......@@ -1357,8 +1360,13 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
crv = CKR_HOST_MEMORY;
break;
}
context->update = (SFTKCipher)(isEncrypt ? AESKeyWrap_Encrypt
: AESKeyWrap_Decrypt);
if (pMechanism->mechanism == CKM_AES_KEY_WRAP_KWP) {
context->update = (SFTKCipher)(isEncrypt ? AESKeyWrap_EncryptKWP
: AESKeyWrap_DecryptKWP);
} else {
context->update = (SFTKCipher)(isEncrypt ? AESKeyWrap_Encrypt
: AESKeyWrap_Decrypt);
}
context->destroy = (SFTKDestroy)AESKeyWrap_DestroyContext;
break;
......@@ -1742,7 +1750,7 @@ NSC_DecryptFinal(CK_SESSION_HANDLE hSession,
crv = sftk_CheckCBCPadding(pLastPart, outlen,
context->blockSize, &padSize);
/* Update pulLastPartLen, in constant time, if crv is OK */
*pulLastPartLen = CT_SEL(sftk_CKRVToMask(crv), outlen - padSize, *pulLastPartLen);
*pulLastPartLen = PORT_CT_SEL(sftk_CKRVToMask(crv), outlen - padSize, *pulLastPartLen);
}
}
}
......@@ -1794,7 +1802,7 @@ NSC_Decrypt(CK_SESSION_HANDLE hSession,
finalLen = maxoutlen;
crv2 = NSC_DecryptFinal(hSession, pData, &finalLen);
if (crv == CKR_OK) {
*pulDataLen = CT_SEL(sftk_CKRVToMask(crv2), updateLen + finalLen, *pulDataLen);
*pulDataLen = PORT_CT_SEL(sftk_CKRVToMask(crv2), updateLen + finalLen, *pulDataLen);
return crv2;
} else {
return crv;
......@@ -1811,7 +1819,7 @@ NSC_Decrypt(CK_SESSION_HANDLE hSession,
crv = sftk_CheckCBCPadding(pData, outlen, context->blockSize,
&padSize);
/* Update pulDataLen, in constant time, if crv is OK */
*pulDataLen = CT_SEL(sftk_CKRVToMask(crv), outlen - padSize, *pulDataLen);
*pulDataLen = PORT_CT_SEL(sftk_CKRVToMask(crv), outlen - padSize, *pulDataLen);
} else {
*pulDataLen = (CK_ULONG)outlen;
}
......@@ -2784,13 +2792,20 @@ NSC_SignInit(CK_SESSION_HANDLE hSession,
context->maxLen = nsslowkey_PrivateModulusLen(info->key);
break;
case CKM_DSA_SHA1:
context->multi = PR_TRUE;
crv = sftk_doSubSHA1(context);
if (crv != CKR_OK)
break;
/* fall through */
#define INIT_DSA_SIGN_MECH(mmm) \
case CKM_DSA_##mmm: \
context->multi = PR_TRUE; \
crv = sftk_doSub##mmm(context); \
if (crv != CKR_OK) \
break; \
goto finish_dsa;
INIT_DSA_SIGN_MECH(SHA1)
INIT_DSA_SIGN_MECH(SHA224)
INIT_DSA_SIGN_MECH(SHA256)
INIT_DSA_SIGN_MECH(SHA384)
INIT_DSA_SIGN_MECH(SHA512)
case CKM_DSA:
finish_dsa:
if (key_type != CKK_DSA) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
......@@ -2806,13 +2821,20 @@ NSC_SignInit(CK_SESSION_HANDLE hSession,
break;
case CKM_ECDSA_SHA1:
context->multi = PR_TRUE;
crv = sftk_doSubSHA1(context);
if (crv != CKR_OK)
break;
/* fall through */
#define INIT_ECDSA_SIGN_MECH(mmm) \
case CKM_ECDSA_##mmm: \
context->multi = PR_TRUE; \
crv = sftk_doSub##mmm(context); \
if (crv != CKR_OK) \
break; \
goto finish_ecdsa;
INIT_ECDSA_SIGN_MECH(SHA1)
INIT_ECDSA_SIGN_MECH(SHA224)
INIT_ECDSA_SIGN_MECH(SHA256)
INIT_ECDSA_SIGN_MECH(SHA384)
INIT_ECDSA_SIGN_MECH(SHA512)
case CKM_ECDSA:
finish_ecdsa:
if (key_type != CKK_EC) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
......
......@@ -71,17 +71,6 @@
* before we start freeing them */
#define MAX_KEY_LEN 256 /* maximum symmetric key length in bytes */
/* From ssl3con.c: Constant-time helper macro that copies the MSB of x to all
* * other bits. */
#define CT_DUPLICATE_MSB_TO_ALL(x) ((unsigned int)((int)(x) >> (sizeof(int) * 8 - 1)))
/* 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) (CT_DUPLICATE_MSB_TO_ALL(((x) | (0 - x))))
/*
* LOG2_BUCKETS_PER_SESSION_LOCK must be a prime number.
* With SESSION_HASH_SIZE=1024, LOG2 can be 9, 5, 1, or 0.
......
......@@ -2051,7 +2051,7 @@ unsigned int
sftk_CKRVToMask(CK_RV rv)
{
PR_STATIC_ASSERT(CKR_OK == 0);
return ~CT_NOT_ZERO(rv);
return ~PORT_CT_NOT_ZERO(rv);
}
/* sftk_CheckCBCPadding checks, in constant time, the padding validity and
......@@ -2065,18 +2065,18 @@ sftk_CheckCBCPadding(CK_BYTE_PTR pBuf, unsigned int bufLen,
unsigned int padSize = (unsigned int)pBuf[bufLen - 1];
/* If padSize <= blockSize, set goodPad to all-1s and all-0s otherwise.*/
unsigned int goodPad = CT_DUPLICATE_MSB_TO_ALL(~(blockSize - padSize));
unsigned int goodPad = PORT_CT_DUPLICATE_MSB_TO_ALL(~(blockSize - padSize));
/* padSize should not be 0 */
goodPad &= CT_NOT_ZERO(padSize);
goodPad &= PORT_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 = CT_DUPLICATE_MSB_TO_ALL(~(padSize - 1 - i));
unsigned int loopMask = PORT_CT_DUPLICATE_MSB_TO_ALL(~(padSize - 1 - i));
/* Get the padding value (should be padSize) from buffer */
unsigned int padVal = pBuf[bufLen - 1 - i];
/* Update goodPad only if i < padSize */
goodPad &= CT_SEL(loopMask, ~(padVal ^ padSize), goodPad);
goodPad &= PORT_CT_SEL(loopMask, ~(padVal ^ padSize), goodPad);
}
/* If any of the final padding bytes had the wrong value, one or more
......@@ -2086,12 +2086,12 @@ sftk_CheckCBCPadding(CK_BYTE_PTR pBuf, unsigned int bufLen,
goodPad &= goodPad >> 2;
goodPad &= goodPad >> 1;
goodPad <<= sizeof(goodPad) * 8 - 1;
goodPad = CT_DUPLICATE_MSB_TO_ALL(goodPad);
goodPad = PORT_CT_DUPLICATE_MSB_TO_ALL(goodPad);
/* Set outPadSize to padSize or 0 */
*outPadSize = CT_SEL(goodPad, padSize, 0);
*outPadSize = PORT_CT_SEL(goodPad, padSize, 0);
/* Return OK if the pad is valid */
return CT_SEL(goodPad, CKR_OK, CKR_ENCRYPTED_DATA_INVALID);
return PORT_CT_SEL(goodPad, CKR_OK, CKR_ENCRYPTED_DATA_INVALID);
}
void
......
......@@ -12292,41 +12292,26 @@ loser : {
return SECFailure;
}
/* These macros return the given value with the MSB copied to all the other
* bits. They use the fact that arithmetic shift shifts-in the sign bit.
* However, this is not ensured by the C standard so you may need to replace
* them with something else for odd compilers. */
#define DUPLICATE_MSB_TO_ALL(x) ((unsigned)((int)(x) >> (sizeof(int) * 8 - 1)))
#define DUPLICATE_MSB_TO_ALL_8(x) ((unsigned char)(DUPLICATE_MSB_TO_ALL(x)))
/* SECStatusToMask returns, in constant time, a mask value of all ones if
* rv == SECSuccess. Otherwise it returns zero. */
static unsigned int
SECStatusToMask(SECStatus rv)
{
unsigned int good;
/* rv ^ SECSuccess is zero iff rv == SECSuccess. Subtracting one results
* in the MSB being set to one iff it was zero before. */
good = rv ^ SECSuccess;
good--;
return DUPLICATE_MSB_TO_ALL(good);
return PORT_CT_EQ(rv, SECSuccess);
}
/* ssl_ConstantTimeGE returns 0xff if a>=b and 0x00 otherwise. */
/* ssl_ConstantTimeGE returns 0xffffffff if a>=b and 0x00 otherwise. */
static unsigned char
ssl_ConstantTimeGE(unsigned int a, unsigned int b)
{
a -= b;
return DUPLICATE_MSB_TO_ALL(~a);
return PORT_CT_GE(a, b);
}
/* ssl_ConstantTimeEQ8 returns 0xff if a==b and 0x00 otherwise. */
/* ssl_ConstantTimeEQ returns 0xffffffff if a==b and 0x00 otherwise. */
static unsigned char
ssl_ConstantTimeEQ8(unsigned char a, unsigned char b)
ssl_ConstantTimeEQ(unsigned char a, unsigned char b)
{
unsigned int c = a ^ b;
c--;
return DUPLICATE_MSB_TO_ALL_8(c);
return PORT_CT_EQ(a, b);
}
/* ssl_constantTimeSelect return a if mask is 0xFF and b if mask is 0x00 */
......@@ -12341,7 +12326,7 @@ ssl_RemoveSSLv3CBCPadding(sslBuffer *plaintext,
unsigned int blockSize,
unsigned int macSize)
{
unsigned int paddingLength, good, t;
unsigned int paddingLength, good;
const unsigned int overhead = 1 /* padding length byte */ + macSize;
/* These lengths are all public so we can test them in non-constant
......@@ -12352,13 +12337,9 @@ ssl_RemoveSSLv3CBCPadding(sslBuffer *plaintext,
paddingLength = plaintext->buf[plaintext->len - 1];
/* SSLv3 padding bytes are random and cannot be checked. */
t = plaintext->len;
t -= paddingLength + overhead;
/* If len >= paddingLength+overhead then the MSB of t is zero. */
good = DUPLICATE_MSB_TO_ALL(~t);
good = PORT_CT_GE(plaintext->len, paddingLength + overhead);
/* SSLv3 requires that the padding is minimal. */
t = blockSize - (paddingLength + 1);
good &= DUPLICATE_MSB_TO_ALL(~t);
good &= PORT_CT_GE(blockSize, paddingLength + 1);
plaintext->len -= good & (paddingLength + 1);
return (good & SECSuccess) | (~good & SECFailure);
}
......@@ -12366,7 +12347,7 @@ ssl_RemoveSSLv3CBCPadding(sslBuffer *plaintext,
SECStatus
ssl_RemoveTLSCBCPadding(sslBuffer *plaintext, unsigned int macSize)
{
unsigned int paddingLength, good, t, toCheck, i;
unsigned int paddingLength, good, toCheck, i;
const unsigned int overhead = 1 /* padding length byte */ + macSize;
/* These lengths are all public so we can test them in non-constant
......@@ -12376,10 +12357,7 @@ ssl_RemoveTLSCBCPadding(sslBuffer *plaintext, unsigned int macSize)
}
paddingLength = plaintext->buf[plaintext->len - 1];
t = plaintext->len;
t -= paddingLength + overhead;
/* If len >= paddingLength+overhead then the MSB of t is zero. */
good = DUPLICATE_MSB_TO_ALL(~t);
good = PORT_CT_GE(plaintext->len, paddingLength + overhead);
/* The padding consists of a length byte at the end of the record and then
* that many bytes of padding, all with the same value as the length byte.
......@@ -12396,10 +12374,9 @@ ssl_RemoveTLSCBCPadding(sslBuffer *plaintext, unsigned int macSize)
}
for (i = 0; i < toCheck; i++) {
t = paddingLength - i;
/* If i <= paddingLength then the MSB of t is zero and mask is
* 0xff. Otherwise, mask is 0. */
unsigned char mask = DUPLICATE_MSB_TO_ALL(~t);
unsigned char mask = PORT_CT_LE(i, paddingLength);
unsigned char b = plaintext->buf[plaintext->len - 1 - i];
/* The final |paddingLength+1| bytes should all have the value
* |paddingLength|. Therefore the XOR should be zero. */
......@@ -12414,7 +12391,7 @@ ssl_RemoveTLSCBCPadding(sslBuffer *plaintext, unsigned int macSize)
good &= good >> 2;
good &= good >> 1;
good <<= sizeof(good) * 8 - 1;
good = DUPLICATE_MSB_TO_ALL(good);
good = PORT_CT_DUPLICATE_MSB_TO_ALL(good);
plaintext->len -= good & (paddingLength + 1);
return (good & SECSuccess) | (~good & SECFailure);
......@@ -12507,7 +12484,7 @@ ssl_CBCExtractMAC(sslBuffer *plaintext,
0, rotateOffset);
for (i = 0; i < macSize; i++) {
for (j = 0; j < macSize; j++) {
out[j] |= rotatedMac[i] & ssl_ConstantTimeEQ8(j, rotateOffset);
out[j] |= rotatedMac[i] & ssl_ConstantTimeEQ(j, rotateOffset);
}
rotateOffset++;
rotateOffset = ssl_constantTimeSelect(ssl_ConstantTimeGE(rotateOffset, macSize),
......
......@@ -302,4 +302,69 @@ PORT_LoadLibraryFromOrigin(const char *existingShLibName,
SEC_END_PROTOS
/*
* Constant time macros
*/
/* These macros use the fact that arithmetic shift shifts-in the sign bit.
* However, this is not ensured by the C standard so you may need to replace
* them with something else for odd compilers. These macros work for object
* sizes up to 32 bits. The inequalities will produce incorrect results if
* abs(a-b) >= PR_UINT32_MAX/2. This can be a voided if unsigned values stay
* within the range 0-PRUINT32_MAX/2 and signed values stay within the range
* -PRINT32_MAX/2-PRINT32_MAX/2. If these are insufficient, we can fix
* this by either expanding the PORT_CT_DUPLICATE_MSB_TO_ALL to PRUint64
* or by creating the following new macros for inequality:
*
* PORT_CT_OVERFLOW prevents the overflow condition by handling the case
* where the high bits in a and b are different specially. Basically if
* the high bit in a and b differs we can just
* copy the high bit of one of the parameters to determine the result as
* follows:
* GxU if a has the high bit on, a>b, so d=a
* LxU if b has the high bit on, a<b, so d=b
* GxS if b has the high bit on, it's negative a>b so d=b
* LxS if a has the high bit on, it's negative a<b so d=a
* where PORT_CT_xxU() macros do unsigned compares and PORT_CT_xxS() do signed
* compares.
*
* #define PORT_CT_OVERFLOW(a,b,c,d) \
* PORT_CT_SEL(PORT_CT_DUPLICATE_MSB_TO_ALL((a)^(b)), \