1/*
2 * Copyright (c) 2008-2010,2012-2014 Apple Inc. All Rights Reserved.
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 *
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"
60#include <Security/SecTrustPriv.h>
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
70CFTypeRef kSecCMSBulkEncryptionAlgorithm = CFSTR("kSecCMSBulkEncryptionAlgorithm");
71CFTypeRef kSecCMSSignDigest = CFSTR("kSecCMSSignDigest");
72CFTypeRef kSecCMSSignDetached = CFSTR("kSecCMSSignDetached");
73CFTypeRef kSecCMSSignHashAlgorithm = CFSTR("kSecCMSSignHashAlgorithm");
74CFTypeRef kSecCMSEncryptionAlgorithmDESCBC = CFSTR("kSecCMSEncryptionAlgorithmDESCBC");
75CFTypeRef kSecCMSEncryptionAlgorithmAESCBC = CFSTR("kSecCMSEncryptionAlgorithmAESCBC");
76CFTypeRef kSecCMSHashingAlgorithmMD5 = CFSTR("kSecCMSHashingAlgorithmMD5");
77CFTypeRef kSecCMSCertChainMode = CFSTR("kSecCMSCertChainMode");
78CFTypeRef kSecCMSCertChainModeNone = CFSTR("0");
79CFTypeRef kSecCMSAdditionalCerts = CFSTR("kSecCMSAdditionalCerts");
80CFTypeRef kSecCMSSignedAttributes = CFSTR("kSecCMSSignedAttributes");
81CFTypeRef kSecCMSSignDate = CFSTR("kSecCMSSignDate");
82CFTypeRef kSecCMSAllCerts = CFSTR("kSecCMSAllCerts");
83
84OSStatus 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
135    status = errSecSuccess;
136out:
137    if (cmsg) SecCmsMessageDestroy(cmsg);
138    return status;
139}
140
141OSStatus 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) {
168        CFRetainSafe(used_recipient);
169        *recipient = used_recipient;
170    }
171    status = errSecSuccess;
172out:
173    if (cmsg) SecCmsMessageDestroy(cmsg);
174    return status;
175}
176
177static SecCmsAttribute *
178make_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
195        SecCmsArrayAdd(poolp, (void ***)&(attr->values), (void *)copiedvalue);
196    }
197
198    attr->encoded = encoded;
199
200loser:
201    return attr;
202}
203
204static void
205signerinfo_add_auth_attr(SecCmsSignerInfoRef signerinfo, /*SECOidTag oidtag*/
206    SecAsn1Item *oid, SecAsn1Item *value, bool encoded)
207{
208    PLArenaPool *poolp  = signerinfo->signedData->contentInfo.cmsg->poolp;
209    PORT_Assert (poolp != NULL);
210    void *mark = PORT_ArenaMark (poolp);
211
212    SecCmsAttribute *attr = make_attr(poolp, oid, value, encoded);
213    if (!attr || SecCmsAttributeArrayAddAttr(poolp, &(signerinfo->authAttr), attr) != SECSuccess)
214            goto loser;
215
216    PORT_ArenaUnmark (poolp, mark);
217    return;
218
219loser:
220    PORT_Assert (mark != NULL);
221    PORT_ArenaRelease (poolp, mark);
222    return;
223}
224
225static void sign_all_attributes(const void *key, const void *value, void *context)
226{
227    SecAsn1Item oid = { CFDataGetLength(key), (uint8_t*)CFDataGetBytePtr(key) },
228            oid_value = { CFDataGetLength(value), (uint8_t*)CFDataGetBytePtr(value) };
229
230    signerinfo_add_auth_attr((SecCmsSignerInfoRef)context, &oid, &oid_value, true);
231}
232
233#if 0
234static void enveloped_data_add_unprotected_attr(SecCmsEnvelopedDataRef envd,
235    SecAsn1Item *oid, SecAsn1Item *value, bool encoded)
236{
237    PLArenaPool *poolp  = envd->contentInfo.cmsg->poolp;
238    PORT_Assert (poolp != NULL);
239    void *mark = PORT_ArenaMark (poolp);
240    SecCmsAttribute *attr = make_attr(poolp, oid, value, encoded);
241
242    if (!attr || SecCmsAttributeArrayAddAttr(
243        poolp,
244        &(envd->unprotectedAttr), attr) != SECSuccess)
245            goto loser;
246
247    PORT_ArenaUnmark (poolp, mark);
248    return;
249
250loser:
251    PORT_Assert (mark != NULL);
252    PORT_ArenaRelease (poolp, mark);
253    return;
254
255}
256#endif
257
258static OSStatus SecCMSSignDataOrDigestAndAttributes(SecIdentityRef identity,
259    CFDataRef data, bool detached, bool data_is_digest, SECOidTag sign_algorithm,
260    CFMutableDataRef signed_data, CFDictionaryRef signed_attributes, SecCmsCertChainMode chainMode, CFArrayRef additional_certs)
261{
262    SecCmsMessageRef cmsg = NULL;
263    SecCmsContentInfoRef cinfo;
264    SecCmsSignedDataRef sigd = NULL;
265    SecCmsSignerInfoRef signerinfo;
266    OSStatus status = errSecParam;
267
268    require(!data_is_digest || detached /* if digest, must be detached */, out);
269
270    require(cmsg = SecCmsMessageCreate(), out);
271    require(sigd = SecCmsSignedDataCreate(cmsg), out);
272    require(cinfo = SecCmsMessageGetContentInfo(cmsg), out);
273    require_noerr(SecCmsContentInfoSetContentSignedData(cinfo, sigd), out);
274    require(cinfo = SecCmsSignedDataGetContentInfo(sigd), out);
275    require_noerr(SecCmsContentInfoSetContentData(cinfo, NULL, detached), out);
276    require(signerinfo = SecCmsSignerInfoCreate(sigd, identity, sign_algorithm), out);
277    if (additional_certs)
278        require_noerr(SecCmsSignedDataAddCertList(sigd, additional_certs), out);
279    require_noerr(SecCmsSignerInfoIncludeCerts(signerinfo, chainMode, certUsageAnyCA), out);
280    require_noerr(SecCmsSignerInfoAddSigningTime(signerinfo, CFAbsoluteTimeGetCurrent()), out);
281
282    if (signed_attributes)
283        CFDictionaryApplyFunction(signed_attributes, sign_all_attributes, signerinfo);
284
285    SecAsn1Item input = {};
286    if (data) {
287        input.Length = CFDataGetLength(data);
288        input.Data = (uint8_t*)CFDataGetBytePtr(data);
289    }
290    if (data_is_digest) {
291        require_noerr(SecCmsSignedDataSetDigestValue(sigd, sign_algorithm, &input), out);
292        require_noerr(SecCmsMessageEncode(cmsg, NULL, signed_data), out);
293    }
294    else
295        require_noerr(SecCmsMessageEncode(cmsg, (data && input.Length) ? &input : NULL, signed_data), out);
296
297    status = errSecSuccess;
298out:
299    if (cmsg) SecCmsMessageDestroy(cmsg);
300    return status;
301}
302
303OSStatus SecCMSSignDataAndAttributes(SecIdentityRef identity, CFDataRef data, bool detached,
304    CFMutableDataRef signed_data, CFDictionaryRef signed_attributes)
305{
306    return SecCMSSignDataOrDigestAndAttributes(identity, data, detached, false, SEC_OID_SHA1,
307        signed_data, signed_attributes, SecCmsCMCertChain, NULL);
308}
309
310OSStatus SecCMSSignDigestAndAttributes(SecIdentityRef identity, CFDataRef digest,
311    CFMutableDataRef signed_data, CFDictionaryRef signed_attributes)
312{
313    return SecCMSSignDataOrDigestAndAttributes(identity, digest, true, true, SEC_OID_SHA1,
314        signed_data, signed_attributes, SecCmsCMCertChain, NULL);
315}
316
317
318OSStatus SecCMSCreateSignedData(SecIdentityRef identity, CFDataRef data,
319    CFDictionaryRef parameters, CFDictionaryRef signed_attributes,
320    CFMutableDataRef signed_data)
321{
322    bool is_digest = false, is_detached = false;
323    CFStringRef algorithm_name = NULL;
324    SecCmsCertChainMode chain_mode = SecCmsCMCertChain;
325    CFArrayRef additional_certs = NULL;
326
327    if (parameters) {
328        is_digest = CFDictionaryGetValueIfPresent(parameters,
329                        kSecCMSSignDigest, NULL);
330        is_detached = CFDictionaryGetValueIfPresent(parameters,
331                        kSecCMSSignDetached, NULL);
332        algorithm_name = CFDictionaryGetValue(parameters,
333                        kSecCMSSignHashAlgorithm);
334
335        CFTypeRef chain_mode_param = CFDictionaryGetValue(parameters, kSecCMSCertChainMode);
336        if (chain_mode_param && (CFGetTypeID(chain_mode_param) == CFStringGetTypeID()))
337            chain_mode = CFStringGetIntValue(chain_mode_param);
338
339        CFTypeRef additional_certs_param = CFDictionaryGetValue(parameters, kSecCMSAdditionalCerts);
340        if (additional_certs_param && (CFGetTypeID(additional_certs_param) == CFArrayGetTypeID()))
341            additional_certs = (CFArrayRef)additional_certs_param;
342    }
343
344    SECOidTag algorithm = SEC_OID_SHA1;
345    if (algorithm_name) {
346        if (CFEqual(kSecCMSHashingAlgorithmMD5, algorithm_name)) {
347            algorithm = SEC_OID_MD5;
348        } else {
349            algorithm = SEC_OID_UNKNOWN;
350        }
351
352    }
353
354    return SecCMSSignDataOrDigestAndAttributes(identity, data,
355        is_detached, is_digest, algorithm,
356        signed_data, signed_attributes, chain_mode, additional_certs);
357}
358
359
360static CFMutableArrayRef copy_signed_attribute_values(SecCmsAttribute *attr)
361{
362    CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
363    SecAsn1Item **item = attr->values;
364    if (item) while (*item) {
365        CFDataRef asn1data = CFDataCreate(kCFAllocatorDefault, (*item)->Data, (*item)->Length);
366        if (asn1data) {
367            CFArrayAppendValue(array, asn1data);
368            CFRelease(asn1data);
369        }
370        item++;
371    }
372    return array;
373}
374
375static OSStatus SecCMSVerifySignedData_internal(CFDataRef message, CFDataRef detached_contents,
376    CFTypeRef policy, SecTrustRef *trustref, CFArrayRef additional_certs,
377    CFDataRef *attached_contents, CFDictionaryRef *signed_attributes)
378{
379    SecCmsMessageRef cmsg = NULL;
380    SecCmsContentInfoRef cinfo;
381    SecCmsSignedDataRef sigd = NULL;
382    OSStatus status = errSecParam;
383
384    SecAsn1Item encoded_message = { CFDataGetLength(message), (uint8_t*)CFDataGetBytePtr(message) };
385    require_noerr_action_quiet(SecCmsMessageDecode(&encoded_message, NULL, NULL, NULL, NULL, NULL, NULL, &cmsg),
386        out, status = errSecDecode);
387    /* expected to be a signed data message at the top level */
388    require_quiet(cinfo = SecCmsMessageContentLevel(cmsg, 0), out);
389    require_quiet(SecCmsContentInfoGetContentTypeTag(cinfo) == SEC_OID_PKCS7_SIGNED_DATA, out);
390    require_quiet(sigd = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(cinfo), out);
391
392    if (detached_contents)
393    {
394        require_quiet(!SecCmsSignedDataHasDigests(sigd), out);
395        SECAlgorithmID **digestalgs = SecCmsSignedDataGetDigestAlgs(sigd);
396        SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestalgs);
397        SecCmsDigestContextUpdate(digcx, CFDataGetBytePtr(detached_contents), CFDataGetLength(detached_contents));
398        SecCmsSignedDataSetDigestContext(sigd, digcx);
399        SecCmsDigestContextDestroy(digcx);
400    }
401
402    if (additional_certs)
403        require_noerr_quiet(SecCmsSignedDataAddCertList(sigd, additional_certs), out);
404
405    if (policy) { /* if no policy is given skip verification */
406        /* find out about signers */
407        int nsigners = SecCmsSignedDataSignerInfoCount(sigd);
408        require_quiet(nsigners == 1, out);
409        require_noerr_action_quiet(SecCmsSignedDataVerifySignerInfo(sigd, 0, NULL, policy, trustref),
410            out, status = errSecAuthFailed);
411    }
412
413    #if 0
414    if (nsigners > 1)
415        trustrefs = CFArrayCreateMutable(kCFAllocatorDefault, nsigners, &kCFTypeArrayCallBacks);
416
417    int j;
418    for (j = 0; j < nsigners; j++)
419    {
420        SecTrustRef trustRef;
421        require_noerr_action_quiet(SecCmsSignedDataVerifySignerInfo(sigd, j, NULL, policy, &trustRef),
422            out, status = errSecAuthFailed);
423        if ((j == 0) && (nsigners == 1))
424            *trustref_or_cfarray_thereof = trustRef;
425        else {
426            CFArrayAppendValue(trustrefs, trustRef);
427            CFRelease(trustRef);
428        }
429    }
430    *trustref_or_cfarray_thereof = trustrefs;
431    trustrefs = NULL;
432#endif
433
434    status = errSecSuccess;
435
436    if (attached_contents) {
437        const SecAsn1Item *content = SecCmsMessageGetContent(cmsg);
438        if (content)
439            *attached_contents = CFDataCreate(kCFAllocatorDefault, content->Data, content->Length);
440        else
441            *attached_contents = NULL;
442    }
443
444    if (signed_attributes) {
445        CFMutableDictionaryRef attrs = CFDictionaryCreateMutable(kCFAllocatorDefault,
446            0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
447        require_quiet(attrs, out);
448        SecCmsAttribute **signed_attrs = sigd->signerInfos[0]->authAttr;
449        if (signed_attrs) while (*signed_attrs) {
450            CFDataRef type = CFDataCreate(kCFAllocatorDefault, (*signed_attrs)->type.Data, (*signed_attrs)->type.Length);
451            if (type) {
452                CFMutableArrayRef attr = copy_signed_attribute_values(*signed_attrs);
453                if (attr) {
454                    CFMutableArrayRef existing_attrs = (CFMutableArrayRef)CFDictionaryGetValue(attrs, type);
455                    if (existing_attrs) {
456                        CFIndex count = CFArrayGetCount(attr);
457                        if (count)
458                            CFArrayAppendArray(existing_attrs, attr, CFRangeMake(0, count));
459                    } else
460                        CFDictionarySetValue(attrs, type, attr);
461                    CFRelease(attr);
462                }
463                CFRelease(type);
464            }
465            signed_attrs++;
466        }
467        CFMutableArrayRef certs = NULL;
468
469        SecAsn1Item **cert_datas = SecCmsSignedDataGetCertificateList(sigd);
470        certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
471        SecAsn1Item *cert_data;
472        if (cert_datas) while ((cert_data = *cert_datas) != NULL) {
473            SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length);
474            if (cert) {
475                CFArrayAppendValue(certs, cert);
476                CFRelease(cert);
477            }
478            cert_datas++;
479        }
480
481        CFDictionaryAddValue(attrs, kSecCMSAllCerts, certs);
482
483        /* Add "cooked" values separately */
484        CFAbsoluteTime signing_time;
485        if (errSecSuccess == SecCmsSignerInfoGetSigningTime(sigd->signerInfos[0], &signing_time)) {
486                CFDateRef signing_date = CFDateCreate(kCFAllocatorDefault, signing_time);
487            if (signing_date){
488                CFDictionarySetValue(attrs, kSecCMSSignDate, signing_date);
489                CFReleaseSafe(signing_date);
490            }
491        }
492
493        *signed_attributes = attrs;
494        CFReleaseSafe(certs);
495    }
496
497
498out:
499    if (cmsg) SecCmsMessageDestroy(cmsg);
500    return status;
501}
502
503OSStatus SecCMSVerifyCopyDataAndAttributes(CFDataRef message, CFDataRef detached_contents,
504    CFTypeRef policy, SecTrustRef *trustref,
505    CFDataRef *attached_contents, CFDictionaryRef *signed_attributes)
506{
507    OSStatus status = SecCMSVerifySignedData_internal(message, detached_contents, policy, trustref, NULL, attached_contents, signed_attributes);
508
509    return status;
510}
511
512OSStatus SecCMSVerifySignedData(CFDataRef message, CFDataRef detached_contents,
513    CFTypeRef policy, SecTrustRef *trustref, CFArrayRef additional_certificates,
514    CFDataRef *attached_contents, CFDictionaryRef *message_attributes)
515{
516    CFDictionaryRef signed_attributes = NULL;
517    OSStatus status = SecCMSVerifySignedData_internal(message, detached_contents, policy, trustref, additional_certificates, attached_contents, &signed_attributes);
518    if (!status && signed_attributes && message_attributes) {
519        *message_attributes = CFDictionaryCreate(kCFAllocatorDefault, &kSecCMSSignedAttributes, (const void **)&signed_attributes, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
520    }
521    CFReleaseSafe(signed_attributes);
522
523    return status;
524}
525
526OSStatus SecCMSVerify(CFDataRef message, CFDataRef detached_contents,
527    CFTypeRef policy, SecTrustRef *trustref,
528    CFDataRef *attached_contents) {
529        return SecCMSVerifySignedData_internal(message, detached_contents, policy, trustref, NULL, attached_contents, NULL);
530}
531
532CFArrayRef SecCMSCertificatesOnlyMessageCopyCertificates(CFDataRef message) {
533    SecCmsMessageRef cmsg = NULL;
534    SecCmsContentInfoRef cinfo;
535    SecCmsSignedDataRef sigd = NULL;
536    CFMutableArrayRef certs = NULL;
537
538    SecAsn1Item encoded_message = { CFDataGetLength(message), (uint8_t*)CFDataGetBytePtr(message) };
539    require_noerr_quiet(SecCmsMessageDecode(&encoded_message, NULL, NULL, NULL, NULL, NULL, NULL, &cmsg), out);
540    /* expected to be a signed data message at the top level */
541    require(cinfo = SecCmsMessageContentLevel(cmsg, 0), out);
542    require(SecCmsContentInfoGetContentTypeTag(cinfo) == SEC_OID_PKCS7_SIGNED_DATA, out);
543    require(sigd = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(cinfo), out);
544
545    /* find out about signers */
546    int nsigners = SecCmsSignedDataSignerInfoCount(sigd);
547    require(nsigners == 0, out);
548
549    SecAsn1Item **cert_datas = SecCmsSignedDataGetCertificateList(sigd);
550    certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
551    SecAsn1Item *cert_data;
552    if (cert_datas) while ((cert_data = *cert_datas) != NULL) {
553        SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length);
554        if (cert) {
555            CFArrayAppendValue(certs, cert);
556            CFRelease(cert);
557        }
558        cert_datas++;
559    }
560
561out:
562    if (cmsg)
563        SecCmsMessageDestroy(cmsg);
564
565    return certs;
566}
567
568
569extern const SecAsn1Template SecCmsMessageTemplate[];
570
571CFDataRef SecCMSCreateCertificatesOnlyMessage(CFTypeRef cert_or_array_thereof) {
572    OSStatus status = errSecParam;
573    SecCmsMessageRef cmsg = NULL;
574    SecCmsContentInfoRef cinfo;
575    SecCmsSignedDataRef sigd = NULL;
576    CFMutableDataRef cert_only_signed_data = NULL;
577    CFArrayRef cert_array = NULL;
578    CFIndex cert_array_count = 0;
579    SecCertificateRef cert = NULL;
580
581    require(cert_or_array_thereof, out);
582
583    require(cmsg = SecCmsMessageCreate(), out);
584    require(sigd = SecCmsSignedDataCreate(cmsg), out);
585    require_noerr(SecCmsContentInfoSetContentData(&(sigd->contentInfo), NULL, PR_TRUE), out);
586    require(cinfo = SecCmsMessageGetContentInfo(cmsg), out);
587    require_noerr(SecCmsContentInfoSetContentSignedData(cinfo, sigd), out);
588    long version = SEC_CMS_SIGNED_DATA_VERSION_BASIC;
589    require(SEC_ASN1EncodeInteger(cmsg->poolp, &(sigd->version), version), out);
590
591    if (CFGetTypeID(cert_or_array_thereof) == SecCertificateGetTypeID()) {
592        cert_array = CFArrayCreate(kCFAllocatorDefault, &cert_or_array_thereof, 1, &kCFTypeArrayCallBacks);
593    } else if (CFGetTypeID(cert_or_array_thereof) == CFArrayGetTypeID()) {
594        cert_array = CFArrayCreateCopy(kCFAllocatorDefault, (CFArrayRef)cert_or_array_thereof);
595    }
596
597    require(cert_array, out);
598    cert_array_count = CFArrayGetCount(cert_array);
599    require(cert_array_count > 0, out);
600
601    sigd->rawCerts = (SecAsn1Item * *)PORT_ArenaAlloc(cmsg->poolp, (cert_array_count + 1) * sizeof(SecAsn1Item *));
602    require(sigd->rawCerts, out);
603    CFIndex ix;
604    for (ix = 0; ix < cert_array_count; ix++) {
605        cert = (SecCertificateRef)CFArrayGetValueAtIndex(cert_array, ix);
606        require(cert, out);
607
608        sigd->rawCerts[ix] = PORT_ArenaZAlloc(cmsg->poolp, sizeof(SecAsn1Item));
609        SecAsn1Item cert_data = { SecCertificateGetLength(cert),
610                                (uint8_t *)SecCertificateGetBytePtr(cert) };
611        *(sigd->rawCerts[ix]) = cert_data;
612    }
613    sigd->rawCerts[ix] = NULL;
614
615	/* this is a SET OF, so we need to sort them guys - we have the DER already, though */
616    if (cert_array_count > 1)
617        SecCmsArraySort((void **)sigd->rawCerts, SecCmsUtilDERCompare, NULL, NULL);
618
619    cert_only_signed_data = CFDataCreateMutable(kCFAllocatorDefault, 0);
620        SecAsn1Item cert_only_signed_data_item = {};
621        require_quiet(SEC_ASN1EncodeItem(cmsg->poolp, &cert_only_signed_data_item,
622            cmsg, SecCmsMessageTemplate), out);
623    CFDataAppendBytes(cert_only_signed_data, cert_only_signed_data_item.Data,
624        cert_only_signed_data_item.Length);
625
626    status = errSecSuccess;
627out:
628    CFReleaseSafe(cert_array);
629    if (status) CFReleaseSafe(cert_only_signed_data);
630    if (cmsg) SecCmsMessageDestroy(cmsg);
631    return cert_only_signed_data;
632}
633
634CFDataRef SecCMSCreateCertificatesOnlyMessageIAP(SecCertificateRef cert)
635{
636    static const uint8_t header[] = {
637        0x30, 0x82, 0x03, 0x6d, 0x06, 0x09, 0x2a, 0x86,
638        0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0,
639        0x82, 0x03, 0x5e, 0x30, 0x82, 0x03, 0x5a, 0x02,
640        0x01, 0x01, 0x31, 0x00, 0x30, 0x0b, 0x06, 0x09,
641        0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
642        0x01, 0xa0, 0x82, 0x03, 0x40
643    };
644
645    static const uint8_t trailer[] = {
646        0xa1, 0x00, 0x31, 0x00
647    };
648
649    CFMutableDataRef message = NULL;
650    CFDataRef certdata;
651    const uint8_t *certbytes;
652    CFIndex certlen;
653    uint8_t *messagebytes;
654    uint16_t messagelen;
655
656    certdata = SecCertificateCopyData(cert);
657    require(certdata, out);
658
659    certbytes = CFDataGetBytePtr(certdata);
660    certlen = CFDataGetLength(certdata);
661    require(certlen > UINT8_MAX, out);
662    require(certlen < UINT16_MAX, out);
663
664    message = CFDataCreateMutable(kCFAllocatorDefault, 0);
665    require(message, out);
666
667    CFDataAppendBytes(message, header, sizeof(header));
668    CFDataAppendBytes(message, certbytes, certlen);
669    CFDataAppendBytes(message, trailer, sizeof(trailer));
670
671    messagebytes = CFDataGetMutableBytePtr(message);
672    messagelen = CFDataGetLength(message);
673
674    messagelen -= 4;
675    messagebytes[2] = messagelen >> 8;
676    messagebytes[3] = messagelen & 0xFF;
677
678    messagelen -= 15;
679    messagebytes[17] = messagelen >> 8;
680    messagebytes[18] = messagelen & 0xFF;
681
682    messagelen -= 4;
683    messagebytes[21] = messagelen >> 8;
684    messagebytes[22] = messagelen & 0xFF;
685
686    messagelen -= 26;
687    messagebytes[43] = messagelen >> 8;
688    messagebytes[44] = messagelen & 0xFF;
689
690out:
691    CFReleaseSafe(certdata);
692    return message;
693}
694
695#endif /* ENABLE_CMS */
696
697