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
253 /* FIXME: There are other versions of this in SecCertifcate.c and SecCertificateP.c */
254 static CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
, const CSSM_OID
*oid
)
256 if (oid
->Length
== 0)
257 return CFSTR("<NULL>");
259 if (oid
->Length
> MAX_OID_SIZE
)
260 return CFSTR("Oid too long");
262 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
264 // The first two levels are encoded into one byte, since the root levelq
265 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
266 // y may be > 39, so we have to add special-case handling for this.
267 uint32_t x
= oid
->Data
[0] / 40;
268 uint32_t y
= oid
->Data
[0] % 40;
271 // Handle special case for large y if x = 2
275 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
278 for (x
= 1; x
< oid
->Length
; ++x
)
280 value
= (value
<< 7) | (oid
->Data
[x
] & 0x7F);
281 /* @@@ value may not span more than 4 bytes. */
282 /* A max number of 20 values is allowed. */
283 if (!(oid
->Data
[x
] & 0x80))
285 CFStringAppendFormat(result
, NULL
, CFSTR(".%lu"), (unsigned long)value
);
293 static void debugSaveCertificates(CSSM_DATA
**outCerts
)
298 CSSM_DATA_PTR
*certp
;
300 const char *certNameBase
= "/tmp/tsa-resp-cert-";
301 char fname
[PATH_MAX
];
302 unsigned certCount
= SecCmsArrayCount((void **)outCerts
);
303 dtprintf("Found %d certs\n",certCount
);
305 for (certp
=outCerts
;*certp
;certp
++, ++jx
)
308 strncpy(fname
, certNameBase
, strlen(certNameBase
)+1);
309 sprintf(numstr
,"%u", jx
);
310 strcat(fname
,numstr
);
311 tsaWriteFileX(fname
, (*certp
)->Data
, (*certp
)->Length
);
313 break; //something wrong
319 static void debugShowSignerInfo(SecCmsSignedDataRef signedData
)
322 int numberOfSigners
= SecCmsSignedDataSignerInfoCount (signedData
);
323 dtprintf("numberOfSigners : %d\n", numberOfSigners
);
325 for (ix
=0;ix
< numberOfSigners
;ix
++)
327 SecCmsSignerInfoRef sigi
= SecCmsSignedDataGetSignerInfo(signedData
,ix
);
330 CFStringRef commonName
= SecCmsSignerInfoGetSignerCommonName(sigi
);
331 const char *signerhdr
= " signer : ";
334 char *cn
= cfStringToChar(commonName
);
335 dtprintf("%s%s\n", signerhdr
, cn
);
340 dtprintf("%s<NULL>\n", signerhdr
);
346 static void debugShowContentTypeOID(SecCmsContentInfoRef contentInfo
)
350 CSSM_OID
*typeOID
= SecCmsContentInfoGetContentTypeOID(contentInfo
);
353 CFStringRef oidCFStr
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, typeOID
);
354 char *oidstr
= cfStringToChar(oidCFStr
);
355 printDataAsHex("oid:", typeOID
, (unsigned int)typeOID
->Length
);
356 dtprintf("\toid: %s\n", oidstr
);
365 uint64_t tsaDER_ToInt(const CSSM_DATA
*DER_Data
)
370 while(i
< DER_Data
->Length
) {
371 rtn
|= DER_Data
->Data
[i
];
372 if(++i
== DER_Data
->Length
) {
380 void displayTSTInfo(SecAsn1TSATSTInfo
*tstInfo
)
383 dtprintf("--- TSTInfo ---\n");
387 if (tstInfo
->version
.Data
)
389 uint64_t vers
= tsaDER_ToInt(&tstInfo
->version
);
390 dtprintf("Version:\t\t%u\n", (int)vers
);
393 if (tstInfo
->serialNumber
.Data
)
395 uint64_t sn
= tsaDER_ToInt(&tstInfo
->serialNumber
);
396 dtprintf("SerialNumber:\t%llu\n", sn
);
399 if (tstInfo
->ordering
.Data
)
401 uint64_t ord
= tsaDER_ToInt(&tstInfo
->ordering
);
402 dtprintf("Ordering:\t\t%s\n", ord
?"yes":"no");
405 if (tstInfo
->nonce
.Data
)
407 uint64_t nonce
= tsaDER_ToInt(&tstInfo
->nonce
);
408 dtprintf("Nonce:\t\t%llu\n", nonce
);
411 dtprintf("Nonce:\t\tnot specified\n");
413 if (tstInfo
->genTime
.Data
)
415 char buf
[tstInfo
->genTime
.Length
+1];
416 memcpy(buf
, (const char *)tstInfo
->genTime
.Data
, tstInfo
->genTime
.Length
);
417 buf
[tstInfo
->genTime
.Length
]=0;
418 dtprintf("GenTime:\t\t%s\n", buf
);
421 dtprintf("-- MessageImprint --\n");
422 if (true) // SecAsn1TSAMessageImprint
424 printDataAsHex(" Algorithm:",&tstInfo
->messageImprint
.hashAlgorithm
.algorithm
, 0);
425 printDataAsHex(" Message :", &tstInfo
->messageImprint
.hashedMessage
, 0);//tstInfo->messageImprint.hashedMessage.Length);
430 #pragma mark ----- TimeStamp Response using XPC -----
432 #include <xpc/private.h>
434 static OSStatus
checkForNonDERResponse(const unsigned char *resp
, size_t respLen
)
437 Good start is something like 30 82 0c 03 30 15 02 01 00 30 10 0c 0e 4f 70 65
439 URL: http://timestamp-int.corp.apple.com/signserver/process?TimeStampSigner
440 Resp: Http/1.1 Service Unavailable
442 URL: http://timestamp-int.corp.apple.com/ts01
445 URL: http://cutandtaste.com/404 (or other forced 404 site)
449 OSStatus status
= noErr
;
450 const char ader
[2] = { 0x30, 0x82 };
451 char *respStr
= NULL
;
453 size_t badResponseCount
;
455 const char *badResponses
[] =
458 "Http/1.1 Service Unavailable",
462 require_action(resp
&& respLen
, xit
, status
= errSecTimestampServiceNotAvailable
);
464 // This is usual case
465 if ((respLen
> 1) && (memcmp(resp
, ader
, 2)==0)) // might be good; pass on to DER decoder
468 badResponseCount
= sizeof(badResponses
)/sizeof(char *);
470 for (ix
= 0; ix
< badResponseCount
; ++ix
)
471 if (strlen(badResponses
[ix
]) > maxlen
)
472 maxlen
= strlen(badResponses
[ix
]);
474 // Prevent a large response from allocating a ton of memory
475 if (respLen
> maxlen
)
478 respStr
= (char *)malloc(respLen
+1);
479 strlcpy(respStr
, (const char *)resp
, respLen
);
481 for (ix
= 0; ix
< badResponseCount
; ++ix
)
482 if (strcmp(respStr
, badResponses
[ix
])==0)
483 return errSecTimestampServiceNotAvailable
;
487 free((void *)respStr
);
492 static OSStatus
sendTSARequestWithXPC(const unsigned char *tsaReq
, size_t tsaReqLength
, const unsigned char *tsaURL
, unsigned char **tsaResp
, size_t *tsaRespLength
)
494 __block OSStatus result
= noErr
;
495 int timeoutInSeconds
= 15;
496 extern xpc_object_t
xpc_create_with_format(const char * format
, ...);
498 dispatch_queue_t xpc_queue
= dispatch_queue_create("com.apple.security.XPCTimeStampingService", DISPATCH_QUEUE_SERIAL
);
499 __block dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
500 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, timeoutInSeconds
* NSEC_PER_SEC
);
502 xpc_connection_t con
= xpc_connection_create("com.apple.security.XPCTimeStampingService", xpc_queue
);
504 xpc_connection_set_event_handler(con
, ^(xpc_object_t event
) {
505 xpc_type_t xtype
= xpc_get_type(event
);
506 if (XPC_TYPE_ERROR
== xtype
)
507 { tsaDebug("default: connection error: %s\n", xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
)); }
509 { tsaDebug("default: unexpected connection event %p\n", event
); }
512 xpc_connection_resume(con
);
514 xpc_object_t tsaReqData
= xpc_data_create(tsaReq
, tsaReqLength
);
515 const char *urlstr
= (tsaURL
?(const char *)tsaURL
:"");
516 xpc_object_t url_as_xpc_string
= xpc_string_create(urlstr
);
518 xpc_object_t message
= xpc_create_with_format("{operation: TimeStampRequest, ServerURL: %value, TimeStampRequest: %value}", url_as_xpc_string
, tsaReqData
);
520 xpc_connection_send_message_with_reply(con
, message
, xpc_queue
, ^(xpc_object_t reply
)
522 tsaDebug("xpc_connection_send_message_with_reply handler called back\n");
523 dispatch_retain(waitSemaphore
);
525 xpc_type_t xtype
= xpc_get_type(reply
);
526 if (XPC_TYPE_ERROR
== xtype
)
527 { tsaDebug("message error: %s\n", xpc_dictionary_get_string(reply
, XPC_ERROR_KEY_DESCRIPTION
)); }
528 else if (XPC_TYPE_CONNECTION
== xtype
)
529 { tsaDebug("received connection\n"); }
530 else if (XPC_TYPE_DICTIONARY
== xtype
)
534 // This is useful for debugging.
535 char *debug = xpc_copy_description(reply);
536 tsaDebug("DEBUG %s\n", debug);
541 xpc_object_t xpcTimeStampReply
= xpc_dictionary_get_value(reply
, "TimeStampReply");
542 size_t xpcTSRLength
= xpc_data_get_length(xpcTimeStampReply
);
543 tsaDebug("xpcTSRLength: %ld bytes of response\n", xpcTSRLength
);
545 xpc_object_t xpcTimeStampError
= xpc_dictionary_get_value(reply
, "TimeStampError");
546 xpc_object_t xpcTimeStampStatus
= xpc_dictionary_get_value(reply
, "TimeStampStatus");
548 if (xpcTimeStampError
|| xpcTimeStampStatus
)
551 if (xpcTimeStampError
)
553 size_t len
= xpc_string_get_length(xpcTimeStampError
);
554 char *buf
= (char *)malloc(len
);
555 strlcpy(buf
, xpc_string_get_string_ptr(xpcTimeStampError
), len
+1);
556 tsaDebug("xpcTimeStampError: %s\n", buf
);
561 if (xpcTimeStampStatus
)
563 result
= (OSStatus
)xpc_int64_get_value(xpcTimeStampStatus
);
564 tsaDebug("xpcTimeStampStatus: %d\n", (int)result
);
568 result
= remapHTTPErrorCodes(result
);
570 if ((result
== noErr
) && tsaResp
&& tsaRespLength
)
572 *tsaRespLength
= xpcTSRLength
;
573 *tsaResp
= (unsigned char *)malloc(xpcTSRLength
);
575 size_t bytesCopied
= xpc_data_get_bytes(xpcTimeStampReply
, *tsaResp
, 0, xpcTSRLength
);
576 if (bytesCopied
!= xpcTSRLength
)
577 { tsaDebug("length mismatch: copied: %ld, xpc: %ld\n", bytesCopied
, xpcTSRLength
); }
579 if ((result
= checkForNonDERResponse(*tsaResp
,bytesCopied
)))
581 tsaDebug("received non-DER response from timestamp server\n");
586 tsaDebug("copied: %ld bytes of response\n", bytesCopied
);
589 tsaDebug("releasing connection\n");
593 { tsaDebug("unexpected message reply type %p\n", xtype
); }
595 dispatch_semaphore_signal(waitSemaphore
);
596 dispatch_release(waitSemaphore
);
599 { tsaDebug("waiting up to %d seconds for response from XPC\n", timeoutInSeconds
); }
600 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
602 dispatch_release(waitSemaphore
);
603 xpc_release(tsaReqData
);
604 xpc_release(message
);
606 { tsaDebug("sendTSARequestWithXPC exit\n"); }
611 #pragma mark ----- TimeStamp request -----
613 #include "tsaTemplates.h"
614 #include <security_asn1/SecAsn1Coder.h>
615 #include <Security/oidsalg.h>
616 #include <AssertMacros.h>
617 #include <libkern/OSByteOrder.h>
619 extern const SecAsn1Template kSecAsn1TSATimeStampReqTemplate
;
620 extern const SecAsn1Template kSecAsn1TSATimeStampRespTemplateDER
;
622 CFMutableDictionaryRef
SecCmsTSAGetDefaultContext(CFErrorRef
*error
)
624 // Caller responsible for retain/release
625 // <rdar://problem/11077440> Update SecCmsTSAGetDefaultContext with actual URL for Apple Timestamp server
626 // URL will be in TimeStampingPrefs.plist
628 CFBundleRef secFWbundle
= NULL
;
629 CFURLRef resourceURL
= NULL
;
630 CFDataRef resourceData
= NULL
;
631 CFPropertyListRef prefs
= NULL
;
632 CFMutableDictionaryRef contextDict
= NULL
;
633 SInt32 errorCode
= 0;
634 CFOptionFlags options
= 0;
635 CFPropertyListFormat format
= 0;
636 OSStatus status
= noErr
;
638 require_action(secFWbundle
= CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")), xit
, status
= errSecInternalError
);
639 require_action(resourceURL
= CFBundleCopyResourceURL(secFWbundle
, CFSTR("TimeStampingPrefs"), CFSTR("plist"), NULL
),
640 xit
, status
= errSecInvalidPrefsDomain
);
642 require(CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
, resourceURL
, &resourceData
,
643 NULL
, NULL
, &errorCode
), xit
);
644 require_action(resourceData
, xit
, status
= errSecDataNotAvailable
);
646 prefs
= CFPropertyListCreateWithData(kCFAllocatorDefault
, resourceData
, options
, &format
, error
);
647 require_action(prefs
&& (CFGetTypeID(prefs
)==CFDictionaryGetTypeID()), xit
, status
= errSecInvalidPrefsDomain
);
649 contextDict
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, prefs
);
657 *error
= CFErrorCreate(kCFAllocatorDefault
, kCFErrorDomainOSStatus
, status
, NULL
);
659 CFRelease(secFWbundle
);
661 CFRelease(resourceURL
);
663 CFRelease(resourceData
);
670 static CFDataRef
_SecTSARequestCopyDEREncoding(SecAsn1TSAMessageImprint
*messageImprint
, bool noCerts
, uint64_t nonce
)
672 // Returns DER encoded TimeStampReq
673 // Modeled on _SecOCSPRequestCopyDEREncoding
674 // The Timestamp Authority supports 64 bit nonces (or more possibly)
676 SecAsn1CoderRef coder
= NULL
;
678 SecAsn1Item vers
= {1, &version
};
679 uint8_t creq
= noCerts
?0:1;
680 SecAsn1Item certReq
= {1, &creq
}; //jch - to request or not?
681 SecAsn1TSATimeStampReq tsreq
= {};
682 CFDataRef der
= NULL
;
683 uint64_t nonceVal
= OSSwapHostToBigConstInt64(nonce
);
684 SecAsn1Item nonceItem
= {sizeof(uint64_t), (unsigned char *)&nonceVal
};
686 uint8_t OID_FakePolicy_Data
[] = { 0x2A, 0x03, 0x04, 0x05, 0x06};
687 const CSSM_OID fakePolicyOID
= {sizeof(OID_FakePolicy_Data
),OID_FakePolicy_Data
};
689 tsreq
.version
= vers
;
691 tsreq
.messageImprint
= *messageImprint
;
692 tsreq
.certReq
= certReq
;
694 // skip reqPolicy, extensions for now - FAKES - jch
695 tsreq
.reqPolicy
= fakePolicyOID
; //policyID;
697 tsreq
.nonce
= nonceItem
;
699 // Encode the request
700 require_noerr(SecAsn1CoderCreate(&coder
), errOut
);
703 require_noerr(SecAsn1EncodeItem(coder
, &tsreq
,
704 &kSecAsn1TSATimeStampReqTemplate
, &encoded
), errOut
);
705 der
= CFDataCreate(kCFAllocatorDefault
, encoded
.Data
,
710 SecAsn1CoderRelease(coder
);
715 OSStatus
SecTSAResponseCopyDEREncoding(SecAsn1CoderRef coder
, const CSSM_DATA
*tsaResponse
, SecAsn1TimeStampRespDER
*respDER
)
717 // Partially decode the response
718 OSStatus status
= paramErr
;
720 require(tsaResponse
&& respDER
, errOut
);
721 require_noerr(SecAsn1DecodeData(coder
, tsaResponse
,
722 &kSecAsn1TSATimeStampRespTemplateDER
, respDER
), errOut
);
730 #pragma mark ----- TS Callback -----
732 OSStatus
SecCmsTSADefaultCallback(CFTypeRef context
, void *messageImprintV
, uint64_t nonce
, CSSM_DATA
*signedDERBlob
)
734 OSStatus result
= paramErr
;
735 const unsigned char *tsaReq
= NULL
;
736 size_t tsaReqLength
= 0;
737 CFDataRef cfreq
= NULL
;
738 unsigned char *tsaURL
= NULL
;
739 bool noCerts
= false;
741 if (!context
|| CFGetTypeID(context
)!=CFDictionaryGetTypeID())
744 SecAsn1TSAMessageImprint
*messageImprint
= (SecAsn1TSAMessageImprint
*)messageImprintV
;
745 if (!messageImprint
|| !signedDERBlob
)
748 CFBooleanRef cfnocerts
= (CFBooleanRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSAContextKeyNoCerts
);
751 tsaDebug("[TSA] Request noCerts\n");
752 noCerts
= CFBooleanGetValue(cfnocerts
);
755 // We must spoof the nonce here, before sending the request.
756 // If we tried to alter the reply, then the signature would break instead.
757 CFBooleanRef cfBadNonce
= (CFBooleanRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSADebugContextKeyBadNonce
);
758 if (cfBadNonce
&& CFBooleanGetValue(cfBadNonce
))
760 tsaDebug("[TSA] Forcing bad TS Request by changing nonce\n");
764 printDataAsHex("[TSA] hashToTimeStamp:", &messageImprint
->hashedMessage
,128);
765 cfreq
= _SecTSARequestCopyDEREncoding(messageImprint
, noCerts
, nonce
);
768 tsaReq
= CFDataGetBytePtr(cfreq
);
769 tsaReqLength
= CFDataGetLength(cfreq
);
773 tsaWriteFileX("/tmp/tsareq.req", tsaReq
, tsaReqLength
);
777 CFStringRef url
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSAContextKeyURL
);
780 tsaDebug("[TSA] missing URL for TSA (key: %s)\n", "kTSAContextKeyURL");
785 If debugging, look at special values in the context to mess things up
788 CFBooleanRef cfBadReq
= (CFBooleanRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSADebugContextKeyBadReq
);
789 if (cfBadReq
&& CFBooleanGetValue(cfBadReq
))
791 tsaDebug("[TSA] Forcing bad TS Request by truncating length from %ld to %ld\n", tsaReqLength
, (tsaReqLength
-4));
795 // need to extract into buffer
796 CFIndex length
= CFStringGetLength(url
); // in 16-bit character units
797 tsaURL
= malloc(6 * length
+ 1); // pessimistic
798 if (!CFStringGetCString(url
, (char *)tsaURL
, 6 * length
+ 1, kCFStringEncodingUTF8
))
801 tsaDebug("[TSA] URL for timestamp server: %s\n", tsaURL
);
803 unsigned char *tsaResp
= NULL
;
804 size_t tsaRespLength
= 0;
805 tsaDebug("calling sendTSARequestWithXPC with %ld bytes of request\n", tsaReqLength
);
807 require_noerr(result
= sendTSARequestWithXPC(tsaReq
, tsaReqLength
, tsaURL
, &tsaResp
, &tsaRespLength
), xit
);
809 tsaDebug("sendTSARequestWithXPC copied: %ld bytes of response\n", tsaRespLength
);
811 signedDERBlob
->Data
= tsaResp
;
812 signedDERBlob
->Length
= tsaRespLength
;
818 free((void *)tsaURL
);
825 #pragma mark ----- TimeStamp Verification -----
827 static OSStatus
convertGeneralizedTimeToCFAbsoluteTime(const char *timeStr
, CFAbsoluteTime
*ptime
)
830 See http://userguide.icu-project.org/formatparse/datetime for date/time format.
831 The "Z" signal a GMT time, but CFDateFormatterGetAbsoluteTimeFromString returns
832 values based on local time.
835 OSStatus result
= noErr
;
836 CFDateFormatterRef formatter
= NULL
;
837 CFStringRef time_string
= NULL
;
838 CFTimeZoneRef gmt
= NULL
;
839 CFLocaleRef locale
= NULL
;
840 CFRange
*rangep
= NULL
;
842 require(timeStr
&& timeStr
[0] && ptime
, xit
);
843 require(formatter
= CFDateFormatterCreate(kCFAllocatorDefault
, locale
, kCFDateFormatterNoStyle
, kCFDateFormatterNoStyle
), xit
);
844 // CFRetain(formatter);
845 CFDateFormatterSetFormat(formatter
, CFSTR("yyyyMMddHHmmss'Z'")); // GeneralizedTime
846 gmt
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
847 CFDateFormatterSetProperty(formatter
, kCFDateFormatterTimeZone
, gmt
);
849 time_string
= CFStringCreateWithCString(kCFAllocatorDefault
, timeStr
, kCFStringEncodingUTF8
);
850 if (!time_string
|| !CFDateFormatterGetAbsoluteTimeFromString(formatter
, time_string
, rangep
, ptime
))
852 dtprintf("%s is not a valid date\n", timeStr
);
858 CFRelease(formatter
);
860 CFRelease(time_string
);
867 static OSStatus
SecTSAValidateTimestamp(const SecAsn1TSATSTInfo
*tstInfo
, CSSM_DATA
**signingCerts
, CFAbsoluteTime
*timestampTime
)
869 // See <rdar://problem/11077708> Properly handle revocation information of timestamping certificate
870 OSStatus result
= paramErr
;
871 CFAbsoluteTime genTime
= 0;
872 char timeStr
[32] = {0,};
873 SecCertificateRef signingCertificate
= NULL
;
875 require(tstInfo
&& signingCerts
&& (tstInfo
->genTime
.Length
< 16), xit
);
877 // Find the leaf signingCert
878 require_noerr(result
= SecCertificateCreateFromData(*signingCerts
,
879 CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_DER
, &signingCertificate
), xit
);
881 memcpy(timeStr
, tstInfo
->genTime
.Data
, tstInfo
->genTime
.Length
);
882 timeStr
[tstInfo
->genTime
.Length
] = 0;
883 require_noerr(convertGeneralizedTimeToCFAbsoluteTime(timeStr
, &genTime
), xit
);
884 if (SecCertificateIsValidX(signingCertificate
, genTime
)) // iOS?
887 result
= errSecTimestampInvalid
;
889 *timestampTime
= genTime
;
894 static OSStatus
verifyTSTInfo(const CSSM_DATA_PTR content
, CSSM_DATA
**signingCerts
, SecAsn1TSATSTInfo
*tstInfo
, CFAbsoluteTime
*timestampTime
, uint64_t expectedNonce
)
896 OSStatus status
= paramErr
;
897 SecAsn1CoderRef coder
= NULL
;
902 require_noerr(SecAsn1CoderCreate(&coder
), xit
);
903 require_noerr(SecAsn1Decode(coder
, content
->Data
, content
->Length
,
904 kSecAsn1TSATSTInfoTemplate
, tstInfo
), xit
);
905 displayTSTInfo(tstInfo
);
908 if (tstInfo
->nonce
.Data
&& expectedNonce
!=0)
910 uint64_t nonce
= tsaDER_ToInt(&tstInfo
->nonce
);
911 // if (expectedNonce!=nonce)
912 dtprintf("verifyTSTInfo nonce: actual: %lld, expected: %lld\n", nonce
, expectedNonce
);
913 require_action(expectedNonce
==nonce
, xit
, status
= errSecTimestampRejection
);
916 status
= SecTSAValidateTimestamp(tstInfo
, signingCerts
, timestampTime
);
917 dtprintf("SecTSAValidateTimestamp result: %ld\n", (long)status
);
921 SecAsn1CoderRelease(coder
);
925 static void debugShowExtendedTrustResult(int index
, CFDictionaryRef extendedResult
)
930 CFStringRef xresStr
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
931 CFSTR("Extended trust result for signer #%d : %@"), index
, extendedResult
);
942 extern const char *cssmErrorString(CSSM_RETURN error
);
944 static void statusBitTest(CSSM_TP_APPLE_CERT_STATUS certStatus
, uint32 bit
, const char *str
)
946 if (certStatus
& bit
)
947 dtprintf("%s ", str
);
951 static void debugShowCertEvidenceInfo(uint16_t certCount
, const CSSM_TP_APPLE_EVIDENCE_INFO
*info
)
954 CSSM_TP_APPLE_CERT_STATUS cs
;
955 // const CSSM_TP_APPLE_EVIDENCE_INFO *pinfo = info;
957 for (ix
=0; info
&& (ix
<certCount
); ix
++, ++info
)
959 cs
= info
->StatusBits
;
960 dtprintf(" cert %u:\n", ix
);
961 dtprintf(" StatusBits : 0x%x", (unsigned)cs
);
965 statusBitTest(cs
, CSSM_CERT_STATUS_EXPIRED
, "EXPIRED");
966 statusBitTest(cs
, CSSM_CERT_STATUS_NOT_VALID_YET
,
968 statusBitTest(cs
, CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
,
969 "IS_IN_INPUT_CERTS");
970 statusBitTest(cs
, CSSM_CERT_STATUS_IS_IN_ANCHORS
,
972 statusBitTest(cs
, CSSM_CERT_STATUS_IS_ROOT
, "IS_ROOT");
973 statusBitTest(cs
, CSSM_CERT_STATUS_IS_FROM_NET
, "IS_FROM_NET");
979 dtprintf(" NumStatusCodes : %u ", info
->NumStatusCodes
);
980 CSSM_RETURN
*pstatuscode
= info
->StatusCodes
;
982 for (jx
=0; pstatuscode
&& (jx
<info
->NumStatusCodes
); jx
++, ++pstatuscode
)
983 dtprintf("%s ", cssmErrorString(*pstatuscode
));
986 dtprintf(" Index: %u\n", info
->Index
);
993 static const char *trustResultTypeString(SecTrustResultType trustResultType
)
995 switch (trustResultType
)
997 case kSecTrustResultProceed
: return "TrustResultProceed";
998 case kSecTrustResultUnspecified
: return "TrustResultUnspecified";
999 case kSecTrustResultDeny
: return "TrustResultDeny"; // user reject
1000 case kSecTrustResultInvalid
: return "TrustResultInvalid";
1001 case kSecTrustResultConfirm
: return "TrustResultConfirm";
1002 case kSecTrustResultRecoverableTrustFailure
: return "TrustResultRecoverableTrustFailure";
1003 case kSecTrustResultFatalTrustFailure
: return "TrustResultUnspecified";
1004 case kSecTrustResultOtherError
: return "TrustResultOtherError";
1005 default: return "TrustResultUnknown";
1011 static OSStatus
verifySigners(SecCmsSignedDataRef signedData
, int numberOfSigners
)
1013 // See <rdar://problem/11077588> Bubble up SecTrustEvaluate of timestamp response to high level callers
1014 // Also <rdar://problem/11077708> Properly handle revocation information of timestamping certificate
1016 SecPolicyRef policy
= NULL
;
1017 int result
=errSecInternalError
;
1020 require(policy
= SecPolicyCreateWithOID(kSecPolicyAppleTimeStamping
), xit
);
1022 for (jx
= 0; jx
< numberOfSigners
; ++jx
)
1024 SecTrustResultType trustResultType
;
1025 SecTrustRef trustRef
= NULL
;
1026 CFDictionaryRef extendedResult
= NULL
;
1027 CFArrayRef certChain
= NULL
;
1028 uint16_t certCount
= 0;
1030 CSSM_TP_APPLE_EVIDENCE_INFO
*statusChain
= NULL
;
1032 // SecCmsSignedDataVerifySignerInfo returns trustRef, which we can call SecTrustEvaluate on
1033 // usually (always?) if result is noErr, the SecTrust*Result calls will return errSecTrustNotAvailable
1034 result
= SecCmsSignedDataVerifySignerInfo (signedData
, jx
, NULL
, policy
, &trustRef
);
1035 dtprintf("[%s] SecCmsSignedDataVerifySignerInfo: result: %d, signer: %d\n",
1036 __FUNCTION__
, result
, jx
);
1037 require_noerr(result
, xit
);
1039 result
= SecTrustEvaluate (trustRef
, &trustResultType
);
1040 dtprintf("[%s] SecTrustEvaluate: result: %d, trustResult: %s (%d)\n",
1041 __FUNCTION__
, result
, trustResultTypeString(trustResultType
), trustResultType
);
1044 switch (trustResultType
)
1046 case kSecTrustResultProceed
:
1047 case kSecTrustResultUnspecified
:
1049 case kSecTrustResultDeny
: // user reject
1050 result
= errSecTimestampNotTrusted
; // SecCmsVSTimestampNotTrusted ?
1052 case kSecTrustResultInvalid
:
1053 assert(false); // should never happen
1054 result
= errSecTimestampNotTrusted
; // SecCmsVSTimestampNotTrusted ?
1056 case kSecTrustResultConfirm
:
1057 case kSecTrustResultRecoverableTrustFailure
:
1058 case kSecTrustResultFatalTrustFailure
:
1059 case kSecTrustResultOtherError
:
1063 There are two "errors" that need to be resolved externally:
1064 CSSMERR_TP_CERT_EXPIRED can be OK if the timestamp was made
1065 before the TSA chain expired; CSSMERR_TP_CERT_NOT_VALID_YET
1066 can happen in the case where the user's clock was set to 0.
1067 We don't want to prevent them using apps automatically, so
1068 return noErr and let codesign or whover decide.
1070 OSStatus resultCode
;
1071 require_action(SecTrustGetCssmResultCode(trustRef
, &resultCode
)==noErr
, xit
, result
= errSecTimestampNotTrusted
);
1072 result
= (resultCode
== CSSMERR_TP_CERT_EXPIRED
|| resultCode
== CSSMERR_TP_CERT_NOT_VALID_YET
)?noErr
:errSecTimestampNotTrusted
;
1077 rx
= SecTrustGetResult(trustRef
, &trustResultType
, &certChain
, &statusChain
);
1078 dtprintf("[%s] SecTrustGetResult: result: %d, type: %d\n", __FUNCTION__
,rx
, trustResultType
);
1079 certCount
= certChain
?CFArrayGetCount(certChain
):0;
1080 debugShowCertEvidenceInfo(certCount
, statusChain
);
1082 rx
= SecTrustCopyExtendedResult(trustRef
, &extendedResult
);
1083 dtprintf("[%s] SecTrustCopyExtendedResult: result: %d\n", __FUNCTION__
, rx
);
1086 debugShowExtendedTrustResult(jx
, extendedResult
);
1087 CFRelease(extendedResult
);
1091 CFRelease (trustRef
);
1100 static OSStatus
impExpImportCertUnCommon(
1101 const CSSM_DATA
*cdata
,
1102 SecKeychainRef importKeychain
, // optional
1103 CFMutableArrayRef outArray
) // optional, append here
1105 // The only difference between this and impExpImportCertCommon is that we append to outArray
1106 // before attempting to add to the keychain
1107 OSStatus status
= noErr
;
1108 SecCertificateRef certRef
= NULL
;
1110 require_action(cdata
, xit
, status
= errSecUnsupportedFormat
);
1112 /* Pass kCFAllocatorNull as bytesDeallocator to assure the bytes aren't freed */
1113 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8
*)cdata
->Data
, (CFIndex
)cdata
->Length
, kCFAllocatorNull
);
1114 require_action(data
, xit
, status
= errSecUnsupportedFormat
);
1116 certRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
);
1117 CFRelease(data
); /* certRef has its own copy of the data now */
1119 dtprintf("impExpHandleCert error\n");
1120 return errSecUnsupportedFormat
;
1124 CFArrayAppendValue(outArray
, certRef
);
1128 status
= SecCertificateAddToKeychain(certRef
, importKeychain
);
1129 if (status
!=noErr
&& status
!=errSecDuplicateItem
)
1130 { dtprintf("SecCertificateAddToKeychain error: %ld\n", (long)status
); }
1139 static void saveTSACertificates(CSSM_DATA
**signingCerts
, CFMutableArrayRef outArray
)
1141 SecKeychainRef defaultKeychain
= NULL
;
1142 // Don't save certificates in keychain to avoid securityd issues
1143 // if (SecKeychainCopyDefault(&defaultKeychain))
1144 // defaultKeychain = NULL;
1146 unsigned certCount
= SecCmsArrayCount((void **)signingCerts
);
1148 for (dex
=0; dex
<certCount
; dex
++)
1150 OSStatus rx
= impExpImportCertUnCommon(signingCerts
[dex
], defaultKeychain
, outArray
);
1151 if (rx
!=noErr
&& rx
!=errSecDuplicateItem
)
1152 dtprintf("impExpImportCertCommon failed: %ld\n", (long)rx
);
1154 if (defaultKeychain
)
1155 CFRelease(defaultKeychain
);
1158 static const char *cfabsoluteTimeToString(CFAbsoluteTime abstime
)
1160 CFGregorianDate greg
= CFAbsoluteTimeGetGregorianDate(abstime
, NULL
);
1162 if (19 != snprintf(str
, 20, "%4.4d-%2.2d-%2.2d_%2.2d:%2.2d:%2.2d",
1163 (int)greg
.year
, greg
.month
, greg
.day
, greg
.hour
, greg
.minute
, (int)greg
.second
))
1165 char *data
= (char *)malloc(20);
1166 strncpy(data
, str
, 20);
1170 static OSStatus
setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo
)
1172 OSStatus status
= noErr
;
1174 if (!signerinfo
->timestampCertList
|| (CFArrayGetCount(signerinfo
->timestampCertList
) == 0))
1175 return SecCmsVSSigningCertNotFound
;
1177 SecCertificateRef tsaLeaf
= (SecCertificateRef
)CFArrayGetValueAtIndex(signerinfo
->timestampCertList
, 0);
1178 require_action(tsaLeaf
, xit
, status
= errSecCertificateCannotOperate
);
1180 signerinfo
->tsaLeafNotBefore
= SecCertificateNotValidBefore(tsaLeaf
); /* Start date for Timestamp Authority leaf */
1181 signerinfo
->tsaLeafNotAfter
= SecCertificateNotValidAfter(tsaLeaf
); /* 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;
1203 From RFC 3161: Time-Stamp Protocol (TSP),August 2001, APPENDIX B:
1205 B) The validity of the digital signature may then be verified in the
1208 1) The time-stamp token itself MUST be verified and it MUST be
1209 verified that it applies to the signature of the signer.
1211 2) The date/time indicated by the TSA in the TimeStampToken
1214 3) The certificate used by the signer MUST be identified and
1217 4) The date/time indicated by the TSA MUST be within the
1218 validity period of the signer's certificate.
1220 5) The revocation information about that certificate, at the
1221 date/time of the Time-Stamping operation, MUST be retrieved.
1223 6) Should the certificate be revoked, then the date/time of
1224 revocation shall be later than the date/time indicated by
1227 If all these conditions are successful, then the digital signature
1228 shall be declared as valid.
1232 OSStatus
decodeTimeStampToken(SecCmsSignerInfoRef signerinfo
, CSSM_DATA_PTR inData
, CSSM_DATA_PTR encDigest
, uint64_t expectedNonce
)
1235 We update signerinfo with timestamp and tsa certificate chain.
1236 encDigest is the original signed blob, which we must hash and compare.
1237 inData comes from the unAuthAttr section of the CMS message
1239 These are set in signerinfo as side effects:
1244 SecCmsDecoderRef decoderContext
= NULL
;
1245 SecCmsMessageRef cmsMessage
= NULL
;
1246 SecCmsContentInfoRef contentInfo
;
1247 SecCmsSignedDataRef signedData
;
1248 SECOidTag contentTypeTag
;
1249 int contentLevelCount
;
1251 OSStatus result
= errSecUnknownFormat
;
1252 CSSM_DATA
**signingCerts
= NULL
;
1254 dtprintf("decodeTimeStampToken top: PORT_GetError() %d -----\n", PORT_GetError());
1257 /* decode the message */
1258 require_noerr(result
= SecCmsDecoderCreate (NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &decoderContext
), xit
);
1259 result
= SecCmsDecoderUpdate(decoderContext
, inData
->Data
, inData
->Length
);
1262 result
= errSecTimestampInvalid
;
1263 SecCmsDecoderDestroy(decoderContext
);
1267 require_noerr(result
= SecCmsDecoderFinish(decoderContext
, &cmsMessage
), xit
);
1269 // process the results
1270 contentLevelCount
= SecCmsMessageContentLevelCount(cmsMessage
);
1273 printDataAsHex("encDigest",encDigest
, 0);
1275 for (ix
= 0; ix
< contentLevelCount
; ++ix
)
1277 dtprintf("\n----- Content Level %d -----\n", ix
);
1278 // get content information
1279 contentInfo
= SecCmsMessageContentLevel (cmsMessage
, ix
);
1280 contentTypeTag
= SecCmsContentInfoGetContentTypeTag (contentInfo
);
1282 // After 2nd round, contentInfo.content.data is the TSTInfo
1284 debugShowContentTypeOID(contentInfo
);
1286 switch (contentTypeTag
)
1288 case SEC_OID_PKCS7_SIGNED_DATA
:
1290 require((signedData
= (SecCmsSignedDataRef
)SecCmsContentInfoGetContent(contentInfo
)) != NULL
, xit
);
1292 debugShowSignerInfo(signedData
);
1294 SECAlgorithmID
**digestAlgorithms
= SecCmsSignedDataGetDigestAlgs(signedData
);
1295 unsigned digestAlgCount
= SecCmsArrayCount((void **)digestAlgorithms
);
1296 dtprintf("digestAlgCount: %d\n", digestAlgCount
);
1297 if (signedData
->digests
)
1301 for (jx
=0;jx
< digestAlgCount
;jx
++)
1303 sprintf(buffer
, " digest[%u]", jx
);
1304 printDataAsHex(buffer
,signedData
->digests
[jx
], 0);
1309 dtprintf("No digests\n");
1310 CSSM_DATA_PTR innerContent
= SecCmsContentInfoGetInnerContent(contentInfo
);
1313 dtprintf("inner content length: %ld\n", innerContent
->Length
);
1314 SecAsn1TSAMessageImprint fakeMessageImprint
= {{{0}},};
1315 OSStatus status
= createTSAMessageImprint(signedData
, innerContent
, &fakeMessageImprint
);
1317 { dtprintf("createTSAMessageImprint status: %d\n", (int)status
); }
1318 printDataAsHex("inner content hash",&fakeMessageImprint
.hashedMessage
, 0);
1319 CSSM_DATA_PTR digestdata
= &fakeMessageImprint
.hashedMessage
;
1320 CSSM_DATA_PTR digests
[2] = {digestdata
, NULL
};
1321 SecCmsSignedDataSetDigests(signedData
, digestAlgorithms
, (CSSM_DATA_PTR
*)&digests
);
1324 dtprintf("no inner content\n");
1328 Import the certificates. We leave this as a warning, since
1329 there are configurations where the certificates are not returned.
1331 signingCerts
= SecCmsSignedDataGetCertificateList(signedData
);
1332 if (signingCerts
== NULL
)
1333 { dtprintf("SecCmsSignedDataGetCertificateList returned NULL\n"); }
1336 if (!signerinfo
->timestampCertList
)
1337 signerinfo
->timestampCertList
= CFArrayCreateMutable(kCFAllocatorDefault
, 10, &kCFTypeArrayCallBacks
);
1338 saveTSACertificates(signingCerts
, signerinfo
->timestampCertList
);
1339 require_noerr(result
= setTSALeafValidityDates(signerinfo
), xit
);
1340 debugSaveCertificates(signingCerts
);
1343 int numberOfSigners
= SecCmsSignedDataSignerInfoCount (signedData
);
1345 result
= verifySigners(signedData
, numberOfSigners
);
1347 dtprintf("verifySigners failed: %ld\n", (long)result
); // warning
1350 if (result
) // remap to SecCmsVSTimestampNotTrusted ?
1355 case SEC_OID_PKCS9_SIGNING_CERTIFICATE
:
1357 dtprintf("SEC_OID_PKCS9_SIGNING_CERTIFICATE seen\n");
1361 case SEC_OID_PKCS9_ID_CT_TSTInfo
:
1363 SecAsn1TSATSTInfo tstInfo
= {{0},};
1364 result
= verifyTSTInfo(contentInfo
->rawContent
, signingCerts
, &tstInfo
, &signerinfo
->timestampTime
, expectedNonce
);
1365 if (signerinfo
->timestampTime
)
1367 const char *tstamp
= cfabsoluteTimeToString(signerinfo
->timestampTime
);
1370 dtprintf("Timestamp Authority timestamp: %s\n", tstamp
);
1371 free((void *)tstamp
);
1378 dtprintf("otherContent : %p\n", (char *)SecCmsContentInfoGetContent (contentInfo
));
1382 dtprintf("ContentTypeTag : %x\n", contentTypeTag
);
1388 SecCmsMessageDestroy(cmsMessage
);