]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2008-2010,2012-2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | * | |
23 | */ | |
24 | ||
25 | /* | |
26 | * signed_data.c | |
27 | * Security | |
28 | * | |
b1ab9ed8 A |
29 | * |
30 | */ | |
31 | #include <AssertMacros.h> | |
32 | ||
33 | #include <TargetConditionals.h> | |
34 | #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) | |
35 | #define ENABLE_CMS 0 | |
36 | #else | |
37 | #define ENABLE_CMS 1 | |
38 | #endif | |
39 | ||
40 | #if ENABLE_CMS | |
41 | #include <Security/SecBase.h> | |
42 | #include <Security/SecCmsMessage.h> | |
43 | #include <Security/SecCmsSignedData.h> | |
44 | #include <Security/SecCmsEnvelopedData.h> | |
45 | #include <Security/SecCmsContentInfo.h> | |
46 | #include <Security/SecCmsSignerInfo.h> | |
47 | #include <Security/SecCmsRecipientInfo.h> | |
48 | #include <Security/SecCmsEncoder.h> | |
49 | #include <Security/SecCmsDecoder.h> | |
50 | #include <Security/SecCmsDigestContext.h> | |
51 | #include <Security/cmstpriv.h> | |
52 | ||
53 | #include <CoreFoundation/CFData.h> | |
54 | ||
55 | #include <Security/SecInternal.h> | |
56 | #include <Security/SecBasePriv.h> | |
57 | #include <Security/SecCertificatePriv.h> | |
58 | ||
59 | #include "SecCMS.h" | |
427c49bc | 60 | #include <Security/SecTrustPriv.h> |
b1ab9ed8 A |
61 | |
62 | ||
63 | #include <security_asn1/secasn1.h> | |
64 | #include <security_asn1/secerr.h> | |
65 | #include <security_asn1/secport.h> | |
66 | #include <Security/SecAsn1Item.h> | |
67 | #include <security_smime/secoid.h> | |
68 | #include <security_smime/cmslocal.h> | |
69 | ||
70 | CFTypeRef kSecCMSBulkEncryptionAlgorithm = CFSTR("kSecCMSBulkEncryptionAlgorithm"); | |
71 | CFTypeRef kSecCMSSignDigest = CFSTR("kSecCMSSignDigest"); | |
72 | CFTypeRef kSecCMSSignDetached = CFSTR("kSecCMSSignDetached"); | |
73 | CFTypeRef kSecCMSSignHashAlgorithm = CFSTR("kSecCMSSignHashAlgorithm"); | |
74 | CFTypeRef kSecCMSEncryptionAlgorithmDESCBC = CFSTR("kSecCMSEncryptionAlgorithmDESCBC"); | |
75 | CFTypeRef kSecCMSEncryptionAlgorithmAESCBC = CFSTR("kSecCMSEncryptionAlgorithmAESCBC"); | |
76 | CFTypeRef kSecCMSHashingAlgorithmMD5 = CFSTR("kSecCMSHashingAlgorithmMD5"); | |
77 | CFTypeRef kSecCMSCertChainMode = CFSTR("kSecCMSCertChainMode"); | |
78 | CFTypeRef kSecCMSCertChainModeNone = CFSTR("0"); | |
79 | CFTypeRef kSecCMSAdditionalCerts = CFSTR("kSecCMSAdditionalCerts"); | |
80 | CFTypeRef kSecCMSSignedAttributes = CFSTR("kSecCMSSignedAttributes"); | |
427c49bc A |
81 | CFTypeRef kSecCMSSignDate = CFSTR("kSecCMSSignDate"); |
82 | CFTypeRef kSecCMSAllCerts = CFSTR("kSecCMSAllCerts"); | |
b1ab9ed8 A |
83 | |
84 | OSStatus SecCMSCreateEnvelopedData(CFTypeRef recipient_or_cfarray_thereof, | |
85 | CFDictionaryRef params, CFDataRef data, CFMutableDataRef enveloped_data) | |
86 | { | |
87 | SecCmsMessageRef cmsg = NULL; | |
88 | SecCmsContentInfoRef cinfo; | |
89 | SecCmsEnvelopedDataRef envd = NULL; | |
90 | SECOidTag algorithmTag = SEC_OID_DES_EDE3_CBC; | |
91 | int keySize = 192; | |
92 | OSStatus status = errSecParam; | |
93 | ||
94 | if (params) { | |
95 | CFStringRef algorithm_name = CFDictionaryGetValue(params, kSecCMSBulkEncryptionAlgorithm); | |
96 | if (algorithm_name) { | |
97 | if (CFEqual(kSecCMSEncryptionAlgorithmDESCBC, algorithm_name)) { | |
98 | algorithmTag = SEC_OID_DES_CBC; | |
99 | keySize = 64; | |
100 | } else if (CFEqual(kSecCMSEncryptionAlgorithmAESCBC, algorithm_name)) { | |
101 | algorithmTag = SEC_OID_AES_128_CBC; | |
102 | keySize = 128; | |
103 | } | |
104 | } | |
105 | } | |
106 | ||
107 | require(cmsg = SecCmsMessageCreate(), out); | |
108 | require(envd = SecCmsEnvelopedDataCreate(cmsg, algorithmTag, keySize), out); | |
109 | require(cinfo = SecCmsMessageGetContentInfo(cmsg), out); | |
110 | require_noerr(SecCmsContentInfoSetContentEnvelopedData(cinfo, envd), out); | |
111 | require(cinfo = SecCmsEnvelopedDataGetContentInfo(envd), out); | |
112 | require_noerr(SecCmsContentInfoSetContentData(cinfo, NULL, false), out); | |
113 | // == wrapper of: require(SECSuccess == SecCmsContentInfoSetContent(cinfo, SEC_OID_PKCS7_DATA, NULL), out); | |
114 | ||
115 | if (CFGetTypeID(recipient_or_cfarray_thereof) == CFArrayGetTypeID()) { | |
116 | CFIndex dex, numCerts = CFArrayGetCount(recipient_or_cfarray_thereof); | |
117 | for(dex=0; dex<numCerts; dex++) { | |
118 | SecCertificateRef recip = | |
119 | (SecCertificateRef)CFArrayGetValueAtIndex(recipient_or_cfarray_thereof, dex); | |
120 | SecCmsRecipientInfoRef rinfo; | |
121 | require(rinfo = SecCmsRecipientInfoCreate(envd, recip), out); | |
122 | } | |
123 | } else if (CFGetTypeID(recipient_or_cfarray_thereof) == SecCertificateGetTypeID()) { | |
124 | require(SecCmsRecipientInfoCreate(envd, (SecCertificateRef)recipient_or_cfarray_thereof), out); | |
125 | } else | |
126 | goto out; | |
127 | ||
128 | SecAsn1Item input = {}; | |
129 | if (data) { | |
130 | input.Length = CFDataGetLength(data); | |
131 | input.Data = (uint8_t*)CFDataGetBytePtr(data); | |
132 | } | |
133 | require_noerr(SecCmsMessageEncode(cmsg, (data && input.Length) ? &input : NULL, enveloped_data), out); | |
134 | ||
427c49bc | 135 | status = errSecSuccess; |
b1ab9ed8 A |
136 | out: |
137 | if (cmsg) SecCmsMessageDestroy(cmsg); | |
138 | return status; | |
139 | } | |
140 | ||
141 | OSStatus SecCMSDecryptEnvelopedData(CFDataRef message, | |
142 | CFMutableDataRef data, SecCertificateRef *recipient) | |
143 | { | |
144 | SecCmsMessageRef cmsg = NULL; | |
145 | SecCmsContentInfoRef cinfo; | |
146 | SecCmsEnvelopedDataRef envd = NULL; | |
147 | SecCertificateRef used_recipient = NULL; | |
148 | OSStatus status = errSecParam; | |
149 | ||
150 | SecAsn1Item encoded_message = { CFDataGetLength(message), (uint8_t*)CFDataGetBytePtr(message) }; | |
151 | require_noerr_action_quiet(SecCmsMessageDecode(&encoded_message, NULL, NULL, NULL, NULL, NULL, NULL, &cmsg), | |
152 | out, status = errSecDecode); | |
153 | require_quiet(cinfo = SecCmsMessageContentLevel(cmsg, 0), out); | |
154 | require_quiet(SecCmsContentInfoGetContentTypeTag(cinfo) == SEC_OID_PKCS7_ENVELOPED_DATA, out); | |
155 | require_quiet(envd = (SecCmsEnvelopedDataRef)SecCmsContentInfoGetContent(cinfo), out); | |
156 | SecCmsRecipientInfoRef *rinfo = envd->recipientInfos; | |
157 | while (!used_recipient && *rinfo) { | |
158 | used_recipient = (*rinfo)->cert; | |
159 | rinfo++; | |
160 | } | |
161 | require_quiet(2 == SecCmsMessageContentLevelCount(cmsg), out); | |
162 | require_quiet(cinfo = SecCmsMessageContentLevel(cmsg, 1), out); | |
163 | require_quiet(SecCmsContentInfoGetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA, out); | |
164 | const SecAsn1Item *content = SecCmsMessageGetContent(cmsg); | |
165 | if (content) | |
166 | CFDataAppendBytes(data, content->Data, content->Length); | |
167 | if (recipient) { | |
427c49bc | 168 | CFRetainSafe(used_recipient); |
b1ab9ed8 A |
169 | *recipient = used_recipient; |
170 | } | |
427c49bc | 171 | status = errSecSuccess; |
b1ab9ed8 A |
172 | out: |
173 | if (cmsg) SecCmsMessageDestroy(cmsg); | |
174 | return status; | |
175 | } | |
176 | ||
177 | static SecCmsAttribute * | |
178 | make_attr(PLArenaPool *poolp, SecAsn1Item *type, SecAsn1Item *value, bool encoded) | |
179 | { | |
180 | SecAsn1Item * copiedvalue; | |
181 | SecCmsAttribute *attr = (SecCmsAttribute *)PORT_ArenaZAlloc(poolp, sizeof(SecCmsAttribute)); | |
182 | if (attr == NULL) | |
183 | goto loser; | |
184 | ||
185 | if (SECITEM_CopyItem(poolp, &(attr->type), type) != SECSuccess) | |
186 | goto loser; | |
187 | ||
188 | if (value != NULL) { | |
189 | if ((copiedvalue = SECITEM_AllocItem(poolp, NULL, value->Length)) == NULL) | |
190 | goto loser; | |
191 | ||
192 | if (SECITEM_CopyItem(poolp, copiedvalue, value) != SECSuccess) | |
193 | goto loser; | |
194 | ||
60c433a9 A |
195 | if (SecCmsArrayAdd(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess) |
196 | goto loser; | |
b1ab9ed8 A |
197 | } |
198 | ||
199 | attr->encoded = encoded; | |
200 | ||
201 | loser: | |
202 | return attr; | |
203 | } | |
204 | ||
205 | static void | |
206 | signerinfo_add_auth_attr(SecCmsSignerInfoRef signerinfo, /*SECOidTag oidtag*/ | |
207 | SecAsn1Item *oid, SecAsn1Item *value, bool encoded) | |
208 | { | |
209 | PLArenaPool *poolp = signerinfo->signedData->contentInfo.cmsg->poolp; | |
210 | PORT_Assert (poolp != NULL); | |
211 | void *mark = PORT_ArenaMark (poolp); | |
212 | ||
213 | SecCmsAttribute *attr = make_attr(poolp, oid, value, encoded); | |
214 | if (!attr || SecCmsAttributeArrayAddAttr(poolp, &(signerinfo->authAttr), attr) != SECSuccess) | |
215 | goto loser; | |
216 | ||
217 | PORT_ArenaUnmark (poolp, mark); | |
218 | return; | |
219 | ||
220 | loser: | |
221 | PORT_Assert (mark != NULL); | |
222 | PORT_ArenaRelease (poolp, mark); | |
223 | return; | |
224 | } | |
225 | ||
226 | static void sign_all_attributes(const void *key, const void *value, void *context) | |
227 | { | |
228 | SecAsn1Item oid = { CFDataGetLength(key), (uint8_t*)CFDataGetBytePtr(key) }, | |
229 | oid_value = { CFDataGetLength(value), (uint8_t*)CFDataGetBytePtr(value) }; | |
230 | ||
231 | signerinfo_add_auth_attr((SecCmsSignerInfoRef)context, &oid, &oid_value, true); | |
232 | } | |
233 | ||
234 | #if 0 | |
235 | static void enveloped_data_add_unprotected_attr(SecCmsEnvelopedDataRef envd, | |
236 | SecAsn1Item *oid, SecAsn1Item *value, bool encoded) | |
237 | { | |
238 | PLArenaPool *poolp = envd->contentInfo.cmsg->poolp; | |
239 | PORT_Assert (poolp != NULL); | |
240 | void *mark = PORT_ArenaMark (poolp); | |
241 | SecCmsAttribute *attr = make_attr(poolp, oid, value, encoded); | |
242 | ||
243 | if (!attr || SecCmsAttributeArrayAddAttr( | |
244 | poolp, | |
245 | &(envd->unprotectedAttr), attr) != SECSuccess) | |
246 | goto loser; | |
247 | ||
248 | PORT_ArenaUnmark (poolp, mark); | |
249 | return; | |
250 | ||
251 | loser: | |
252 | PORT_Assert (mark != NULL); | |
253 | PORT_ArenaRelease (poolp, mark); | |
254 | return; | |
255 | ||
256 | } | |
257 | #endif | |
258 | ||
259 | static OSStatus SecCMSSignDataOrDigestAndAttributes(SecIdentityRef identity, | |
260 | CFDataRef data, bool detached, bool data_is_digest, SECOidTag sign_algorithm, | |
261 | CFMutableDataRef signed_data, CFDictionaryRef signed_attributes, SecCmsCertChainMode chainMode, CFArrayRef additional_certs) | |
262 | { | |
263 | SecCmsMessageRef cmsg = NULL; | |
264 | SecCmsContentInfoRef cinfo; | |
265 | SecCmsSignedDataRef sigd = NULL; | |
266 | SecCmsSignerInfoRef signerinfo; | |
267 | OSStatus status = errSecParam; | |
268 | ||
269 | require(!data_is_digest || detached /* if digest, must be detached */, out); | |
270 | ||
271 | require(cmsg = SecCmsMessageCreate(), out); | |
272 | require(sigd = SecCmsSignedDataCreate(cmsg), out); | |
273 | require(cinfo = SecCmsMessageGetContentInfo(cmsg), out); | |
274 | require_noerr(SecCmsContentInfoSetContentSignedData(cinfo, sigd), out); | |
275 | require(cinfo = SecCmsSignedDataGetContentInfo(sigd), out); | |
276 | require_noerr(SecCmsContentInfoSetContentData(cinfo, NULL, detached), out); | |
277 | require(signerinfo = SecCmsSignerInfoCreate(sigd, identity, sign_algorithm), out); | |
278 | if (additional_certs) | |
279 | require_noerr(SecCmsSignedDataAddCertList(sigd, additional_certs), out); | |
280 | require_noerr(SecCmsSignerInfoIncludeCerts(signerinfo, chainMode, certUsageAnyCA), out); | |
281 | require_noerr(SecCmsSignerInfoAddSigningTime(signerinfo, CFAbsoluteTimeGetCurrent()), out); | |
282 | ||
283 | if (signed_attributes) | |
284 | CFDictionaryApplyFunction(signed_attributes, sign_all_attributes, signerinfo); | |
285 | ||
286 | SecAsn1Item input = {}; | |
287 | if (data) { | |
288 | input.Length = CFDataGetLength(data); | |
289 | input.Data = (uint8_t*)CFDataGetBytePtr(data); | |
290 | } | |
291 | if (data_is_digest) { | |
292 | require_noerr(SecCmsSignedDataSetDigestValue(sigd, sign_algorithm, &input), out); | |
293 | require_noerr(SecCmsMessageEncode(cmsg, NULL, signed_data), out); | |
294 | } | |
295 | else | |
296 | require_noerr(SecCmsMessageEncode(cmsg, (data && input.Length) ? &input : NULL, signed_data), out); | |
297 | ||
427c49bc | 298 | status = errSecSuccess; |
b1ab9ed8 A |
299 | out: |
300 | if (cmsg) SecCmsMessageDestroy(cmsg); | |
301 | return status; | |
302 | } | |
303 | ||
304 | OSStatus SecCMSSignDataAndAttributes(SecIdentityRef identity, CFDataRef data, bool detached, | |
305 | CFMutableDataRef signed_data, CFDictionaryRef signed_attributes) | |
306 | { | |
307 | return SecCMSSignDataOrDigestAndAttributes(identity, data, detached, false, SEC_OID_SHA1, | |
308 | signed_data, signed_attributes, SecCmsCMCertChain, NULL); | |
309 | } | |
310 | ||
311 | OSStatus SecCMSSignDigestAndAttributes(SecIdentityRef identity, CFDataRef digest, | |
312 | CFMutableDataRef signed_data, CFDictionaryRef signed_attributes) | |
313 | { | |
314 | return SecCMSSignDataOrDigestAndAttributes(identity, digest, true, true, SEC_OID_SHA1, | |
315 | signed_data, signed_attributes, SecCmsCMCertChain, NULL); | |
316 | } | |
317 | ||
318 | ||
319 | OSStatus SecCMSCreateSignedData(SecIdentityRef identity, CFDataRef data, | |
320 | CFDictionaryRef parameters, CFDictionaryRef signed_attributes, | |
321 | CFMutableDataRef signed_data) | |
322 | { | |
323 | bool is_digest = false, is_detached = false; | |
324 | CFStringRef algorithm_name = NULL; | |
325 | SecCmsCertChainMode chain_mode = SecCmsCMCertChain; | |
326 | CFArrayRef additional_certs = NULL; | |
327 | ||
328 | if (parameters) { | |
329 | is_digest = CFDictionaryGetValueIfPresent(parameters, | |
330 | kSecCMSSignDigest, NULL); | |
331 | is_detached = CFDictionaryGetValueIfPresent(parameters, | |
332 | kSecCMSSignDetached, NULL); | |
333 | algorithm_name = CFDictionaryGetValue(parameters, | |
334 | kSecCMSSignHashAlgorithm); | |
335 | ||
336 | CFTypeRef chain_mode_param = CFDictionaryGetValue(parameters, kSecCMSCertChainMode); | |
337 | if (chain_mode_param && (CFGetTypeID(chain_mode_param) == CFStringGetTypeID())) | |
338 | chain_mode = CFStringGetIntValue(chain_mode_param); | |
339 | ||
340 | CFTypeRef additional_certs_param = CFDictionaryGetValue(parameters, kSecCMSAdditionalCerts); | |
341 | if (additional_certs_param && (CFGetTypeID(additional_certs_param) == CFArrayGetTypeID())) | |
342 | additional_certs = (CFArrayRef)additional_certs_param; | |
343 | } | |
344 | ||
345 | SECOidTag algorithm = SEC_OID_SHA1; | |
346 | if (algorithm_name) { | |
347 | if (CFEqual(kSecCMSHashingAlgorithmMD5, algorithm_name)) { | |
348 | algorithm = SEC_OID_MD5; | |
349 | } else { | |
350 | algorithm = SEC_OID_UNKNOWN; | |
351 | } | |
352 | ||
353 | } | |
354 | ||
355 | return SecCMSSignDataOrDigestAndAttributes(identity, data, | |
356 | is_detached, is_digest, algorithm, | |
357 | signed_data, signed_attributes, chain_mode, additional_certs); | |
358 | } | |
359 | ||
360 | ||
427c49bc | 361 | static CFMutableArrayRef copy_signed_attribute_values(SecCmsAttribute *attr) |
b1ab9ed8 A |
362 | { |
363 | CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); | |
364 | SecAsn1Item **item = attr->values; | |
365 | if (item) while (*item) { | |
366 | CFDataRef asn1data = CFDataCreate(kCFAllocatorDefault, (*item)->Data, (*item)->Length); | |
367 | if (asn1data) { | |
368 | CFArrayAppendValue(array, asn1data); | |
369 | CFRelease(asn1data); | |
370 | } | |
371 | item++; | |
372 | } | |
373 | return array; | |
374 | } | |
375 | ||
376 | static OSStatus SecCMSVerifySignedData_internal(CFDataRef message, CFDataRef detached_contents, | |
427c49bc | 377 | CFTypeRef policy, SecTrustRef *trustref, CFArrayRef additional_certs, |
b1ab9ed8 A |
378 | CFDataRef *attached_contents, CFDictionaryRef *signed_attributes) |
379 | { | |
380 | SecCmsMessageRef cmsg = NULL; | |
381 | SecCmsContentInfoRef cinfo; | |
382 | SecCmsSignedDataRef sigd = NULL; | |
383 | OSStatus status = errSecParam; | |
384 | ||
385 | SecAsn1Item encoded_message = { CFDataGetLength(message), (uint8_t*)CFDataGetBytePtr(message) }; | |
386 | require_noerr_action_quiet(SecCmsMessageDecode(&encoded_message, NULL, NULL, NULL, NULL, NULL, NULL, &cmsg), | |
387 | out, status = errSecDecode); | |
388 | /* expected to be a signed data message at the top level */ | |
389 | require_quiet(cinfo = SecCmsMessageContentLevel(cmsg, 0), out); | |
390 | require_quiet(SecCmsContentInfoGetContentTypeTag(cinfo) == SEC_OID_PKCS7_SIGNED_DATA, out); | |
391 | require_quiet(sigd = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(cinfo), out); | |
392 | ||
393 | if (detached_contents) | |
394 | { | |
395 | require_quiet(!SecCmsSignedDataHasDigests(sigd), out); | |
396 | SECAlgorithmID **digestalgs = SecCmsSignedDataGetDigestAlgs(sigd); | |
397 | SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestalgs); | |
398 | SecCmsDigestContextUpdate(digcx, CFDataGetBytePtr(detached_contents), CFDataGetLength(detached_contents)); | |
399 | SecCmsSignedDataSetDigestContext(sigd, digcx); | |
400 | SecCmsDigestContextDestroy(digcx); | |
401 | } | |
402 | ||
403 | if (additional_certs) | |
404 | require_noerr_quiet(SecCmsSignedDataAddCertList(sigd, additional_certs), out); | |
405 | ||
406 | if (policy) { /* if no policy is given skip verification */ | |
407 | /* find out about signers */ | |
408 | int nsigners = SecCmsSignedDataSignerInfoCount(sigd); | |
409 | require_quiet(nsigners == 1, out); | |
410 | require_noerr_action_quiet(SecCmsSignedDataVerifySignerInfo(sigd, 0, NULL, policy, trustref), | |
411 | out, status = errSecAuthFailed); | |
412 | } | |
413 | ||
427c49bc | 414 | #if 0 |
b1ab9ed8 A |
415 | if (nsigners > 1) |
416 | trustrefs = CFArrayCreateMutable(kCFAllocatorDefault, nsigners, &kCFTypeArrayCallBacks); | |
417 | ||
418 | int j; | |
419 | for (j = 0; j < nsigners; j++) | |
420 | { | |
421 | SecTrustRef trustRef; | |
422 | require_noerr_action_quiet(SecCmsSignedDataVerifySignerInfo(sigd, j, NULL, policy, &trustRef), | |
423 | out, status = errSecAuthFailed); | |
424 | if ((j == 0) && (nsigners == 1)) | |
425 | *trustref_or_cfarray_thereof = trustRef; | |
426 | else { | |
427 | CFArrayAppendValue(trustrefs, trustRef); | |
428 | CFRelease(trustRef); | |
429 | } | |
430 | } | |
431 | *trustref_or_cfarray_thereof = trustrefs; | |
432 | trustrefs = NULL; | |
433 | #endif | |
434 | ||
427c49bc | 435 | status = errSecSuccess; |
b1ab9ed8 A |
436 | |
437 | if (attached_contents) { | |
438 | const SecAsn1Item *content = SecCmsMessageGetContent(cmsg); | |
439 | if (content) | |
440 | *attached_contents = CFDataCreate(kCFAllocatorDefault, content->Data, content->Length); | |
441 | else | |
442 | *attached_contents = NULL; | |
443 | } | |
444 | ||
445 | if (signed_attributes) { | |
446 | CFMutableDictionaryRef attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, | |
447 | 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
448 | require_quiet(attrs, out); | |
449 | SecCmsAttribute **signed_attrs = sigd->signerInfos[0]->authAttr; | |
450 | if (signed_attrs) while (*signed_attrs) { | |
451 | CFDataRef type = CFDataCreate(kCFAllocatorDefault, (*signed_attrs)->type.Data, (*signed_attrs)->type.Length); | |
452 | if (type) { | |
427c49bc | 453 | CFMutableArrayRef attr = copy_signed_attribute_values(*signed_attrs); |
b1ab9ed8 A |
454 | if (attr) { |
455 | CFMutableArrayRef existing_attrs = (CFMutableArrayRef)CFDictionaryGetValue(attrs, type); | |
456 | if (existing_attrs) { | |
457 | CFIndex count = CFArrayGetCount(attr); | |
458 | if (count) | |
459 | CFArrayAppendArray(existing_attrs, attr, CFRangeMake(0, count)); | |
460 | } else | |
461 | CFDictionarySetValue(attrs, type, attr); | |
462 | CFRelease(attr); | |
463 | } | |
464 | CFRelease(type); | |
465 | } | |
466 | signed_attrs++; | |
467 | } | |
427c49bc A |
468 | CFMutableArrayRef certs = NULL; |
469 | ||
470 | SecAsn1Item **cert_datas = SecCmsSignedDataGetCertificateList(sigd); | |
471 | certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); | |
472 | SecAsn1Item *cert_data; | |
473 | if (cert_datas) while ((cert_data = *cert_datas) != NULL) { | |
474 | SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length); | |
475 | if (cert) { | |
476 | CFArrayAppendValue(certs, cert); | |
477 | CFRelease(cert); | |
478 | } | |
479 | cert_datas++; | |
480 | } | |
481 | ||
482 | CFDictionaryAddValue(attrs, kSecCMSAllCerts, certs); | |
483 | ||
484 | /* Add "cooked" values separately */ | |
485 | CFAbsoluteTime signing_time; | |
486 | if (errSecSuccess == SecCmsSignerInfoGetSigningTime(sigd->signerInfos[0], &signing_time)) { | |
487 | CFDateRef signing_date = CFDateCreate(kCFAllocatorDefault, signing_time); | |
488 | if (signing_date){ | |
489 | CFDictionarySetValue(attrs, kSecCMSSignDate, signing_date); | |
490 | CFReleaseSafe(signing_date); | |
491 | } | |
492 | } | |
493 | ||
b1ab9ed8 | 494 | *signed_attributes = attrs; |
d8f41ccd | 495 | CFReleaseSafe(certs); |
b1ab9ed8 A |
496 | } |
497 | ||
427c49bc | 498 | |
b1ab9ed8 A |
499 | out: |
500 | if (cmsg) SecCmsMessageDestroy(cmsg); | |
501 | return status; | |
502 | } | |
503 | ||
504 | OSStatus SecCMSVerifyCopyDataAndAttributes(CFDataRef message, CFDataRef detached_contents, | |
427c49bc | 505 | CFTypeRef policy, SecTrustRef *trustref, |
b1ab9ed8 A |
506 | CFDataRef *attached_contents, CFDictionaryRef *signed_attributes) |
507 | { | |
427c49bc A |
508 | OSStatus status = SecCMSVerifySignedData_internal(message, detached_contents, policy, trustref, NULL, attached_contents, signed_attributes); |
509 | ||
510 | return status; | |
b1ab9ed8 A |
511 | } |
512 | ||
513 | OSStatus SecCMSVerifySignedData(CFDataRef message, CFDataRef detached_contents, | |
427c49bc | 514 | CFTypeRef policy, SecTrustRef *trustref, CFArrayRef additional_certificates, |
b1ab9ed8 A |
515 | CFDataRef *attached_contents, CFDictionaryRef *message_attributes) |
516 | { | |
517 | CFDictionaryRef signed_attributes = NULL; | |
518 | OSStatus status = SecCMSVerifySignedData_internal(message, detached_contents, policy, trustref, additional_certificates, attached_contents, &signed_attributes); | |
519 | if (!status && signed_attributes && message_attributes) { | |
520 | *message_attributes = CFDictionaryCreate(kCFAllocatorDefault, &kSecCMSSignedAttributes, (const void **)&signed_attributes, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
521 | } | |
522 | CFReleaseSafe(signed_attributes); | |
427c49bc | 523 | |
b1ab9ed8 A |
524 | return status; |
525 | } | |
526 | ||
527 | OSStatus SecCMSVerify(CFDataRef message, CFDataRef detached_contents, | |
427c49bc | 528 | CFTypeRef policy, SecTrustRef *trustref, |
b1ab9ed8 A |
529 | CFDataRef *attached_contents) { |
530 | return SecCMSVerifySignedData_internal(message, detached_contents, policy, trustref, NULL, attached_contents, NULL); | |
531 | } | |
532 | ||
533 | CFArrayRef SecCMSCertificatesOnlyMessageCopyCertificates(CFDataRef message) { | |
534 | SecCmsMessageRef cmsg = NULL; | |
535 | SecCmsContentInfoRef cinfo; | |
536 | SecCmsSignedDataRef sigd = NULL; | |
537 | CFMutableArrayRef certs = NULL; | |
538 | ||
539 | SecAsn1Item encoded_message = { CFDataGetLength(message), (uint8_t*)CFDataGetBytePtr(message) }; | |
540 | require_noerr_quiet(SecCmsMessageDecode(&encoded_message, NULL, NULL, NULL, NULL, NULL, NULL, &cmsg), out); | |
541 | /* expected to be a signed data message at the top level */ | |
542 | require(cinfo = SecCmsMessageContentLevel(cmsg, 0), out); | |
543 | require(SecCmsContentInfoGetContentTypeTag(cinfo) == SEC_OID_PKCS7_SIGNED_DATA, out); | |
544 | require(sigd = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(cinfo), out); | |
545 | ||
546 | /* find out about signers */ | |
547 | int nsigners = SecCmsSignedDataSignerInfoCount(sigd); | |
548 | require(nsigners == 0, out); | |
549 | ||
550 | SecAsn1Item **cert_datas = SecCmsSignedDataGetCertificateList(sigd); | |
551 | certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); | |
552 | SecAsn1Item *cert_data; | |
553 | if (cert_datas) while ((cert_data = *cert_datas) != NULL) { | |
554 | SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length); | |
555 | if (cert) { | |
556 | CFArrayAppendValue(certs, cert); | |
557 | CFRelease(cert); | |
558 | } | |
559 | cert_datas++; | |
560 | } | |
561 | ||
562 | out: | |
563 | if (cmsg) | |
564 | SecCmsMessageDestroy(cmsg); | |
565 | ||
566 | return certs; | |
567 | } | |
568 | ||
569 | ||
570 | extern const SecAsn1Template SecCmsMessageTemplate[]; | |
571 | ||
572 | CFDataRef SecCMSCreateCertificatesOnlyMessage(CFTypeRef cert_or_array_thereof) { | |
573 | OSStatus status = errSecParam; | |
574 | SecCmsMessageRef cmsg = NULL; | |
575 | SecCmsContentInfoRef cinfo; | |
576 | SecCmsSignedDataRef sigd = NULL; | |
577 | CFMutableDataRef cert_only_signed_data = NULL; | |
578 | CFArrayRef cert_array = NULL; | |
579 | CFIndex cert_array_count = 0; | |
580 | SecCertificateRef cert = NULL; | |
581 | ||
582 | require(cert_or_array_thereof, out); | |
583 | ||
584 | require(cmsg = SecCmsMessageCreate(), out); | |
585 | require(sigd = SecCmsSignedDataCreate(cmsg), out); | |
586 | require_noerr(SecCmsContentInfoSetContentData(&(sigd->contentInfo), NULL, PR_TRUE), out); | |
587 | require(cinfo = SecCmsMessageGetContentInfo(cmsg), out); | |
588 | require_noerr(SecCmsContentInfoSetContentSignedData(cinfo, sigd), out); | |
589 | long version = SEC_CMS_SIGNED_DATA_VERSION_BASIC; | |
590 | require(SEC_ASN1EncodeInteger(cmsg->poolp, &(sigd->version), version), out); | |
591 | ||
592 | if (CFGetTypeID(cert_or_array_thereof) == SecCertificateGetTypeID()) { | |
593 | cert_array = CFArrayCreate(kCFAllocatorDefault, &cert_or_array_thereof, 1, &kCFTypeArrayCallBacks); | |
594 | } else if (CFGetTypeID(cert_or_array_thereof) == CFArrayGetTypeID()) { | |
595 | cert_array = CFArrayCreateCopy(kCFAllocatorDefault, (CFArrayRef)cert_or_array_thereof); | |
596 | } | |
597 | ||
598 | require(cert_array, out); | |
599 | cert_array_count = CFArrayGetCount(cert_array); | |
600 | require(cert_array_count > 0, out); | |
601 | ||
602 | sigd->rawCerts = (SecAsn1Item * *)PORT_ArenaAlloc(cmsg->poolp, (cert_array_count + 1) * sizeof(SecAsn1Item *)); | |
603 | require(sigd->rawCerts, out); | |
604 | CFIndex ix; | |
605 | for (ix = 0; ix < cert_array_count; ix++) { | |
606 | cert = (SecCertificateRef)CFArrayGetValueAtIndex(cert_array, ix); | |
607 | require(cert, out); | |
608 | ||
609 | sigd->rawCerts[ix] = PORT_ArenaZAlloc(cmsg->poolp, sizeof(SecAsn1Item)); | |
610 | SecAsn1Item cert_data = { SecCertificateGetLength(cert), | |
611 | (uint8_t *)SecCertificateGetBytePtr(cert) }; | |
612 | *(sigd->rawCerts[ix]) = cert_data; | |
613 | } | |
614 | sigd->rawCerts[ix] = NULL; | |
615 | ||
616 | /* this is a SET OF, so we need to sort them guys - we have the DER already, though */ | |
617 | if (cert_array_count > 1) | |
618 | SecCmsArraySort((void **)sigd->rawCerts, SecCmsUtilDERCompare, NULL, NULL); | |
619 | ||
620 | cert_only_signed_data = CFDataCreateMutable(kCFAllocatorDefault, 0); | |
621 | SecAsn1Item cert_only_signed_data_item = {}; | |
622 | require_quiet(SEC_ASN1EncodeItem(cmsg->poolp, &cert_only_signed_data_item, | |
623 | cmsg, SecCmsMessageTemplate), out); | |
624 | CFDataAppendBytes(cert_only_signed_data, cert_only_signed_data_item.Data, | |
625 | cert_only_signed_data_item.Length); | |
626 | ||
427c49bc | 627 | status = errSecSuccess; |
b1ab9ed8 A |
628 | out: |
629 | CFReleaseSafe(cert_array); | |
630 | if (status) CFReleaseSafe(cert_only_signed_data); | |
631 | if (cmsg) SecCmsMessageDestroy(cmsg); | |
632 | return cert_only_signed_data; | |
633 | } | |
634 | ||
635 | CFDataRef SecCMSCreateCertificatesOnlyMessageIAP(SecCertificateRef cert) | |
636 | { | |
637 | static const uint8_t header[] = { | |
638 | 0x30, 0x82, 0x03, 0x6d, 0x06, 0x09, 0x2a, 0x86, | |
639 | 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, | |
640 | 0x82, 0x03, 0x5e, 0x30, 0x82, 0x03, 0x5a, 0x02, | |
641 | 0x01, 0x01, 0x31, 0x00, 0x30, 0x0b, 0x06, 0x09, | |
642 | 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, | |
643 | 0x01, 0xa0, 0x82, 0x03, 0x40 | |
644 | }; | |
645 | ||
646 | static const uint8_t trailer[] = { | |
647 | 0xa1, 0x00, 0x31, 0x00 | |
648 | }; | |
649 | ||
650 | CFMutableDataRef message = NULL; | |
651 | CFDataRef certdata; | |
652 | const uint8_t *certbytes; | |
653 | CFIndex certlen; | |
654 | uint8_t *messagebytes; | |
655 | uint16_t messagelen; | |
656 | ||
657 | certdata = SecCertificateCopyData(cert); | |
658 | require(certdata, out); | |
659 | ||
660 | certbytes = CFDataGetBytePtr(certdata); | |
661 | certlen = CFDataGetLength(certdata); | |
662 | require(certlen > UINT8_MAX, out); | |
663 | require(certlen < UINT16_MAX, out); | |
664 | ||
665 | message = CFDataCreateMutable(kCFAllocatorDefault, 0); | |
666 | require(message, out); | |
667 | ||
668 | CFDataAppendBytes(message, header, sizeof(header)); | |
669 | CFDataAppendBytes(message, certbytes, certlen); | |
670 | CFDataAppendBytes(message, trailer, sizeof(trailer)); | |
671 | ||
672 | messagebytes = CFDataGetMutableBytePtr(message); | |
673 | messagelen = CFDataGetLength(message); | |
674 | ||
675 | messagelen -= 4; | |
676 | messagebytes[2] = messagelen >> 8; | |
677 | messagebytes[3] = messagelen & 0xFF; | |
678 | ||
679 | messagelen -= 15; | |
680 | messagebytes[17] = messagelen >> 8; | |
681 | messagebytes[18] = messagelen & 0xFF; | |
682 | ||
683 | messagelen -= 4; | |
684 | messagebytes[21] = messagelen >> 8; | |
685 | messagebytes[22] = messagelen & 0xFF; | |
686 | ||
687 | messagelen -= 26; | |
688 | messagebytes[43] = messagelen >> 8; | |
689 | messagebytes[44] = messagelen & 0xFF; | |
690 | ||
691 | out: | |
692 | CFReleaseSafe(certdata); | |
693 | return message; | |
694 | } | |
695 | ||
696 | #endif /* ENABLE_CMS */ | |
697 |