| /* 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 |
| |
| /* 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) |
| { |
| return PORT_CT_GE(a, b); |
| } |
| |
| /* constantTimeEQ8 returns 0xff if a==b and 0x00 otherwise. */ |
| static unsigned char |
| constantTimeEQ(unsigned char a, unsigned char b) |
| { |
| return PORT_CT_EQ(a, b); |
| } |
| |
| /* 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 may |
| * 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; |
| } |
| if (hashObj->type == HASH_AlgMD5) { |
| /* MD5 appends a little-endian length. */ |
| for (i = 0; i < 4; i++) { |
| lengthBytes[i + j] = bits >> (8 * i); |
| } |
| } else { |
| /* All other TLS hash functions use a big-endian length. */ |
| for (i = 0; i < 4; i++) { |
| lengthBytes[4 + i + j] = bits >> (8 * (3 - 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 = constantTimeEQ(i, indexA); |
| unsigned char isBlockB = constantTimeEQ(i, indexB); |
| for (j = 0; j < mdBlockSize; j++) { |
| unsigned char isPastC = isBlockA & constantTimeGE(j, c); |
| unsigned char isPastCPlus1 = isBlockA & constantTimeGE(j, c + 1); |
| unsigned char b = 0; |
| if (k < headerLen) { |
| b = header[k]; |
| } else if (k < dataTotalLen + headerLen) { |
| b = data[k - headerLen]; |
| } |
| k++; |
| |
| /* 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); |
| |
| PORT_Memset(lengthBytes, 0, sizeof lengthBytes); |
| PORT_Memset(hmacPad, 0, sizeof hmacPad); |
| PORT_Memset(firstBlock, 0, sizeof firstBlock); |
| PORT_Memset(macOut, 0, sizeof macOut); |
| |
| 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 */); |
| } |