tor-browser

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

cmsdigest.c (7319B)


      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 /*
      6 * CMS digesting.
      7 */
      8 
      9 #include "cmslocal.h"
     10 
     11 #include "cert.h"
     12 #include "keyhi.h"
     13 #include "secitem.h"
     14 #include "secoid.h"
     15 #include "pk11func.h"
     16 #include "prtime.h"
     17 #include "secerr.h"
     18 #include "smime.h"
     19 
     20 /*  #define CMS_FIND_LEAK_MULTIPLE 1 */
     21 #ifdef CMS_FIND_LEAK_MULTIPLE
     22 static int stop_on_err = 1;
     23 static int global_num_digests = 0;
     24 #endif
     25 
     26 struct digestPairStr {
     27    const SECHashObject *digobj;
     28    void *digcx;
     29 };
     30 typedef struct digestPairStr digestPair;
     31 
     32 struct NSSCMSDigestContextStr {
     33    PRBool saw_contents;
     34    PLArenaPool *pool;
     35    int digcnt;
     36    digestPair *digPairs;
     37 };
     38 
     39 /*
     40 * NSS_CMSDigestContext_StartMultiple - start digest calculation using all the
     41 *  digest algorithms in "digestalgs" in parallel.
     42 */
     43 NSSCMSDigestContext *
     44 NSS_CMSDigestContext_StartMultiple(SECAlgorithmID **digestalgs)
     45 {
     46    PLArenaPool *pool;
     47    NSSCMSDigestContext *cmsdigcx;
     48    int digcnt;
     49    int i;
     50 
     51 #ifdef CMS_FIND_LEAK_MULTIPLE
     52    PORT_Assert(global_num_digests == 0 || !stop_on_err);
     53 #endif
     54 
     55    digcnt = (digestalgs == NULL) ? 0 : NSS_CMSArray_Count((void **)digestalgs);
     56    /* It's OK if digcnt is zero.  We have to allow this for "certs only"
     57    ** messages.
     58    */
     59    pool = PORT_NewArena(2048);
     60    if (!pool)
     61        return NULL;
     62 
     63    cmsdigcx = PORT_ArenaNew(pool, NSSCMSDigestContext);
     64    if (cmsdigcx == NULL)
     65        goto loser;
     66 
     67    cmsdigcx->saw_contents = PR_FALSE;
     68    cmsdigcx->pool = pool;
     69    cmsdigcx->digcnt = digcnt;
     70 
     71    cmsdigcx->digPairs = PORT_ArenaZNewArray(pool, digestPair, digcnt);
     72    if (cmsdigcx->digPairs == NULL) {
     73        goto loser;
     74    }
     75 
     76    /*
     77     * Create a digest object context for each algorithm.
     78     */
     79    for (i = 0; i < digcnt; i++) {
     80        const SECHashObject *digobj;
     81        void *digcx;
     82 
     83        if (!NSS_SMIMEUtil_SigningAllowed(digestalgs[i])) {
     84            goto loser;
     85        }
     86        digobj = NSS_CMSUtil_GetHashObjByAlgID(digestalgs[i]);
     87        /*
     88         * Skip any algorithm we do not even recognize; obviously,
     89         * this could be a problem, but if it is critical then the
     90         * result will just be that the signature does not verify.
     91         * We do not necessarily want to error out here, because
     92         * the particular algorithm may not actually be important,
     93         * but we cannot know that until later.
     94         */
     95        if (digobj == NULL)
     96            continue;
     97 
     98        digcx = (*digobj->create)();
     99        if (digcx != NULL) {
    100            (*digobj->begin)(digcx);
    101            cmsdigcx->digPairs[i].digobj = digobj;
    102            cmsdigcx->digPairs[i].digcx = digcx;
    103 #ifdef CMS_FIND_LEAK_MULTIPLE
    104            global_num_digests++;
    105 #endif
    106        }
    107    }
    108    return cmsdigcx;
    109 
    110 loser:
    111    /* free any earlier digest objects that may have bee allocated. */
    112    for (i = 0; i < digcnt; i++) {
    113        digestPair *pair = &cmsdigcx->digPairs[i];
    114        if (pair->digobj) {
    115            (*pair->digobj->destroy)(pair->digcx, PR_TRUE);
    116 #ifdef CMS_FIND_LEAK_MULTIPLE
    117            --global_num_digests;
    118 #endif
    119        }
    120    }
    121    if (pool) {
    122        PORT_FreeArena(pool, PR_FALSE);
    123    }
    124    return NULL;
    125 }
    126 
    127 /*
    128 * NSS_CMSDigestContext_StartSingle - same as
    129 * NSS_CMSDigestContext_StartMultiple, but only one algorithm.
    130 */
    131 NSSCMSDigestContext *
    132 NSS_CMSDigestContext_StartSingle(SECAlgorithmID *digestalg)
    133 {
    134    SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */
    135 
    136    digestalgs[0] = digestalg;
    137    return NSS_CMSDigestContext_StartMultiple(digestalgs);
    138 }
    139 
    140 /*
    141 * NSS_CMSDigestContext_Update - feed more data into the digest machine
    142 */
    143 void
    144 NSS_CMSDigestContext_Update(NSSCMSDigestContext *cmsdigcx,
    145                            const unsigned char *data, int len)
    146 {
    147    int i;
    148    digestPair *pair = cmsdigcx->digPairs;
    149 
    150    cmsdigcx->saw_contents = PR_TRUE;
    151 
    152    for (i = 0; i < cmsdigcx->digcnt; i++, pair++) {
    153        if (pair->digcx) {
    154            (*pair->digobj->update)(pair->digcx, data, len);
    155        }
    156    }
    157 }
    158 
    159 /*
    160 * NSS_CMSDigestContext_Cancel - cancel digesting operation
    161 */
    162 void
    163 NSS_CMSDigestContext_Cancel(NSSCMSDigestContext *cmsdigcx)
    164 {
    165    int i;
    166    digestPair *pair = cmsdigcx->digPairs;
    167 
    168    for (i = 0; i < cmsdigcx->digcnt; i++, pair++) {
    169        if (pair->digcx) {
    170            (*pair->digobj->destroy)(pair->digcx, PR_TRUE);
    171 #ifdef CMS_FIND_LEAK_MULTIPLE
    172            --global_num_digests;
    173 #endif
    174        }
    175    }
    176 #ifdef CMS_FIND_LEAK_MULTIPLE
    177    PORT_Assert(global_num_digests == 0 || !stop_on_err);
    178 #endif
    179    PORT_FreeArena(cmsdigcx->pool, PR_FALSE);
    180 }
    181 
    182 /*
    183 * NSS_CMSDigestContext_FinishMultiple - finish the digests and put them
    184 *  into an array of SECItems (allocated on poolp)
    185 */
    186 SECStatus
    187 NSS_CMSDigestContext_FinishMultiple(NSSCMSDigestContext *cmsdigcx,
    188                                    PLArenaPool *poolp,
    189                                    SECItem ***digestsp)
    190 {
    191    SECItem **digests = NULL;
    192    digestPair *pair;
    193    void *mark;
    194    int i;
    195    SECStatus rv;
    196 
    197    /* no contents? do not finish digests */
    198    if (digestsp == NULL || !cmsdigcx->saw_contents) {
    199        rv = SECSuccess;
    200        goto cleanup;
    201    }
    202 
    203    mark = PORT_ArenaMark(poolp);
    204 
    205    /* allocate digest array & SECItems on arena */
    206    digests = PORT_ArenaNewArray(poolp, SECItem *, cmsdigcx->digcnt + 1);
    207 
    208    rv = ((digests == NULL) ? SECFailure : SECSuccess);
    209    pair = cmsdigcx->digPairs;
    210    for (i = 0; rv == SECSuccess && i < cmsdigcx->digcnt; i++, pair++) {
    211        SECItem digest;
    212        unsigned char hash[HASH_LENGTH_MAX];
    213 
    214        if (!pair->digcx) {
    215            digests[i] = NULL;
    216            continue;
    217        }
    218 
    219        digest.type = siBuffer;
    220        digest.data = hash;
    221        digest.len = pair->digobj->length;
    222        (*pair->digobj->end)(pair->digcx, hash, &digest.len, digest.len);
    223        digests[i] = SECITEM_ArenaDupItem(poolp, &digest);
    224        if (!digests[i]) {
    225            rv = SECFailure;
    226        }
    227    }
    228    digests[i] = NULL;
    229    if (rv == SECSuccess) {
    230        PORT_ArenaUnmark(poolp, mark);
    231    } else
    232        PORT_ArenaRelease(poolp, mark);
    233 
    234 cleanup:
    235    NSS_CMSDigestContext_Cancel(cmsdigcx);
    236    /* Don't change the caller's digests pointer if we have no digests.
    237    **  NSS_CMSSignedData_Encode_AfterData depends on this behavior.
    238    */
    239    if (rv == SECSuccess && digestsp && digests) {
    240        *digestsp = digests;
    241    }
    242    return rv;
    243 }
    244 
    245 /*
    246 * NSS_CMSDigestContext_FinishSingle - same as
    247 * NSS_CMSDigestContext_FinishMultiple, but for one digest.
    248 */
    249 SECStatus
    250 NSS_CMSDigestContext_FinishSingle(NSSCMSDigestContext *cmsdigcx,
    251                                  PLArenaPool *poolp,
    252                                  SECItem *digest)
    253 {
    254    SECStatus rv = SECFailure;
    255    SECItem **dp = NULL;
    256    PLArenaPool *arena = NULL;
    257 
    258    if ((arena = PORT_NewArena(1024)) == NULL)
    259        goto loser;
    260 
    261    /* get the digests into arena, then copy the first digest into poolp */
    262    rv = NSS_CMSDigestContext_FinishMultiple(cmsdigcx, arena, &dp);
    263    if (rv == SECSuccess && dp && dp[0]) {
    264        /* now copy it into poolp */
    265        rv = SECITEM_CopyItem(poolp, digest, dp[0]);
    266    }
    267 loser:
    268    if (arena)
    269        PORT_FreeArena(arena, PR_FALSE);
    270 
    271    return rv;
    272 }