Coverage Report

Created: 2025-08-18 06:36

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