Coverage Report

Created: 2025-11-19 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/smime/cmsdigest.c
Line
Count
Source
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
2.31k
{
46
2.31k
    PLArenaPool *pool;
47
2.31k
    NSSCMSDigestContext *cmsdigcx;
48
2.31k
    int digcnt;
49
2.31k
    int i;
50
51
#ifdef CMS_FIND_LEAK_MULTIPLE
52
    PORT_Assert(global_num_digests == 0 || !stop_on_err);
53
#endif
54
55
2.31k
    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
2.31k
    pool = PORT_NewArena(2048);
60
2.31k
    if (!pool)
61
0
        return NULL;
62
63
2.31k
    cmsdigcx = PORT_ArenaNew(pool, NSSCMSDigestContext);
64
2.31k
    if (cmsdigcx == NULL)
65
0
        goto loser;
66
67
2.31k
    cmsdigcx->saw_contents = PR_FALSE;
68
2.31k
    cmsdigcx->pool = pool;
69
2.31k
    cmsdigcx->digcnt = digcnt;
70
71
2.31k
    cmsdigcx->digPairs = PORT_ArenaZNewArray(pool, digestPair, digcnt);
72
2.31k
    if (cmsdigcx->digPairs == NULL) {
73
0
        goto loser;
74
0
    }
75
76
    /*
77
     * Create a digest object context for each algorithm.
78
     */
79
371k
    for (i = 0; i < digcnt; i++) {
80
369k
        const SECHashObject *digobj;
81
369k
        void *digcx;
82
83
369k
        if (!NSS_SMIMEUtil_SigningAllowed(digestalgs[i])) {
84
53
            goto loser;
85
53
        }
86
369k
        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
369k
        if (digobj == NULL)
96
82.7k
            continue;
97
98
286k
        digcx = (*digobj->create)();
99
286k
        if (digcx != NULL) {
100
286k
            (*digobj->begin)(digcx);
101
286k
            cmsdigcx->digPairs[i].digobj = digobj;
102
286k
            cmsdigcx->digPairs[i].digcx = digcx;
103
#ifdef CMS_FIND_LEAK_MULTIPLE
104
            global_num_digests++;
105
#endif
106
286k
        }
107
286k
    }
108
2.26k
    return cmsdigcx;
109
110
53
loser:
111
    /* free any earlier digest objects that may have bee allocated. */
112
96.4k
    for (i = 0; i < digcnt; i++) {
113
96.3k
        digestPair *pair = &cmsdigcx->digPairs[i];
114
96.3k
        if (pair->digobj) {
115
26.5k
            (*pair->digobj->destroy)(pair->digcx, PR_TRUE);
116
#ifdef CMS_FIND_LEAK_MULTIPLE
117
            --global_num_digests;
118
#endif
119
26.5k
        }
120
96.3k
    }
121
53
    if (pool) {
122
53
        PORT_FreeArena(pool, PR_FALSE);
123
53
    }
124
53
    return NULL;
125
2.31k
}
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
35
{
134
35
    SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */
135
136
35
    digestalgs[0] = digestalg;
137
35
    return NSS_CMSDigestContext_StartMultiple(digestalgs);
138
35
}
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
2.11M
{
147
2.11M
    int i;
148
2.11M
    digestPair *pair = cmsdigcx->digPairs;
149
150
2.11M
    cmsdigcx->saw_contents = PR_TRUE;
151
152
16.9M
    for (i = 0; i < cmsdigcx->digcnt; i++, pair++) {
153
14.8M
        if (pair->digcx) {
154
9.36M
            (*pair->digobj->update)(pair->digcx, data, len);
155
9.36M
        }
156
14.8M
    }
157
2.11M
}
158
159
/*
160
 * NSS_CMSDigestContext_Cancel - cancel digesting operation
161
 */
162
void
163
NSS_CMSDigestContext_Cancel(NSSCMSDigestContext *cmsdigcx)
164
2.26k
{
165
2.26k
    int i;
166
2.26k
    digestPair *pair = cmsdigcx->digPairs;
167
168
339k
    for (i = 0; i < cmsdigcx->digcnt; i++, pair++) {
169
337k
        if (pair->digcx) {
170
260k
            (*pair->digobj->destroy)(pair->digcx, PR_TRUE);
171
#ifdef CMS_FIND_LEAK_MULTIPLE
172
            --global_num_digests;
173
#endif
174
260k
        }
175
337k
    }
176
#ifdef CMS_FIND_LEAK_MULTIPLE
177
    PORT_Assert(global_num_digests == 0 || !stop_on_err);
178
#endif
179
2.26k
    PORT_FreeArena(cmsdigcx->pool, PR_FALSE);
180
2.26k
}
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
324
{
191
324
    SECItem **digests = NULL;
192
324
    digestPair *pair;
193
324
    void *mark;
194
324
    int i;
195
324
    SECStatus rv;
196
197
    /* no contents? do not finish digests */
198
324
    if (digestsp == NULL || !cmsdigcx->saw_contents) {
199
23
        rv = SECSuccess;
200
23
        goto cleanup;
201
23
    }
202
203
301
    mark = PORT_ArenaMark(poolp);
204
205
    /* allocate digest array & SECItems on arena */
206
301
    digests = PORT_ArenaNewArray(poolp, SECItem *, cmsdigcx->digcnt + 1);
207
208
301
    rv = ((digests == NULL) ? SECFailure : SECSuccess);
209
301
    pair = cmsdigcx->digPairs;
210
136k
    for (i = 0; rv == SECSuccess && i < cmsdigcx->digcnt; i++, pair++) {
211
135k
        SECItem digest;
212
135k
        unsigned char hash[HASH_LENGTH_MAX];
213
214
135k
        if (!pair->digcx) {
215
32.7k
            digests[i] = NULL;
216
32.7k
            continue;
217
32.7k
        }
218
219
103k
        digest.type = siBuffer;
220
103k
        digest.data = hash;
221
103k
        digest.len = pair->digobj->length;
222
103k
        (*pair->digobj->end)(pair->digcx, hash, &digest.len, digest.len);
223
103k
        digests[i] = SECITEM_ArenaDupItem(poolp, &digest);
224
103k
        if (!digests[i]) {
225
0
            rv = SECFailure;
226
0
        }
227
103k
    }
228
301
    digests[i] = NULL;
229
301
    if (rv == SECSuccess) {
230
301
        PORT_ArenaUnmark(poolp, mark);
231
301
    } else
232
0
        PORT_ArenaRelease(poolp, mark);
233
234
324
cleanup:
235
324
    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
324
    if (rv == SECSuccess && digestsp && digests) {
240
301
        *digestsp = digests;
241
301
    }
242
324
    return rv;
243
301
}
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
8
{
254
8
    SECStatus rv = SECFailure;
255
8
    SECItem **dp = NULL;
256
8
    PLArenaPool *arena = NULL;
257
258
8
    if ((arena = PORT_NewArena(1024)) == NULL)
259
0
        goto loser;
260
261
    /* get the digests into arena, then copy the first digest into poolp */
262
8
    rv = NSS_CMSDigestContext_FinishMultiple(cmsdigcx, arena, &dp);
263
8
    if (rv == SECSuccess && dp && dp[0]) {
264
        /* now copy it into poolp */
265
1
        rv = SECITEM_CopyItem(poolp, digest, dp[0]);
266
1
    }
267
8
loser:
268
8
    if (arena)
269
8
        PORT_FreeArena(arena, PR_FALSE);
270
271
8
    return rv;
272
8
}