add more robust pad/verify checks

This commit is contained in:
toddouska 2012-12-27 16:35:43 -08:00
parent 60f4919ee6
commit f0bc61a5d3
2 changed files with 266 additions and 27 deletions

View File

@ -394,6 +394,12 @@ enum Misc {
PAD_MD5 = 48, /* pad length for finished */
PAD_SHA = 40, /* pad length for finished */
MAX_PAD_SIZE = 256, /* maximum length of padding */
COMPRESS_DUMMY_SIZE = 64, /* compression dummy round size */
COMPRESS_CONSTANT = 13, /* compression calc constant */
COMPRESS_UPPER = 55, /* compression calc numerator */
COMPRESS_LOWER = 64, /* compression calc denominator */
PEM_LINE_LEN = 80, /* PEM line max + fudge */
LENGTH_SZ = 2, /* length field for HMAC, data only */
VERSION_SZ = 2, /* length of proctocol version */

View File

@ -3092,6 +3092,252 @@ static int DecryptMessage(CYASSL* ssl, byte* input, word32 sz, word32* idx)
}
#ifndef NO_MD5
static INLINE void Md5Round(byte* data, int sz)
{
Md5 md5;
InitMd5(&md5);
Md5Update(&md5, data, sz);
}
#endif
static INLINE void ShaRound(byte* data, int sz)
{
Sha sha;
InitSha(&sha);
ShaUpdate(&sha, data, sz);
}
#ifndef NO_SHA256
static INLINE void Sha256Round(byte* data, int sz)
{
Sha256 sha256;
InitSha256(&sha256);
Sha256Update(&sha256, data, sz);
}
#endif
#ifdef CYASSL_SHA384
static INLINE void Sha384Round(byte* data, int sz)
{
Sha384 sha384;
InitSha384(&sha384);
Sha384Update(&sha384, data, sz);
}
#endif
#ifdef CYASSL_SHA512
static INLINE void Sha512Round(byte* data, int sz)
{
Sha512 sha512;
InitSha512(&sha512);
Sha512Update(&sha512, data, sz);
}
#endif
#ifdef CYASSL_RIPEMD
static INLINE void RmdRound(byte* data, int sz)
{
Ripemd ripemd;
InitRipemd(&ripemd);
RipemdUpdate(&ripemd, data, sz);
}
#endif
static INLINE void DoRound(int type, byte* data, int sz)
{
switch (type) {
case no_mac :
break;
#ifndef NO_MD5
case md5_mac :
Md5Round(data, sz);
break;
#endif
case sha_mac :
ShaRound(data, sz);
break;
#ifndef NO_SHA256
case sha256_mac :
Sha256Round(data, sz);
break;
#endif
#ifdef CYASSL_SHA384
case sha384_mac :
Sha384Round(data, sz);
break;
#endif
#ifdef CYASSL_SHA512
case sha512_mac :
Sha512Round(data, sz);
break;
#endif
#ifdef CYASSL_RIPEMD
case rmd_mac :
RmdRound(data, sz);
break;
#endif
default:
CYASSL_MSG("Bad round type");
break;
}
}
/* do number of compression rounds on dummy data */
static INLINE void CompressRounds(CYASSL* ssl, int rounds)
{
int i;
byte dummy[COMPRESS_DUMMY_SIZE];
XMEMSET(dummy, 1, sizeof(dummy));
for (i = 0; i < rounds; i++)
DoRound(ssl->specs.mac_algorithm, dummy, sizeof(dummy));
}
/* check all length bytes for equality, return 0 on success */
static int ConstantCompare(const byte* a, const byte* b, int length)
{
int i;
int good = 0;
int bad = 0;
for (i = 0; i < length; i++) {
if (a[i] == b[i])
good++;
else
bad++;
}
if (good == length)
return 0;
else
return 0 - bad; /* compare failed */
}
/* check all length bytes for the pad value, return 0 on success */
static int PadCheck(const byte* input, byte pad, int length)
{
int i;
int good = 0;
int bad = 0;
for (i = 0; i < length; i++) {
if (input[i] == pad)
good++;
else
bad++;
}
if (good == length)
return 0;
else
return 0 - bad; /* pad check failed */
}
/* get compression extra rounds */
static int GetRounds(int pLen, int padLen, int t)
{
int roundL1 = 1; /* round up flags */
int roundL2 = 1;
int L1 = COMPRESS_CONSTANT + pLen - t;
int L2 = COMPRESS_CONSTANT + pLen - padLen - 1 - t;
L1 -= COMPRESS_UPPER;
L2 -= COMPRESS_UPPER;
if ( (L1 % COMPRESS_LOWER) == 0)
roundL1 = 0;
if ( (L2 % COMPRESS_LOWER) == 0)
roundL2 = 0;
L1 /= COMPRESS_LOWER;
L2 /= COMPRESS_LOWER;
L1 += roundL1;
L2 += roundL2;
return L1 - L2;
}
/* timing resistant pad/verify check, return 0 on success */
static int TimingPadVerify(CYASSL* ssl, const byte* input, int padLen, int t,
int pLen)
{
byte verify[SHA256_DIGEST_SIZE];
byte dummy[MAX_PAD_SIZE];
XMEMSET(dummy, 1, sizeof(dummy));
if ( (t + padLen + 1) > pLen) {
CYASSL_MSG("Plain Len not long enough for pad/mac");
PadCheck(dummy, (byte)padLen, MAX_PAD_SIZE);
ssl->hmac(ssl, verify, input, pLen - t, application_data, 1);
ConstantCompare(verify, input + pLen - t, t);
return VERIFY_MAC_ERROR;
}
if (PadCheck(input + pLen - (padLen + 1), (byte)padLen, padLen + 1) != 0) {
CYASSL_MSG("PadCheck failed");
PadCheck(dummy, (byte)padLen, MAX_PAD_SIZE - padLen - 1);
ssl->hmac(ssl, verify, input, pLen - t, application_data, 1);
ConstantCompare(verify, input + pLen - t, t);
return VERIFY_MAC_ERROR;
}
PadCheck(dummy, (byte)padLen, MAX_PAD_SIZE - padLen - 1);
ssl->hmac(ssl, verify, input, pLen - padLen - 1 - t, application_data, 1);
CompressRounds(ssl, GetRounds(pLen, padLen, t));
if (ConstantCompare(verify, input + (pLen - padLen - 1 - t), t) != 0) {
CYASSL_MSG("Verify MAC compare failed");
return VERIFY_MAC_ERROR;
}
return 0;
}
int DoApplicationData(CYASSL* ssl, byte* input, word32* inOutIdx)
{
word32 msgSz = ssl->keys.encryptSz;
@ -3099,15 +3345,13 @@ int DoApplicationData(CYASSL* ssl, byte* input, word32* inOutIdx)
padByte = 0,
idx = *inOutIdx,
digestSz = ssl->specs.hash_size;
int dataSz;
int dataSz, ret;
int ivExtra = 0;
byte* rawData = input + idx; /* keep current for hmac */
#ifdef HAVE_LIBZ
byte decomp[MAX_RECORD_SIZE + MAX_COMP_EXTRA];
#endif
byte verify[SHA256_DIGEST_SIZE];
const byte* mac;
byte verify[SHA256_DIGEST_SIZE];
if (ssl->options.handShakeState != HANDSHAKE_DONE) {
CYASSL_MSG("Received App data before handshake complete");
@ -3119,8 +3363,17 @@ int DoApplicationData(CYASSL* ssl, byte* input, word32* inOutIdx)
ivExtra = ssl->specs.block_size;
pad = *(input + idx + msgSz - ivExtra - 1);
padByte = 1;
ret = TimingPadVerify(ssl, input + idx, pad, digestSz, msgSz - ivExtra);
if (ret != 0)
return ret;
}
if (ssl->specs.cipher_type == aead) {
else if (ssl->specs.cipher_type == stream) {
ssl->hmac(ssl, verify, rawData, msgSz - digestSz, application_data, 1);
if (ConstantCompare(verify, input + msgSz - digestSz, digestSz) != 0) {
return VERIFY_MAC_ERROR;
}
}
else if (ssl->specs.cipher_type == aead) {
ivExtra = AES_GCM_EXP_IV_SZ;
digestSz = AEAD_AUTH_TAG_SZ;
}
@ -3133,10 +3386,7 @@ int DoApplicationData(CYASSL* ssl, byte* input, word32* inOutIdx)
/* read data */
if (dataSz) {
int rawSz = dataSz; /* keep raw size for hmac */
if (ssl->specs.cipher_type != aead)
ssl->hmac(ssl, verify, rawData, rawSz, application_data, 1);
int rawSz = dataSz; /* keep raw size for idx adjustment */
#ifdef HAVE_LIBZ
if (ssl->options.usingCompression) {
@ -3144,34 +3394,17 @@ int DoApplicationData(CYASSL* ssl, byte* input, word32* inOutIdx)
if (dataSz < 0) return dataSz;
}
#endif
if (ssl->options.usingCompression)
idx += rawSz;
else
idx += dataSz;
idx += rawSz;
ssl->buffers.clearOutputBuffer.buffer = rawData;
ssl->buffers.clearOutputBuffer.length = dataSz;
}
/* read mac and fill */
mac = input + idx;
idx += digestSz;
idx += pad;
if (padByte)
idx++;
/* verify */
if (dataSz) {
if (ssl->specs.cipher_type != aead && XMEMCMP(mac, verify, digestSz)) {
CYASSL_MSG("App data verify mac error");
return VERIFY_MAC_ERROR;
}
}
else
GetSEQIncrement(ssl, 1); /* even though no data, increment verify */
#ifdef HAVE_LIBZ
/* decompress could be bigger, overwrite after verify */
if (ssl->options.usingCompression)