|
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 "key.h" |
|
13 #include "secitem.h" |
|
14 #include "secoid.h" |
|
15 #include "pk11func.h" |
|
16 #include "prtime.h" |
|
17 #include "secerr.h" |
|
18 |
|
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 |
|
24 |
|
25 struct digestPairStr { |
|
26 const SECHashObject * digobj; |
|
27 void * digcx; |
|
28 }; |
|
29 typedef struct digestPairStr digestPair; |
|
30 |
|
31 struct NSSCMSDigestContextStr { |
|
32 PRBool saw_contents; |
|
33 PLArenaPool * pool; |
|
34 int digcnt; |
|
35 digestPair * digPairs; |
|
36 }; |
|
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 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; |
|
94 |
|
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; |
|
106 |
|
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 } |
|
114 |
|
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 */ |
|
123 |
|
124 digestalgs[0] = digestalg; |
|
125 return NSS_CMSDigestContext_StartMultiple(digestalgs); |
|
126 } |
|
127 |
|
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; |
|
137 |
|
138 cmsdigcx->saw_contents = PR_TRUE; |
|
139 |
|
140 for (i = 0; i < cmsdigcx->digcnt; i++, pair++) { |
|
141 if (pair->digcx) { |
|
142 (*pair->digobj->update)(pair->digcx, data, len); |
|
143 } |
|
144 } |
|
145 } |
|
146 |
|
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; |
|
155 |
|
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 } |
|
169 |
|
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; |
|
184 |
|
185 /* no contents? do not finish digests */ |
|
186 if (digestsp == NULL || !cmsdigcx->saw_contents) { |
|
187 rv = SECSuccess; |
|
188 goto cleanup; |
|
189 } |
|
190 |
|
191 mark = PORT_ArenaMark (poolp); |
|
192 |
|
193 /* allocate digest array & SECItems on arena */ |
|
194 digests = PORT_ArenaNewArray( poolp, SECItem *, cmsdigcx->digcnt + 1); |
|
195 |
|
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]; |
|
201 |
|
202 if (!pair->digcx) { |
|
203 digests[i] = NULL; |
|
204 continue; |
|
205 } |
|
206 |
|
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); |
|
221 |
|
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 } |
|
232 |
|
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; |
|
245 |
|
246 if ((arena = PORT_NewArena(1024)) == NULL) |
|
247 goto loser; |
|
248 |
|
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); |
|
258 |
|
259 return rv; |
|
260 } |