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 macAlg; /* 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.macAlg = 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);