Source code

Revision control

Copy as Markdown

Other Tools

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