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