mirror of https://github.com/wolfSSL/wolfssl.git
add more robust pad/verify checks
parent
60f4919ee6
commit
f0bc61a5d3
|
@ -394,6 +394,12 @@ enum Misc {
|
||||||
|
|
||||||
PAD_MD5 = 48, /* pad length for finished */
|
PAD_MD5 = 48, /* pad length for finished */
|
||||||
PAD_SHA = 40, /* 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 */
|
PEM_LINE_LEN = 80, /* PEM line max + fudge */
|
||||||
LENGTH_SZ = 2, /* length field for HMAC, data only */
|
LENGTH_SZ = 2, /* length field for HMAC, data only */
|
||||||
VERSION_SZ = 2, /* length of proctocol version */
|
VERSION_SZ = 2, /* length of proctocol version */
|
||||||
|
|
287
src/internal.c
287
src/internal.c
|
@ -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)
|
int DoApplicationData(CYASSL* ssl, byte* input, word32* inOutIdx)
|
||||||
{
|
{
|
||||||
word32 msgSz = ssl->keys.encryptSz;
|
word32 msgSz = ssl->keys.encryptSz;
|
||||||
|
@ -3099,15 +3345,13 @@ int DoApplicationData(CYASSL* ssl, byte* input, word32* inOutIdx)
|
||||||
padByte = 0,
|
padByte = 0,
|
||||||
idx = *inOutIdx,
|
idx = *inOutIdx,
|
||||||
digestSz = ssl->specs.hash_size;
|
digestSz = ssl->specs.hash_size;
|
||||||
int dataSz;
|
int dataSz, ret;
|
||||||
int ivExtra = 0;
|
int ivExtra = 0;
|
||||||
byte* rawData = input + idx; /* keep current for hmac */
|
byte* rawData = input + idx; /* keep current for hmac */
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
byte decomp[MAX_RECORD_SIZE + MAX_COMP_EXTRA];
|
byte decomp[MAX_RECORD_SIZE + MAX_COMP_EXTRA];
|
||||||
#endif
|
#endif
|
||||||
|
byte verify[SHA256_DIGEST_SIZE];
|
||||||
byte verify[SHA256_DIGEST_SIZE];
|
|
||||||
const byte* mac;
|
|
||||||
|
|
||||||
if (ssl->options.handShakeState != HANDSHAKE_DONE) {
|
if (ssl->options.handShakeState != HANDSHAKE_DONE) {
|
||||||
CYASSL_MSG("Received App data before handshake complete");
|
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;
|
ivExtra = ssl->specs.block_size;
|
||||||
pad = *(input + idx + msgSz - ivExtra - 1);
|
pad = *(input + idx + msgSz - ivExtra - 1);
|
||||||
padByte = 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;
|
ivExtra = AES_GCM_EXP_IV_SZ;
|
||||||
digestSz = AEAD_AUTH_TAG_SZ;
|
digestSz = AEAD_AUTH_TAG_SZ;
|
||||||
}
|
}
|
||||||
|
@ -3133,10 +3386,7 @@ int DoApplicationData(CYASSL* ssl, byte* input, word32* inOutIdx)
|
||||||
|
|
||||||
/* read data */
|
/* read data */
|
||||||
if (dataSz) {
|
if (dataSz) {
|
||||||
int rawSz = dataSz; /* keep raw size for hmac */
|
int rawSz = dataSz; /* keep raw size for idx adjustment */
|
||||||
|
|
||||||
if (ssl->specs.cipher_type != aead)
|
|
||||||
ssl->hmac(ssl, verify, rawData, rawSz, application_data, 1);
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
if (ssl->options.usingCompression) {
|
if (ssl->options.usingCompression) {
|
||||||
|
@ -3144,34 +3394,17 @@ int DoApplicationData(CYASSL* ssl, byte* input, word32* inOutIdx)
|
||||||
if (dataSz < 0) return dataSz;
|
if (dataSz < 0) return dataSz;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
idx += rawSz;
|
||||||
if (ssl->options.usingCompression)
|
|
||||||
idx += rawSz;
|
|
||||||
else
|
|
||||||
idx += dataSz;
|
|
||||||
|
|
||||||
ssl->buffers.clearOutputBuffer.buffer = rawData;
|
ssl->buffers.clearOutputBuffer.buffer = rawData;
|
||||||
ssl->buffers.clearOutputBuffer.length = dataSz;
|
ssl->buffers.clearOutputBuffer.length = dataSz;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read mac and fill */
|
|
||||||
mac = input + idx;
|
|
||||||
idx += digestSz;
|
idx += digestSz;
|
||||||
|
|
||||||
idx += pad;
|
idx += pad;
|
||||||
if (padByte)
|
if (padByte)
|
||||||
idx++;
|
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
|
#ifdef HAVE_LIBZ
|
||||||
/* decompress could be bigger, overwrite after verify */
|
/* decompress could be bigger, overwrite after verify */
|
||||||
if (ssl->options.usingCompression)
|
if (ssl->options.usingCompression)
|
||||||
|
|
Loading…
Reference in New Issue