/* 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 */); }