summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwtc%google.com <devnull@localhost>2013-02-05 18:10:46 +0000
committerwtc%google.com <devnull@localhost>2013-02-05 18:10:46 +0000
commitacd57f58f4f76d551ba25e6ad95f823c9c9fb3a9 (patch)
tree21d136245493d951eb7b1f53f626ee3100359f2a
parent81fad6d3605d9159d6323e3ad74b9cf0f87f15c3 (diff)
downloadnss-hg-acd57f58f4f76d551ba25e6ad95f823c9c9fb3a9.tar.gz
Bug 822365: Make CBC decoding constant time. This patch makes the decoding
of SSLv3 and TLS CBC records constant time. Without this, a timing side channel can be used to build a padding oracle and mount Vaudenay's attack. The patch is contributed by Adam Langley <agl@chromium.org>. r=rrelyea,ryan.sleevi. Modified Files: lib/freebl/blapi.h lib/freebl/ldvector.c lib/freebl/loader.c lib/freebl/loader.h lib/freebl/manifest.mn lib/freebl/md5.c lib/freebl/rawhash.c lib/freebl/sha512.c lib/freebl/sha_fast.c lib/freebl/sha_fast.h lib/nss/nss.def lib/pk11wrap/pk11obj.c lib/pk11wrap/pk11pub.h lib/softoken/manifest.mn lib/softoken/pkcs11.c lib/softoken/pkcs11c.c lib/softoken/pkcs11i.h lib/ssl/ssl3con.c lib/util/hasht.h lib/util/pkcs11n.h Added Files: lib/freebl/hmacct.c lib/freebl/hmacct.h lib/softoken/sftkhmac.c
-rw-r--r--security/nss/lib/freebl/blapi.h32
-rw-r--r--security/nss/lib/freebl/hmacct.c317
-rw-r--r--security/nss/lib/freebl/hmacct.h38
-rw-r--r--security/nss/lib/freebl/ldvector.c8
-rw-r--r--security/nss/lib/freebl/loader.c44
-rw-r--r--security/nss/lib/freebl/loader.h32
-rw-r--r--security/nss/lib/freebl/manifest.mn2
-rw-r--r--security/nss/lib/freebl/md5.c26
-rw-r--r--security/nss/lib/freebl/rawhash.c30
-rw-r--r--security/nss/lib/freebl/sha512.c67
-rw-r--r--security/nss/lib/freebl/sha_fast.c16
-rw-r--r--security/nss/lib/freebl/sha_fast.h12
-rw-r--r--security/nss/lib/nss/nss.def6
-rw-r--r--security/nss/lib/pk11wrap/pk11obj.c45
-rw-r--r--security/nss/lib/pk11wrap/pk11pub.h2
-rw-r--r--security/nss/lib/softoken/manifest.mn1
-rw-r--r--security/nss/lib/softoken/pkcs11.c5
-rw-r--r--security/nss/lib/softoken/pkcs11c.c89
-rw-r--r--security/nss/lib/softoken/pkcs11i.h22
-rw-r--r--security/nss/lib/softoken/sftkhmac.c175
-rw-r--r--security/nss/lib/ssl/ssl3con.c441
-rw-r--r--security/nss/lib/util/hasht.h1
-rw-r--r--security/nss/lib/util/pkcs11n.h10
23 files changed, 1332 insertions, 89 deletions
diff --git a/security/nss/lib/freebl/blapi.h b/security/nss/lib/freebl/blapi.h
index a6e999676..06dd0dfb1 100644
--- a/security/nss/lib/freebl/blapi.h
+++ b/security/nss/lib/freebl/blapi.h
@@ -875,6 +875,18 @@ extern void MD5_End(MD5Context *cx, unsigned char *digest,
unsigned int *digestLen, unsigned int maxDigestLen);
/*
+** Export the raw state of the MD5 hash without appending the standard padding
+** and length bytes. Produce the digested results in "digest"
+** "cx" the context
+** "digest" where the 16 bytes of digest data are stored
+** "digestLen" where the digest length (16) is stored (optional)
+** "maxDigestLen" the maximum amount of data that can ever be
+** stored in "digest"
+*/
+extern void MD5_EndRaw(MD5Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
+
+/*
* Return the the size of a buffer needed to flatten the MD5 Context into
* "cx" the context
* returns size;
@@ -1031,6 +1043,18 @@ extern void SHA1_End(SHA1Context *cx, unsigned char *digest,
unsigned int *digestLen, unsigned int maxDigestLen);
/*
+** Export the current state of the SHA-1 hash without appending the standard
+** padding and length. Produce the digested results in "digest"
+** "cx" the context
+** "digest" where the 16 bytes of digest data are stored
+** "digestLen" where the digest length (20) is stored (optional)
+** "maxDigestLen" the maximum amount of data that can ever be
+** stored in "digest"
+*/
+extern void SHA1_EndRaw(SHA1Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
+
+/*
** trace the intermediate state info of the SHA1 hash.
*/
extern void SHA1_TraceState(SHA1Context *cx);
@@ -1068,6 +1092,8 @@ extern void SHA224_Update(SHA224Context *cx, const unsigned char *input,
unsigned int inputLen);
extern void SHA224_End(SHA224Context *cx, unsigned char *digest,
unsigned int *digestLen, unsigned int maxDigestLen);
+extern void SHA224_EndRaw(SHA224Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
extern SECStatus SHA224_HashBuf(unsigned char *dest, const unsigned char *src,
uint32 src_length);
extern SECStatus SHA224_Hash(unsigned char *dest, const char *src);
@@ -1086,6 +1112,8 @@ extern void SHA256_Update(SHA256Context *cx, const unsigned char *input,
unsigned int inputLen);
extern void SHA256_End(SHA256Context *cx, unsigned char *digest,
unsigned int *digestLen, unsigned int maxDigestLen);
+extern void SHA256_EndRaw(SHA256Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
extern SECStatus SHA256_HashBuf(unsigned char *dest, const unsigned char *src,
uint32 src_length);
extern SECStatus SHA256_Hash(unsigned char *dest, const char *src);
@@ -1102,6 +1130,8 @@ extern void SHA512_DestroyContext(SHA512Context *cx, PRBool freeit);
extern void SHA512_Begin(SHA512Context *cx);
extern void SHA512_Update(SHA512Context *cx, const unsigned char *input,
unsigned int inputLen);
+extern void SHA512_EndRaw(SHA512Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
extern void SHA512_End(SHA512Context *cx, unsigned char *digest,
unsigned int *digestLen, unsigned int maxDigestLen);
extern SECStatus SHA512_HashBuf(unsigned char *dest, const unsigned char *src,
@@ -1122,6 +1152,8 @@ extern void SHA384_Update(SHA384Context *cx, const unsigned char *input,
unsigned int inputLen);
extern void SHA384_End(SHA384Context *cx, unsigned char *digest,
unsigned int *digestLen, unsigned int maxDigestLen);
+extern void SHA384_EndRaw(SHA384Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
extern SECStatus SHA384_HashBuf(unsigned char *dest, const unsigned char *src,
uint32 src_length);
extern SECStatus SHA384_Hash(unsigned char *dest, const char *src);
diff --git a/security/nss/lib/freebl/hmacct.c b/security/nss/lib/freebl/hmacct.c
new file mode 100644
index 000000000..93390eb5e
--- /dev/null
+++ b/security/nss/lib/freebl/hmacct.c
@@ -0,0 +1,317 @@
+/* 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/. */
+
+#ifdef FREEBL_NO_DEPEND
+#include "stubs.h"
+#endif
+
+#include "secport.h"
+#include "hasht.h"
+#include "blapit.h"
+#include "hmacct.h"
+#include "secerr.h"
+
+/* MAX_HASH_BIT_COUNT_BYTES is the maximum number of bytes in the hash's length
+ * 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);
+}
+
+/* constantTimeEQ8 returns 0xff if a==b and 0x00 otherwise. */
+static unsigned char constantTimeEQ8(unsigned char a, unsigned char b) {
+ unsigned int c = a ^ b;
+ c--;
+ return DUPLICATE_MSB_TO_ALL_8(c);
+}
+
+/* mac performs a constant time SSLv3/TLS MAC of |dataLen| bytes of |data|,
+ * where |dataLen| includes both the authenticated bytes and the MAC tag from
+ * the sender. |dataLen| must be >= the length of the MAC tag.
+ *
+ * |dataTotalLen| is >= |dataLen| and also accounts for any padding bytes
+ * that may follow the sender's MAC. (Only a single block of padding my follow
+ * in SSLv3, or up to 255 bytes in TLS.)
+ *
+ * Since the results of decryption are secret information (otherwise a
+ * padding-oracle is created), this function is constant-time with respect to
+ * |dataLen|.
+ *
+ * |header| contains either the 13-byte TLS header (containing the sequence
+ * number, record type etc), or it contains the SSLv3 header with the SSLv3
+ * padding bytes etc. */
+static SECStatus mac(
+ unsigned char *mdOut,
+ unsigned int *mdOutLen,
+ unsigned int mdOutMax,
+ const SECHashObject *hashObj,
+ const unsigned char *macSecret,
+ unsigned int macSecretLen,
+ const unsigned char *header,
+ unsigned int headerLen,
+ const unsigned char *data,
+ unsigned int dataLen,
+ unsigned int dataTotalLen,
+ unsigned char isSSLv3) {
+
+ void *mdState = hashObj->create();
+ const unsigned int mdSize = hashObj->length;
+ const unsigned int mdBlockSize = hashObj->blocklength;
+ /* mdLengthSize is the number of bytes in the length field that terminates
+ * the hash.
+ *
+ * This assumes that hash functions with a 64 byte block size use a 64-bit
+ * length, and otherwise they use a 128-bit length. This is true of {MD5,
+ * SHA*} (which are all of the hash functions specified for use with TLS
+ * today). */
+ const unsigned int mdLengthSize = mdBlockSize == 64 ? 8 : 16;
+
+ const unsigned int sslv3PadLen = hashObj->type == HASH_AlgMD5 ? 48 : 40;
+
+ /* varianceBlocks is the number of blocks of the hash that we have to
+ * calculate in constant time because they could be altered by the
+ * padding value.
+ *
+ * In SSLv3, the padding must be minimal so the end of the plaintext
+ * varies by, at most, 15+20 = 35 bytes. (We conservatively assume that
+ * the MAC size varies from 0..20 bytes.) In case the 9 bytes of hash
+ * termination (0x80 + 64-bit length) don't fit in the final block, we
+ * say that the final two blocks can vary based on the padding.
+ *
+ * TLSv1 has MACs up to 48 bytes long (SHA-384) and the padding is not
+ * required to be minimal. Therefore we say that the final six blocks
+ * can vary based on the padding.
+ *
+ * Later in the function, if the message is short and there obviously
+ * cannot be this many blocks then varianceBlocks can be reduced. */
+ unsigned int varianceBlocks = isSSLv3 ? 2 : 6;
+ /* From now on we're dealing with the MAC, which conceptually has 13
+ * bytes of `header' before the start of the data (TLS) or 71/75 bytes
+ * (SSLv3) */
+ const unsigned int len = dataTotalLen + headerLen;
+ /* maxMACBytes contains the maximum bytes of bytes in the MAC, including
+ * |header|, assuming that there's no padding. */
+ const unsigned int maxMACBytes = len - mdSize - 1;
+ /* numBlocks is the maximum number of hash blocks. */
+ const unsigned int numBlocks =
+ (maxMACBytes + 1 + mdLengthSize + mdBlockSize - 1) / mdBlockSize;
+ /* macEndOffset is the index just past the end of the data to be
+ * MACed. */
+ const unsigned int macEndOffset = dataLen + headerLen - mdSize;
+ /* c is the index of the 0x80 byte in the final hash block that
+ * contains application data. */
+ const unsigned int c = macEndOffset % mdBlockSize;
+ /* indexA is the hash block number that contains the 0x80 terminating
+ * value. */
+ const unsigned int indexA = macEndOffset / mdBlockSize;
+ /* indexB is the hash block number that contains the 64-bit hash
+ * length, in bits. */
+ const unsigned int indexB = (macEndOffset + mdLengthSize) / mdBlockSize;
+ /* bits is the hash-length in bits. It includes the additional hash
+ * block for the masked HMAC key, or whole of |header| in the case of
+ * SSLv3. */
+ unsigned int bits;
+ /* In order to calculate the MAC in constant time we have to handle
+ * the final blocks specially because the padding value could cause the
+ * end to appear somewhere in the final |varianceBlocks| blocks and we
+ * can't leak where. However, |numStartingBlocks| worth of data can
+ * be hashed right away because no padding value can affect whether
+ * they are plaintext. */
+ unsigned int numStartingBlocks = 0;
+ /* k is the starting byte offset into the conceptual header||data where
+ * we start processing. */
+ unsigned int k = 0;
+ unsigned char lengthBytes[MAX_HASH_BIT_COUNT_BYTES];
+ /* hmacPad is the masked HMAC key. */
+ unsigned char hmacPad[HASH_BLOCK_LENGTH_MAX];
+ unsigned char firstBlock[HASH_BLOCK_LENGTH_MAX];
+ unsigned char macOut[HASH_LENGTH_MAX];
+ unsigned i, j;
+
+ /* For SSLv3, if we're going to have any starting blocks then we need
+ * at least two because the header is larger than a single block. */
+ if (numBlocks > varianceBlocks + (isSSLv3 ? 1 : 0)) {
+ numStartingBlocks = numBlocks - varianceBlocks;
+ k = mdBlockSize*numStartingBlocks;
+ }
+
+ bits = 8*macEndOffset;
+ hashObj->begin(mdState);
+ if (!isSSLv3) {
+ /* Compute the initial HMAC block. For SSLv3, the padding and
+ * secret bytes are included in |header| because they take more
+ * than a single block. */
+ bits += 8*mdBlockSize;
+ memset(hmacPad, 0, mdBlockSize);
+ PORT_Assert(macSecretLen <= sizeof(hmacPad));
+ memcpy(hmacPad, macSecret, macSecretLen);
+ for (i = 0; i < mdBlockSize; i++)
+ hmacPad[i] ^= 0x36;
+ hashObj->update(mdState, hmacPad, mdBlockSize);
+ }
+
+ j = 0;
+ memset(lengthBytes, 0, sizeof(lengthBytes));
+ if (mdLengthSize == 16) {
+ j = 8;
+ }
+ for (i = 0; i < 4; i++) {
+ lengthBytes[4+i+j] = bits >> (8*(7-i));
+ }
+
+ if (k > 0) {
+ if (isSSLv3) {
+ /* The SSLv3 header is larger than a single block.
+ * overhang is the number of bytes beyond a single
+ * block that the header consumes: either 7 bytes
+ * (SHA1) or 11 bytes (MD5). */
+ const unsigned int overhang = headerLen-mdBlockSize;
+ hashObj->update(mdState, header, mdBlockSize);
+ memcpy(firstBlock, header + mdBlockSize, overhang);
+ memcpy(firstBlock + overhang, data, mdBlockSize-overhang);
+ hashObj->update(mdState, firstBlock, mdBlockSize);
+ for (i = 1; i < k/mdBlockSize - 1; i++) {
+ hashObj->update(mdState, data + mdBlockSize*i - overhang, mdBlockSize);
+ }
+ } else {
+ /* k is a multiple of mdBlockSize. */
+ memcpy(firstBlock, header, 13);
+ memcpy(firstBlock+13, data, mdBlockSize-13);
+ hashObj->update(mdState, firstBlock, mdBlockSize);
+ for (i = 1; i < k/mdBlockSize; i++) {
+ hashObj->update(mdState, data + mdBlockSize*i - 13, mdBlockSize);
+ }
+ }
+ }
+
+ memset(macOut, 0, sizeof(macOut));
+
+ /* We now process the final hash blocks. For each block, we construct
+ * it in constant time. If i == indexA then we'll include the 0x80
+ * bytes and zero pad etc. For each block we selectively copy it, in
+ * 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);
+ for (j = 0; j < mdBlockSize; j++) {
+ unsigned char b = 0;
+ if (k < headerLen) {
+ b = header[k];
+ } else if (k < dataTotalLen + headerLen) {
+ b = data[k-headerLen];
+ }
+ k++;
+
+ unsigned char isPastC = isBlockA & constantTimeGE(j, c);
+ unsigned char isPastCPlus1 = isBlockA & constantTimeGE(j, c+1);
+ /* If this is the block containing the end of the
+ * application data, and we are at the offset for the
+ * 0x80 value, then overwrite b with 0x80. */
+ b = (b&~isPastC) | (0x80&isPastC);
+ /* If this the the block containing the end of the
+ * application data and we're past the 0x80 value then
+ * just write zero. */
+ b = b&~isPastCPlus1;
+ /* If this is indexB (the final block), but not
+ * indexA (the end of the data), then the 64-bit
+ * length didn't fit into indexA and we're having to
+ * add an extra block of zeros. */
+ b &= ~isBlockB | isBlockA;
+
+ /* The final bytes of one of the blocks contains the length. */
+ if (j >= mdBlockSize - mdLengthSize) {
+ /* If this is indexB, write a length byte. */
+ b = (b&~isBlockB) | (isBlockB&lengthBytes[j-(mdBlockSize-mdLengthSize)]);
+ }
+ block[j] = b;
+ }
+
+ hashObj->update(mdState, block, mdBlockSize);
+ hashObj->end_raw(mdState, block, NULL, mdSize);
+ /* If this is indexB, copy the hash value to |macOut|. */
+ for (j = 0; j < mdSize; j++) {
+ macOut[j] |= block[j]&isBlockB;
+ }
+ }
+
+ hashObj->begin(mdState);
+
+ if (isSSLv3) {
+ /* We repurpose |hmacPad| to contain the SSLv3 pad2 block. */
+ for (i = 0; i < sslv3PadLen; i++)
+ hmacPad[i] = 0x5c;
+
+ hashObj->update(mdState, macSecret, macSecretLen);
+ hashObj->update(mdState, hmacPad, sslv3PadLen);
+ hashObj->update(mdState, macOut, mdSize);
+ } else {
+ /* Complete the HMAC in the standard manner. */
+ for (i = 0; i < mdBlockSize; i++)
+ hmacPad[i] ^= 0x6a;
+
+ hashObj->update(mdState, hmacPad, mdBlockSize);
+ hashObj->update(mdState, macOut, mdSize);
+ }
+
+ hashObj->end(mdState, mdOut, mdOutLen, mdOutMax);
+ hashObj->destroy(mdState, PR_TRUE);
+
+ return SECSuccess;
+}
+
+SECStatus HMAC_ConstantTime(
+ unsigned char *result,
+ unsigned int *resultLen,
+ unsigned int maxResultLen,
+ const SECHashObject *hashObj,
+ const unsigned char *secret,
+ unsigned int secretLen,
+ const unsigned char *header,
+ unsigned int headerLen,
+ const unsigned char *body,
+ unsigned int bodyLen,
+ unsigned int bodyTotalLen) {
+ if (hashObj->end_raw == NULL)
+ return SECFailure;
+ return mac(result, resultLen, maxResultLen, hashObj, secret, secretLen,
+ header, headerLen, body, bodyLen, bodyTotalLen,
+ 0 /* not SSLv3 */);
+}
+
+SECStatus SSLv3_MAC_ConstantTime(
+ unsigned char *result,
+ unsigned int *resultLen,
+ unsigned int maxResultLen,
+ const SECHashObject *hashObj,
+ const unsigned char *secret,
+ unsigned int secretLen,
+ const unsigned char *header,
+ unsigned int headerLen,
+ const unsigned char *body,
+ unsigned int bodyLen,
+ unsigned int bodyTotalLen) {
+ if (hashObj->end_raw == NULL)
+ return SECFailure;
+ return mac(result, resultLen, maxResultLen, hashObj, secret, secretLen,
+ header, headerLen, body, bodyLen, bodyTotalLen,
+ 1 /* SSLv3 */);
+}
+
diff --git a/security/nss/lib/freebl/hmacct.h b/security/nss/lib/freebl/hmacct.h
new file mode 100644
index 000000000..8b0e91c8a
--- /dev/null
+++ b/security/nss/lib/freebl/hmacct.h
@@ -0,0 +1,38 @@
+/* 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/. */
+
+#ifndef _ALGHMACCT_H_
+#define _ALGHMACCT_H_
+
+SEC_BEGIN_PROTOS
+
+extern SECStatus HMAC_ConstantTime(
+ unsigned char *result,
+ unsigned int *resultLen,
+ unsigned int maxResultLen,
+ const SECHashObject *hashObj,
+ const unsigned char *secret,
+ unsigned int secretLen,
+ const unsigned char *header,
+ unsigned int headerLen,
+ const unsigned char *body,
+ unsigned int bodyLen,
+ unsigned int bodyTotalLen);
+
+extern SECStatus SSLv3_MAC_ConstantTime(
+ unsigned char *result,
+ unsigned int *resultLen,
+ unsigned int maxResultLen,
+ const SECHashObject *hashObj,
+ const unsigned char *secret,
+ unsigned int secretLen,
+ const unsigned char *header,
+ unsigned int headerLen,
+ const unsigned char *body,
+ unsigned int bodyLen,
+ unsigned int bodyTotalLen);
+
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/freebl/ldvector.c b/security/nss/lib/freebl/ldvector.c
index 1b51afb22..186ae2f5e 100644
--- a/security/nss/lib/freebl/ldvector.c
+++ b/security/nss/lib/freebl/ldvector.c
@@ -12,6 +12,7 @@ extern int FREEBL_InitStubs(void);
#include "loader.h"
#include "alghmac.h"
+#include "hmacct.h"
static const struct FREEBLVectorStr vector =
@@ -258,9 +259,14 @@ static const struct FREEBLVectorStr vector =
/* End of Version 3.013 */
PQG_ParamGenV2,
- PRNGTEST_RunHealthTests
+ PRNGTEST_RunHealthTests,
/* End of Version 3.014 */
+
+ HMAC_ConstantTime,
+ SSLv3_MAC_ConstantTime
+
+ /* End of Version 3.015 */
};
const FREEBLVector *
diff --git a/security/nss/lib/freebl/loader.c b/security/nss/lib/freebl/loader.c
index 71daabd1c..ae3025199 100644
--- a/security/nss/lib/freebl/loader.c
+++ b/security/nss/lib/freebl/loader.c
@@ -1858,3 +1858,47 @@ PRNGTEST_RunHealthTests(void)
return SECFailure;
return vector->p_PRNGTEST_RunHealthTests();
}
+
+SECStatus SSLv3_MAC_ConstantTime(
+ unsigned char *result,
+ unsigned int *resultLen,
+ unsigned int maxResultLen,
+ const SECHashObject *hashObj,
+ const unsigned char *secret,
+ unsigned int secretLen,
+ const unsigned char *header,
+ unsigned int headerLen,
+ const unsigned char *body,
+ unsigned int bodyLen,
+ unsigned int bodyTotalLen) {
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
+ return SECFailure;
+ return (vector->p_SSLv3_MAC_ConstantTime)(
+ result, resultLen, maxResultLen,
+ hashObj,
+ secret, secretLen,
+ header, headerLen,
+ body, bodyLen, bodyTotalLen);
+}
+
+SECStatus HMAC_ConstantTime(
+ unsigned char *result,
+ unsigned int *resultLen,
+ unsigned int maxResultLen,
+ const SECHashObject *hashObj,
+ const unsigned char *secret,
+ unsigned int secretLen,
+ const unsigned char *header,
+ unsigned int headerLen,
+ const unsigned char *body,
+ unsigned int bodyLen,
+ unsigned int bodyTotalLen) {
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
+ return SECFailure;
+ return (vector->p_HMAC_ConstantTime)(
+ result, resultLen, maxResultLen,
+ hashObj,
+ secret, secretLen,
+ header, headerLen,
+ body, bodyLen, bodyTotalLen);
+}
diff --git a/security/nss/lib/freebl/loader.h b/security/nss/lib/freebl/loader.h
index d5d80b7b4..4ea2afda7 100644
--- a/security/nss/lib/freebl/loader.h
+++ b/security/nss/lib/freebl/loader.h
@@ -11,7 +11,7 @@
#include "blapi.h"
-#define FREEBL_VERSION 0x030E
+#define FREEBL_VERSION 0x030F
struct FREEBLVectorStr {
@@ -569,7 +569,35 @@ struct FREEBLVectorStr {
SECStatus (*p_PRNGTEST_RunHealthTests)(void);
/* Version 3.014 came to here */
-};
+
+ SECStatus (* p_HMAC_ConstantTime)(
+ unsigned char *result,
+ unsigned int *resultLen,
+ unsigned int maxResultLen,
+ const SECHashObject *hashObj,
+ const unsigned char *secret,
+ unsigned int secretLen,
+ const unsigned char *header,
+ unsigned int headerLen,
+ const unsigned char *body,
+ unsigned int bodyLen,
+ unsigned int bodyTotalLen);
+
+ SECStatus (* p_SSLv3_MAC_ConstantTime)(
+ unsigned char *result,
+ unsigned int *resultLen,
+ unsigned int maxResultLen,
+ const SECHashObject *hashObj,
+ const unsigned char *secret,
+ unsigned int secretLen,
+ const unsigned char *header,
+ unsigned int headerLen,
+ const unsigned char *body,
+ unsigned int bodyLen,
+ unsigned int bodyTotalLen);
+
+ /* Version 3.015 came to here */
+ };
typedef struct FREEBLVectorStr FREEBLVector;
diff --git a/security/nss/lib/freebl/manifest.mn b/security/nss/lib/freebl/manifest.mn
index ea29a8eaa..f7f98c15b 100644
--- a/security/nss/lib/freebl/manifest.mn
+++ b/security/nss/lib/freebl/manifest.mn
@@ -56,6 +56,7 @@ EXPORTS = \
PRIVATE_EXPORTS = \
alghmac.h \
blapi.h \
+ hmacct.h \
secmpi.h \
secrng.h \
ec.h \
@@ -102,6 +103,7 @@ CSRCS = \
cts.c \
ctr.c \
gcm.c \
+ hmacct.c \
rijndael.c \
aeskeywrap.c \
camellia.c \
diff --git a/security/nss/lib/freebl/md5.c b/security/nss/lib/freebl/md5.c
index 705a85735..919ef7fdb 100644
--- a/security/nss/lib/freebl/md5.c
+++ b/security/nss/lib/freebl/md5.c
@@ -523,7 +523,8 @@ MD5_End(MD5Context *cx, unsigned char *digest,
md5_compress(cx, cx->u.w);
/* Copy the resulting values out of the chain variables into return buf. */
- *digestLen = MD5_HASH_LEN;
+ if (digestLen)
+ *digestLen = MD5_HASH_LEN;
#ifndef IS_LITTLE_ENDIAN
cx->cv[0] = lendian(cx->cv[0]);
cx->cv[1] = lendian(cx->cv[1]);
@@ -533,6 +534,29 @@ MD5_End(MD5Context *cx, unsigned char *digest,
memcpy(digest, cx->cv, MD5_HASH_LEN);
}
+void
+MD5_EndRaw(MD5Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+ PRUint32 cv[4];
+
+ if (maxDigestLen < MD5_HASH_LEN) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return;
+ }
+
+ memcpy(cv, cx->cv, sizeof(cv));
+#ifndef IS_LITTLE_ENDIAN
+ cv[0] = lendian(cv[0]);
+ cv[1] = lendian(cv[1]);
+ cv[2] = lendian(cv[2]);
+ cv[3] = lendian(cv[3]);
+#endif
+ memcpy(digest, cv, MD5_HASH_LEN);
+ if (digestLen)
+ *digestLen = MD5_HASH_LEN;
+}
+
unsigned int
MD5_FlattenSize(MD5Context *cx)
{
diff --git a/security/nss/lib/freebl/rawhash.c b/security/nss/lib/freebl/rawhash.c
index 253b0fe73..7962b1fff 100644
--- a/security/nss/lib/freebl/rawhash.c
+++ b/security/nss/lib/freebl/rawhash.c
@@ -58,7 +58,9 @@ const SECHashObject SECRawHashObjects[] = {
(void (*)(void *, unsigned char *, unsigned int *,
unsigned int)) null_hash_end,
0,
- HASH_AlgNULL
+ HASH_AlgNULL,
+ (void (*)(void *, unsigned char *, unsigned int *,
+ unsigned int)) null_hash_end
},
{ MD2_LENGTH,
(void * (*)(void)) MD2_NewContext,
@@ -68,7 +70,8 @@ const SECHashObject SECRawHashObjects[] = {
(void (*)(void *, const unsigned char *, unsigned int)) MD2_Update,
(void (*)(void *, unsigned char *, unsigned int *, unsigned int)) MD2_End,
MD2_BLOCK_LENGTH,
- HASH_AlgMD2
+ HASH_AlgMD2,
+ NULL /* end_raw */
},
{ MD5_LENGTH,
(void * (*)(void)) MD5_NewContext,
@@ -78,7 +81,8 @@ const SECHashObject SECRawHashObjects[] = {
(void (*)(void *, const unsigned char *, unsigned int)) MD5_Update,
(void (*)(void *, unsigned char *, unsigned int *, unsigned int)) MD5_End,
MD5_BLOCK_LENGTH,
- HASH_AlgMD5
+ HASH_AlgMD5,
+ (void (*)(void *, unsigned char *, unsigned int *, unsigned int)) MD5_EndRaw
},
{ SHA1_LENGTH,
(void * (*)(void)) SHA1_NewContext,
@@ -88,7 +92,9 @@ const SECHashObject SECRawHashObjects[] = {
(void (*)(void *, const unsigned char *, unsigned int)) SHA1_Update,
(void (*)(void *, unsigned char *, unsigned int *, unsigned int)) SHA1_End,
SHA1_BLOCK_LENGTH,
- HASH_AlgSHA1
+ HASH_AlgSHA1,
+ (void (*)(void *, unsigned char *, unsigned int *, unsigned int))
+ SHA1_EndRaw
},
{ SHA256_LENGTH,
(void * (*)(void)) SHA256_NewContext,
@@ -99,7 +105,9 @@ const SECHashObject SECRawHashObjects[] = {
(void (*)(void *, unsigned char *, unsigned int *,
unsigned int)) SHA256_End,
SHA256_BLOCK_LENGTH,
- HASH_AlgSHA256
+ HASH_AlgSHA256,
+ (void (*)(void *, unsigned char *, unsigned int *,
+ unsigned int)) SHA256_EndRaw
},
{ SHA384_LENGTH,
(void * (*)(void)) SHA384_NewContext,
@@ -110,7 +118,9 @@ const SECHashObject SECRawHashObjects[] = {
(void (*)(void *, unsigned char *, unsigned int *,
unsigned int)) SHA384_End,
SHA384_BLOCK_LENGTH,
- HASH_AlgSHA384
+ HASH_AlgSHA384,
+ (void (*)(void *, unsigned char *, unsigned int *,
+ unsigned int)) SHA384_EndRaw
},
{ SHA512_LENGTH,
(void * (*)(void)) SHA512_NewContext,
@@ -121,7 +131,9 @@ const SECHashObject SECRawHashObjects[] = {
(void (*)(void *, unsigned char *, unsigned int *,
unsigned int)) SHA512_End,
SHA512_BLOCK_LENGTH,
- HASH_AlgSHA512
+ HASH_AlgSHA512,
+ (void (*)(void *, unsigned char *, unsigned int *,
+ unsigned int)) SHA512_EndRaw
},
{ SHA224_LENGTH,
(void * (*)(void)) SHA224_NewContext,
@@ -132,7 +144,9 @@ const SECHashObject SECRawHashObjects[] = {
(void (*)(void *, unsigned char *, unsigned int *,
unsigned int)) SHA224_End,
SHA224_BLOCK_LENGTH,
- HASH_AlgSHA224
+ HASH_AlgSHA224,
+ (void (*)(void *, unsigned char *, unsigned int *,
+ unsigned int)) SHA224_EndRaw
},
};
diff --git a/security/nss/lib/freebl/sha512.c b/security/nss/lib/freebl/sha512.c
index 910241ad6..301656e54 100644
--- a/security/nss/lib/freebl/sha512.c
+++ b/security/nss/lib/freebl/sha512.c
@@ -462,6 +462,32 @@ SHA256_End(SHA256Context *ctx, unsigned char *digest,
*digestLen = padLen;
}
+void
+SHA256_EndRaw(SHA256Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+ PRUint32 h[8];
+ unsigned int len;
+
+ memcpy(&h, ctx->h, sizeof(h));
+
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP4(h[0]);
+ BYTESWAP4(h[1]);
+ BYTESWAP4(h[2]);
+ BYTESWAP4(h[3]);
+ BYTESWAP4(h[4]);
+ BYTESWAP4(h[5]);
+ BYTESWAP4(h[6]);
+ BYTESWAP4(h[7]);
+#endif
+
+ len = PR_MIN(SHA256_LENGTH, maxDigestLen);
+ memcpy(digest, h, len);
+ if (digestLen)
+ *digestLen = len;
+}
+
SECStatus
SHA256_HashBuf(unsigned char *dest, const unsigned char *src,
uint32 src_length)
@@ -556,6 +582,14 @@ SHA224_End(SHA256Context *ctx, unsigned char *digest,
SHA256_End(ctx, digest, digestLen, maxLen);
}
+void
+SHA224_EndRaw(SHA256Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+ unsigned int maxLen = SHA_MIN(maxDigestLen, SHA224_LENGTH);
+ SHA256_EndRaw(ctx, digest, digestLen, maxLen);
+}
+
SECStatus
SHA224_HashBuf(unsigned char *dest, const unsigned char *src,
uint32 src_length)
@@ -1228,6 +1262,31 @@ SHA512_End(SHA512Context *ctx, unsigned char *digest,
*digestLen = padLen;
}
+void
+SHA512_EndRaw(SHA512Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+ PRUint64 h[8];
+ unsigned int len;
+
+ memcpy(h, ctx->h, sizeof(h));
+
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP8(h[0]);
+ BYTESWAP8(h[1]);
+ BYTESWAP8(h[2]);
+ BYTESWAP8(h[3]);
+ BYTESWAP8(h[4]);
+ BYTESWAP8(h[5]);
+ BYTESWAP8(h[6]);
+ BYTESWAP8(h[7]);
+#endif
+ len = PR_MIN(SHA512_LENGTH, maxDigestLen);
+ memcpy(digest, h, len);
+ if (digestLen)
+ *digestLen = len;
+}
+
SECStatus
SHA512_HashBuf(unsigned char *dest, const unsigned char *src,
uint32 src_length)
@@ -1336,6 +1395,14 @@ SHA384_End(SHA384Context *ctx, unsigned char *digest,
SHA512_End(ctx, digest, digestLen, maxLen);
}
+void
+SHA384_EndRaw(SHA384Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+ unsigned int maxLen = SHA_MIN(maxDigestLen, SHA384_LENGTH);
+ SHA512_EndRaw(ctx, digest, digestLen, maxLen);
+}
+
SECStatus
SHA384_HashBuf(unsigned char *dest, const unsigned char *src,
uint32 src_length)
diff --git a/security/nss/lib/freebl/sha_fast.c b/security/nss/lib/freebl/sha_fast.c
index cc7895c4b..6044610ab 100644
--- a/security/nss/lib/freebl/sha_fast.c
+++ b/security/nss/lib/freebl/sha_fast.c
@@ -148,6 +148,7 @@ SHA1_End(SHA1Context *ctx, unsigned char *hashout,
{
register PRUint64 size;
register PRUint32 lenB;
+ PRUint32 tmpbuf[5];
static const unsigned char bulk_pad[64] = { 0x80,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -174,8 +175,21 @@ SHA1_End(SHA1Context *ctx, unsigned char *hashout,
* Output hash
*/
SHA_STORE_RESULT;
- *pDigestLen = SHA1_LENGTH;
+ if (pDigestLen) {
+ *pDigestLen = SHA1_LENGTH;
+ }
+}
+void
+SHA1_EndRaw(SHA1Context *ctx, unsigned char *hashout,
+ unsigned int *pDigestLen, unsigned int maxDigestLen)
+{
+ PRUint32 tmpbuf[5];
+ PORT_Assert (maxDigestLen >= SHA1_LENGTH);
+
+ SHA_STORE_RESULT;
+ if (pDigestLen)
+ *pDigestLen = SHA1_LENGTH;
}
#undef B
diff --git a/security/nss/lib/freebl/sha_fast.h b/security/nss/lib/freebl/sha_fast.h
index fb490b056..9d47aba42 100644
--- a/security/nss/lib/freebl/sha_fast.h
+++ b/security/nss/lib/freebl/sha_fast.h
@@ -147,12 +147,12 @@ static __inline__ PRUint32 swap4b(PRUint32 value)
SHA_STORE(3); \
SHA_STORE(4); \
} else { \
- ctx->u.w[0] = SHA_HTONL(ctx->H[0]); \
- ctx->u.w[1] = SHA_HTONL(ctx->H[1]); \
- ctx->u.w[2] = SHA_HTONL(ctx->H[2]); \
- ctx->u.w[3] = SHA_HTONL(ctx->H[3]); \
- ctx->u.w[4] = SHA_HTONL(ctx->H[4]); \
- memcpy(hashout, ctx->u.w, SHA1_LENGTH); \
+ tmpbuf[0] = SHA_HTONL(ctx->H[0]); \
+ tmpbuf[1] = SHA_HTONL(ctx->H[1]); \
+ tmpbuf[2] = SHA_HTONL(ctx->H[2]); \
+ tmpbuf[3] = SHA_HTONL(ctx->H[3]); \
+ tmpbuf[4] = SHA_HTONL(ctx->H[4]); \
+ memcpy(hashout, tmpbuf, SHA1_LENGTH); \
}
#else
diff --git a/security/nss/lib/nss/nss.def b/security/nss/lib/nss/nss.def
index aecd92870..7fbe2ba2a 100644
--- a/security/nss/lib/nss/nss.def
+++ b/security/nss/lib/nss/nss.def
@@ -1021,3 +1021,9 @@ CERT_CreateOCSPSingleResponseRevoked;
;+ local:
;+ *;
;+};
+;+NSS_3.14.2 { # NSS 3.14.2 release
+;+ global:
+PK11_SignWithSymKey;
+;+ local:
+;+ *;
+;+};
diff --git a/security/nss/lib/pk11wrap/pk11obj.c b/security/nss/lib/pk11wrap/pk11obj.c
index be701d217..4fa5dde98 100644
--- a/security/nss/lib/pk11wrap/pk11obj.c
+++ b/security/nss/lib/pk11wrap/pk11obj.c
@@ -778,6 +778,51 @@ PK11_Sign(SECKEYPrivateKey *key, SECItem *sig, const SECItem *hash)
}
/*
+ * sign data with a MAC key.
+ */
+SECStatus
+PK11_SignWithSymKey(PK11SymKey *symKey, CK_MECHANISM_TYPE mechanism,
+ SECItem *param, SECItem *sig, const SECItem *data)
+{
+ PK11SlotInfo *slot = symKey->slot;
+ CK_MECHANISM mech = {0, NULL, 0 };
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ PRBool haslock = PR_FALSE;
+ CK_ULONG len;
+ CK_RV crv;
+
+ mech.mechanism = mechanism;
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ }
+
+ session = pk11_GetNewSession(slot,&owner);
+ haslock = (!owner || !(slot->isThreadSafe));
+ if (haslock) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_SignInit(session,&mech,symKey->objectID);
+ if (crv != CKR_OK) {
+ if (haslock) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+
+ len = sig->len;
+ crv = PK11_GETTAB(slot)->C_Sign(session,data->data,
+ data->len, sig->data, &len);
+ if (haslock) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ sig->len = len;
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
* Now SSL 2.0 uses raw RSA stuff. These next to functions *must* use
* RSA keys, or they'll fail. We do the checks up front. If anyone comes
* up with a meaning for rawdecrypt for any other public key operation,
diff --git a/security/nss/lib/pk11wrap/pk11pub.h b/security/nss/lib/pk11wrap/pk11pub.h
index 314ccba40..f15d6da81 100644
--- a/security/nss/lib/pk11wrap/pk11pub.h
+++ b/security/nss/lib/pk11wrap/pk11pub.h
@@ -660,6 +660,8 @@ int PK11_SignatureLen(SECKEYPrivateKey *key);
PK11SlotInfo * PK11_GetSlotFromPrivateKey(SECKEYPrivateKey *key);
SECStatus PK11_Sign(SECKEYPrivateKey *key, SECItem *sig,
const SECItem *hash);
+SECStatus PK11_SignWithSymKey(PK11SymKey *symKey, CK_MECHANISM_TYPE mechanism,
+ SECItem *param, SECItem *sig, const SECItem *data);
SECStatus PK11_VerifyRecover(SECKEYPublicKey *key, const SECItem *sig,
SECItem *dsig, void * wincx);
SECStatus PK11_Verify(SECKEYPublicKey *key, const SECItem *sig,
diff --git a/security/nss/lib/softoken/manifest.mn b/security/nss/lib/softoken/manifest.mn
index eefbb19fc..e36bf6075 100644
--- a/security/nss/lib/softoken/manifest.mn
+++ b/security/nss/lib/softoken/manifest.mn
@@ -47,6 +47,7 @@ CSRCS = \
rsawrapr.c \
sdb.c \
sftkdb.c \
+ sftkhmac.c \
sftkpars.c \
sftkpwd.c \
softkver.c \
diff --git a/security/nss/lib/softoken/pkcs11.c b/security/nss/lib/softoken/pkcs11.c
index 226b9c60b..d526b453c 100644
--- a/security/nss/lib/softoken/pkcs11.c
+++ b/security/nss/lib/softoken/pkcs11.c
@@ -488,7 +488,10 @@ static const struct mechanismList mechanisms[] = {
{CKM_NSS_JPAKE_FINAL_SHA1, {0, 0, CKF_DERIVE}, PR_TRUE},
{CKM_NSS_JPAKE_FINAL_SHA256, {0, 0, CKF_DERIVE}, PR_TRUE},
{CKM_NSS_JPAKE_FINAL_SHA384, {0, 0, CKF_DERIVE}, PR_TRUE},
- {CKM_NSS_JPAKE_FINAL_SHA512, {0, 0, CKF_DERIVE}, PR_TRUE}
+ {CKM_NSS_JPAKE_FINAL_SHA512, {0, 0, CKF_DERIVE}, PR_TRUE},
+ /* -------------------- Constant Time TLS MACs ----------------------- */
+ {CKM_NSS_HMAC_CONSTANT_TIME, {0, 0, CKF_DIGEST}, PR_TRUE},
+ {CKM_NSS_SSLV3_MAC_CONSTANT_TIME, {0, 0, CKF_DIGEST}, PR_TRUE}
};
static const CK_ULONG mechanismCount = sizeof(mechanisms)/sizeof(mechanisms[0]);
diff --git a/security/nss/lib/softoken/pkcs11c.c b/security/nss/lib/softoken/pkcs11c.c
index dfe12e12b..666fbfcb1 100644
--- a/security/nss/lib/softoken/pkcs11c.c
+++ b/security/nss/lib/softoken/pkcs11c.c
@@ -1529,17 +1529,23 @@ DOSUB(SHA256)
DOSUB(SHA384)
DOSUB(SHA512)
-/*
- * HMAC General copies only a portion of the result. This update routine likes
- * the final HMAC output with the signature.
- */
-static SECStatus
-sftk_HMACCopy(CK_ULONG *copyLen,unsigned char *sig,unsigned int *sigLen,
- unsigned int maxLen,unsigned char *hash, unsigned int hashLen)
-{
- if (maxLen < *copyLen) return SECFailure;
- PORT_Memcpy(sig,hash,*copyLen);
- *sigLen = *copyLen;
+SECStatus sftk_SignCopy(
+ CK_ULONG *copyLen,
+ void *out, unsigned int *outLength,
+ unsigned int maxLength,
+ const unsigned char *hashResult,
+ unsigned int hashResultLength) {
+ unsigned int toCopy = *copyLen;
+ if (toCopy > maxLength) {
+ toCopy = maxLength;
+ }
+ if (toCopy > hashResultLength) {
+ toCopy = hashResultLength;
+ }
+ memcpy(out, hashResult, toCopy);
+ if (outLength) {
+ *outLength = toCopy;
+ }
return SECSuccess;
}
@@ -1595,7 +1601,7 @@ sftk_doHMACInit(SFTKSessionContext *context,HASH_HashType hash,
*intpointer = mac_size;
context->cipherInfo = (void *) intpointer;
context->destroy = (SFTKDestroy) sftk_Space;
- context->update = (SFTKCipher) sftk_HMACCopy;
+ context->update = (SFTKCipher) sftk_SignCopy;
context->verify = (SFTKVerify) sftk_HMACCmp;
context->maxLen = hashObj->length;
HMAC_Begin(HMACcontext);
@@ -2241,6 +2247,65 @@ finish_rsa:
case CKM_TLS_PRF_GENERAL:
crv = sftk_TLSPRFInit(context, key, key_type);
break;
+
+ case CKM_NSS_HMAC_CONSTANT_TIME: {
+ sftk_MACConstantTimeCtx *ctx = sftk_HMACConstantTime_New(pMechanism,key);
+ int *intpointer;
+
+ if (ctx == NULL) {
+ crv = CKR_ARGUMENTS_BAD;
+ break;
+ }
+ intpointer = PORT_Alloc(sizeof(int));
+ if (intpointer == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ *intpointer = ctx->hash->length;
+
+ context->cipherInfo = intpointer;
+ context->hashInfo = (void *) ctx;
+ context->currentMech = pMechanism->mechanism;
+ context->hashUpdate = sftk_HMACConstantTime_Update;
+ context->hashdestroy = sftk_MACConstantTime_DestroyContext;
+ context->end = sftk_MACConstantTime_EndHash;
+ context->update = sftk_SignCopy;
+ context->destroy = sftk_Space;
+ context->maxLen = 64;
+ context->multi = PR_TRUE;
+ if (ctx == NULL)
+ crv = CKR_ARGUMENTS_BAD;
+ break;
+ }
+
+ case CKM_NSS_SSLV3_MAC_CONSTANT_TIME: {
+ sftk_MACConstantTimeCtx *ctx = sftk_SSLv3MACConstantTime_New(pMechanism,key);
+ int *intpointer;
+
+ if (ctx == NULL) {
+ crv = CKR_ARGUMENTS_BAD;
+ break;
+ }
+ intpointer = PORT_Alloc(sizeof(int));
+ if (intpointer == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ *intpointer = ctx->hash->length;
+
+ context->cipherInfo = intpointer;
+ context->hashInfo = (void *) ctx;
+ context->currentMech = pMechanism->mechanism;
+ context->hashUpdate = sftk_SSLv3MACConstantTime_Update;
+ context->hashdestroy = sftk_MACConstantTime_DestroyContext;
+ context->end = sftk_MACConstantTime_EndHash;
+ context->update = sftk_SignCopy;
+ context->destroy = sftk_Space;
+ context->maxLen = 64;
+ context->multi = PR_TRUE;
+ break;
+ }
+
default:
crv = CKR_MECHANISM_INVALID;
break;
diff --git a/security/nss/lib/softoken/pkcs11i.h b/security/nss/lib/softoken/pkcs11i.h
index 45001b8d0..904a0621a 100644
--- a/security/nss/lib/softoken/pkcs11i.h
+++ b/security/nss/lib/softoken/pkcs11i.h
@@ -708,6 +708,28 @@ CK_RV jpake_Final(HASH_HashType hashType,
const CK_NSS_JPAKEFinalParams * params,
SFTKObject * sourceKey, SFTKObject * key);
+/* Constant time MAC functions (hmacct.c) */
+
+struct sftk_MACConstantTimeCtxStr {
+ const SECHashObject *hash;
+ unsigned char mac[64];
+ unsigned char secret[64];
+ unsigned int headerLength;
+ unsigned int secretLength;
+ unsigned int totalLength;
+ unsigned char header[75];
+};
+typedef struct sftk_MACConstantTimeCtxStr sftk_MACConstantTimeCtx;
+sftk_MACConstantTimeCtx* sftk_HMACConstantTime_New(
+ CK_MECHANISM_PTR mech, SFTKObject *key);
+sftk_MACConstantTimeCtx* sftk_SSLv3MACConstantTime_New(
+ CK_MECHANISM_PTR mech, SFTKObject *key);
+void sftk_HMACConstantTime_Update(void *pctx, void *data, unsigned int len);
+void sftk_SSLv3MACConstantTime_Update(void *pctx, void *data, unsigned int len);
+void sftk_MACConstantTime_EndHash(
+ void *pctx, void *out, unsigned int *outLength, unsigned int maxLength);
+void sftk_MACConstantTime_DestroyContext(void *pctx, PRBool);
+
/****************************************
* implement TLS Pseudo Random Function (PRF)
*/
diff --git a/security/nss/lib/softoken/sftkhmac.c b/security/nss/lib/softoken/sftkhmac.c
new file mode 100644
index 000000000..412ee4dc4
--- /dev/null
+++ b/security/nss/lib/softoken/sftkhmac.c
@@ -0,0 +1,175 @@
+/* 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 "seccomon.h"
+#include "secerr.h"
+#include "blapi.h"
+#include "pkcs11i.h"
+#include "softoken.h"
+#include "hmacct.h"
+
+/* mechanismToHash converts a PKCS#11 hash mechanism into a freebl hash type. */
+static HASH_HashType mechanismToHash(CK_MECHANISM_TYPE mech) {
+ switch (mech) {
+ case CKM_MD5:
+ case CKM_MD5_HMAC:
+ case CKM_SSL3_MD5_MAC:
+ return HASH_AlgMD5;
+ case CKM_SHA_1:
+ case CKM_SHA_1_HMAC:
+ case CKM_SSL3_SHA1_MAC:
+ return HASH_AlgSHA1;
+ case CKM_SHA224:
+ return HASH_AlgSHA224;
+ case CKM_SHA256:
+ return HASH_AlgSHA256;
+ case CKM_SHA384:
+ return HASH_AlgSHA384;
+ case CKM_SHA512:
+ return HASH_AlgSHA512;
+ }
+ return HASH_AlgNULL;
+}
+
+static sftk_MACConstantTimeCtx* SetupMAC(CK_MECHANISM_PTR mech,
+ SFTKObject *key) {
+ CK_NSS_MACConstantTimeParams* params =
+ (CK_NSS_MACConstantTimeParams*) mech->pParameter;
+ sftk_MACConstantTimeCtx* ctx;
+ HASH_HashType alg;
+ SFTKAttribute *keyval;
+ unsigned char secret[sizeof(ctx->secret)];
+ unsigned int secretLength;
+
+ if (mech->ulParameterLen != sizeof(CK_NSS_MACConstantTimeParams)) {
+ return NULL;
+ }
+
+ alg = mechanismToHash(params->hashAlg);
+ if (alg == HASH_AlgNULL) {
+ return NULL;
+ }
+
+ keyval = sftk_FindAttribute(key,CKA_VALUE);
+ if (keyval == NULL) {
+ return NULL;
+ }
+ secretLength = keyval->attrib.ulValueLen;
+ if (secretLength > sizeof(secret)) {
+ sftk_FreeAttribute(keyval);
+ return NULL;
+ }
+ memcpy(secret, keyval->attrib.pValue, secretLength);
+ sftk_FreeAttribute(keyval);
+
+ ctx = PORT_Alloc(sizeof(sftk_MACConstantTimeCtx));
+ if (!ctx) {
+ return NULL;
+ }
+
+ memcpy(ctx->secret, secret, secretLength);
+ ctx->secretLength = secretLength;
+ ctx->hash = HASH_GetRawHashObject(alg);
+ ctx->totalLength = params->ulBodyTotalLength;
+
+ return ctx;
+}
+
+sftk_MACConstantTimeCtx* sftk_HMACConstantTime_New(CK_MECHANISM_PTR mech,
+ SFTKObject *key) {
+ CK_NSS_MACConstantTimeParams* params =
+ (CK_NSS_MACConstantTimeParams*) mech->pParameter;
+ sftk_MACConstantTimeCtx* ctx;
+
+ if (params->ulHeaderLength > sizeof(ctx->header)) {
+ return NULL;
+ }
+ ctx = SetupMAC(mech, key);
+ if (!ctx) {
+ return NULL;
+ }
+
+ ctx->headerLength = params->ulHeaderLength;
+ memcpy(ctx->header, params->pHeader, params->ulHeaderLength);
+ return ctx;
+}
+
+sftk_MACConstantTimeCtx* sftk_SSLv3MACConstantTime_New(CK_MECHANISM_PTR mech,
+ SFTKObject *key) {
+ CK_NSS_MACConstantTimeParams* params =
+ (CK_NSS_MACConstantTimeParams*) mech->pParameter;
+ unsigned int padLength = 40, j;
+
+ sftk_MACConstantTimeCtx* ctx = SetupMAC(mech, key);
+ if (!ctx) {
+ return NULL;
+ }
+
+ if (params->hashAlg == CKM_MD5) {
+ padLength = 48;
+ }
+
+ ctx->headerLength =
+ ctx->secretLength +
+ padLength +
+ params->ulHeaderLength;
+
+ if (ctx->headerLength > sizeof(ctx->header)) {
+ goto loser;
+ }
+
+ j = 0;
+ memcpy(&ctx->header[j], ctx->secret, ctx->secretLength);
+ j += ctx->secretLength;
+ memset(&ctx->header[j], 0x36, padLength);
+ j += padLength;
+ memcpy(&ctx->header[j], params->pHeader, params->ulHeaderLength);
+
+ return ctx;
+
+loser:
+ PORT_Free(ctx);
+ return NULL;
+}
+
+void sftk_HMACConstantTime_Update(void *pctx, void *data, unsigned int len) {
+ sftk_MACConstantTimeCtx* ctx = (sftk_MACConstantTimeCtx*) pctx;
+ SECStatus rv = HMAC_ConstantTime(
+ ctx->mac, NULL, sizeof(ctx->mac),
+ ctx->hash,
+ ctx->secret, ctx->secretLength,
+ ctx->header, ctx->headerLength,
+ data, len,
+ ctx->totalLength);
+ PORT_Assert(rv == SECSuccess);
+}
+
+void sftk_SSLv3MACConstantTime_Update(void *pctx, void *data, unsigned int len) {
+ sftk_MACConstantTimeCtx* ctx = (sftk_MACConstantTimeCtx*) pctx;
+ SECStatus rv = SSLv3_MAC_ConstantTime(
+ ctx->mac, NULL, sizeof(ctx->mac),
+ ctx->hash,
+ ctx->secret, ctx->secretLength,
+ ctx->header, ctx->headerLength,
+ data, len,
+ ctx->totalLength);
+ PORT_Assert(rv == SECSuccess);
+}
+
+void sftk_MACConstantTime_EndHash(void *pctx, void *out, unsigned int *outLength,
+ unsigned int maxLength) {
+ const sftk_MACConstantTimeCtx* ctx = (sftk_MACConstantTimeCtx*) pctx;
+ unsigned int toCopy = ctx->hash->length;
+ if (toCopy > maxLength) {
+ toCopy = maxLength;
+ }
+ memcpy(out, ctx->mac, toCopy);
+ if (outLength) {
+ *outLength = toCopy;
+ }
+}
+
+void sftk_MACConstantTime_DestroyContext(void *pctx, PRBool free) {
+ PORT_Free(pctx);
+}
diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c
index 301902876..d5d6ef0f9 100644
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -1844,7 +1844,6 @@ static const unsigned char mac_pad_2 [60] = {
};
/* Called from: ssl3_SendRecord()
-** ssl3_HandleRecord()
** Caller must already hold the SpecReadLock. (wish we could assert that!)
*/
static SECStatus
@@ -2026,6 +2025,135 @@ ssl3_ComputeRecordMAC(
return rv;
}
+/* Called from: ssl3_HandleRecord()
+ * Caller must already hold the SpecReadLock. (wish we could assert that!)
+ *
+ * On entry:
+ * originalLen >= inputLen >= MAC size
+*/
+static SECStatus
+ssl3_ComputeRecordMACConstantTime(
+ ssl3CipherSpec * spec,
+ PRBool useServerMacKey,
+ PRBool isDTLS,
+ SSL3ContentType type,
+ SSL3ProtocolVersion version,
+ SSL3SequenceNumber seq_num,
+ const SSL3Opaque * input,
+ int inputLen,
+ int originalLen,
+ unsigned char * outbuf,
+ unsigned int * outLen)
+{
+ CK_MECHANISM_TYPE macType;
+ CK_NSS_MACConstantTimeParams params;
+ PK11Context * mac_context;
+ SECItem param, inputItem, outputItem;
+ SECStatus rv;
+ unsigned char header[13];
+ PK11SymKey * key;
+ int recordLength;
+
+ PORT_Assert(inputLen >= spec->mac_size);
+ PORT_Assert(originalLen >= inputLen);
+
+ if (spec->bypassCiphers) {
+ /* This function doesn't support PKCS#11 bypass. We fallback on the
+ * non-constant time version. */
+ goto fallback;
+ }
+
+ if (spec->cipher_def->cipher == cipher_rc2_40) {
+ /* This function doesn't work for SSL3_RSA_EXPORT_WITH_RC2_CBC_40_MD5.
+ * We fallback on the non-constant time version. */
+ goto fallback;
+ }
+
+ if (spec->mac_def->mac == mac_null) {
+ *outLen = 0;
+ return SECSuccess;
+ }
+
+ header[0] = (unsigned char)(seq_num.high >> 24);
+ header[1] = (unsigned char)(seq_num.high >> 16);
+ header[2] = (unsigned char)(seq_num.high >> 8);
+ header[3] = (unsigned char)(seq_num.high >> 0);
+ header[4] = (unsigned char)(seq_num.low >> 24);
+ header[5] = (unsigned char)(seq_num.low >> 16);
+ header[6] = (unsigned char)(seq_num.low >> 8);
+ header[7] = (unsigned char)(seq_num.low >> 0);
+ header[8] = type;
+
+ macType = CKM_NSS_HMAC_CONSTANT_TIME;
+ recordLength = inputLen - spec->mac_size;
+ if (spec->version <= SSL_LIBRARY_VERSION_3_0) {
+ macType = CKM_NSS_SSLV3_MAC_CONSTANT_TIME;
+ header[9] = recordLength >> 8;
+ header[10] = recordLength;
+ params.ulHeaderLength = 11;
+ } else {
+ if (isDTLS) {
+ SSL3ProtocolVersion dtls_version;
+
+ dtls_version = dtls_TLSVersionToDTLSVersion(version);
+ header[9] = dtls_version >> 8;
+ header[10] = dtls_version;
+ } else {
+ header[9] = version >> 8;
+ header[10] = version;
+ }
+ header[11] = recordLength >> 8;
+ header[12] = recordLength;
+ params.ulHeaderLength = 13;
+ }
+
+ params.hashAlg = spec->mac_def->mmech;
+ params.ulBodyTotalLength = originalLen;
+ params.pHeader = header;
+
+ param.data = (unsigned char*) &params;
+ param.len = sizeof(params);
+ param.type = 0;
+
+ inputItem.data = (unsigned char *) input;
+ inputItem.len = inputLen;
+ inputItem.type = 0;
+
+ outputItem.data = outbuf;
+ outputItem.len = *outLen;
+ outputItem.type = 0;
+
+ key = spec->server.write_mac_key;
+ if (!useServerMacKey) {
+ key = spec->client.write_mac_key;
+ }
+
+ rv = PK11_SignWithSymKey(key, macType, &param, &outputItem, &inputItem);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_INVALID_ALGORITHM) {
+ goto fallback;
+ }
+
+ *outLen = 0;
+ rv = SECFailure;
+ ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
+ return rv;
+ }
+
+ PORT_Assert(outputItem.len == (unsigned)spec->mac_size);
+ *outLen = outputItem.len;
+
+ return rv;
+
+fallback:
+ /* ssl3_ComputeRecordMAC expects the MAC to have been removed from the
+ * length already. */
+ inputLen -= spec->mac_size;
+ return ssl3_ComputeRecordMAC(spec, useServerMacKey, isDTLS, type,
+ version, seq_num, input, inputLen,
+ outbuf, outLen);
+}
+
static PRBool
ssl3_ClientAuthTokenPresent(sslSessionID *sid) {
PK11SlotInfo *slot = NULL;
@@ -9534,6 +9662,177 @@ ssl3_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
return SECSuccess;
}
+/* 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 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);
+}
+
+/* ssl_ConstantTimeGE returns 0xff if a>=b and 0x00 otherwise. */
+static unsigned char ssl_ConstantTimeGE(unsigned a, unsigned b)
+{
+ a -= b;
+ return DUPLICATE_MSB_TO_ALL(~a);
+}
+
+/* ssl_ConstantTimeEQ8 returns 0xff if a==b and 0x00 otherwise. */
+static unsigned char ssl_ConstantTimeEQ8(unsigned char a, unsigned char b)
+{
+ unsigned c = a ^ b;
+ c--;
+ return DUPLICATE_MSB_TO_ALL_8(c);
+}
+
+static SECStatus ssl_RemoveSSLv3CBCPadding(sslBuffer *plaintext,
+ unsigned blockSize,
+ unsigned macSize) {
+ unsigned int paddingLength, good, t;
+ const unsigned int overhead = 1 /* padding length byte */ + macSize;
+
+ /* These lengths are all public so we can test them in non-constant
+ * time. */
+ if (overhead > plaintext->len) {
+ return SECFailure;
+ }
+
+ paddingLength = plaintext->buf[plaintext->len-1];
+ /* SSLv3 padding bytes are random and cannot be checked. */
+ t = plaintext->len;
+ t -= paddingLength+overhead;
+ /* If len >= padding_length+overhead then the MSB of t is zero. */
+ good = DUPLICATE_MSB_TO_ALL(~t);
+ /* SSLv3 requires that the padding is minimal. */
+ t = blockSize - (paddingLength+1);
+ good &= DUPLICATE_MSB_TO_ALL(~t);
+ plaintext->len -= good & (paddingLength+1);
+ return (good & SECSuccess) | (~good & SECFailure);
+}
+
+
+static SECStatus ssl_RemoveTLSCBCPadding(sslBuffer *plaintext,
+ unsigned macSize) {
+ unsigned int paddingLength, good, t, toCheck, i;
+ const unsigned int overhead = 1 /* padding length byte */ + macSize;
+
+ /* These lengths are all public so we can test them in non-constant
+ * time. */
+ if (overhead > plaintext->len) {
+ return SECFailure;
+ }
+
+ 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);
+
+ /* 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.
+ * Thus, with the length byte included, there are paddingLength+1 bytes of
+ * padding.
+ *
+ * We can't check just |paddingLength+1| bytes because that leaks
+ * decrypted information. Therefore we always have to check the maximum
+ * amount of padding possible. (Again, the length of the record is
+ * public information so we can use it.) */
+ toCheck = 255; /* maximum amount of padding. */
+ if (toCheck > plaintext->len-1) {
+ toCheck = plaintext->len-1;
+ }
+
+ for (i = 0; i < toCheck; i++) {
+ unsigned int 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 b = plaintext->buf[plaintext->len-1-i];
+ /* The final |paddingLength+1| bytes should all have the value
+ * |paddingLength|. Therefore the XOR should be zero. */
+ good &= ~(mask&(paddingLength ^ b));
+ }
+
+ /* If any of the final |paddingLength+1| bytes had the wrong value,
+ * one or more of the lower eight bits of |good| will be cleared. We
+ * AND the bottom 8 bits together and duplicate the result to all the
+ * bits. */
+ good &= good >> 4;
+ good &= good >> 2;
+ good &= good >> 1;
+ good <<= sizeof(good)*8-1;
+ good = DUPLICATE_MSB_TO_ALL(good);
+
+ plaintext->len -= good & (paddingLength+1);
+ return (good & SECSuccess) | (~good & SECFailure);
+}
+
+/* On entry:
+ * originalLength >= macSize
+ * macSize <= MAX_MAC_LENGTH
+ * plaintext->len >= macSize
+ */
+static void ssl_CBCExtractMAC(sslBuffer *plaintext,
+ unsigned int originalLength,
+ SSL3Opaque* out,
+ unsigned int macSize) {
+ unsigned char rotatedMac[MAX_MAC_LENGTH];
+ /* macEnd is the index of |plaintext->buf| just after the end of the MAC. */
+ unsigned macEnd = plaintext->len;
+ unsigned macStart = macEnd - macSize;
+ /* scanStart contains the number of bytes that we can ignore because
+ * the MAC's position can only vary by 255 bytes. */
+ unsigned scanStart = 0;
+ unsigned i, j, divSpoiler;
+ unsigned char rotateOffset;
+
+ if (originalLength > macSize + 255 + 1)
+ scanStart = originalLength - (macSize + 255 + 1);
+
+ /* divSpoiler contains a multiple of macSize that is used to cause the
+ * modulo operation to be constant time. Without this, the time varies
+ * based on the amount of padding when running on Intel chips at least.
+ *
+ * The aim of right-shifting macSize is so that the compiler doesn't
+ * figure out that it can remove divSpoiler as that would require it
+ * to prove that macSize is always even, which I hope is beyond it. */
+ divSpoiler = macSize >> 1;
+ divSpoiler <<= (sizeof(divSpoiler)-1)*8;
+ rotateOffset = (divSpoiler + macStart - scanStart) % macSize;
+
+ memset(rotatedMac, 0, macSize);
+ for (i = scanStart; i < originalLength;) {
+ for (j = 0; j < macSize && i < originalLength; i++, j++) {
+ unsigned char macStarted = ssl_ConstantTimeGE(i, macStart);
+ unsigned char macEnded = ssl_ConstantTimeGE(i, macEnd);
+ unsigned char b = 0;
+ b = plaintext->buf[i];
+ rotatedMac[j] |= b & macStarted & ~macEnded;
+ }
+ }
+
+ /* Now rotate the MAC. If we knew that the MAC fit into a CPU cache line we
+ * could line-align |rotatedMac| and rotate in place. */
+ memset(out, 0, macSize);
+ for (i = 0; i < macSize; i++) {
+ unsigned char offset = (divSpoiler + macSize - rotateOffset + i) % macSize;
+ for (j = 0; j < macSize; j++) {
+ out[j] |= rotatedMac[i] & ssl_ConstantTimeEQ8(j, offset);
+ }
+ }
+}
+
/* if cText is non-null, then decipher, check MAC, and decompress the
* SSL record from cText->buf (typically gs->inbuf)
* into databuf (typically gs->buf), and any previous contents of databuf
@@ -9563,15 +9862,18 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
ssl3CipherSpec * crSpec;
SECStatus rv;
unsigned int hashBytes = MAX_MAC_LENGTH + 1;
- unsigned int padding_length;
PRBool isTLS;
- PRBool padIsBad = PR_FALSE;
SSL3ContentType rType;
SSL3Opaque hash[MAX_MAC_LENGTH];
+ SSL3Opaque givenHashBuf[MAX_MAC_LENGTH];
+ SSL3Opaque *givenHash;
sslBuffer *plaintext;
sslBuffer temp_buf;
PRUint64 dtls_seq_num;
unsigned int ivLen = 0;
+ unsigned int originalLen = 0;
+ unsigned int good;
+ unsigned int minLength;
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
@@ -9639,6 +9941,30 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
}
}
+ good = (unsigned)-1;
+ minLength = crSpec->mac_size;
+ if (cipher_def->type == type_block) {
+ /* CBC records have a padding length byte at the end. */
+ minLength++;
+ if (crSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
+ /* With >= TLS 1.1, CBC records have an explicit IV. */
+ minLength += cipher_def->iv_size;
+ }
+ }
+
+ /* We can perform this test in variable time because the record's total
+ * length and the ciphersuite are both public knowledge. */
+ if (cText->buf->len < minLength) {
+ SSL_DBG(("%d: SSL3[%d]: HandleRecord, record too small.",
+ SSL_GETPID(), ss->fd));
+ /* must not hold spec lock when calling SSL3_SendAlert. */
+ ssl_ReleaseSpecReadLock(ss);
+ SSL3_SendAlert(ss, alert_fatal, bad_record_mac);
+ /* always log mac error, in case attacker can read server logs. */
+ PORT_SetError(SSL_ERROR_BAD_MAC_READ);
+ return SECFailure;
+ }
+
if (cipher_def->type == type_block &&
crSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
/* Consume the per-record explicit IV. RFC 4346 Section 6.2.3.2 states
@@ -9656,16 +9982,6 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
- if (ivLen > cText->buf->len) {
- SSL_DBG(("%d: SSL3[%d]: HandleRecord, IV length check failed",
- SSL_GETPID(), ss->fd));
- /* must not hold spec lock when calling SSL3_SendAlert. */
- ssl_ReleaseSpecReadLock(ss);
- SSL3_SendAlert(ss, alert_fatal, bad_record_mac);
- /* always log mac error, in case attacker can read server logs. */
- PORT_SetError(SSL_ERROR_BAD_MAC_READ);
- return SECFailure;
- }
PRINT_BUF(80, (ss, "IV (ciphertext):", cText->buf->buf, ivLen));
@@ -9676,12 +9992,7 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
rv = crSpec->decode(crSpec->decodeContext, iv, &decoded,
sizeof(iv), cText->buf->buf, ivLen);
- if (rv != SECSuccess) {
- /* All decryption failures must be treated like a bad record
- * MAC; see RFC 5246 (TLS 1.2).
- */
- padIsBad = PR_TRUE;
- }
+ good &= SECStatusToMask(rv);
}
/* If we will be decompressing the buffer we need to decrypt somewhere
@@ -9723,54 +10034,70 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
rv = crSpec->decode(
crSpec->decodeContext, plaintext->buf, (int *)&plaintext->len,
plaintext->space, cText->buf->buf + ivLen, cText->buf->len - ivLen);
+ good &= SECStatusToMask(rv);
PRINT_BUF(80, (ss, "cleartext:", plaintext->buf, plaintext->len));
- if (rv != SECSuccess) {
- /* All decryption failures must be treated like a bad record
- * MAC; see RFC 5246 (TLS 1.2).
- */
- padIsBad = PR_TRUE;
- }
+
+ originalLen = plaintext->len;
/* If it's a block cipher, check and strip the padding. */
- if (cipher_def->type == type_block && !padIsBad) {
- PRUint8 * pPaddingLen = plaintext->buf + plaintext->len - 1;
- padding_length = *pPaddingLen;
- /* TLS permits padding to exceed the block size, up to 255 bytes. */
- if (padding_length + 1 + crSpec->mac_size > plaintext->len)
- padIsBad = PR_TRUE;
- else {
- plaintext->len -= padding_length + 1;
- /* In TLS all padding bytes must be equal to the padding length. */
- if (isTLS) {
- PRUint8 *p;
- for (p = pPaddingLen - padding_length; p < pPaddingLen; ++p) {
- padIsBad |= *p ^ padding_length;
- }
- }
- }
- }
+ if (cipher_def->type == type_block) {
+ const unsigned int blockSize = cipher_def->iv_size;
+ const unsigned int macSize = crSpec->mac_size;
- /* Remove the MAC. */
- if (plaintext->len >= crSpec->mac_size)
- plaintext->len -= crSpec->mac_size;
- else
- padIsBad = PR_TRUE; /* really macIsBad */
+ if (crSpec->version <= SSL_LIBRARY_VERSION_3_0) {
+ good &= SECStatusToMask(ssl_RemoveSSLv3CBCPadding(
+ plaintext, blockSize, macSize));
+ } else {
+ good &= SECStatusToMask(ssl_RemoveTLSCBCPadding(
+ plaintext, macSize));
+ }
+ }
/* compute the MAC */
rType = cText->type;
- rv = ssl3_ComputeRecordMAC( crSpec, (PRBool)(!ss->sec.isServer),
- IS_DTLS(ss), rType, cText->version,
- IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
- plaintext->buf, plaintext->len, hash, &hashBytes);
- if (rv != SECSuccess) {
- padIsBad = PR_TRUE; /* really macIsBad */
+ if (cipher_def->type == type_block) {
+ rv = ssl3_ComputeRecordMACConstantTime(
+ crSpec, (PRBool)(!ss->sec.isServer),
+ IS_DTLS(ss), rType, cText->version,
+ IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
+ plaintext->buf, plaintext->len, originalLen,
+ hash, &hashBytes);
+
+ ssl_CBCExtractMAC(plaintext, originalLen, givenHashBuf,
+ crSpec->mac_size);
+ givenHash = givenHashBuf;
+
+ /* plaintext->len will always have enough space to remove the MAC
+ * because in ssl_Remove{SSLv3|TLS}CBCPadding we only adjust
+ * plaintext->len if the result has enough space for the MAC and we
+ * tested the unadjusted size against minLength, above. */
+ plaintext->len -= crSpec->mac_size;
+ } else {
+ /* This is safe because we checked the minLength above. */
+ plaintext->len -= crSpec->mac_size;
+
+ rv = ssl3_ComputeRecordMAC(
+ crSpec, (PRBool)(!ss->sec.isServer),
+ IS_DTLS(ss), rType, cText->version,
+ IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
+ plaintext->buf, plaintext->len,
+ hash, &hashBytes);
+
+ /* We can read the MAC directly from the record because its location is
+ * public when a stream cipher is used. */
+ givenHash = plaintext->buf + plaintext->len;
+ }
+
+ good &= SECStatusToMask(rv);
+
+ if (hashBytes != (unsigned)crSpec->mac_size ||
+ NSS_SecureMemcmp(givenHash, hash, crSpec->mac_size) != 0) {
+ /* We're allowed to leak whether or not the MAC check was correct */
+ good = 0;
}
- /* Check the MAC */
- if (hashBytes != (unsigned)crSpec->mac_size || padIsBad ||
- NSS_SecureMemcmp(plaintext->buf + plaintext->len, hash,
- crSpec->mac_size) != 0) {
+ if (good == 0) {
/* must not hold spec lock when calling SSL3_SendAlert. */
ssl_ReleaseSpecReadLock(ss);
diff --git a/security/nss/lib/util/hasht.h b/security/nss/lib/util/hasht.h
index 484f8442a..6fd12f13a 100644
--- a/security/nss/lib/util/hasht.h
+++ b/security/nss/lib/util/hasht.h
@@ -51,6 +51,7 @@ struct SECHashObjectStr {
void (*end)(void *, unsigned char *, unsigned int *, unsigned int);
unsigned int blocklength; /* hash input block size (in bytes) */
HASH_HashType type;
+ void (*end_raw)(void *, unsigned char *, unsigned int *, unsigned int);
};
struct HASHContextStr {
diff --git a/security/nss/lib/util/pkcs11n.h b/security/nss/lib/util/pkcs11n.h
index 6f89f1531..a6fceec1e 100644
--- a/security/nss/lib/util/pkcs11n.h
+++ b/security/nss/lib/util/pkcs11n.h
@@ -195,6 +195,9 @@ static const char CKT_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$";
#define CKM_NSS_JPAKE_FINAL_SHA384 (CKM_NSS + 17)
#define CKM_NSS_JPAKE_FINAL_SHA512 (CKM_NSS + 18)
+#define CKM_NSS_HMAC_CONSTANT_TIME (CKM_NSS + 19)
+#define CKM_NSS_SSLV3_MAC_CONSTANT_TIME (CKM_NSS + 20)
+
/*
* HISTORICAL:
* Do not attempt to use these. They are only used by NETSCAPE's internal
@@ -240,6 +243,13 @@ typedef struct CK_NSS_JPAKEFinalParams {
CK_NSS_JPAKEPublicValue B; /* in */
} CK_NSS_JPAKEFinalParams;
+typedef struct CK_NSS_MACConstantTimeParams {
+ CK_MECHANISM_TYPE hashAlg; /* in */
+ CK_ULONG ulBodyTotalLength; /* in */
+ CK_BYTE * pHeader; /* in */
+ CK_ULONG ulHeaderLength; /* in */
+} CK_NSS_MACConstantTimeParams;
+
/*
* NSS-defined return values
*