tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

sftkhmac.c (15105B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "seccomon.h"
      6 #include "secerr.h"
      7 #include "blapi.h"
      8 #include "pkcs11i.h"
      9 #include "softoken.h"
     10 #include "hmacct.h"
     11 
     12 /* Wrappers to avoid undefined behavior calling functions through a pointer of incorrect type. */
     13 static void
     14 SFTKMAC_CMAC_Destroy(void *ctx, PRBool freeit)
     15 {
     16    CMACContext *cctx = ctx;
     17    CMAC_Destroy(cctx, freeit);
     18 }
     19 
     20 static void
     21 SFTKMAC_HMAC_Destroy(void *ctx, PRBool freeit)
     22 {
     23    HMACContext *hctx = ctx;
     24    HMAC_Destroy(hctx, freeit);
     25 }
     26 
     27 /* sftk_HMACMechanismToHash converts a PKCS#11 MAC mechanism into a freebl hash
     28 * type. */
     29 HASH_HashType
     30 sftk_HMACMechanismToHash(CK_MECHANISM_TYPE mech)
     31 {
     32    switch (mech) {
     33        case CKM_MD2_HMAC:
     34            return HASH_AlgMD2;
     35        case CKM_MD5_HMAC:
     36        case CKM_SSL3_MD5_MAC:
     37            return HASH_AlgMD5;
     38        case CKM_SHA_1_HMAC:
     39        case CKM_SSL3_SHA1_MAC:
     40            return HASH_AlgSHA1;
     41        case CKM_SHA224_HMAC:
     42            return HASH_AlgSHA224;
     43        case CKM_SHA256_HMAC:
     44            return HASH_AlgSHA256;
     45        case CKM_SHA384_HMAC:
     46            return HASH_AlgSHA384;
     47        case CKM_SHA512_HMAC:
     48            return HASH_AlgSHA512;
     49        case CKM_SHA3_224_HMAC:
     50            return HASH_AlgSHA3_224;
     51        case CKM_SHA3_256_HMAC:
     52            return HASH_AlgSHA3_256;
     53        case CKM_SHA3_384_HMAC:
     54            return HASH_AlgSHA3_384;
     55        case CKM_SHA3_512_HMAC:
     56            return HASH_AlgSHA3_512;
     57    }
     58    return HASH_AlgNULL;
     59 }
     60 
     61 static sftk_MACConstantTimeCtx *
     62 SetupMAC(CK_MECHANISM_PTR mech, SFTKObject *key)
     63 {
     64    CK_NSS_MAC_CONSTANT_TIME_PARAMS *params =
     65        (CK_NSS_MAC_CONSTANT_TIME_PARAMS *)mech->pParameter;
     66    sftk_MACConstantTimeCtx *ctx;
     67    HASH_HashType alg;
     68    SFTKAttribute *keyval;
     69    unsigned char secret[sizeof(ctx->secret)];
     70    unsigned int secretLength;
     71 
     72    if (mech->ulParameterLen != sizeof(CK_NSS_MAC_CONSTANT_TIME_PARAMS)) {
     73        return NULL;
     74    }
     75 
     76    alg = sftk_HMACMechanismToHash(params->macAlg);
     77    if (alg == HASH_AlgNULL) {
     78        return NULL;
     79    }
     80 
     81    keyval = sftk_FindAttribute(key, CKA_VALUE);
     82    if (keyval == NULL) {
     83        return NULL;
     84    }
     85    secretLength = keyval->attrib.ulValueLen;
     86    if (secretLength > sizeof(secret)) {
     87        sftk_FreeAttribute(keyval);
     88        return NULL;
     89    }
     90    memcpy(secret, keyval->attrib.pValue, secretLength);
     91    sftk_FreeAttribute(keyval);
     92 
     93    ctx = PORT_Alloc(sizeof(sftk_MACConstantTimeCtx));
     94    if (!ctx) {
     95        PORT_Memset(secret, 0, secretLength);
     96        return NULL;
     97    }
     98 
     99    memcpy(ctx->secret, secret, secretLength);
    100    ctx->secretLength = secretLength;
    101    ctx->hash = HASH_GetRawHashObject(alg);
    102    ctx->totalLength = params->ulBodyTotalLen;
    103    PORT_Memset(secret, 0, secretLength);
    104 
    105    return ctx;
    106 }
    107 
    108 sftk_MACConstantTimeCtx *
    109 sftk_HMACConstantTime_New(CK_MECHANISM_PTR mech, SFTKObject *key)
    110 {
    111    CK_NSS_MAC_CONSTANT_TIME_PARAMS *params =
    112        (CK_NSS_MAC_CONSTANT_TIME_PARAMS *)mech->pParameter;
    113    sftk_MACConstantTimeCtx *ctx;
    114 
    115    if (params->ulHeaderLen > sizeof(ctx->header)) {
    116        return NULL;
    117    }
    118    ctx = SetupMAC(mech, key);
    119    if (!ctx) {
    120        return NULL;
    121    }
    122 
    123    ctx->headerLength = params->ulHeaderLen;
    124    memcpy(ctx->header, params->pHeader, params->ulHeaderLen);
    125    return ctx;
    126 }
    127 
    128 sftk_MACConstantTimeCtx *
    129 sftk_SSLv3MACConstantTime_New(CK_MECHANISM_PTR mech, SFTKObject *key)
    130 {
    131    CK_NSS_MAC_CONSTANT_TIME_PARAMS *params =
    132        (CK_NSS_MAC_CONSTANT_TIME_PARAMS *)mech->pParameter;
    133    unsigned int padLength = 40, j;
    134    sftk_MACConstantTimeCtx *ctx;
    135 
    136    if (params->macAlg != CKM_SSL3_MD5_MAC &&
    137        params->macAlg != CKM_SSL3_SHA1_MAC) {
    138        return NULL;
    139    }
    140    ctx = SetupMAC(mech, key);
    141    if (!ctx) {
    142        return NULL;
    143    }
    144 
    145    if (params->macAlg == CKM_SSL3_MD5_MAC) {
    146        padLength = 48;
    147    }
    148 
    149    ctx->headerLength =
    150        ctx->secretLength +
    151        padLength +
    152        params->ulHeaderLen;
    153 
    154    if (ctx->headerLength > sizeof(ctx->header)) {
    155        goto loser;
    156    }
    157 
    158    j = 0;
    159    memcpy(&ctx->header[j], ctx->secret, ctx->secretLength);
    160    j += ctx->secretLength;
    161    memset(&ctx->header[j], 0x36, padLength);
    162    j += padLength;
    163    memcpy(&ctx->header[j], params->pHeader, params->ulHeaderLen);
    164 
    165    return ctx;
    166 
    167 loser:
    168    PORT_Free(ctx);
    169    return NULL;
    170 }
    171 
    172 void
    173 sftk_HMACConstantTime_Update(void *pctx, const unsigned char *data, unsigned int len)
    174 {
    175    sftk_MACConstantTimeCtx *ctx = (sftk_MACConstantTimeCtx *)pctx;
    176    PORT_CheckSuccess(HMAC_ConstantTime(
    177        ctx->mac, NULL, sizeof(ctx->mac),
    178        ctx->hash,
    179        ctx->secret, ctx->secretLength,
    180        ctx->header, ctx->headerLength,
    181        data, len,
    182        ctx->totalLength));
    183 }
    184 
    185 void
    186 sftk_SSLv3MACConstantTime_Update(void *pctx, const unsigned char *data, unsigned int len)
    187 {
    188    sftk_MACConstantTimeCtx *ctx = (sftk_MACConstantTimeCtx *)pctx;
    189    PORT_CheckSuccess(SSLv3_MAC_ConstantTime(
    190        ctx->mac, NULL, sizeof(ctx->mac),
    191        ctx->hash,
    192        ctx->secret, ctx->secretLength,
    193        ctx->header, ctx->headerLength,
    194        data, len,
    195        ctx->totalLength));
    196 }
    197 
    198 void
    199 sftk_MACConstantTime_EndHash(void *pctx, unsigned char *out, unsigned int *outLength,
    200                             unsigned int maxLength)
    201 {
    202    const sftk_MACConstantTimeCtx *ctx = (sftk_MACConstantTimeCtx *)pctx;
    203    unsigned int toCopy = ctx->hash->length;
    204    if (toCopy > maxLength) {
    205        toCopy = maxLength;
    206    }
    207    memcpy(out, ctx->mac, toCopy);
    208    if (outLength) {
    209        *outLength = toCopy;
    210    }
    211 }
    212 
    213 void
    214 sftk_MACConstantTime_DestroyContext(void *pctx, PRBool free)
    215 {
    216    PORT_ZFree(pctx, sizeof(sftk_MACConstantTimeCtx));
    217 }
    218 
    219 CK_RV
    220 sftk_MAC_Create(CK_MECHANISM_TYPE mech, SFTKObject *key, sftk_MACCtx **ret_ctx)
    221 {
    222    CK_RV ret;
    223 
    224    if (ret_ctx == NULL || key == NULL) {
    225        return CKR_HOST_MEMORY;
    226    }
    227 
    228    *ret_ctx = PORT_New(sftk_MACCtx);
    229    if (*ret_ctx == NULL) {
    230        return CKR_HOST_MEMORY;
    231    }
    232 
    233    ret = sftk_MAC_Init(*ret_ctx, mech, key);
    234    if (ret != CKR_OK) {
    235        sftk_MAC_DestroyContext(*ret_ctx, PR_TRUE);
    236    }
    237 
    238    return ret;
    239 }
    240 
    241 CK_RV
    242 sftk_MAC_Init(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, SFTKObject *key)
    243 {
    244    SFTKAttribute *keyval = NULL;
    245    PRBool isFIPS = sftk_isFIPS(key->slot->slotID);
    246    CK_RV ret = CKR_OK;
    247 
    248    /* Find the actual value of the key. */
    249    keyval = sftk_FindAttribute(key, CKA_VALUE);
    250    if (keyval == NULL) {
    251        ret = CKR_KEY_SIZE_RANGE;
    252        goto done;
    253    }
    254 
    255    ret = sftk_MAC_InitRaw(ctx, mech,
    256                           (const unsigned char *)keyval->attrib.pValue,
    257                           keyval->attrib.ulValueLen, isFIPS);
    258 
    259 done:
    260    if (keyval) {
    261        sftk_FreeAttribute(keyval);
    262    }
    263    return ret;
    264 }
    265 
    266 CK_RV
    267 sftk_MAC_InitRaw(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, const unsigned char *key, unsigned int key_len, PRBool isFIPS)
    268 {
    269    const SECHashObject *hashObj = NULL;
    270    CK_RV ret = CKR_OK;
    271 
    272    if (ctx == NULL) {
    273        return CKR_HOST_MEMORY;
    274    }
    275 
    276    /* Clear the context before use. */
    277    PORT_Memset(ctx, 0, sizeof(*ctx));
    278 
    279    /* Save the mech. */
    280    ctx->mech = mech;
    281 
    282    /* Initialize the correct MAC context. */
    283    switch (mech) {
    284        case CKM_MD2_HMAC:
    285        case CKM_MD5_HMAC:
    286        case CKM_SHA_1_HMAC:
    287        case CKM_SHA224_HMAC:
    288        case CKM_SHA256_HMAC:
    289        case CKM_SHA384_HMAC:
    290        case CKM_SHA512_HMAC:
    291        case CKM_SHA3_224_HMAC:
    292        case CKM_SHA3_256_HMAC:
    293        case CKM_SHA3_384_HMAC:
    294        case CKM_SHA3_512_HMAC:
    295            hashObj = HASH_GetRawHashObject(sftk_HMACMechanismToHash(mech));
    296 
    297            /* Because we condition above only on hashes we know to be valid,
    298             * hashObj should never be NULL. This assert is only useful when
    299             * adding a new hash function (for which only partial support has
    300             * been added); thus there is no need to turn it into an if and
    301             * avoid the NULL dereference on the following line. */
    302            PR_ASSERT(hashObj != NULL);
    303            ctx->mac_size = hashObj->length;
    304 
    305            goto hmac;
    306        case CKM_AES_CMAC:
    307            ctx->mac.cmac = CMAC_Create(CMAC_AES, key, key_len);
    308            ctx->destroy_func = SFTKMAC_CMAC_Destroy;
    309 
    310            /* Copy the behavior of sftk_doCMACInit here. */
    311            if (ctx->mac.cmac == NULL) {
    312                if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) {
    313                    ret = CKR_KEY_SIZE_RANGE;
    314                    goto done;
    315                }
    316 
    317                ret = CKR_HOST_MEMORY;
    318                goto done;
    319            }
    320 
    321            ctx->mac_size = AES_BLOCK_SIZE;
    322 
    323            goto done;
    324        default:
    325            ret = CKR_MECHANISM_PARAM_INVALID;
    326            goto done;
    327    }
    328 
    329 hmac:
    330    ctx->mac.hmac = HMAC_Create(hashObj, key, key_len, isFIPS);
    331    ctx->destroy_func = SFTKMAC_HMAC_Destroy;
    332 
    333    /* Copy the behavior of sftk_doHMACInit here. */
    334    if (ctx->mac.hmac == NULL) {
    335        if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) {
    336            ret = CKR_KEY_SIZE_RANGE;
    337            goto done;
    338        }
    339        ret = CKR_HOST_MEMORY;
    340        goto done;
    341    }
    342 
    343    /* Semantics: HMAC and CMAC should behave the same. Begin HMAC now. */
    344    HMAC_Begin(ctx->mac.hmac);
    345 
    346 done:
    347    /* Handle a failure: ctx->mac.raw should be NULL, but make sure
    348     * destroy_func isn't set. */
    349    if (ret != CKR_OK) {
    350        ctx->destroy_func = NULL;
    351    }
    352 
    353    return ret;
    354 }
    355 
    356 CK_RV
    357 sftk_MAC_Reset(sftk_MACCtx *ctx)
    358 {
    359    /* Useful for resetting the state of MAC prior to calling update again
    360     *
    361     * This lets the caller keep a single MAC instance and re-use it as long
    362     * as the key stays the same. */
    363    switch (ctx->mech) {
    364        case CKM_MD2_HMAC:
    365        case CKM_MD5_HMAC:
    366        case CKM_SHA_1_HMAC:
    367        case CKM_SHA224_HMAC:
    368        case CKM_SHA256_HMAC:
    369        case CKM_SHA384_HMAC:
    370        case CKM_SHA512_HMAC:
    371        case CKM_SHA3_224_HMAC:
    372        case CKM_SHA3_256_HMAC:
    373        case CKM_SHA3_384_HMAC:
    374        case CKM_SHA3_512_HMAC:
    375            HMAC_Begin(ctx->mac.hmac);
    376            break;
    377        case CKM_AES_CMAC:
    378            if (CMAC_Begin(ctx->mac.cmac) != SECSuccess) {
    379                return CKR_FUNCTION_FAILED;
    380            }
    381            break;
    382        default:
    383            /* This shouldn't happen -- asserting indicates partial support
    384             * for a new MAC type. */
    385            PR_ASSERT(PR_FALSE);
    386            return CKR_FUNCTION_FAILED;
    387    }
    388 
    389    return CKR_OK;
    390 }
    391 
    392 CK_RV
    393 sftk_MAC_Update(sftk_MACCtx *ctx, const CK_BYTE *data, unsigned int data_len)
    394 {
    395    switch (ctx->mech) {
    396        case CKM_MD2_HMAC:
    397        case CKM_MD5_HMAC:
    398        case CKM_SHA_1_HMAC:
    399        case CKM_SHA224_HMAC:
    400        case CKM_SHA256_HMAC:
    401        case CKM_SHA384_HMAC:
    402        case CKM_SHA512_HMAC:
    403        case CKM_SHA3_224_HMAC:
    404        case CKM_SHA3_256_HMAC:
    405        case CKM_SHA3_384_HMAC:
    406        case CKM_SHA3_512_HMAC:
    407            /* HMAC doesn't indicate failure in the return code. */
    408            HMAC_Update(ctx->mac.hmac, data, data_len);
    409            break;
    410        case CKM_AES_CMAC:
    411            /* CMAC indicates failure in the return code, however this is
    412             * unlikely to occur. */
    413            if (CMAC_Update(ctx->mac.cmac, data, data_len) != SECSuccess) {
    414                return CKR_FUNCTION_FAILED;
    415            }
    416            break;
    417        default:
    418            /* This shouldn't happen -- asserting indicates partial support
    419             * for a new MAC type. */
    420            PR_ASSERT(PR_FALSE);
    421            return CKR_FUNCTION_FAILED;
    422    }
    423    return CKR_OK;
    424 }
    425 
    426 CK_RV
    427 sftk_MAC_End(sftk_MACCtx *ctx, CK_BYTE_PTR result, unsigned int *result_len, unsigned int max_result_len)
    428 {
    429    unsigned int actual_result_len;
    430 
    431    switch (ctx->mech) {
    432        case CKM_MD2_HMAC:
    433        case CKM_MD5_HMAC:
    434        case CKM_SHA_1_HMAC:
    435        case CKM_SHA224_HMAC:
    436        case CKM_SHA256_HMAC:
    437        case CKM_SHA384_HMAC:
    438        case CKM_SHA512_HMAC:
    439        case CKM_SHA3_224_HMAC:
    440        case CKM_SHA3_256_HMAC:
    441        case CKM_SHA3_384_HMAC:
    442        case CKM_SHA3_512_HMAC:
    443            /* HMAC doesn't indicate failure in the return code. Additionally,
    444             * unlike CMAC, it doesn't support partial results. This means that we
    445             * need to allocate a buffer if max_result_len < ctx->mac_size. */
    446            if (max_result_len >= ctx->mac_size) {
    447                /* Split this into two calls to avoid an unnecessary stack
    448                 * allocation and memcpy when possible. */
    449                HMAC_Finish(ctx->mac.hmac, result, &actual_result_len, max_result_len);
    450            } else {
    451                uint8_t tmp_buffer[SFTK_MAX_MAC_LENGTH];
    452 
    453                /* Assumption: buffer is large enough to hold this HMAC's
    454                 * output. */
    455                PR_ASSERT(SFTK_MAX_MAC_LENGTH >= ctx->mac_size);
    456 
    457                HMAC_Finish(ctx->mac.hmac, tmp_buffer, &actual_result_len, SFTK_MAX_MAC_LENGTH);
    458 
    459                if (actual_result_len > max_result_len) {
    460                    /* This should always be true since:
    461                     *
    462                     *   (SFTK_MAX_MAC_LENGTH >= ctx->mac_size =
    463                     *       actual_result_len) > max_result_len,
    464                     *
    465                     * but guard this truncation just in case. */
    466                    actual_result_len = max_result_len;
    467                }
    468 
    469                PORT_Memcpy(result, tmp_buffer, actual_result_len);
    470            }
    471            break;
    472        case CKM_AES_CMAC:
    473            /* CMAC indicates failure in the return code, however this is
    474             * unlikely to occur. */
    475            if (CMAC_Finish(ctx->mac.cmac, result, &actual_result_len, max_result_len) != SECSuccess) {
    476                return CKR_FUNCTION_FAILED;
    477            }
    478            break;
    479        default:
    480            /* This shouldn't happen -- asserting indicates partial support
    481             * for a new MAC type. */
    482            PR_ASSERT(PR_FALSE);
    483            return CKR_FUNCTION_FAILED;
    484    }
    485 
    486    if (result_len) {
    487        /* When result length is passed, inform the caller of its value. */
    488        *result_len = actual_result_len;
    489    } else if (max_result_len == ctx->mac_size) {
    490        /* Validate that the amount requested was what was actually given; the
    491         * caller assumes that what they passed was the output size of the
    492         * underlying MAC and that they got all the bytes the asked for. */
    493        PR_ASSERT(actual_result_len == max_result_len);
    494    }
    495 
    496    return CKR_OK;
    497 }
    498 
    499 void
    500 sftk_MAC_DestroyContext(sftk_MACCtx *ctx, PRBool free_it)
    501 {
    502    if (ctx == NULL) {
    503        return;
    504    }
    505 
    506    if (ctx->mac.raw != NULL && ctx->destroy_func != NULL) {
    507        ctx->destroy_func(ctx->mac.raw, PR_TRUE);
    508    }
    509 
    510    /* Clean up the struct so we don't double free accidentally. */
    511    PORT_Memset(ctx, 0, sizeof(sftk_MACCtx));
    512 
    513    if (free_it == PR_TRUE) {
    514        PORT_Free(ctx);
    515    }
    516 }