Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
5 /*
6 * CMS digesting.
7 */
9 #include "cmslocal.h"
11 #include "cert.h"
12 #include "key.h"
13 #include "secitem.h"
14 #include "secoid.h"
15 #include "pk11func.h"
16 #include "prtime.h"
17 #include "secerr.h"
19 /* #define CMS_FIND_LEAK_MULTIPLE 1 */
20 #ifdef CMS_FIND_LEAK_MULTIPLE
21 static int stop_on_err = 1;
22 static int global_num_digests = 0;
23 #endif
25 struct digestPairStr {
26 const SECHashObject * digobj;
27 void * digcx;
28 };
29 typedef struct digestPairStr digestPair;
31 struct NSSCMSDigestContextStr {
32 PRBool saw_contents;
33 PLArenaPool * pool;
34 int digcnt;
35 digestPair * digPairs;
36 };
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;
51 #ifdef CMS_FIND_LEAK_MULTIPLE
52 PORT_Assert(global_num_digests == 0 || !stop_on_err);
53 #endif
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;
63 cmsdigcx = PORT_ArenaNew(pool, NSSCMSDigestContext);
64 if (cmsdigcx == NULL)
65 goto loser;
67 cmsdigcx->saw_contents = PR_FALSE;
68 cmsdigcx->pool = pool;
69 cmsdigcx->digcnt = digcnt;
71 cmsdigcx->digPairs = PORT_ArenaZNewArray(pool, digestPair, digcnt);
72 if (cmsdigcx->digPairs == NULL) {
73 goto loser;
74 }
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;
83 digobj = NSS_CMSUtil_GetHashObjByAlgID(digestalgs[i]);
84 /*
85 * Skip any algorithm we do not even recognize; obviously,
86 * this could be a problem, but if it is critical then the
87 * result will just be that the signature does not verify.
88 * We do not necessarily want to error out here, because
89 * the particular algorithm may not actually be important,
90 * but we cannot know that until later.
91 */
92 if (digobj == NULL)
93 continue;
95 digcx = (*digobj->create)();
96 if (digcx != NULL) {
97 (*digobj->begin) (digcx);
98 cmsdigcx->digPairs[i].digobj = digobj;
99 cmsdigcx->digPairs[i].digcx = digcx;
100 #ifdef CMS_FIND_LEAK_MULTIPLE
101 global_num_digests++;
102 #endif
103 }
104 }
105 return cmsdigcx;
107 loser:
108 /* no digest objects have been created, or need to be destroyed. */
109 if (pool) {
110 PORT_FreeArena(pool, PR_FALSE);
111 }
112 return NULL;
113 }
115 /*
116 * NSS_CMSDigestContext_StartSingle - same as
117 * NSS_CMSDigestContext_StartMultiple, but only one algorithm.
118 */
119 NSSCMSDigestContext *
120 NSS_CMSDigestContext_StartSingle(SECAlgorithmID *digestalg)
121 {
122 SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */
124 digestalgs[0] = digestalg;
125 return NSS_CMSDigestContext_StartMultiple(digestalgs);
126 }
128 /*
129 * NSS_CMSDigestContext_Update - feed more data into the digest machine
130 */
131 void
132 NSS_CMSDigestContext_Update(NSSCMSDigestContext *cmsdigcx,
133 const unsigned char *data, int len)
134 {
135 int i;
136 digestPair *pair = cmsdigcx->digPairs;
138 cmsdigcx->saw_contents = PR_TRUE;
140 for (i = 0; i < cmsdigcx->digcnt; i++, pair++) {
141 if (pair->digcx) {
142 (*pair->digobj->update)(pair->digcx, data, len);
143 }
144 }
145 }
147 /*
148 * NSS_CMSDigestContext_Cancel - cancel digesting operation
149 */
150 void
151 NSS_CMSDigestContext_Cancel(NSSCMSDigestContext *cmsdigcx)
152 {
153 int i;
154 digestPair *pair = cmsdigcx->digPairs;
156 for (i = 0; i < cmsdigcx->digcnt; i++, pair++) {
157 if (pair->digcx) {
158 (*pair->digobj->destroy)(pair->digcx, PR_TRUE);
159 #ifdef CMS_FIND_LEAK_MULTIPLE
160 --global_num_digests;
161 #endif
162 }
163 }
164 #ifdef CMS_FIND_LEAK_MULTIPLE
165 PORT_Assert(global_num_digests == 0 || !stop_on_err);
166 #endif
167 PORT_FreeArena(cmsdigcx->pool, PR_FALSE);
168 }
170 /*
171 * NSS_CMSDigestContext_FinishMultiple - finish the digests and put them
172 * into an array of SECItems (allocated on poolp)
173 */
174 SECStatus
175 NSS_CMSDigestContext_FinishMultiple(NSSCMSDigestContext *cmsdigcx,
176 PLArenaPool *poolp,
177 SECItem ***digestsp)
178 {
179 SECItem ** digests = NULL;
180 digestPair *pair;
181 void * mark;
182 int i;
183 SECStatus rv;
185 /* no contents? do not finish digests */
186 if (digestsp == NULL || !cmsdigcx->saw_contents) {
187 rv = SECSuccess;
188 goto cleanup;
189 }
191 mark = PORT_ArenaMark (poolp);
193 /* allocate digest array & SECItems on arena */
194 digests = PORT_ArenaNewArray( poolp, SECItem *, cmsdigcx->digcnt + 1);
196 rv = ((digests == NULL) ? SECFailure : SECSuccess);
197 pair = cmsdigcx->digPairs;
198 for (i = 0; rv == SECSuccess && i < cmsdigcx->digcnt; i++, pair++) {
199 SECItem digest;
200 unsigned char hash[HASH_LENGTH_MAX];
202 if (!pair->digcx) {
203 digests[i] = NULL;
204 continue;
205 }
207 digest.type = siBuffer;
208 digest.data = hash;
209 digest.len = pair->digobj->length;
210 (* pair->digobj->end)(pair->digcx, hash, &digest.len, digest.len);
211 digests[i] = SECITEM_ArenaDupItem(poolp, &digest);
212 if (!digests[i]) {
213 rv = SECFailure;
214 }
215 }
216 digests[i] = NULL;
217 if (rv == SECSuccess) {
218 PORT_ArenaUnmark(poolp, mark);
219 } else
220 PORT_ArenaRelease(poolp, mark);
222 cleanup:
223 NSS_CMSDigestContext_Cancel(cmsdigcx);
224 /* Don't change the caller's digests pointer if we have no digests.
225 ** NSS_CMSSignedData_Encode_AfterData depends on this behavior.
226 */
227 if (rv == SECSuccess && digestsp && digests) {
228 *digestsp = digests;
229 }
230 return rv;
231 }
233 /*
234 * NSS_CMSDigestContext_FinishSingle - same as
235 * NSS_CMSDigestContext_FinishMultiple, but for one digest.
236 */
237 SECStatus
238 NSS_CMSDigestContext_FinishSingle(NSSCMSDigestContext *cmsdigcx,
239 PLArenaPool *poolp,
240 SECItem *digest)
241 {
242 SECStatus rv = SECFailure;
243 SECItem **dp;
244 PLArenaPool *arena = NULL;
246 if ((arena = PORT_NewArena(1024)) == NULL)
247 goto loser;
249 /* get the digests into arena, then copy the first digest into poolp */
250 rv = NSS_CMSDigestContext_FinishMultiple(cmsdigcx, arena, &dp);
251 if (rv == SECSuccess) {
252 /* now copy it into poolp */
253 rv = SECITEM_CopyItem(poolp, digest, dp[0]);
254 }
255 loser:
256 if (arena)
257 PORT_FreeArena(arena, PR_FALSE);
259 return rv;
260 }