Bug 1539788 - Add length checks for cryptographic primitives r=mt,jcj
authorKevin Jacobs <kjacobs@mozilla.com>
Tue, 13 Aug 2019 15:58:55 +0000
changeset 15252 dfd6996fe7425eb0437346d11a01082f16fcfe34
parent 15251 9bc47e69613e9ee9c8aaaf555150f815fc8161d9
child 15253 9d1f5e71773d4e3146524096d74cb96c8df51abe
push id3463
push userjjones@mozilla.com
push dateTue, 13 Aug 2019 23:49:02 +0000
reviewersmt, jcj
bugs1539788
Bug 1539788 - Add length checks for cryptographic primitives r=mt,jcj This patch adds additional length checks around cryptographic primitives. Differential Revision: https://phabricator.services.mozilla.com/D36079
lib/freebl/chacha20poly1305.c
lib/freebl/ctr.c
lib/freebl/gcm.c
lib/freebl/intel-gcm-wrap.c
lib/freebl/rsapkcs.c
--- a/lib/freebl/chacha20poly1305.c
+++ b/lib/freebl/chacha20poly1305.c
@@ -253,16 +253,21 @@ ChaCha20Poly1305_Open(const ChaCha20Poly
         PORT_SetError(SEC_ERROR_INPUT_LEN);
         return SECFailure;
     }
     ciphertextLen = inputLen - ctx->tagLen;
     if (maxOutputLen < ciphertextLen) {
         PORT_SetError(SEC_ERROR_OUTPUT_LEN);
         return SECFailure;
     }
+    // ChaCha has a 64 octet block, with a 32-bit block counter.
+    if (inputLen >= (1ULL << (6 + 32)) + ctx->tagLen) {
+        PORT_SetError(SEC_ERROR_INPUT_LEN);
+        return SECFailure;
+    }
 
     PORT_Memset(block, 0, sizeof(block));
     // Generate a block of keystream. The first 32 bytes will be the poly1305
     // key. The remainder of the block is discarded.
     ChaCha20Xor(block, (uint8_t *)block, sizeof(block), (uint8_t *)ctx->key,
                 (uint8_t *)nonce, 0);
     Poly1305Do(tag, ad, adLen, input, ciphertextLen, block);
     if (NSS_SecureMemcmp(tag, &input[ciphertextLen], ctx->tagLen) != 0) {
--- a/lib/freebl/ctr.c
+++ b/lib/freebl/ctr.c
@@ -123,16 +123,21 @@ SECStatus
 CTR_Update(CTRContext *ctr, unsigned char *outbuf,
            unsigned int *outlen, unsigned int maxout,
            const unsigned char *inbuf, unsigned int inlen,
            unsigned int blocksize)
 {
     unsigned int tmp;
     SECStatus rv;
 
+    // Limit block count to 2^counterBits - 2
+    if (inlen > ((1ULL << ctr->counterBits) - 2) * AES_BLOCK_SIZE) {
+        PORT_SetError(SEC_ERROR_INPUT_LEN);
+        return SECFailure;
+    }
     if (maxout < inlen) {
         *outlen = inlen;
         PORT_SetError(SEC_ERROR_OUTPUT_LEN);
         return SECFailure;
     }
     *outlen = 0;
     if (ctr->bufPtr != blocksize) {
         unsigned int needed = PR_MIN(blocksize - ctr->bufPtr, inlen);
@@ -194,16 +199,21 @@ CTR_Update_HW_AES(CTRContext *ctr, unsig
                   unsigned int *outlen, unsigned int maxout,
                   const unsigned char *inbuf, unsigned int inlen,
                   unsigned int blocksize)
 {
     unsigned int fullblocks;
     unsigned int tmp;
     SECStatus rv;
 
+    // Limit block count to 2^counterBits - 2
+    if (inlen > ((1ULL << ctr->counterBits) - 2) * AES_BLOCK_SIZE) {
+        PORT_SetError(SEC_ERROR_INPUT_LEN);
+        return SECFailure;
+    }
     if (maxout < inlen) {
         *outlen = inlen;
         PORT_SetError(SEC_ERROR_OUTPUT_LEN);
         return SECFailure;
     }
     *outlen = 0;
     if (ctr->bufPtr != blocksize) {
         unsigned int needed = PR_MIN(blocksize - ctr->bufPtr, inlen);
--- a/lib/freebl/gcm.c
+++ b/lib/freebl/gcm.c
@@ -474,16 +474,22 @@ cleanup:
 }
 
 SECStatus
 gcmHash_Reset(gcmHashContext *ghash, const unsigned char *AAD,
               unsigned int AADLen)
 {
     SECStatus rv;
 
+    // Limit AADLen in accordance with SP800-38D
+    if (sizeof(AADLen) >= 8 && AADLen > (1ULL << 61) - 1) {
+        PORT_SetError(SEC_ERROR_INPUT_LEN);
+        return SECFailure;
+    }
+
     ghash->cLen = 0;
     PORT_Memset(ghash->counterBuf, 0, GCM_HASH_LEN_LEN * 2);
     ghash->bufLen = 0;
     rv = gcm_zeroX(ghash);
     if (rv != SECSuccess) {
         return rv;
     }
 
--- a/lib/freebl/intel-gcm-wrap.c
+++ b/lib/freebl/intel-gcm-wrap.c
@@ -57,16 +57,22 @@ intel_AES_GCM_CreateContext(void *contex
     __m128i ONE = _mm_set_epi32(0, 0, 0, 1);
     unsigned int j;
     SECStatus rv;
 
     if (gcmParams->ulIvLen == 0) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return NULL;
     }
+    // Limit AADLen in accordance with SP800-38D
+    if (sizeof(AAD_whole_len) >= 8 && AAD_whole_len > (1ULL << 61) - 1) {
+        PORT_SetError(SEC_ERROR_INPUT_LEN);
+        return NULL;
+    }
+
     gcm = PORT_ZNew(intel_AES_GCMContext);
     if (gcm == NULL) {
         return NULL;
     }
 
     /* initialize context fields */
     gcm->aes_context = aes;
     gcm->tagBits = gcmParams->ulTagBits;
@@ -155,16 +161,24 @@ intel_AES_GCM_EncryptUpdate(intel_AES_GC
                             unsigned int *outlen, unsigned int maxout,
                             const unsigned char *inbuf, unsigned int inlen,
                             unsigned int blocksize)
 {
     unsigned int tagBytes;
     unsigned char T[AES_BLOCK_SIZE];
     unsigned int j;
 
+    // GCM has a 16 octet block, with a 32-bit block counter
+    // Limit in accordance with SP800-38D
+    if (sizeof(inlen) > 4 &&
+        inlen >= ((1ULL << 32) - 2) * AES_BLOCK_SIZE) {
+        PORT_SetError(SEC_ERROR_INPUT_LEN);
+        return SECFailure;
+    }
+
     tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE;
     if (UINT_MAX - inlen < tagBytes) {
         PORT_SetError(SEC_ERROR_INPUT_LEN);
         return SECFailure;
     }
     if (maxout < inlen + tagBytes) {
         *outlen = inlen + tagBytes;
         PORT_SetError(SEC_ERROR_OUTPUT_LEN);
@@ -212,16 +226,24 @@ intel_AES_GCM_DecryptUpdate(intel_AES_GC
     if (inlen < tagBytes) {
         PORT_SetError(SEC_ERROR_INPUT_LEN);
         return SECFailure;
     }
 
     inlen -= tagBytes;
     intag = inbuf + inlen;
 
+    // GCM has a 16 octet block, with a 32-bit block counter
+    // Limit in accordance with SP800-38D
+    if (sizeof(inlen) > 4 &&
+        inlen >= ((1ULL << 32) - 2) * AES_BLOCK_SIZE) {
+        PORT_SetError(SEC_ERROR_INPUT_LEN);
+        return SECFailure;
+    }
+
     if (maxout < inlen) {
         *outlen = inlen;
         PORT_SetError(SEC_ERROR_OUTPUT_LEN);
         return SECFailure;
     }
 
     intel_aes_gcmDEC(
         inbuf,
--- a/lib/freebl/rsapkcs.c
+++ b/lib/freebl/rsapkcs.c
@@ -110,17 +110,17 @@ rsa_modulusBits(SECItem *modulus)
  */
 static unsigned char *
 rsa_FormatOneBlock(unsigned modulusLen,
                    RSA_BlockType blockType,
                    SECItem *data)
 {
     unsigned char *block;
     unsigned char *bp;
-    int padLen;
+    unsigned int padLen;
     int i, j;
     SECStatus rv;
 
     block = (unsigned char *)PORT_Alloc(modulusLen);
     if (block == NULL)
         return NULL;
 
     bp = block;
@@ -130,24 +130,25 @@ rsa_FormatOneBlock(unsigned modulusLen,
      *  0x00 || BlockType
      */
     *bp++ = RSA_BLOCK_FIRST_OCTET;
     *bp++ = (unsigned char)blockType;
 
     switch (blockType) {
 
         /*
-       * Blocks intended for private-key operation.
-       */
+         * Blocks intended for private-key operation.
+         */
         case RSA_BlockPrivate: /* preferred method */
             /*
-         * 0x00 || BT || Pad || 0x00 || ActualData
-         *   1      1   padLen    1      data->len
-         * Pad is either all 0x00 or all 0xff bytes, depending on blockType.
-         */
+             * 0x00 || BT || Pad || 0x00 || ActualData
+             *   1      1   padLen    1      data->len
+             * padLen must be at least RSA_BLOCK_MIN_PAD_LEN (8) bytes.
+             * Pad is either all 0x00 or all 0xff bytes, depending on blockType.
+             */
             padLen = modulusLen - data->len - 3;
             PORT_Assert(padLen >= RSA_BLOCK_MIN_PAD_LEN);
             if (padLen < RSA_BLOCK_MIN_PAD_LEN) {
                 PORT_Free(block);
                 return NULL;
             }
             PORT_Memset(bp, RSA_BLOCK_PRIVATE_PAD_OCTET, padLen);
             bp += padLen;
@@ -157,25 +158,26 @@ rsa_FormatOneBlock(unsigned modulusLen,
 
         /*
          * Blocks intended for public-key operation.
          */
         case RSA_BlockPublic:
             /*
              * 0x00 || BT || Pad || 0x00 || ActualData
              *   1      1   padLen    1      data->len
-             * Pad is all non-zero random bytes.
+             * Pad is 8 or more non-zero random bytes.
              *
              * Build the block left to right.
              * Fill the entire block from Pad to the end with random bytes.
              * Use the bytes after Pad as a supply of extra random bytes from
              * which to find replacements for the zero bytes in Pad.
              * If we need more than that, refill the bytes after Pad with
              * new random bytes as necessary.
              */
+
             padLen = modulusLen - (data->len + 3);
             PORT_Assert(padLen >= RSA_BLOCK_MIN_PAD_LEN);
             if (padLen < RSA_BLOCK_MIN_PAD_LEN) {
                 PORT_Free(block);
                 return NULL;
             }
             j = modulusLen - 2;
             rv = RNG_GenerateGlobalRandomBytes(bp, j);
@@ -231,18 +233,19 @@ rsa_FormatBlock(SECItem *result,
         case RSA_BlockPrivate:
         case RSA_BlockPublic:
             /*
              * 0x00 || BT || Pad || 0x00 || ActualData
              *
              * The "3" below is the first octet + the second octet + the 0x00
              * octet that always comes just before the ActualData.
              */
-            PORT_Assert(data->len <= (modulusLen - (3 + RSA_BLOCK_MIN_PAD_LEN)));
-
+            if (data->len > (modulusLen - (3 + RSA_BLOCK_MIN_PAD_LEN))) {
+                return SECFailure;
+            }
             result->data = rsa_FormatOneBlock(modulusLen, blockType, data);
             if (result->data == NULL) {
                 result->len = 0;
                 return SECFailure;
             }
             result->len = modulusLen;
 
             break;