2 * Copyright (c) 2012 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 * tsaSupport.c - ASN1 templates Time Stamping Authority requests and responses
27 #include <Security/SecCmsDigestContext.h>
28 #include <Security/SecCmsMessage.h>
29 #include <security_asn1/secasn1.h>
30 #include <security_asn1/secerr.h>
33 #include <security_utilities/debugging.h>
34 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
36 #include <Security/SecCmsDecoder.h>
37 #include <Security/SecCmsMessage.h>
38 #include <Security/SecCmsContentInfo.h>
39 #include <Security/SecCmsSignedData.h>
40 #include <Security/SecCmsSignerInfo.h>
41 #include "tsaTemplates.h"
42 #include <Security/SecAsn1Coder.h>
43 #include <AssertMacros.h>
44 #include <Security/SecPolicy.h>
45 #include <Security/SecTrustPriv.h>
46 #include <Security/SecImportExport.h>
47 #include <Security/SecCertificatePriv.h>
48 #include <security_keychain/SecCertificateP.h>
49 #include <security_keychain/SecCertificatePrivP.h>
51 #include "tsaSupport.h"
52 #include "tsaSupportPriv.h"
53 #include "tsaTemplates.h"
60 const CFStringRef kTSAContextKeyURL
= CFSTR("ServerURL");
61 const CFStringRef kTSAContextKeyNoCerts
= CFSTR("NoCerts");
62 const CFStringRef kTSADebugContextKeyBadReq
= CFSTR("DebugBadReq");
63 const CFStringRef kTSADebugContextKeyBadNonce
= CFSTR("DebugBadNonce");
65 extern const SecAsn1Template kSecAsn1TSATSTInfoTemplate
[];
67 extern OSStatus
impExpImportCertCommon(
68 const CSSM_DATA
*cdata
,
69 SecKeychainRef importKeychain
, // optional
70 CFMutableArrayRef outArray
); // optional, append here
72 #pragma mark ----- Debug Logs -----
75 #define TSA_USE_SYSLOG 1
82 #define tsaDebug(fmt, ...) \
85 struct timeval time_now; \
86 gettimeofday(&time_now, NULL); \
87 struct tm* time_info = localtime(&time_now.tv_sec); \
88 strftime(buf, sizeof(buf), "[%Y-%m-%d %H:%M:%S]", time_info); \
89 fprintf(stderr, "%s " fmt, buf, ## __VA_ARGS__); \
90 syslog(LOG_ERR, " " fmt, ## __VA_ARGS__); \
92 #define tsa_secdebug(scope, format...) \
94 syslog(LOG_NOTICE, format); \
95 secdebug(scope, format); \
99 #define tsaDebug(args...) tsa_secdebug("tsa", ## args)
100 #define tsa_secdebug(scope, format...) \
101 secdebug(scope, format)
105 #define TSTINFO_DEBUG 1 //jch
109 #define dtprintf(args...) tsaDebug(args)
111 #define dtprintf(args...)
114 #define kHTTPResponseCodeContinue 100
115 #define kHTTPResponseCodeOK 200
116 #define kHTTPResponseCodeNoContent 204
117 #define kHTTPResponseCodeBadRequest 400
118 #define kHTTPResponseCodeUnauthorized 401
119 #define kHTTPResponseCodeForbidden 403
120 #define kHTTPResponseCodeNotFound 404
121 #define kHTTPResponseCodeConflict 409
122 #define kHTTPResponseCodeExpectationFailed 417
123 #define kHTTPResponseCodeServFail 500
124 #define kHTTPResponseCodeServiceUnavailable 503
125 #define kHTTPResponseCodeInsufficientStorage 507
127 #pragma mark ----- Debug/Utilities -----
129 static OSStatus
remapHTTPErrorCodes(OSStatus status
)
133 case kHTTPResponseCodeOK
:
134 case kHTTPResponseCodeContinue
:
136 case kHTTPResponseCodeBadRequest
:
137 return errSecTimestampBadRequest
;
138 case kHTTPResponseCodeNoContent
:
139 case kHTTPResponseCodeUnauthorized
:
140 case kHTTPResponseCodeForbidden
:
141 case kHTTPResponseCodeNotFound
:
142 case kHTTPResponseCodeConflict
:
143 case kHTTPResponseCodeExpectationFailed
:
144 case kHTTPResponseCodeServFail
:
145 case kHTTPResponseCodeInsufficientStorage
:
146 case kHTTPResponseCodeServiceUnavailable
:
147 return errSecTimestampServiceNotAvailable
;
155 static void printDataAsHex(const char *title
, const CSSM_DATA
*d
, unsigned maxToPrint
) // 0 means print it all
160 uint32 len
= (uint32
)d
->Length
;
165 const int wrapwid
= 24; // large enough so SHA-1 hashes fit on one line...
167 if ((maxToPrint
!= 0) && (len
> maxToPrint
))
173 bufferSize
= wrapwid
+3*len
;
174 buffer
= (char *)malloc(bufferSize
);
176 offset
= sprintf(buffer
, "%s [len = %u]\n", title
, len
);
177 dtprintf("%s", buffer
);
180 for (i
=0; (i
< len
) && (offset
+3 < bufferSize
); i
++, offset
+= sz
)
182 sz
= sprintf(buffer
+ offset
, " %02x", (unsigned int)cp
[i
] & 0xff);
183 if ((i
% wrapwid
) == (wrapwid
-1))
185 dtprintf("%s", buffer
);
191 sz
=sprintf(buffer
+ offset
, more
?" ...\n":"\n");
195 // fprintf(stderr, "%s", buffer);
196 dtprintf("%s", buffer
);
201 int tsaWriteFileX(const char *fileName
, const unsigned char *bytes
, size_t numBytes
)
206 fd
= open(fileName
, O_RDWR
| O_CREAT
| O_TRUNC
, 0600);
210 rtn
= (int)write(fd
, bytes
, numBytes
);
211 if(rtn
!= (int)numBytes
)
214 fprintf(stderr
, "writeFile: short write\n");
225 char *cfStringToChar(CFStringRef inStr
)
229 const char *str
= NULL
;
232 return strdup(""); // return a null string
235 if ((str
= CFStringGetCStringPtr(inStr
, kCFStringEncodingUTF8
))) {
236 result
= strdup(str
);
238 // need to extract into buffer
239 CFIndex length
= CFStringGetLength(inStr
); // in 16-bit character units
240 CFIndex bytesToAllocate
= CFStringGetMaximumSizeForEncoding(length
, kCFStringEncodingUTF8
) + 1;
241 result
= malloc(bytesToAllocate
);
242 if (!CFStringGetCString(inStr
, result
, bytesToAllocate
, kCFStringEncodingUTF8
))
249 /* Oids longer than this are considered invalid. */
250 #define MAX_OID_SIZE 32
252 static CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
, const CSSM_OID
*oid
)
254 if (oid
->Length
== 0)
255 return CFSTR("<NULL>");
257 if (oid
->Length
> MAX_OID_SIZE
)
258 return CFSTR("Oid too long");
260 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
262 // The first two levels are encoded into one byte, since the root levelq
263 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
264 // y may be > 39, so we have to add special-case handling for this.
265 uint32_t x
= oid
->Data
[0] / 40;
266 uint32_t y
= oid
->Data
[0] % 40;
269 // Handle special case for large y if x = 2
273 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
276 for (x
= 1; x
< oid
->Length
; ++x
)
278 value
= (value
<< 7) | (oid
->Data
[x
] & 0x7F);
279 /* @@@ value may not span more than 4 bytes. */
280 /* A max number of 20 values is allowed. */
281 if (!(oid
->Data
[x
] & 0x80))
283 CFStringAppendFormat(result
, NULL
, CFSTR(".%lu"), value
);
290 static void debugSaveCertificates(CSSM_DATA
**outCerts
)
295 CSSM_DATA_PTR
*certp
;
297 const char *certNameBase
= "/tmp/tsa-resp-cert-";
298 char fname
[PATH_MAX
];
299 unsigned certCount
= SecCmsArrayCount((void **)outCerts
);
300 dtprintf("Found %d certs\n",certCount
);
302 for (certp
=outCerts
;*certp
;certp
++, ++jx
)
305 strncpy(fname
, certNameBase
, strlen(certNameBase
)+1);
306 sprintf(numstr
,"%u", jx
);
307 strcat(fname
,numstr
);
308 tsaWriteFileX(fname
, (*certp
)->Data
, (*certp
)->Length
);
310 break; //something wrong
316 static void debugShowSignerInfo(SecCmsSignedDataRef signedData
)
319 int numberOfSigners
= SecCmsSignedDataSignerInfoCount (signedData
);
320 dtprintf("numberOfSigners : %d\n", numberOfSigners
);
322 for (ix
=0;ix
< numberOfSigners
;ix
++)
324 SecCmsSignerInfoRef sigi
= SecCmsSignedDataGetSignerInfo(signedData
,ix
);
327 CFStringRef commonName
= SecCmsSignerInfoGetSignerCommonName(sigi
);
328 const char *signerhdr
= " signer : ";
331 char *cn
= cfStringToChar(commonName
);
332 dtprintf("%s%s\n", signerhdr
, cn
);
337 dtprintf("%s<NULL>\n", signerhdr
);
343 static void debugShowContentTypeOID(SecCmsContentInfoRef contentInfo
)
347 CSSM_OID
*typeOID
= SecCmsContentInfoGetContentTypeOID(contentInfo
);
350 CFStringRef oidCFStr
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, typeOID
);
351 char *oidstr
= cfStringToChar(oidCFStr
);
352 printDataAsHex("oid:", typeOID
, (unsigned int)typeOID
->Length
);
353 dtprintf("\toid: %s\n", oidstr
);
362 uint64_t tsaDER_ToInt(const CSSM_DATA
*DER_Data
)
367 while(i
< DER_Data
->Length
) {
368 rtn
|= DER_Data
->Data
[i
];
369 if(++i
== DER_Data
->Length
) {
377 void displayTSTInfo(SecAsn1TSATSTInfo
*tstInfo
)
380 dtprintf("--- TSTInfo ---\n");
384 if (tstInfo
->version
.Data
)
386 uint64_t vers
= tsaDER_ToInt(&tstInfo
->version
);
387 dtprintf("Version:\t\t%u\n", (int)vers
);
390 if (tstInfo
->serialNumber
.Data
)
392 uint64_t sn
= tsaDER_ToInt(&tstInfo
->serialNumber
);
393 dtprintf("SerialNumber:\t%llu\n", sn
);
396 if (tstInfo
->ordering
.Data
)
398 uint64_t ord
= tsaDER_ToInt(&tstInfo
->ordering
);
399 dtprintf("Ordering:\t\t%s\n", ord
?"yes":"no");
402 if (tstInfo
->nonce
.Data
)
404 uint64_t nonce
= tsaDER_ToInt(&tstInfo
->nonce
);
405 dtprintf("Nonce:\t\t%llu\n", nonce
);
408 dtprintf("Nonce:\t\tnot specified\n");
410 if (tstInfo
->genTime
.Data
)
412 char buf
[tstInfo
->genTime
.Length
+1];
413 memcpy(buf
, (const char *)tstInfo
->genTime
.Data
, tstInfo
->genTime
.Length
);
414 buf
[tstInfo
->genTime
.Length
]=0;
415 dtprintf("GenTime:\t\t%s\n", buf
);
418 dtprintf("-- MessageImprint --\n");
419 if (true) // SecAsn1TSAMessageImprint
421 printDataAsHex(" Algorithm:",&tstInfo
->messageImprint
.hashAlgorithm
.algorithm
, 0);
422 printDataAsHex(" Message :", &tstInfo
->messageImprint
.hashedMessage
, 0);//tstInfo->messageImprint.hashedMessage.Length);
427 #pragma mark ----- TimeStamp Response using XPC -----
429 #include <xpc/private.h>
431 static OSStatus
checkForNonDERResponse(const unsigned char *resp
, size_t respLen
)
434 Good start is something like 30 82 0c 03 30 15 02 01 00 30 10 0c 0e 4f 70 65
436 URL: http://timestamp-int.corp.apple.com/signserver/process?TimeStampSigner
437 Resp: Http/1.1 Service Unavailable
439 URL: http://timestamp-int.corp.apple.com/ts01
442 URL: http://cutandtaste.com/404 (or other forced 404 site)
446 OSStatus status
= noErr
;
447 const char ader
[2] = { 0x30, 0x82 };
448 char *respStr
= NULL
;
450 size_t badResponseCount
;
452 const char *badResponses
[] =
455 "Http/1.1 Service Unavailable",
459 require_action(resp
&& respLen
, xit
, status
= errSecTimestampServiceNotAvailable
);
461 // This is usual case
462 if ((respLen
> 1) && (memcmp(resp
, ader
, 2)==0)) // might be good; pass on to DER decoder
465 badResponseCount
= sizeof(badResponses
)/sizeof(char *);
467 for (ix
= 0; ix
< badResponseCount
; ++ix
)
468 if (strlen(badResponses
[ix
]) > maxlen
)
469 maxlen
= strlen(badResponses
[ix
]);
471 // Prevent a large response from allocating a ton of memory
472 if (respLen
> maxlen
)
475 respStr
= (char *)malloc(respLen
+1);
476 strlcpy(respStr
, (const char *)resp
, respLen
);
478 for (ix
= 0; ix
< badResponseCount
; ++ix
)
479 if (strcmp(respStr
, badResponses
[ix
])==0)
480 return errSecTimestampServiceNotAvailable
;
484 free((void *)respStr
);
489 static OSStatus
sendTSARequestWithXPC(const unsigned char *tsaReq
, size_t tsaReqLength
, const unsigned char *tsaURL
, unsigned char **tsaResp
, size_t *tsaRespLength
)
491 __block OSStatus result
= noErr
;
492 int timeoutInSeconds
= 15;
493 extern xpc_object_t
xpc_create_with_format(const char * format
, ...);
495 dispatch_queue_t xpc_queue
= dispatch_queue_create("com.apple.security.XPCTimeStampingService", DISPATCH_QUEUE_SERIAL
);
496 __block dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
497 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, timeoutInSeconds
* NSEC_PER_SEC
);
499 xpc_connection_t con
= xpc_connection_create("com.apple.security.XPCTimeStampingService", xpc_queue
);
501 xpc_connection_set_event_handler(con
, ^(xpc_object_t event
) {
502 xpc_type_t xtype
= xpc_get_type(event
);
503 if (XPC_TYPE_ERROR
== xtype
)
504 { tsaDebug("default: connection error: %s\n", xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
)); }
506 { tsaDebug("default: unexpected connection event %p\n", event
); }
509 xpc_connection_resume(con
);
511 xpc_object_t tsaReqData
= xpc_data_create(tsaReq
, tsaReqLength
);
512 const char *urlstr
= (tsaURL
?(const char *)tsaURL
:"");
513 xpc_object_t url_as_xpc_string
= xpc_string_create(urlstr
);
515 xpc_object_t message
= xpc_create_with_format("{operation: TimeStampRequest, ServerURL: %value, TimeStampRequest: %value}", url_as_xpc_string
, tsaReqData
);
517 xpc_connection_send_message_with_reply(con
, message
, xpc_queue
, ^(xpc_object_t reply
)
519 tsaDebug("xpc_connection_send_message_with_reply handler called back\n");
520 dispatch_retain(waitSemaphore
);
522 xpc_type_t xtype
= xpc_get_type(reply
);
523 if (XPC_TYPE_ERROR
== xtype
)
524 { tsaDebug("message error: %s\n", xpc_dictionary_get_string(reply
, XPC_ERROR_KEY_DESCRIPTION
)); }
525 else if (XPC_TYPE_CONNECTION
== xtype
)
526 { tsaDebug("received connection\n"); }
527 else if (XPC_TYPE_DICTIONARY
== xtype
)
531 // This is useful for debugging.
532 char *debug = xpc_copy_description(reply);
533 tsaDebug("DEBUG %s\n", debug);
538 xpc_object_t xpcTimeStampReply
= xpc_dictionary_get_value(reply
, "TimeStampReply");
539 size_t xpcTSRLength
= xpc_data_get_length(xpcTimeStampReply
);
540 tsaDebug("xpcTSRLength: %ld bytes of response\n", xpcTSRLength
);
542 xpc_object_t xpcTimeStampError
= xpc_dictionary_get_value(reply
, "TimeStampError");
543 xpc_object_t xpcTimeStampStatus
= xpc_dictionary_get_value(reply
, "TimeStampStatus");
545 if (xpcTimeStampError
|| xpcTimeStampStatus
)
548 if (xpcTimeStampError
)
550 size_t len
= xpc_string_get_length(xpcTimeStampError
);
551 char *buf
= (char *)malloc(len
);
552 strlcpy(buf
, xpc_string_get_string_ptr(xpcTimeStampError
), len
+1);
553 tsaDebug("xpcTimeStampError: %s\n", buf
);
558 if (xpcTimeStampStatus
)
560 result
= (OSStatus
)xpc_int64_get_value(xpcTimeStampStatus
);
561 tsaDebug("xpcTimeStampStatus: %d\n", (int)result
);
565 result
= remapHTTPErrorCodes(result
);
567 if ((result
== noErr
) && tsaResp
&& tsaRespLength
)
569 *tsaRespLength
= xpcTSRLength
;
570 *tsaResp
= (unsigned char *)malloc(xpcTSRLength
);
572 size_t bytesCopied
= xpc_data_get_bytes(xpcTimeStampReply
, *tsaResp
, 0, xpcTSRLength
);
573 if (bytesCopied
!= xpcTSRLength
)
574 { tsaDebug("length mismatch: copied: %ld, xpc: %ld\n", bytesCopied
, xpcTSRLength
); }
576 if ((result
= checkForNonDERResponse(*tsaResp
,bytesCopied
)))
578 tsaDebug("received non-DER response from timestamp server\n");
583 tsaDebug("copied: %ld bytes of response\n", bytesCopied
);
586 tsaDebug("releasing connection\n");
590 { tsaDebug("unexpected message reply type %p\n", xtype
); }
592 dispatch_semaphore_signal(waitSemaphore
);
593 dispatch_release(waitSemaphore
);
596 { tsaDebug("waiting up to %d seconds for response from XPC\n", timeoutInSeconds
); }
597 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
599 dispatch_release(waitSemaphore
);
600 xpc_release(tsaReqData
);
601 xpc_release(message
);
603 { tsaDebug("sendTSARequestWithXPC exit\n"); }
608 #pragma mark ----- TimeStamp request -----
610 #include "tsaTemplates.h"
611 #include <security_asn1/SecAsn1Coder.h>
612 #include <Security/oidsalg.h>
613 #include <AssertMacros.h>
614 #include <libkern/OSByteOrder.h>
616 extern const SecAsn1Template kSecAsn1TSATimeStampReqTemplate
;
617 extern const SecAsn1Template kSecAsn1TSATimeStampRespTemplateDER
;
619 CFMutableDictionaryRef
SecCmsTSAGetDefaultContext(CFErrorRef
*error
)
621 // Caller responsible for retain/release
622 // <rdar://problem/11077440> Update SecCmsTSAGetDefaultContext with actual URL for Apple Timestamp server
623 // URL will be in TimeStampingPrefs.plist
625 CFBundleRef secFWbundle
= NULL
;
626 CFURLRef resourceURL
= NULL
;
627 CFDataRef resourceData
= NULL
;
628 CFPropertyListRef prefs
= NULL
;
629 CFMutableDictionaryRef contextDict
= NULL
;
630 SInt32 errorCode
= 0;
631 CFOptionFlags options
= 0;
632 CFPropertyListFormat format
= 0;
633 OSStatus status
= noErr
;
635 require_action(secFWbundle
= CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")), xit
, status
= errSecInternalError
);
636 require_action(resourceURL
= CFBundleCopyResourceURL(secFWbundle
, CFSTR("TimeStampingPrefs"), CFSTR("plist"), NULL
),
637 xit
, status
= errSecInvalidPrefsDomain
);
639 require(CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
, resourceURL
, &resourceData
,
640 NULL
, NULL
, &errorCode
), xit
);
641 require_action(resourceData
, xit
, status
= errSecDataNotAvailable
);
643 prefs
= CFPropertyListCreateWithData(kCFAllocatorDefault
, resourceData
, options
, &format
, error
);
644 require_action(prefs
&& (CFGetTypeID(prefs
)==CFDictionaryGetTypeID()), xit
, status
= errSecInvalidPrefsDomain
);
646 contextDict
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, prefs
);
654 *error
= CFErrorCreate(kCFAllocatorDefault
, kCFErrorDomainOSStatus
, status
, NULL
);
656 CFRelease(secFWbundle
);
658 CFRelease(resourceURL
);
660 CFRelease(resourceData
);
667 static CFDataRef
_SecTSARequestCopyDEREncoding(SecAsn1TSAMessageImprint
*messageImprint
, bool noCerts
, uint64_t nonce
)
669 // Returns DER encoded TimeStampReq
670 // Modeled on _SecOCSPRequestCopyDEREncoding
671 // The Timestamp Authority supports 64 bit nonces (or more possibly)
673 SecAsn1CoderRef coder
= NULL
;
675 SecAsn1Item vers
= {1, &version
};
676 uint8_t creq
= noCerts
?0:1;
677 SecAsn1Item certReq
= {1, &creq
}; //jch - to request or not?
678 SecAsn1TSATimeStampReq tsreq
= {};
679 CFDataRef der
= NULL
;
680 uint64_t nonceVal
= OSSwapHostToBigConstInt64(nonce
);
681 SecAsn1Item nonceItem
= {sizeof(uint64_t), (unsigned char *)&nonceVal
};
683 uint8_t OID_FakePolicy_Data
[] = { 0x2A, 0x03, 0x04, 0x05, 0x06};
684 const CSSM_OID fakePolicyOID
= {sizeof(OID_FakePolicy_Data
),OID_FakePolicy_Data
};
686 tsreq
.version
= vers
;
688 tsreq
.messageImprint
= *messageImprint
;
689 tsreq
.certReq
= certReq
;
691 // skip reqPolicy, extensions for now - FAKES - jch
692 tsreq
.reqPolicy
= fakePolicyOID
; //policyID;
694 tsreq
.nonce
= nonceItem
;
696 // Encode the request
697 require_noerr(SecAsn1CoderCreate(&coder
), errOut
);
700 require_noerr(SecAsn1EncodeItem(coder
, &tsreq
,
701 &kSecAsn1TSATimeStampReqTemplate
, &encoded
), errOut
);
702 der
= CFDataCreate(kCFAllocatorDefault
, encoded
.Data
,
707 SecAsn1CoderRelease(coder
);
712 OSStatus
SecTSAResponseCopyDEREncoding(SecAsn1CoderRef coder
, const CSSM_DATA
*tsaResponse
, SecAsn1TimeStampRespDER
*respDER
)
714 // Partially decode the response
715 OSStatus status
= paramErr
;
717 require(tsaResponse
&& respDER
, errOut
);
718 require_noerr(SecAsn1DecodeData(coder
, tsaResponse
,
719 &kSecAsn1TSATimeStampRespTemplateDER
, respDER
), errOut
);
727 #pragma mark ----- TS Callback -----
729 OSStatus
SecCmsTSADefaultCallback(CFTypeRef context
, void *messageImprintV
, uint64_t nonce
, CSSM_DATA
*signedDERBlob
)
731 OSStatus result
= paramErr
;
732 const unsigned char *tsaReq
= NULL
;
733 size_t tsaReqLength
= 0;
734 CFDataRef cfreq
= NULL
;
735 unsigned char *tsaURL
= NULL
;
736 bool noCerts
= false;
738 if (!context
|| CFGetTypeID(context
)!=CFDictionaryGetTypeID())
741 SecAsn1TSAMessageImprint
*messageImprint
= (SecAsn1TSAMessageImprint
*)messageImprintV
;
742 if (!messageImprint
|| !signedDERBlob
)
745 CFBooleanRef cfnocerts
= (CFBooleanRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSAContextKeyNoCerts
);
748 tsaDebug("[TSA] Request noCerts\n");
749 noCerts
= CFBooleanGetValue(cfnocerts
);
752 // We must spoof the nonce here, before sending the request.
753 // If we tried to alter the reply, then the signature would break instead.
754 CFBooleanRef cfBadNonce
= (CFBooleanRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSADebugContextKeyBadNonce
);
755 if (cfBadNonce
&& CFBooleanGetValue(cfBadNonce
))
757 tsaDebug("[TSA] Forcing bad TS Request by changing nonce\n");
761 printDataAsHex("[TSA] hashToTimeStamp:", &messageImprint
->hashedMessage
,128);
762 cfreq
= _SecTSARequestCopyDEREncoding(messageImprint
, noCerts
, nonce
);
765 tsaReq
= CFDataGetBytePtr(cfreq
);
766 tsaReqLength
= CFDataGetLength(cfreq
);
770 tsaWriteFileX("/tmp/tsareq.req", tsaReq
, tsaReqLength
);
774 CFStringRef url
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSAContextKeyURL
);
777 tsaDebug("[TSA] missing URL for TSA (key: %s)\n", "kTSAContextKeyURL");
782 If debugging, look at special values in the context to mess things up
785 CFBooleanRef cfBadReq
= (CFBooleanRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSADebugContextKeyBadReq
);
786 if (cfBadReq
&& CFBooleanGetValue(cfBadReq
))
788 tsaDebug("[TSA] Forcing bad TS Request by truncating length from %ld to %ld\n", tsaReqLength
, (tsaReqLength
-4));
792 // need to extract into buffer
793 CFIndex length
= CFStringGetLength(url
); // in 16-bit character units
794 tsaURL
= malloc(6 * length
+ 1); // pessimistic
795 if (!CFStringGetCString(url
, (char *)tsaURL
, 6 * length
+ 1, kCFStringEncodingUTF8
))
798 tsaDebug("[TSA] URL for timestamp server: %s\n", tsaURL
);
800 unsigned char *tsaResp
= NULL
;
801 size_t tsaRespLength
= 0;
802 tsaDebug("calling sendTSARequestWithXPC with %ld bytes of request\n", tsaReqLength
);
804 require_noerr(result
= sendTSARequestWithXPC(tsaReq
, tsaReqLength
, tsaURL
, &tsaResp
, &tsaRespLength
), xit
);
806 tsaDebug("sendTSARequestWithXPC copied: %ld bytes of response\n", tsaRespLength
);
808 signedDERBlob
->Data
= tsaResp
;
809 signedDERBlob
->Length
= tsaRespLength
;
815 free((void *)tsaURL
);
822 #pragma mark ----- TimeStamp Verification -----
824 static OSStatus
convertGeneralizedTimeToCFAbsoluteTime(const char *timeStr
, CFAbsoluteTime
*ptime
)
827 See http://userguide.icu-project.org/formatparse/datetime for date/time format.
828 The "Z" signal a GMT time, but CFDateFormatterGetAbsoluteTimeFromString returns
829 values based on local time.
832 OSStatus result
= noErr
;
833 CFDateFormatterRef formatter
= NULL
;
834 CFStringRef time_string
= NULL
;
835 CFTimeZoneRef gmt
= NULL
;
836 CFLocaleRef locale
= NULL
;
837 CFRange
*rangep
= NULL
;
839 require(timeStr
&& timeStr
[0] && ptime
, xit
);
840 require(formatter
= CFDateFormatterCreate(kCFAllocatorDefault
, locale
, kCFDateFormatterNoStyle
, kCFDateFormatterNoStyle
), xit
);
841 // CFRetain(formatter);
842 CFDateFormatterSetFormat(formatter
, CFSTR("yyyyMMddHHmmss'Z'")); // GeneralizedTime
843 gmt
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
844 CFDateFormatterSetProperty(formatter
, kCFDateFormatterTimeZone
, gmt
);
846 time_string
= CFStringCreateWithCString(kCFAllocatorDefault
, timeStr
, kCFStringEncodingUTF8
);
847 if (!time_string
|| !CFDateFormatterGetAbsoluteTimeFromString(formatter
, time_string
, rangep
, ptime
))
849 dtprintf("%s is not a valid date\n", timeStr
);
855 CFRelease(formatter
);
857 CFRelease(time_string
);
864 static OSStatus
SecTSAValidateTimestamp(const SecAsn1TSATSTInfo
*tstInfo
, CSSM_DATA
**signingCerts
, CFAbsoluteTime
*timestampTime
)
866 // See <rdar://problem/11077708> Properly handle revocation information of timestamping certificate
867 OSStatus result
= paramErr
;
868 CFAbsoluteTime genTime
= 0;
869 char timeStr
[32] = {0,};
870 SecCertificateRef signingCertificate
= NULL
;
872 require(tstInfo
&& signingCerts
&& (tstInfo
->genTime
.Length
< 16), xit
);
874 // Find the leaf signingCert
875 require_noerr(result
= SecCertificateCreateFromData(*signingCerts
,
876 CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_DER
, &signingCertificate
), xit
);
878 memcpy(timeStr
, tstInfo
->genTime
.Data
, tstInfo
->genTime
.Length
);
879 timeStr
[tstInfo
->genTime
.Length
] = 0;
880 require_noerr(convertGeneralizedTimeToCFAbsoluteTime(timeStr
, &genTime
), xit
);
881 if (SecCertificateIsValidX(signingCertificate
, genTime
)) // iOS?
884 result
= errSecTimestampInvalid
;
886 *timestampTime
= genTime
;
891 static OSStatus
verifyTSTInfo(const CSSM_DATA_PTR content
, CSSM_DATA
**signingCerts
, SecAsn1TSATSTInfo
*tstInfo
, CFAbsoluteTime
*timestampTime
, uint64_t expectedNonce
)
893 OSStatus status
= paramErr
;
894 SecAsn1CoderRef coder
= NULL
;
899 require_noerr(SecAsn1CoderCreate(&coder
), xit
);
900 require_noerr(SecAsn1Decode(coder
, content
->Data
, content
->Length
,
901 kSecAsn1TSATSTInfoTemplate
, tstInfo
), xit
);
902 displayTSTInfo(tstInfo
);
905 if (tstInfo
->nonce
.Data
&& expectedNonce
!=0)
907 uint64_t nonce
= tsaDER_ToInt(&tstInfo
->nonce
);
908 // if (expectedNonce!=nonce)
909 dtprintf("verifyTSTInfo nonce: actual: %lld, expected: %lld\n", nonce
, expectedNonce
);
910 require_action(expectedNonce
==nonce
, xit
, status
= errSecTimestampRejection
);
913 status
= SecTSAValidateTimestamp(tstInfo
, signingCerts
, timestampTime
);
914 dtprintf("SecTSAValidateTimestamp result: %ld\n", (long)status
);
918 SecAsn1CoderRelease(coder
);
922 static void debugShowExtendedTrustResult(int index
, CFDictionaryRef extendedResult
)
927 CFStringRef xresStr
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
928 CFSTR("Extended trust result for signer #%d : %@"), index
, extendedResult
);
939 extern const char *cssmErrorString(CSSM_RETURN error
);
941 static void statusBitTest(CSSM_TP_APPLE_CERT_STATUS certStatus
, uint32 bit
, const char *str
)
943 if (certStatus
& bit
)
944 dtprintf("%s ", str
);
948 static void debugShowCertEvidenceInfo(uint16_t certCount
, const CSSM_TP_APPLE_EVIDENCE_INFO
*info
)
951 CSSM_TP_APPLE_CERT_STATUS cs
;
952 // const CSSM_TP_APPLE_EVIDENCE_INFO *pinfo = info;
954 for (ix
=0; info
&& (ix
<certCount
); ix
++, ++info
)
956 cs
= info
->StatusBits
;
957 dtprintf(" cert %u:\n", ix
);
958 dtprintf(" StatusBits : 0x%x", (unsigned)cs
);
962 statusBitTest(cs
, CSSM_CERT_STATUS_EXPIRED
, "EXPIRED");
963 statusBitTest(cs
, CSSM_CERT_STATUS_NOT_VALID_YET
,
965 statusBitTest(cs
, CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
,
966 "IS_IN_INPUT_CERTS");
967 statusBitTest(cs
, CSSM_CERT_STATUS_IS_IN_ANCHORS
,
969 statusBitTest(cs
, CSSM_CERT_STATUS_IS_ROOT
, "IS_ROOT");
970 statusBitTest(cs
, CSSM_CERT_STATUS_IS_FROM_NET
, "IS_FROM_NET");
976 dtprintf(" NumStatusCodes : %u ", info
->NumStatusCodes
);
977 CSSM_RETURN
*pstatuscode
= info
->StatusCodes
;
979 for (jx
=0; pstatuscode
&& (jx
<info
->NumStatusCodes
); jx
++, ++pstatuscode
)
980 dtprintf("%s ", cssmErrorString(*pstatuscode
));
983 dtprintf(" Index: %u\n", info
->Index
);
989 static const char *trustResultTypeString(SecTrustResultType trustResultType
)
992 switch (trustResultType
)
994 case kSecTrustResultProceed
: return "TrustResultProceed";
995 case kSecTrustResultUnspecified
: return "TrustResultUnspecified";
996 case kSecTrustResultDeny
: return "TrustResultDeny"; // user reject
997 case kSecTrustResultInvalid
: return "TrustResultInvalid";
998 case kSecTrustResultConfirm
: return "TrustResultConfirm";
999 case kSecTrustResultRecoverableTrustFailure
: return "TrustResultRecoverableTrustFailure";
1000 case kSecTrustResultFatalTrustFailure
: return "TrustResultUnspecified";
1001 case kSecTrustResultOtherError
: return "TrustResultOtherError";
1002 default: return "TrustResultUnknown";
1008 static OSStatus
verifySigners(SecCmsSignedDataRef signedData
, int numberOfSigners
)
1010 // See <rdar://problem/11077588> Bubble up SecTrustEvaluate of timestamp response to high level callers
1011 // Also <rdar://problem/11077708> Properly handle revocation information of timestamping certificate
1013 SecPolicyRef policy
= NULL
;
1016 require(policy
= SecPolicyCreateWithOID(kSecPolicyAppleTimeStamping
), xit
);
1018 for (jx
= 0; jx
< numberOfSigners
; ++jx
)
1020 SecTrustResultType trustResultType
;
1021 SecTrustRef trustRef
= NULL
;
1022 CFDictionaryRef extendedResult
= NULL
;
1023 CFArrayRef certChain
= NULL
;
1024 uint16_t certCount
= 0;
1026 CSSM_TP_APPLE_EVIDENCE_INFO
*statusChain
= NULL
;
1028 // SecCmsSignedDataVerifySignerInfo returns trustRef, which we can call SecTrustEvaluate on
1029 // usually (always?) if result is noErr, the SecTrust*Result calls will return errSecTrustNotAvailable
1030 result
= SecCmsSignedDataVerifySignerInfo (signedData
, jx
, NULL
, policy
, &trustRef
);
1031 dtprintf("[%s] SecCmsSignedDataVerifySignerInfo: result: %d, signer: %d\n",
1032 __FUNCTION__
, result
, jx
);
1033 require_noerr(result
, xit
);
1035 result
= SecTrustEvaluate (trustRef
, &trustResultType
);
1036 dtprintf("[%s] SecTrustEvaluate: result: %d, trustResult: %s (%d)\n",
1037 __FUNCTION__
, result
, trustResultTypeString(trustResultType
), trustResultType
);
1040 switch (trustResultType
)
1042 case kSecTrustResultProceed
:
1043 case kSecTrustResultUnspecified
:
1045 case kSecTrustResultDeny
: // user reject
1046 result
= errSecTimestampNotTrusted
; // SecCmsVSTimestampNotTrusted ?
1048 case kSecTrustResultInvalid
:
1049 assert(false); // should never happen
1050 result
= errSecTimestampNotTrusted
; // SecCmsVSTimestampNotTrusted ?
1052 case kSecTrustResultConfirm
:
1053 case kSecTrustResultRecoverableTrustFailure
:
1054 case kSecTrustResultFatalTrustFailure
:
1055 case kSecTrustResultOtherError
:
1059 There are two "errors" that need to be resolved externally:
1060 CSSMERR_TP_CERT_EXPIRED can be OK if the timestamp was made
1061 before the TSA chain expired; CSSMERR_TP_CERT_NOT_VALID_YET
1062 can happen in the case where the user's clock was set to 0.
1063 We don't want to prevent them using apps automatically, so
1064 return noErr and let codesign or whover decide.
1066 OSStatus resultCode
;
1067 require_action(SecTrustGetCssmResultCode(trustRef
, &resultCode
)==noErr
, xit
, result
= errSecTimestampNotTrusted
);
1068 result
= (resultCode
== CSSMERR_TP_CERT_EXPIRED
|| resultCode
== CSSMERR_TP_CERT_NOT_VALID_YET
)?noErr
:errSecTimestampNotTrusted
;
1073 rx
= SecTrustGetResult(trustRef
, &trustResultType
, &certChain
, &statusChain
);
1074 dtprintf("[%s] SecTrustGetResult: result: %d, type: %d\n", __FUNCTION__
,rx
, trustResultType
);
1075 certCount
= certChain
?CFArrayGetCount(certChain
):0;
1076 debugShowCertEvidenceInfo(certCount
, statusChain
);
1078 rx
= SecTrustCopyExtendedResult(trustRef
, &extendedResult
);
1079 dtprintf("[%s] SecTrustCopyExtendedResult: result: %d\n", __FUNCTION__
, rx
);
1082 debugShowExtendedTrustResult(jx
, extendedResult
);
1083 CFRelease(extendedResult
);
1087 CFRelease (trustRef
);
1096 static OSStatus
impExpImportCertUnCommon(
1097 const CSSM_DATA
*cdata
,
1098 SecKeychainRef importKeychain
, // optional
1099 CFMutableArrayRef outArray
) // optional, append here
1101 // The only difference between this and impExpImportCertCommon is that we append to outArray
1102 // before attempting to add to the keychain
1103 OSStatus status
= noErr
;
1104 SecCertificateRef certRef
= NULL
;
1106 require_action(cdata
, xit
, status
= errSecUnsupportedFormat
);
1108 /* Pass kCFAllocatorNull as bytesDeallocator to assure the bytes aren't freed */
1109 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8
*)cdata
->Data
, (CFIndex
)cdata
->Length
, kCFAllocatorNull
);
1110 require_action(data
, xit
, status
= errSecUnsupportedFormat
);
1112 certRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
);
1113 CFRelease(data
); /* certRef has its own copy of the data now */
1115 dtprintf("impExpHandleCert error\n");
1116 return errSecUnsupportedFormat
;
1120 CFArrayAppendValue(outArray
, certRef
);
1124 status
= SecCertificateAddToKeychain(certRef
, importKeychain
);
1125 if (status
!=noErr
&& status
!=errSecDuplicateItem
)
1126 { dtprintf("SecCertificateAddToKeychain error: %ld\n", (long)status
); }
1135 static void saveTSACertificates(CSSM_DATA
**signingCerts
, CFMutableArrayRef outArray
)
1137 SecKeychainRef defaultKeychain
= NULL
;
1138 // Don't save certificates in keychain to avoid securityd issues
1139 // if (SecKeychainCopyDefault(&defaultKeychain))
1140 // defaultKeychain = NULL;
1142 unsigned certCount
= SecCmsArrayCount((void **)signingCerts
);
1144 for (dex
=0; dex
<certCount
; dex
++)
1146 OSStatus rx
= impExpImportCertUnCommon(signingCerts
[dex
], defaultKeychain
, outArray
);
1147 if (rx
!=noErr
&& rx
!=errSecDuplicateItem
)
1148 dtprintf("impExpImportCertCommon failed: %ld\n", (long)rx
);
1150 if (defaultKeychain
)
1151 CFRelease(defaultKeychain
);
1154 static const char *cfabsoluteTimeToString(CFAbsoluteTime abstime
)
1156 CFGregorianDate greg
= CFAbsoluteTimeGetGregorianDate(abstime
, NULL
);
1158 if (19 != snprintf(str
, 20, "%4.4d-%2.2d-%2.2d_%2.2d:%2.2d:%2.2d",
1159 (int)greg
.year
, greg
.month
, greg
.day
, greg
.hour
, greg
.minute
, (int)greg
.second
))
1161 char *data
= (char *)malloc(20);
1162 strncpy(data
, str
, 20);
1166 static OSStatus
setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo
)
1168 OSStatus status
= noErr
;
1170 if (!signerinfo
->timestampCertList
|| (CFArrayGetCount(signerinfo
->timestampCertList
) == 0))
1171 return SecCmsVSSigningCertNotFound
;
1173 SecCertificateRef tsaLeaf
= (SecCertificateRef
)CFArrayGetValueAtIndex(signerinfo
->timestampCertList
, 0);
1175 CFDataRef certData
= SecCertificateCopyData(tsaLeaf
);
1176 SecCertificateRefP certP
= certData
?SecCertificateCreateWithDataP(kCFAllocatorDefault
, certData
):NULL
;
1178 require_action(certP
, xit
, status
= errSecCertificateCannotOperate
);
1180 signerinfo
->tsaLeafNotBefore
= SecCertificateNotValidBefore(certP
); /* Start date for Timestamp Authority leaf */
1181 signerinfo
->tsaLeafNotAfter
= SecCertificateNotValidAfter(certP
); /* Expiration date for Timestamp Authority leaf */
1183 const char *nbefore
= cfabsoluteTimeToString(signerinfo
->tsaLeafNotBefore
);
1184 const char *nafter
= cfabsoluteTimeToString(signerinfo
->tsaLeafNotAfter
);
1185 if (nbefore
&& nafter
)
1187 dtprintf("Timestamp Authority leaf valid from %s to %s\n", nbefore
, nafter
);
1188 free((void *)nbefore
);free((void *)nafter
);
1193 status = errSecCertificateNotValidYet;
1195 status = errSecCertificateExpired;
1199 if (certData
) CFRelease(certData
);
1200 if (certP
) CFRelease(certP
);
1206 From RFC 3161: Time-Stamp Protocol (TSP),August 2001, APPENDIX B:
1208 B) The validity of the digital signature may then be verified in the
1211 1) The time-stamp token itself MUST be verified and it MUST be
1212 verified that it applies to the signature of the signer.
1214 2) The date/time indicated by the TSA in the TimeStampToken
1217 3) The certificate used by the signer MUST be identified and
1220 4) The date/time indicated by the TSA MUST be within the
1221 validity period of the signer's certificate.
1223 5) The revocation information about that certificate, at the
1224 date/time of the Time-Stamping operation, MUST be retrieved.
1226 6) Should the certificate be revoked, then the date/time of
1227 revocation shall be later than the date/time indicated by
1230 If all these conditions are successful, then the digital signature
1231 shall be declared as valid.
1235 OSStatus
decodeTimeStampToken(SecCmsSignerInfoRef signerinfo
, CSSM_DATA_PTR inData
, CSSM_DATA_PTR encDigest
, uint64_t expectedNonce
)
1238 We update signerinfo with timestamp and tsa certificate chain.
1239 encDigest is the original signed blob, which we must hash and compare.
1240 inData comes from the unAuthAttr section of the CMS message
1242 These are set in signerinfo as side effects:
1247 SecCmsDecoderRef decoderContext
= NULL
;
1248 SecCmsMessageRef cmsMessage
= NULL
;
1249 SecCmsContentInfoRef contentInfo
;
1250 SecCmsSignedDataRef signedData
;
1251 SECOidTag contentTypeTag
;
1252 int contentLevelCount
;
1254 OSStatus result
= errSecUnknownFormat
;
1255 CSSM_DATA
**signingCerts
= NULL
;
1257 OSStatus currentPORTErr
= PORT_GetError();
1258 dtprintf("decodeTimeStampToken top: PORT_GetError() %d -----\n", (int)currentPORTErr
);
1261 /* decode the message */
1262 require_noerr(result
= SecCmsDecoderCreate (NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &decoderContext
), xit
);
1263 result
= SecCmsDecoderUpdate(decoderContext
, inData
->Data
, inData
->Length
);
1266 result
= errSecTimestampInvalid
;
1267 SecCmsDecoderDestroy(decoderContext
);
1271 require_noerr(result
= SecCmsDecoderFinish(decoderContext
, &cmsMessage
), xit
);
1273 // process the results
1274 contentLevelCount
= SecCmsMessageContentLevelCount(cmsMessage
);
1277 printDataAsHex("encDigest",encDigest
, 0);
1279 for (ix
= 0; ix
< contentLevelCount
; ++ix
)
1281 dtprintf("\n----- Content Level %d -----\n", ix
);
1282 // get content information
1283 contentInfo
= SecCmsMessageContentLevel (cmsMessage
, ix
);
1284 contentTypeTag
= SecCmsContentInfoGetContentTypeTag (contentInfo
);
1286 // After 2nd round, contentInfo.content.data is the TSTInfo
1288 debugShowContentTypeOID(contentInfo
);
1290 switch (contentTypeTag
)
1292 case SEC_OID_PKCS7_SIGNED_DATA
:
1294 require((signedData
= (SecCmsSignedDataRef
)SecCmsContentInfoGetContent(contentInfo
)) != NULL
, xit
);
1296 debugShowSignerInfo(signedData
);
1298 SECAlgorithmID
**digestAlgorithms
= SecCmsSignedDataGetDigestAlgs(signedData
);
1299 unsigned digestAlgCount
= SecCmsArrayCount((void **)digestAlgorithms
);
1300 dtprintf("digestAlgCount: %d\n", digestAlgCount
);
1301 if (signedData
->digests
)
1305 for (jx
=0;jx
< digestAlgCount
;jx
++)
1307 sprintf(buffer
, " digest[%u]", jx
);
1308 printDataAsHex(buffer
,signedData
->digests
[jx
], 0);
1313 dtprintf("No digests\n");
1314 CSSM_DATA_PTR innerContent
= SecCmsContentInfoGetInnerContent(contentInfo
);
1317 dtprintf("inner content length: %ld\n", innerContent
->Length
);
1318 SecAsn1TSAMessageImprint fakeMessageImprint
= {{{0}},};
1319 OSStatus status
= createTSAMessageImprint(signedData
, innerContent
, &fakeMessageImprint
);
1321 { dtprintf("createTSAMessageImprint status: %d\n", (int)status
); }
1322 printDataAsHex("inner content hash",&fakeMessageImprint
.hashedMessage
, 0);
1323 CSSM_DATA_PTR digestdata
= &fakeMessageImprint
.hashedMessage
;
1324 CSSM_DATA_PTR digests
[2] = {digestdata
, NULL
};
1325 SecCmsSignedDataSetDigests(signedData
, digestAlgorithms
, (CSSM_DATA_PTR
*)&digests
);
1328 dtprintf("no inner content\n");
1332 Import the certificates. We leave this as a warning, since
1333 there are configurations where the certificates are not returned.
1335 signingCerts
= SecCmsSignedDataGetCertificateList(signedData
);
1336 if (signingCerts
== NULL
)
1337 { dtprintf("SecCmsSignedDataGetCertificateList returned NULL\n"); }
1340 if (!signerinfo
->timestampCertList
)
1341 signerinfo
->timestampCertList
= CFArrayCreateMutable(kCFAllocatorDefault
, 10, &kCFTypeArrayCallBacks
);
1342 saveTSACertificates(signingCerts
, signerinfo
->timestampCertList
);
1343 require_noerr(result
= setTSALeafValidityDates(signerinfo
), xit
);
1344 debugSaveCertificates(signingCerts
);
1347 int numberOfSigners
= SecCmsSignedDataSignerInfoCount (signedData
);
1349 result
= verifySigners(signedData
, numberOfSigners
);
1351 dtprintf("verifySigners failed: %ld\n", (long)result
); // warning
1354 if (result
) // remap to SecCmsVSTimestampNotTrusted ?
1359 case SEC_OID_PKCS9_SIGNING_CERTIFICATE
:
1361 dtprintf("SEC_OID_PKCS9_SIGNING_CERTIFICATE seen\n");
1365 case SEC_OID_PKCS9_ID_CT_TSTInfo
:
1367 SecAsn1TSATSTInfo tstInfo
= {{0},};
1368 result
= verifyTSTInfo(contentInfo
->rawContent
, signingCerts
, &tstInfo
, &signerinfo
->timestampTime
, expectedNonce
);
1369 if (signerinfo
->timestampTime
)
1371 const char *tstamp
= cfabsoluteTimeToString(signerinfo
->timestampTime
);
1374 dtprintf("Timestamp Authority timestamp: %s\n", tstamp
);
1375 free((void *)tstamp
);
1382 char *otherContent
= (char *)SecCmsContentInfoGetContent (contentInfo
);
1383 dtprintf("otherContent : %p\n", otherContent
);
1387 dtprintf("ContentTypeTag : %x\n", contentTypeTag
);
1393 SecCmsMessageDestroy(cmsMessage
);