diff options
-rw-r--r-- | net/third_party/nss/README.chromium | 5 | ||||
-rwxr-xr-x | net/third_party/nss/patches/applypatches.sh | 2 | ||||
-rw-r--r-- | net/third_party/nss/patches/cbc.patch | 519 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3con.c | 442 |
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*) ¶ms; ++ 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, ¶m); ++ 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*) ¶ms; + 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, ¶m); + 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); |