Bug 1577953 - Support longer (up to RFC maximum) HKDF outputs r=jcj NSS_3_46_BRANCH
authorKevin Jacobs <kjacobs@mozilla.com>
Tue, 01 Oct 2019 18:13:17 +0000
branchNSS_3_46_BRANCH
changeset 15323 f8dc0ce54c16b5094fcbea3befb1634cece457b0
parent 15322 e2945c4342867ffb29000910df3a14f32a10a17e
child 15324 42682c941fd6e8e89200aaa4adfa496d1102e03e
push id3524
push userjjones@mozilla.com
push dateWed, 02 Oct 2019 21:47:02 +0000
reviewersjcj
bugs1577953
Bug 1577953 - Support longer (up to RFC maximum) HKDF outputs r=jcj HKDF-Expand enforces a maximum output length much shorter than stated in the RFC. This patch aligns the implementation with the RFC by allocating more output space when necessary. Differential Revision: https://phabricator.services.mozilla.com/D45249
lib/softoken/pkcs11c.c
--- a/lib/softoken/pkcs11c.c
+++ b/lib/softoken/pkcs11c.c
@@ -7858,33 +7858,35 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession
         hkdf : {
             const CK_NSS_HKDFParams *params =
                 (const CK_NSS_HKDFParams *)pMechanism->pParameter;
             const SECHashObject *rawHash;
             unsigned hashLen;
             CK_BYTE hashbuf[HASH_LENGTH_MAX];
             CK_BYTE *prk; /* psuedo-random key */
             CK_ULONG prkLen;
-            CK_BYTE *okm; /* output keying material */
+            CK_BYTE *okm;                 /* output keying material */
+            unsigned allocated_space = 0; /* If we need more work space, track it */
+            unsigned char *key_buf = &key_block[0];
 
             rawHash = HASH_GetRawHashObject(hashType);
             if (rawHash == NULL || rawHash->length > sizeof(hashbuf)) {
                 crv = CKR_FUNCTION_FAILED;
                 break;
             }
             hashLen = rawHash->length;
 
             if (pMechanism->ulParameterLen != sizeof(CK_NSS_HKDFParams) ||
                 !params || (!params->bExpand && !params->bExtract) ||
                 (params->bExtract && params->ulSaltLen > 0 && !params->pSalt) ||
                 (params->bExpand && params->ulInfoLen > 0 && !params->pInfo)) {
                 crv = CKR_MECHANISM_PARAM_INVALID;
                 break;
             }
-            if (keySize == 0 || keySize > sizeof key_block ||
+            if (keySize == 0 ||
                 (!params->bExpand && keySize > hashLen) ||
                 (params->bExpand && keySize > 255 * hashLen)) {
                 crv = CKR_TEMPLATE_INCONSISTENT;
                 break;
             }
             crv = sftk_DeriveSensitiveCheck(sourceKey, key);
             if (crv != CKR_OK)
                 break;
@@ -7924,44 +7926,59 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession
 
             /* HKDF-Expand */
             if (!params->bExpand) {
                 okm = prk;
             } else {
                 /* T(1) = HMAC-Hash(prk, "" | info | 0x01)
                  * T(n) = HMAC-Hash(prk, T(n-1) | info | n
                  * key material = T(1) | ... | T(n)
+                 *
+                 * If the requested output length does not fit
+                 * within |key_block|, allocate space for expansion.
                  */
                 HMACContext *hmac;
                 CK_BYTE bi;
-                unsigned iterations = PR_ROUNDUP(keySize, hashLen) / hashLen;
+                unsigned n_bytes = PR_ROUNDUP(keySize, hashLen);
+                unsigned iterations = n_bytes / hashLen;
                 hmac = HMAC_Create(rawHash, prk, prkLen, isFIPS);
                 if (hmac == NULL) {
                     crv = CKR_HOST_MEMORY;
                     break;
                 }
-                for (bi = 1; bi <= iterations; ++bi) {
+                if (n_bytes > sizeof(key_block)) {
+                    key_buf = PORT_Alloc(n_bytes);
+                    if (key_buf == NULL) {
+                        crv = CKR_HOST_MEMORY;
+                        break;
+                    }
+                    allocated_space = n_bytes;
+                }
+                for (bi = 1; bi <= iterations && bi > 0; ++bi) {
                     unsigned len;
                     HMAC_Begin(hmac);
                     if (bi > 1) {
-                        HMAC_Update(hmac, key_block + ((bi - 2) * hashLen), hashLen);
+                        HMAC_Update(hmac, key_buf + ((bi - 2) * hashLen), hashLen);
                     }
                     if (params->ulInfoLen != 0) {
                         HMAC_Update(hmac, params->pInfo, params->ulInfoLen);
                     }
                     HMAC_Update(hmac, &bi, 1);
-                    HMAC_Finish(hmac, key_block + ((bi - 1) * hashLen), &len,
+                    HMAC_Finish(hmac, key_buf + ((bi - 1) * hashLen), &len,
                                 hashLen);
                     PORT_Assert(len == hashLen);
                 }
                 HMAC_Destroy(hmac, PR_TRUE);
-                okm = key_block;
+                okm = key_buf;
             }
             /* key material = prk */
             crv = sftk_forceAttribute(key, CKA_VALUE, okm, keySize);
+            if (allocated_space) {
+                PORT_ZFree(key_buf, allocated_space);
+            }
             break;
         } /* end of CKM_NSS_HKDF_* */
 
         case CKM_NSS_JPAKE_ROUND2_SHA1:
             hashType = HASH_AlgSHA1;
             goto jpake2;
         case CKM_NSS_JPAKE_ROUND2_SHA256:
             hashType = HASH_AlgSHA256;