summaryrefslogtreecommitdiffstats
path: root/net/third_party
diff options
context:
space:
mode:
Diffstat (limited to 'net/third_party')
-rw-r--r--net/third_party/nss/README.chromium5
-rwxr-xr-xnet/third_party/nss/patches/applypatches.sh2
-rw-r--r--net/third_party/nss/patches/cbc.patch519
-rw-r--r--net/third_party/nss/ssl/ssl3con.c442
4 files changed, 911 insertions, 57 deletions
diff --git a/net/third_party/nss/README.chromium b/net/third_party/nss/README.chromium
index 71d6148..1067fdcc 100644
--- a/net/third_party/nss/README.chromium
+++ b/net/third_party/nss/README.chromium
@@ -83,6 +83,11 @@ Patches:
This change was made in https://chromiumcodereview.appspot.com/10454066.
patches/secretexporterlocks.patch
+ * Implement CBC processing in constant-time to address the "Lucky Thirteen"
+ attack.
+ patches/cbc.patch
+ https://bugzilla.mozilla.org/show_bug.cgi?id=822365
+
Apply the patches to NSS by running the patches/applypatches.sh script. Read
the comments at the top of patches/applypatches.sh for instructions.
diff --git a/net/third_party/nss/patches/applypatches.sh b/net/third_party/nss/patches/applypatches.sh
index 1992ad0..de12ec4 100755
--- a/net/third_party/nss/patches/applypatches.sh
+++ b/net/third_party/nss/patches/applypatches.sh
@@ -41,3 +41,5 @@ patch -p5 < $patches_dir/sslkeylogerror.patch
patch -p5 < $patches_dir/ecpointform.patch
patch -p5 < $patches_dir/secretexporterlocks.patch
+
+patch -p6 < $patches_dir/cbc.patch
diff --git a/net/third_party/nss/patches/cbc.patch b/net/third_party/nss/patches/cbc.patch
new file mode 100644
index 0000000..85da578
--- /dev/null
+++ b/net/third_party/nss/patches/cbc.patch
@@ -0,0 +1,519 @@
+diff --git a/mozilla/security/nss/lib/ssl/ssl3con.c b/mozilla/security/nss/lib/ssl/ssl3con.c
+index c3706fe..4b79321 100644
+--- a/mozilla/security/nss/lib/ssl/ssl3con.c
++++ b/mozilla/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,136 @@ ssl3_ComputeRecordMAC(
+ return rv;
+ }
+
++/* This is a bodge to allow this code to be compiled against older NSS headers
++ * that don't contain the CBC constant-time changes. */
++#ifndef CKM_NSS_HMAC_CONSTANT_TIME
++#define CKM_NSS_HMAC_CONSTANT_TIME (CKM_NSS + 19)
++#define CKM_NSS_SSL3_MAC_CONSTANT_TIME (CKM_NSS + 20)
++
++typedef struct CK_NSS_MAC_CONSTANT_TIME_PARAMS {
++ CK_MECHANISM_TYPE hashAlg; /* in */
++ CK_ULONG ulBodyTotalLen; /* in */
++ CK_BYTE * pHeader; /* in */
++ CK_ULONG ulHeaderLen; /* in */
++} CK_NSS_MAC_CONSTANT_TIME_PARAMS;
++#endif
++
++/* 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_MAC_CONSTANT_TIME_PARAMS params;
++ PK11Context * mac_context;
++ SECItem param;
++ 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->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_SSL3_MAC_CONSTANT_TIME;
++ header[9] = recordLength >> 8;
++ header[10] = recordLength;
++ params.ulHeaderLen = 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.ulHeaderLen = 13;
++ }
++
++ params.hashAlg = spec->mac_def->mmech;
++ params.ulBodyTotalLen = originalLen;
++ params.pHeader = header;
++
++ param.data = (unsigned char*) &params;
++ param.len = sizeof(params);
++ param.type = 0;
++
++ key = spec->server.write_mac_key;
++ if (!useServerMacKey) {
++ key = spec->client.write_mac_key;
++ }
++ mac_context = PK11_CreateContextBySymKey(macType, CKA_SIGN, key, &param);
++ if (mac_context == NULL) {
++ /* Older versions of NSS may not support constant-time MAC. */
++ goto fallback;
++ }
++
++ rv = PK11_DigestBegin(mac_context);
++ rv |= PK11_DigestOp(mac_context, input, inputLen);
++ rv |= PK11_DigestFinal(mac_context, outbuf, outLen, spec->mac_size);
++ PK11_DestroyContext(mac_context, PR_TRUE);
++
++ PORT_Assert(rv != SECSuccess || *outLen == (unsigned)spec->mac_size);
++
++ if (rv != SECSuccess) {
++ rv = SECFailure;
++ ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
++ }
++ 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;
+@@ -9530,6 +9659,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
+@@ -9559,15 +9859,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) );
+
+@@ -9635,6 +9938,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
+@@ -9652,16 +9979,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));
+
+@@ -9672,12 +9989,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
+@@ -9719,54 +10031,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/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c
index 5060447..e38a624 100644
--- a/net/third_party/nss/ssl/ssl3con.c
+++ b/net/third_party/nss/ssl/ssl3con.c
@@ -1846,7 +1846,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
@@ -2028,6 +2027,136 @@ ssl3_ComputeRecordMAC(
return rv;
}
+/* This is a bodge to allow this code to be compiled against older NSS headers
+ * that don't contain the CBC constant-time changes. */
+#ifndef CKM_NSS_HMAC_CONSTANT_TIME
+#define CKM_NSS_HMAC_CONSTANT_TIME (CKM_NSS + 19)
+#define CKM_NSS_SSL3_MAC_CONSTANT_TIME (CKM_NSS + 20)
+
+typedef struct CK_NSS_MAC_CONSTANT_TIME_PARAMS {
+ CK_MECHANISM_TYPE hashAlg; /* in */
+ CK_ULONG ulBodyTotalLen; /* in */
+ CK_BYTE * pHeader; /* in */
+ CK_ULONG ulHeaderLen; /* in */
+} CK_NSS_MAC_CONSTANT_TIME_PARAMS;
+#endif
+
+/* 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_MAC_CONSTANT_TIME_PARAMS params;
+ PK11Context * mac_context;
+ SECItem param;
+ 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->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_SSL3_MAC_CONSTANT_TIME;
+ header[9] = recordLength >> 8;
+ header[10] = recordLength;
+ params.ulHeaderLen = 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.ulHeaderLen = 13;
+ }
+
+ params.hashAlg = spec->mac_def->mmech;
+ params.ulBodyTotalLen = originalLen;
+ params.pHeader = header;
+
+ param.data = (unsigned char*) &params;
+ param.len = sizeof(params);
+ param.type = 0;
+
+ key = spec->server.write_mac_key;
+ if (!useServerMacKey) {
+ key = spec->client.write_mac_key;
+ }
+ mac_context = PK11_CreateContextBySymKey(macType, CKA_SIGN, key, &param);
+ if (mac_context == NULL) {
+ /* Older versions of NSS may not support constant-time MAC. */
+ goto fallback;
+ }
+
+ rv = PK11_DigestBegin(mac_context);
+ rv |= PK11_DigestOp(mac_context, input, inputLen);
+ rv |= PK11_DigestFinal(mac_context, outbuf, outLen, spec->mac_size);
+ PK11_DestroyContext(mac_context, PR_TRUE);
+
+ PORT_Assert(rv != SECSuccess || *outLen == (unsigned)spec->mac_size);
+
+ if (rv != SECSuccess) {
+ rv = SECFailure;
+ ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
+ }
+ 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;
@@ -10045,6 +10174,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
@@ -10074,15 +10374,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) );
@@ -10150,6 +10453,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
@@ -10167,16 +10494,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));
@@ -10187,12 +10504,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
@@ -10234,54 +10546,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);