andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #ifdef FREEBL_NO_DEPEND andre@0: #include "stubs.h" andre@0: #endif andre@0: andre@0: #include "secport.h" andre@0: #include "hasht.h" andre@0: #include "blapit.h" andre@0: #include "hmacct.h" andre@0: #include "secerr.h" andre@0: andre@0: /* MAX_HASH_BIT_COUNT_BYTES is the maximum number of bytes in the hash's length andre@0: * field. (SHA-384/512 have 128-bit length.) */ andre@0: #define MAX_HASH_BIT_COUNT_BYTES 16 andre@0: andre@0: /* Some utility functions are needed: andre@0: * andre@0: * These macros return the given value with the MSB copied to all the other andre@0: * bits. They use the fact that an arithmetic shift shifts-in the sign bit. andre@0: * However, this is not ensured by the C standard so you may need to replace andre@0: * them with something else on odd CPUs. andre@0: * andre@0: * Note: the argument to these macros must be an unsigned int. andre@0: * */ andre@0: #define DUPLICATE_MSB_TO_ALL(x) ( (unsigned int)( (int)(x) >> (sizeof(int)*8-1) ) ) andre@0: #define DUPLICATE_MSB_TO_ALL_8(x) ( (unsigned char)(DUPLICATE_MSB_TO_ALL(x)) ) andre@0: andre@0: /* constantTimeGE returns 0xff if a>=b and 0x00 otherwise, where a, b < andre@0: * MAX_UINT/2. */ andre@0: static unsigned char andre@0: constantTimeGE(unsigned int a, unsigned int b) andre@0: { andre@0: a -= b; andre@0: return DUPLICATE_MSB_TO_ALL(~a); andre@0: } andre@0: andre@0: /* constantTimeEQ8 returns 0xff if a==b and 0x00 otherwise. */ andre@0: static unsigned char andre@0: constantTimeEQ8(unsigned char a, unsigned char b) andre@0: { andre@0: unsigned int c = a ^ b; andre@0: c--; andre@0: return DUPLICATE_MSB_TO_ALL_8(c); andre@0: } andre@0: andre@0: /* MAC performs a constant time SSLv3/TLS MAC of |dataLen| bytes of |data|, andre@0: * where |dataLen| includes both the authenticated bytes and the MAC tag from andre@0: * the sender. |dataLen| must be >= the length of the MAC tag. andre@0: * andre@0: * |dataTotalLen| is >= |dataLen| and also accounts for any padding bytes andre@0: * that may follow the sender's MAC. (Only a single block of padding may andre@0: * follow in SSLv3, or up to 255 bytes in TLS.) andre@0: * andre@0: * Since the results of decryption are secret information (otherwise a andre@0: * padding-oracle is created), this function is constant-time with respect to andre@0: * |dataLen|. andre@0: * andre@0: * |header| contains either the 13-byte TLS header (containing the sequence andre@0: * number, record type etc), or it contains the SSLv3 header with the SSLv3 andre@0: * padding bytes etc. */ andre@0: static SECStatus andre@0: MAC(unsigned char *mdOut, andre@0: unsigned int *mdOutLen, andre@0: unsigned int mdOutMax, andre@0: const SECHashObject *hashObj, andre@0: const unsigned char *macSecret, andre@0: unsigned int macSecretLen, andre@0: const unsigned char *header, andre@0: unsigned int headerLen, andre@0: const unsigned char *data, andre@0: unsigned int dataLen, andre@0: unsigned int dataTotalLen, andre@0: unsigned char isSSLv3) andre@0: { andre@0: void *mdState = hashObj->create(); andre@0: const unsigned int mdSize = hashObj->length; andre@0: const unsigned int mdBlockSize = hashObj->blocklength; andre@0: /* mdLengthSize is the number of bytes in the length field that terminates andre@0: * the hash. andre@0: * andre@0: * This assumes that hash functions with a 64 byte block size use a 64-bit andre@0: * length, and otherwise they use a 128-bit length. This is true of {MD5, andre@0: * SHA*} (which are all of the hash functions specified for use with TLS andre@0: * today). */ andre@0: const unsigned int mdLengthSize = mdBlockSize == 64 ? 8 : 16; andre@0: andre@0: const unsigned int sslv3PadLen = hashObj->type == HASH_AlgMD5 ? 48 : 40; andre@0: andre@0: /* varianceBlocks is the number of blocks of the hash that we have to andre@0: * calculate in constant time because they could be altered by the andre@0: * padding value. andre@0: * andre@0: * In SSLv3, the padding must be minimal so the end of the plaintext andre@0: * varies by, at most, 15+20 = 35 bytes. (We conservatively assume that andre@0: * the MAC size varies from 0..20 bytes.) In case the 9 bytes of hash andre@0: * termination (0x80 + 64-bit length) don't fit in the final block, we andre@0: * say that the final two blocks can vary based on the padding. andre@0: * andre@0: * TLSv1 has MACs up to 48 bytes long (SHA-384) and the padding is not andre@0: * required to be minimal. Therefore we say that the final six blocks andre@0: * can vary based on the padding. andre@0: * andre@0: * Later in the function, if the message is short and there obviously andre@0: * cannot be this many blocks then varianceBlocks can be reduced. */ andre@0: unsigned int varianceBlocks = isSSLv3 ? 2 : 6; andre@0: /* From now on we're dealing with the MAC, which conceptually has 13 andre@0: * bytes of `header' before the start of the data (TLS) or 71/75 bytes andre@0: * (SSLv3) */ andre@0: const unsigned int len = dataTotalLen + headerLen; andre@0: /* maxMACBytes contains the maximum bytes of bytes in the MAC, including andre@0: * |header|, assuming that there's no padding. */ andre@0: const unsigned int maxMACBytes = len - mdSize - 1; andre@0: /* numBlocks is the maximum number of hash blocks. */ andre@0: const unsigned int numBlocks = andre@0: (maxMACBytes + 1 + mdLengthSize + mdBlockSize - 1) / mdBlockSize; andre@0: /* macEndOffset is the index just past the end of the data to be andre@0: * MACed. */ andre@0: const unsigned int macEndOffset = dataLen + headerLen - mdSize; andre@0: /* c is the index of the 0x80 byte in the final hash block that andre@0: * contains application data. */ andre@0: const unsigned int c = macEndOffset % mdBlockSize; andre@0: /* indexA is the hash block number that contains the 0x80 terminating andre@0: * value. */ andre@0: const unsigned int indexA = macEndOffset / mdBlockSize; andre@0: /* indexB is the hash block number that contains the 64-bit hash andre@0: * length, in bits. */ andre@0: const unsigned int indexB = (macEndOffset + mdLengthSize) / mdBlockSize; andre@0: /* bits is the hash-length in bits. It includes the additional hash andre@0: * block for the masked HMAC key, or whole of |header| in the case of andre@0: * SSLv3. */ andre@0: unsigned int bits; andre@0: /* In order to calculate the MAC in constant time we have to handle andre@0: * the final blocks specially because the padding value could cause the andre@0: * end to appear somewhere in the final |varianceBlocks| blocks and we andre@0: * can't leak where. However, |numStartingBlocks| worth of data can andre@0: * be hashed right away because no padding value can affect whether andre@0: * they are plaintext. */ andre@0: unsigned int numStartingBlocks = 0; andre@0: /* k is the starting byte offset into the conceptual header||data where andre@0: * we start processing. */ andre@0: unsigned int k = 0; andre@0: unsigned char lengthBytes[MAX_HASH_BIT_COUNT_BYTES]; andre@0: /* hmacPad is the masked HMAC key. */ andre@0: unsigned char hmacPad[HASH_BLOCK_LENGTH_MAX]; andre@0: unsigned char firstBlock[HASH_BLOCK_LENGTH_MAX]; andre@0: unsigned char macOut[HASH_LENGTH_MAX]; andre@0: unsigned i, j; andre@0: andre@0: /* For SSLv3, if we're going to have any starting blocks then we need andre@0: * at least two because the header is larger than a single block. */ andre@0: if (numBlocks > varianceBlocks + (isSSLv3 ? 1 : 0)) { andre@0: numStartingBlocks = numBlocks - varianceBlocks; andre@0: k = mdBlockSize*numStartingBlocks; andre@0: } andre@0: andre@0: bits = 8*macEndOffset; andre@0: hashObj->begin(mdState); andre@0: if (!isSSLv3) { andre@0: /* Compute the initial HMAC block. For SSLv3, the padding and andre@0: * secret bytes are included in |header| because they take more andre@0: * than a single block. */ andre@0: bits += 8*mdBlockSize; andre@0: memset(hmacPad, 0, mdBlockSize); andre@0: PORT_Assert(macSecretLen <= sizeof(hmacPad)); andre@0: memcpy(hmacPad, macSecret, macSecretLen); andre@0: for (i = 0; i < mdBlockSize; i++) andre@0: hmacPad[i] ^= 0x36; andre@0: hashObj->update(mdState, hmacPad, mdBlockSize); andre@0: } andre@0: andre@0: j = 0; andre@0: memset(lengthBytes, 0, sizeof(lengthBytes)); andre@0: if (mdLengthSize == 16) { andre@0: j = 8; andre@0: } andre@0: if (hashObj->type == HASH_AlgMD5) { andre@0: /* MD5 appends a little-endian length. */ andre@0: for (i = 0; i < 4; i++) { andre@0: lengthBytes[i+j] = bits >> (8*i); andre@0: } andre@0: } else { andre@0: /* All other TLS hash functions use a big-endian length. */ andre@0: for (i = 0; i < 4; i++) { andre@0: lengthBytes[4+i+j] = bits >> (8*(3-i)); andre@0: } andre@0: } andre@0: andre@0: if (k > 0) { andre@0: if (isSSLv3) { andre@0: /* The SSLv3 header is larger than a single block. andre@0: * overhang is the number of bytes beyond a single andre@0: * block that the header consumes: either 7 bytes andre@0: * (SHA1) or 11 bytes (MD5). */ andre@0: const unsigned int overhang = headerLen-mdBlockSize; andre@0: hashObj->update(mdState, header, mdBlockSize); andre@0: memcpy(firstBlock, header + mdBlockSize, overhang); andre@0: memcpy(firstBlock + overhang, data, mdBlockSize-overhang); andre@0: hashObj->update(mdState, firstBlock, mdBlockSize); andre@0: for (i = 1; i < k/mdBlockSize - 1; i++) { andre@0: hashObj->update(mdState, data + mdBlockSize*i - overhang, andre@0: mdBlockSize); andre@0: } andre@0: } else { andre@0: /* k is a multiple of mdBlockSize. */ andre@0: memcpy(firstBlock, header, 13); andre@0: memcpy(firstBlock+13, data, mdBlockSize-13); andre@0: hashObj->update(mdState, firstBlock, mdBlockSize); andre@0: for (i = 1; i < k/mdBlockSize; i++) { andre@0: hashObj->update(mdState, data + mdBlockSize*i - 13, andre@0: mdBlockSize); andre@0: } andre@0: } andre@0: } andre@0: andre@0: memset(macOut, 0, sizeof(macOut)); andre@0: andre@0: /* We now process the final hash blocks. For each block, we construct andre@0: * it in constant time. If i == indexA then we'll include the 0x80 andre@0: * bytes and zero pad etc. For each block we selectively copy it, in andre@0: * constant time, to |macOut|. */ andre@0: for (i = numStartingBlocks; i <= numStartingBlocks+varianceBlocks; i++) { andre@0: unsigned char block[HASH_BLOCK_LENGTH_MAX]; andre@0: unsigned char isBlockA = constantTimeEQ8(i, indexA); andre@0: unsigned char isBlockB = constantTimeEQ8(i, indexB); andre@0: for (j = 0; j < mdBlockSize; j++) { andre@0: unsigned char isPastC = isBlockA & constantTimeGE(j, c); andre@0: unsigned char isPastCPlus1 = isBlockA & constantTimeGE(j, c+1); andre@0: unsigned char b = 0; andre@0: if (k < headerLen) { andre@0: b = header[k]; andre@0: } else if (k < dataTotalLen + headerLen) { andre@0: b = data[k-headerLen]; andre@0: } andre@0: k++; andre@0: andre@0: /* If this is the block containing the end of the andre@0: * application data, and we are at the offset for the andre@0: * 0x80 value, then overwrite b with 0x80. */ andre@0: b = (b&~isPastC) | (0x80&isPastC); andre@0: /* If this the the block containing the end of the andre@0: * application data and we're past the 0x80 value then andre@0: * just write zero. */ andre@0: b = b&~isPastCPlus1; andre@0: /* If this is indexB (the final block), but not andre@0: * indexA (the end of the data), then the 64-bit andre@0: * length didn't fit into indexA and we're having to andre@0: * add an extra block of zeros. */ andre@0: b &= ~isBlockB | isBlockA; andre@0: andre@0: /* The final bytes of one of the blocks contains the length. */ andre@0: if (j >= mdBlockSize - mdLengthSize) { andre@0: /* If this is indexB, write a length byte. */ andre@0: b = (b&~isBlockB) | andre@0: (isBlockB&lengthBytes[j-(mdBlockSize-mdLengthSize)]); andre@0: } andre@0: block[j] = b; andre@0: } andre@0: andre@0: hashObj->update(mdState, block, mdBlockSize); andre@0: hashObj->end_raw(mdState, block, NULL, mdSize); andre@0: /* If this is indexB, copy the hash value to |macOut|. */ andre@0: for (j = 0; j < mdSize; j++) { andre@0: macOut[j] |= block[j]&isBlockB; andre@0: } andre@0: } andre@0: andre@0: hashObj->begin(mdState); andre@0: andre@0: if (isSSLv3) { andre@0: /* We repurpose |hmacPad| to contain the SSLv3 pad2 block. */ andre@0: for (i = 0; i < sslv3PadLen; i++) andre@0: hmacPad[i] = 0x5c; andre@0: andre@0: hashObj->update(mdState, macSecret, macSecretLen); andre@0: hashObj->update(mdState, hmacPad, sslv3PadLen); andre@0: hashObj->update(mdState, macOut, mdSize); andre@0: } else { andre@0: /* Complete the HMAC in the standard manner. */ andre@0: for (i = 0; i < mdBlockSize; i++) andre@0: hmacPad[i] ^= 0x6a; andre@0: andre@0: hashObj->update(mdState, hmacPad, mdBlockSize); andre@0: hashObj->update(mdState, macOut, mdSize); andre@0: } andre@0: andre@0: hashObj->end(mdState, mdOut, mdOutLen, mdOutMax); andre@0: hashObj->destroy(mdState, PR_TRUE); andre@0: andre@0: return SECSuccess; andre@0: } andre@0: andre@0: SECStatus andre@0: HMAC_ConstantTime( andre@0: unsigned char *result, andre@0: unsigned int *resultLen, andre@0: unsigned int maxResultLen, andre@0: const SECHashObject *hashObj, andre@0: const unsigned char *secret, andre@0: unsigned int secretLen, andre@0: const unsigned char *header, andre@0: unsigned int headerLen, andre@0: const unsigned char *body, andre@0: unsigned int bodyLen, andre@0: unsigned int bodyTotalLen) andre@0: { andre@0: if (hashObj->end_raw == NULL) andre@0: return SECFailure; andre@0: return MAC(result, resultLen, maxResultLen, hashObj, secret, secretLen, andre@0: header, headerLen, body, bodyLen, bodyTotalLen, andre@0: 0 /* not SSLv3 */); andre@0: } andre@0: andre@0: SECStatus andre@0: SSLv3_MAC_ConstantTime( andre@0: unsigned char *result, andre@0: unsigned int *resultLen, andre@0: unsigned int maxResultLen, andre@0: const SECHashObject *hashObj, andre@0: const unsigned char *secret, andre@0: unsigned int secretLen, andre@0: const unsigned char *header, andre@0: unsigned int headerLen, andre@0: const unsigned char *body, andre@0: unsigned int bodyLen, andre@0: unsigned int bodyTotalLen) andre@0: { andre@0: if (hashObj->end_raw == NULL) andre@0: return SECFailure; andre@0: return MAC(result, resultLen, maxResultLen, hashObj, secret, secretLen, andre@0: header, headerLen, body, bodyLen, bodyTotalLen, andre@0: 1 /* SSLv3 */); andre@0: } andre@0: