]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * The contents of this file are subject to the Mozilla Public | |
3 | * License Version 1.1 (the "License"); you may not use this file | |
4 | * except in compliance with the License. You may obtain a copy of | |
5 | * the License at http://www.mozilla.org/MPL/ | |
6 | * | |
7 | * Software distributed under the License is distributed on an "AS | |
8 | * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | |
9 | * implied. See the License for the specific language governing | |
10 | * rights and limitations under the License. | |
11 | * | |
12 | * The Original Code is the Netscape security libraries. | |
13 | * | |
14 | * The Initial Developer of the Original Code is Netscape | |
15 | * Communications Corporation. Portions created by Netscape are | |
16 | * Copyright (C) 1994-2000 Netscape Communications Corporation. All | |
17 | * Rights Reserved. | |
18 | * | |
19 | * Contributor(s): | |
20 | * | |
21 | * Alternatively, the contents of this file may be used under the | |
22 | * terms of the GNU General Public License Version 2 or later (the | |
23 | * "GPL"), in which case the provisions of the GPL are applicable | |
24 | * instead of those above. If you wish to allow use of your | |
25 | * version of this file only under the terms of the GPL and not to | |
26 | * allow others to use your version of this file under the MPL, | |
27 | * indicate your decision by deleting the provisions above and | |
28 | * replace them with the notice and other provisions required by | |
29 | * the GPL. If you do not delete the provisions above, a recipient | |
30 | * may use your version of this file under either the MPL or the | |
31 | * GPL. | |
32 | */ | |
33 | ||
34 | /* | |
35 | * CMS miscellaneous utility functions. | |
36 | */ | |
37 | ||
38 | #include <Security/SecCmsEncoder.h> /* @@@ Remove this when we move the Encoder method. */ | |
39 | #include <Security/SecCmsSignerInfo.h> | |
40 | #include "cmslocal.h" | |
41 | ||
42 | #include "secitem.h" | |
43 | #include "secoid.h" | |
44 | #include "cryptohi.h" | |
45 | ||
46 | #include <security_asn1/secasn1.h> | |
47 | #include <security_asn1/secerr.h> | |
48 | #include <Security/cssmapi.h> | |
49 | #include <Security/cssmapple.h> | |
50 | #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> | |
b3971512 | 51 | #include <CommonCrypto/CommonDigest.h> |
d8f41ccd A |
52 | |
53 | ||
54 | /* | |
55 | * SecCmsArraySortByDER - sort array of objects by objects' DER encoding | |
56 | * | |
57 | * make sure that the order of the objects guarantees valid DER (which must be | |
58 | * in lexigraphically ascending order for a SET OF); if reordering is necessary it | |
59 | * will be done in place (in objs). | |
60 | */ | |
61 | OSStatus | |
62 | SecCmsArraySortByDER(void **objs, const SecAsn1Template *objtemplate, void **objs2) | |
63 | { | |
64 | PRArenaPool *poolp; | |
65 | int num_objs; | |
66 | CSSM_DATA_PTR *enc_objs; | |
67 | OSStatus rv = SECFailure; | |
68 | int i; | |
69 | ||
70 | if (objs == NULL) /* already sorted */ | |
71 | return SECSuccess; | |
72 | ||
73 | num_objs = SecCmsArrayCount((void **)objs); | |
74 | if (num_objs == 0 || num_objs == 1) /* already sorted. */ | |
75 | return SECSuccess; | |
76 | ||
77 | poolp = PORT_NewArena (1024); /* arena for temporaries */ | |
78 | if (poolp == NULL) | |
79 | return SECFailure; /* no memory; nothing we can do... */ | |
80 | ||
81 | /* | |
82 | * Allocate arrays to hold the individual encodings which we will use | |
83 | * for comparisons and the reordered attributes as they are sorted. | |
84 | */ | |
60c433a9 A |
85 | // Security check to prevent under-allocation |
86 | if (num_objs<0 || num_objs>=(int)((INT_MAX/sizeof(CSSM_DATA_PTR))-1)) { | |
87 | goto loser; | |
88 | } | |
d8f41ccd A |
89 | enc_objs = (CSSM_DATA_PTR *)PORT_ArenaZAlloc(poolp, (num_objs + 1) * sizeof(CSSM_DATA_PTR)); |
90 | if (enc_objs == NULL) | |
91 | goto loser; | |
92 | ||
93 | /* DER encode each individual object. */ | |
94 | for (i = 0; i < num_objs; i++) { | |
95 | enc_objs[i] = SEC_ASN1EncodeItem(poolp, NULL, objs[i], objtemplate); | |
96 | if (enc_objs[i] == NULL) | |
97 | goto loser; | |
98 | } | |
99 | enc_objs[num_objs] = NULL; | |
100 | ||
101 | /* now compare and sort objs by the order of enc_objs */ | |
102 | SecCmsArraySort((void **)enc_objs, SecCmsUtilDERCompare, objs, objs2); | |
103 | ||
104 | rv = SECSuccess; | |
105 | ||
106 | loser: | |
107 | PORT_FreeArena (poolp, PR_FALSE); | |
108 | return rv; | |
109 | } | |
110 | ||
111 | /* | |
112 | * SecCmsUtilDERCompare - for use with SecCmsArraySort to | |
113 | * sort arrays of CSSM_DATAs containing DER | |
114 | */ | |
115 | int | |
116 | SecCmsUtilDERCompare(void *a, void *b) | |
117 | { | |
118 | CSSM_DATA_PTR der1 = (CSSM_DATA_PTR)a; | |
119 | CSSM_DATA_PTR der2 = (CSSM_DATA_PTR)b; | |
120 | int j; | |
121 | ||
122 | /* | |
123 | * Find the lowest (lexigraphically) encoding. One that is | |
124 | * shorter than all the rest is known to be "less" because each | |
125 | * attribute is of the same type (a SEQUENCE) and so thus the | |
126 | * first octet of each is the same, and the second octet is | |
127 | * the length (or the length of the length with the high bit | |
128 | * set, followed by the length, which also works out to always | |
129 | * order the shorter first). Two (or more) that have the | |
130 | * same length need to be compared byte by byte until a mismatch | |
131 | * is found. | |
132 | */ | |
133 | if (der1->Length != der2->Length) | |
134 | return (der1->Length < der2->Length) ? -1 : 1; | |
135 | ||
136 | for (j = 0; j < der1->Length; j++) { | |
137 | if (der1->Data[j] == der2->Data[j]) | |
138 | continue; | |
139 | return (der1->Data[j] < der2->Data[j]) ? -1 : 1; | |
140 | } | |
141 | return 0; | |
142 | } | |
143 | ||
144 | /* | |
145 | * SecCmsAlgArrayGetIndexByAlgID - find a specific algorithm in an array of | |
146 | * algorithms. | |
147 | * | |
148 | * algorithmArray - array of algorithm IDs | |
149 | * algid - algorithmid of algorithm to pick | |
150 | * | |
151 | * Returns: | |
152 | * An integer containing the index of the algorithm in the array or -1 if | |
153 | * algorithm was not found. | |
154 | */ | |
155 | int | |
156 | SecCmsAlgArrayGetIndexByAlgID(SECAlgorithmID **algorithmArray, SECAlgorithmID *algid) | |
157 | { | |
158 | int i; | |
159 | ||
160 | if (algorithmArray == NULL || algorithmArray[0] == NULL) | |
161 | return -1; | |
162 | ||
163 | for (i = 0; algorithmArray[i] != NULL; i++) { | |
164 | if (SECOID_CompareAlgorithmID(algorithmArray[i], algid) == SECEqual) | |
165 | break; /* bingo */ | |
166 | } | |
167 | ||
168 | if (algorithmArray[i] == NULL) | |
169 | return -1; /* not found */ | |
170 | ||
171 | return i; | |
172 | } | |
173 | ||
174 | /* | |
175 | * SecCmsAlgArrayGetIndexByAlgTag - find a specific algorithm in an array of | |
176 | * algorithms. | |
177 | * | |
178 | * algorithmArray - array of algorithm IDs | |
179 | * algtag - algorithm tag of algorithm to pick | |
180 | * | |
181 | * Returns: | |
182 | * An integer containing the index of the algorithm in the array or -1 if | |
183 | * algorithm was not found. | |
184 | */ | |
185 | int | |
186 | SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray, | |
187 | SECOidTag algtag) | |
188 | { | |
189 | SECOidData *algid; | |
190 | int i = -1; | |
191 | ||
192 | if (algorithmArray == NULL || algorithmArray[0] == NULL) | |
193 | return i; | |
194 | ||
195 | #ifdef ORDER_N_SQUARED | |
196 | for (i = 0; algorithmArray[i] != NULL; i++) { | |
197 | algid = SECOID_FindOID(&(algorithmArray[i]->algorithm)); | |
198 | if (algid->offset == algtag) | |
199 | break; /* bingo */ | |
200 | } | |
201 | #else | |
202 | algid = SECOID_FindOIDByTag(algtag); | |
203 | if (!algid) | |
204 | return i; | |
205 | for (i = 0; algorithmArray[i] != NULL; i++) { | |
206 | if (SECITEM_ItemsAreEqual(&algorithmArray[i]->algorithm, &algid->oid)) | |
207 | break; /* bingo */ | |
208 | } | |
209 | #endif | |
210 | ||
211 | if (algorithmArray[i] == NULL) | |
212 | return -1; /* not found */ | |
213 | ||
214 | return i; | |
215 | } | |
216 | ||
b3971512 | 217 | void * |
d8f41ccd A |
218 | SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid) |
219 | { | |
220 | SECOidData *oidData = SECOID_FindOID(&(algid->algorithm)); | |
221 | if (oidData) | |
222 | { | |
b3971512 A |
223 | void *digobj = NULL; |
224 | switch (oidData->offset) { | |
225 | case SEC_OID_SHA1: | |
226 | digobj = calloc(1, sizeof(CC_SHA1_CTX)); | |
227 | CC_SHA1_Init(digobj); | |
228 | break; | |
229 | case SEC_OID_MD5: | |
230 | digobj = calloc(1, sizeof(CC_MD5_CTX)); | |
231 | CC_MD5_Init(digobj); | |
232 | break; | |
233 | case SEC_OID_SHA224: | |
234 | digobj = calloc(1, sizeof(CC_SHA256_CTX)); | |
235 | CC_SHA224_Init(digobj); | |
236 | break; | |
237 | case SEC_OID_SHA256: | |
238 | digobj = calloc(1, sizeof(CC_SHA256_CTX)); | |
239 | CC_SHA256_Init(digobj); | |
240 | break; | |
241 | case SEC_OID_SHA384: | |
242 | digobj = calloc(1, sizeof(CC_SHA512_CTX)); | |
243 | CC_SHA384_Init(digobj); | |
244 | break; | |
245 | case SEC_OID_SHA512: | |
246 | digobj = calloc(1, sizeof(CC_SHA512_CTX)); | |
247 | CC_SHA512_Init(digobj); | |
248 | break; | |
249 | default: | |
250 | break; | |
251 | } | |
252 | return digobj; | |
d8f41ccd A |
253 | } |
254 | ||
255 | return 0; | |
256 | } | |
257 | ||
258 | /* | |
259 | * XXX I would *really* like to not have to do this, but the current | |
260 | * signing interface gives me little choice. | |
261 | */ | |
262 | SECOidTag | |
263 | SecCmsUtilMakeSignatureAlgorithm(SECOidTag hashalg, SECOidTag encalg) | |
264 | { | |
265 | switch (encalg) { | |
266 | case SEC_OID_PKCS1_RSA_ENCRYPTION: | |
267 | switch (hashalg) { | |
268 | case SEC_OID_MD2: | |
269 | return SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION; | |
270 | case SEC_OID_MD5: | |
271 | return SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; | |
272 | case SEC_OID_SHA1: | |
273 | return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; | |
274 | case SEC_OID_SHA256: | |
275 | return SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; | |
276 | case SEC_OID_SHA384: | |
277 | return SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; | |
278 | case SEC_OID_SHA512: | |
279 | return SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; | |
280 | default: | |
281 | return SEC_OID_UNKNOWN; | |
282 | } | |
283 | case SEC_OID_ANSIX9_DSA_SIGNATURE: | |
284 | case SEC_OID_MISSI_KEA_DSS: | |
285 | case SEC_OID_MISSI_DSS: | |
286 | switch (hashalg) { | |
287 | case SEC_OID_SHA1: | |
288 | return SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST; | |
289 | default: | |
290 | return SEC_OID_UNKNOWN; | |
291 | } | |
292 | case SEC_OID_EC_PUBLIC_KEY: | |
293 | switch(hashalg) { | |
fa7225c8 | 294 | /* |
d8f41ccd A |
295 | * Note this is only used when signing and verifying signed attributes, |
296 | * In which case we really do want the combined ECDSA_WithSHA1 alg... | |
297 | */ | |
298 | case SEC_OID_SHA1: | |
299 | return SEC_OID_ECDSA_WithSHA1; | |
fa7225c8 A |
300 | case SEC_OID_SHA256: |
301 | return SEC_OID_ECDSA_WITH_SHA256; | |
302 | case SEC_OID_SHA384: | |
303 | return SEC_OID_ECDSA_WITH_SHA384; | |
304 | case SEC_OID_SHA512: | |
305 | return SEC_OID_ECDSA_WITH_SHA512; | |
d8f41ccd A |
306 | default: |
307 | return SEC_OID_UNKNOWN; | |
308 | } | |
309 | default: | |
310 | break; | |
311 | } | |
312 | ||
313 | return encalg; /* maybe it is already the right algid */ | |
314 | } | |
315 | ||
316 | const SecAsn1Template * | |
317 | SecCmsUtilGetTemplateByTypeTag(SECOidTag type) | |
318 | { | |
319 | const SecAsn1Template *template; | |
320 | extern const SecAsn1Template SecCmsSignedDataTemplate[]; | |
321 | extern const SecAsn1Template SecCmsEnvelopedDataTemplate[]; | |
322 | extern const SecAsn1Template SecCmsEncryptedDataTemplate[]; | |
323 | extern const SecAsn1Template SecCmsDigestedDataTemplate[]; | |
324 | ||
325 | switch (type) { | |
326 | case SEC_OID_PKCS7_SIGNED_DATA: | |
327 | template = SecCmsSignedDataTemplate; | |
328 | break; | |
329 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
330 | template = SecCmsEnvelopedDataTemplate; | |
331 | break; | |
332 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
333 | template = SecCmsEncryptedDataTemplate; | |
334 | break; | |
335 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
336 | template = SecCmsDigestedDataTemplate; | |
337 | break; | |
338 | default: | |
339 | case SEC_OID_PKCS7_DATA: | |
340 | case SEC_OID_OTHER: | |
341 | template = NULL; | |
342 | break; | |
343 | } | |
344 | return template; | |
345 | } | |
346 | ||
347 | size_t | |
348 | SecCmsUtilGetSizeByTypeTag(SECOidTag type) | |
349 | { | |
350 | size_t size; | |
351 | ||
352 | switch (type) { | |
353 | case SEC_OID_PKCS7_SIGNED_DATA: | |
354 | size = sizeof(SecCmsSignedData); | |
355 | break; | |
356 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
357 | size = sizeof(SecCmsEnvelopedData); | |
358 | break; | |
359 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
360 | size = sizeof(SecCmsEncryptedData); | |
361 | break; | |
362 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
363 | size = sizeof(SecCmsDigestedData); | |
364 | break; | |
365 | default: | |
366 | case SEC_OID_PKCS7_DATA: | |
367 | size = 0; | |
368 | break; | |
369 | } | |
370 | return size; | |
371 | } | |
372 | ||
373 | SecCmsContentInfoRef | |
374 | SecCmsContentGetContentInfo(void *msg, SECOidTag type) | |
375 | { | |
376 | SecCmsContent c; | |
377 | SecCmsContentInfoRef cinfo; | |
378 | ||
379 | if (!msg) | |
380 | return NULL; | |
381 | c.pointer = msg; | |
382 | switch (type) { | |
383 | case SEC_OID_PKCS7_SIGNED_DATA: | |
384 | cinfo = &(c.signedData->contentInfo); | |
385 | break; | |
386 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
387 | cinfo = &(c.envelopedData->contentInfo); | |
388 | break; | |
389 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
390 | cinfo = &(c.encryptedData->contentInfo); | |
391 | break; | |
392 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
393 | cinfo = &(c.digestedData->contentInfo); | |
394 | break; | |
395 | default: | |
396 | cinfo = NULL; | |
397 | } | |
398 | return cinfo; | |
399 | } | |
400 | ||
401 | // @@@ Return CFStringRef and do localization. | |
402 | const char * | |
403 | SecCmsUtilVerificationStatusToString(SecCmsVerificationStatus vs) | |
404 | { | |
405 | switch (vs) { | |
406 | case SecCmsVSUnverified: return "Unverified"; | |
407 | case SecCmsVSGoodSignature: return "GoodSignature"; | |
408 | case SecCmsVSBadSignature: return "BadSignature"; | |
409 | case SecCmsVSDigestMismatch: return "DigestMismatch"; | |
410 | case SecCmsVSSigningCertNotFound: return "SigningCertNotFound"; | |
411 | case SecCmsVSSigningCertNotTrusted: return "SigningCertNotTrusted"; | |
412 | case SecCmsVSSignatureAlgorithmUnknown: return "SignatureAlgorithmUnknown"; | |
413 | case SecCmsVSSignatureAlgorithmUnsupported: return "SignatureAlgorithmUnsupported"; | |
414 | case SecCmsVSMalformedSignature: return "MalformedSignature"; | |
415 | case SecCmsVSProcessingError: return "ProcessingError"; | |
416 | default: return "Unknown"; | |
417 | } | |
418 | } | |
419 | ||
420 | OSStatus | |
421 | SecArenaPoolCreate(size_t chunksize, SecArenaPoolRef *outArena) | |
422 | { | |
423 | OSStatus status; | |
424 | ||
425 | if (!outArena) { | |
426 | status = paramErr; | |
427 | goto loser; | |
428 | } | |
429 | ||
430 | *outArena = (SecArenaPoolRef)PORT_NewArena(chunksize); | |
431 | if (*outArena) | |
432 | status = 0; | |
433 | else | |
434 | status = PORT_GetError(); | |
435 | ||
436 | loser: | |
437 | return status; | |
438 | } | |
439 | ||
440 | void | |
441 | SecArenaPoolFree(SecArenaPoolRef arena, Boolean zero) | |
442 | { | |
443 | PORT_FreeArena((PLArenaPool *)arena, zero); | |
444 | } |