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
; 
 234     CFRetain(inStr
);    // compensate for release on exit 
 237         if ((str 
= CFStringGetCStringPtr(inStr
, kCFStringEncodingUTF8
))) 
 239         size_t len 
= strlen(str
); 
 240         result 
= malloc(len
); 
 245         // need to extract into buffer 
 246         CFIndex length 
= CFStringGetLength(inStr
);  // in 16-bit character units 
 247     size_t len 
= 6 * length 
+ 1; 
 248         result 
= malloc(len
);                       // pessimistic 
 249         if (!CFStringGetCString(inStr
, result
, len
, kCFStringEncodingUTF8
)) 
 256 /* Oids longer than this are considered invalid. */ 
 257 #define MAX_OID_SIZE                            32 
 259 static CFStringRef 
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
, const CSSM_OID 
*oid
) 
 261         if (oid
->Length 
== 0) 
 262         return CFSTR("<NULL>"); 
 264         if (oid
->Length 
> MAX_OID_SIZE
) 
 265         return CFSTR("Oid too long"); 
 267     CFMutableStringRef result 
= CFStringCreateMutable(allocator
, 0); 
 269         // The first two levels are encoded into one byte, since the root levelq 
 270         // has only 3 nodes (40*x + y).  However if x = joint-iso-itu-t(2) then 
 271         // y may be > 39, so we have to add special-case handling for this. 
 272         uint32_t x 
= oid
->Data
[0] / 40; 
 273         uint32_t y 
= oid
->Data
[0] % 40; 
 276                 // Handle special case for large y if x = 2 
 280     CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
); 
 283         for (x 
= 1; x 
< oid
->Length
; ++x
) 
 285                 value 
= (value 
<< 7) | (oid
->Data
[x
] & 0x7F); 
 286         /* @@@ value may not span more than 4 bytes. */ 
 287         /* A max number of 20 values is allowed. */ 
 288                 if (!(oid
->Data
[x
] & 0x80)) 
 290             CFStringAppendFormat(result
, NULL
, CFSTR(".%lu"), value
); 
 297 static void debugSaveCertificates(CSSM_DATA 
**outCerts
) 
 302         CSSM_DATA_PTR 
*certp
; 
 304         const char *certNameBase 
= "/tmp/tsa-resp-cert-"; 
 305         char fname
[PATH_MAX
]; 
 306         unsigned certCount 
= SecCmsArrayCount((void **)outCerts
); 
 307         dtprintf("Found %d certs\n",certCount
); 
 309         for (certp
=outCerts
;*certp
;certp
++, ++jx
) 
 312             strncpy(fname
, certNameBase
, strlen(certNameBase
)+1); 
 313             sprintf(numstr
,"%u", jx
); 
 314             strcat(fname
,numstr
); 
 315             tsaWriteFileX(fname
, (*certp
)->Data
, (*certp
)->Length
); 
 317                 break;  //something wrong 
 323 static void debugShowSignerInfo(SecCmsSignedDataRef signedData
) 
 326     int numberOfSigners 
= SecCmsSignedDataSignerInfoCount (signedData
); 
 327     dtprintf("numberOfSigners : %d\n", numberOfSigners
); 
 329     for (ix
=0;ix 
< numberOfSigners
;ix
++) 
 331         SecCmsSignerInfoRef sigi 
= SecCmsSignedDataGetSignerInfo(signedData
,ix
); 
 334             CFStringRef commonName 
= SecCmsSignerInfoGetSignerCommonName(sigi
); 
 335             const char *signerhdr 
= "      signer    : "; 
 338                 char *cn 
= cfStringToChar(commonName
); 
 339                 dtprintf("%s%s\n", signerhdr
, cn
); 
 344                 dtprintf("%s<NULL>\n", signerhdr
); 
 350 static void debugShowContentTypeOID(SecCmsContentInfoRef contentInfo
) 
 354     CSSM_OID 
*typeOID 
= SecCmsContentInfoGetContentTypeOID(contentInfo
); 
 357         CFStringRef oidCFStr 
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, typeOID
); 
 358         char *oidstr 
= cfStringToChar(oidCFStr
); 
 359         printDataAsHex("oid:", typeOID
, (unsigned int)typeOID
->Length
); 
 360         dtprintf("\toid: %s\n", oidstr
); 
 369 uint64_t tsaDER_ToInt(const CSSM_DATA 
*DER_Data
) 
 374         while(i 
< DER_Data
->Length
) { 
 375                 rtn 
|= DER_Data
->Data
[i
]; 
 376                 if(++i 
== DER_Data
->Length
) { 
 384 void displayTSTInfo(SecAsn1TSATSTInfo 
*tstInfo
) 
 387     dtprintf("--- TSTInfo ---\n"); 
 391     if (tstInfo
->version
.Data
) 
 393         uint64_t vers 
= tsaDER_ToInt(&tstInfo
->version
); 
 394         dtprintf("Version:\t\t%u\n", (int)vers
); 
 397     if (tstInfo
->serialNumber
.Data
) 
 399         uint64_t sn 
= tsaDER_ToInt(&tstInfo
->serialNumber
); 
 400         dtprintf("SerialNumber:\t%llu\n", sn
); 
 403     if (tstInfo
->ordering
.Data
) 
 405         uint64_t ord 
= tsaDER_ToInt(&tstInfo
->ordering
); 
 406         dtprintf("Ordering:\t\t%s\n", ord
?"yes":"no"); 
 409     if (tstInfo
->nonce
.Data
) 
 411         uint64_t nonce 
= tsaDER_ToInt(&tstInfo
->nonce
); 
 412         dtprintf("Nonce:\t\t%llu\n", nonce
); 
 415         dtprintf("Nonce:\t\tnot specified\n"); 
 417     if (tstInfo
->genTime
.Data
) 
 419         char buf
[tstInfo
->genTime
.Length
+1]; 
 420         memcpy(buf
, (const char *)tstInfo
->genTime
.Data
, tstInfo
->genTime
.Length
); 
 421         buf
[tstInfo
->genTime
.Length
]=0; 
 422         dtprintf("GenTime:\t\t%s\n", buf
); 
 425     dtprintf("-- MessageImprint --\n"); 
 426     if (true)   // SecAsn1TSAMessageImprint 
 428         printDataAsHex(" Algorithm:",&tstInfo
->messageImprint
.hashAlgorithm
.algorithm
, 0); 
 429         printDataAsHex(" Message  :", &tstInfo
->messageImprint
.hashedMessage
, 0);//tstInfo->messageImprint.hashedMessage.Length); 
 434 #pragma mark ----- TimeStamp Response using XPC ----- 
 436 #include <xpc/private.h> 
 438 static OSStatus 
checkForNonDERResponse(const unsigned char *resp
, size_t respLen
) 
 441         Good start is something like 30 82 0c 03 30 15 02 01  00 30 10 0c 0e 4f 70 65 
 443         URL:    http://timestamp-int.corp.apple.com/signserver/process?TimeStampSigner 
 444         Resp:   Http/1.1 Service Unavailable 
 446         URL:    http://timestamp-int.corp.apple.com/ts01 
 449         URL:    http://cutandtaste.com/404 (or other forced 404 site) 
 453     OSStatus status 
= noErr
; 
 454     const char ader
[2] = { 0x30, 0x82 }; 
 455     char *respStr 
= NULL
; 
 457     size_t badResponseCount
; 
 459     const char *badResponses
[] = 
 462         "Http/1.1 Service Unavailable", 
 466     require_action(resp 
&& respLen
, xit
, status 
= errSecTimestampServiceNotAvailable
); 
 468     // This is usual case 
 469     if ((respLen 
> 1) && (memcmp(resp
, ader
, 2)==0))    // might be good; pass on to DER decoder 
 472     badResponseCount 
= sizeof(badResponses
)/sizeof(char *); 
 474     for (ix 
= 0; ix 
< badResponseCount
; ++ix
) 
 475         if (strlen(badResponses
[ix
]) > maxlen
) 
 476             maxlen 
= strlen(badResponses
[ix
]); 
 478     // Prevent a large response from allocating a ton of memory 
 479     if (respLen 
> maxlen
) 
 482     respStr 
= (char *)malloc(respLen
+1); 
 483     strlcpy(respStr
, (const char *)resp
, respLen
); 
 485     for (ix 
= 0; ix 
< badResponseCount
; ++ix
) 
 486         if (strcmp(respStr
, badResponses
[ix
])==0) 
 487             return errSecTimestampServiceNotAvailable
; 
 491         free((void *)respStr
); 
 496 static OSStatus 
sendTSARequestWithXPC(const unsigned char *tsaReq
, size_t tsaReqLength
, const unsigned char *tsaURL
, unsigned char **tsaResp
, size_t *tsaRespLength
) 
 498     __block OSStatus result 
= noErr
; 
 499     int timeoutInSeconds 
= 15; 
 500     extern xpc_object_t 
xpc_create_with_format(const char * format
, ...); 
 502         dispatch_queue_t xpc_queue 
= dispatch_queue_create("com.apple.security.XPCTimeStampingService", DISPATCH_QUEUE_SERIAL
); 
 503     __block dispatch_semaphore_t waitSemaphore 
= dispatch_semaphore_create(0); 
 504     dispatch_time_t finishTime 
= dispatch_time(DISPATCH_TIME_NOW
, timeoutInSeconds 
* NSEC_PER_SEC
); 
 506     xpc_connection_t con 
= xpc_connection_create("com.apple.security.XPCTimeStampingService", xpc_queue
); 
 508     xpc_connection_set_event_handler(con
, ^(xpc_object_t event
) { 
 509         xpc_type_t xtype 
= xpc_get_type(event
); 
 510         if (XPC_TYPE_ERROR 
== xtype
) 
 511         {    tsaDebug("default: connection error: %s\n", xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
)); } 
 513          {   tsaDebug("default: unexpected connection event %p\n", event
); }  
 516     xpc_connection_resume(con
); 
 518     xpc_object_t tsaReqData 
= xpc_data_create(tsaReq
, tsaReqLength
); 
 519     const char *urlstr 
= (tsaURL
?(const char *)tsaURL
:""); 
 520     xpc_object_t url_as_xpc_string 
= xpc_string_create(urlstr
); 
 522     xpc_object_t message 
= xpc_create_with_format("{operation: TimeStampRequest, ServerURL: %value, TimeStampRequest: %value}", url_as_xpc_string
, tsaReqData
); 
 524     xpc_connection_send_message_with_reply(con
, message
, xpc_queue
, ^(xpc_object_t reply
) 
 526         tsaDebug("xpc_connection_send_message_with_reply handler called back\n"); 
 527         dispatch_retain(waitSemaphore
); 
 529     xpc_type_t xtype 
= xpc_get_type(reply
); 
 530         if (XPC_TYPE_ERROR 
== xtype
) 
 531             {   tsaDebug("message error: %s\n", xpc_dictionary_get_string(reply
, XPC_ERROR_KEY_DESCRIPTION
)); } 
 532         else if (XPC_TYPE_CONNECTION 
== xtype
) 
 533             { tsaDebug("received connection\n"); } 
 534         else if (XPC_TYPE_DICTIONARY 
== xtype
) 
 538          // This is useful for debugging. 
 539         char *debug = xpc_copy_description(reply); 
 540         tsaDebug("DEBUG %s\n", debug); 
 545         xpc_object_t xpcTimeStampReply 
= xpc_dictionary_get_value(reply
, "TimeStampReply"); 
 546         size_t xpcTSRLength 
= xpc_data_get_length(xpcTimeStampReply
); 
 547         tsaDebug("xpcTSRLength: %ld bytes of response\n", xpcTSRLength
); 
 549         xpc_object_t xpcTimeStampError 
= xpc_dictionary_get_value(reply
, "TimeStampError"); 
 550         xpc_object_t xpcTimeStampStatus 
= xpc_dictionary_get_value(reply
, "TimeStampStatus"); 
 552         if (xpcTimeStampError 
|| xpcTimeStampStatus
) 
 555             if (xpcTimeStampError
) 
 557                 size_t len 
= xpc_string_get_length(xpcTimeStampError
); 
 558                 char *buf 
= (char *)malloc(len
); 
 559                 strlcpy(buf
, xpc_string_get_string_ptr(xpcTimeStampError
), len
+1); 
 560                 tsaDebug("xpcTimeStampError: %s\n", buf
); 
 565             if (xpcTimeStampStatus
) 
 567                 result 
= (OSStatus
)xpc_int64_get_value(xpcTimeStampStatus
); 
 568                 tsaDebug("xpcTimeStampStatus: %d\n", (int)result
); 
 572         result 
= remapHTTPErrorCodes(result
); 
 574         if ((result 
== noErr
) && tsaResp 
&& tsaRespLength
) 
 576             *tsaRespLength 
= xpcTSRLength
; 
 577             *tsaResp 
= (unsigned char *)malloc(xpcTSRLength
); 
 579             size_t bytesCopied 
= xpc_data_get_bytes(xpcTimeStampReply
, *tsaResp
, 0, xpcTSRLength
); 
 580             if (bytesCopied 
!= xpcTSRLength
) 
 581             {    tsaDebug("length mismatch: copied: %ld, xpc: %ld\n", bytesCopied
, xpcTSRLength
); } 
 583             if ((result 
= checkForNonDERResponse(*tsaResp
,bytesCopied
))) 
 585                 tsaDebug("received non-DER response from timestamp server\n"); 
 590                 tsaDebug("copied: %ld bytes of response\n", bytesCopied
); 
 593         tsaDebug("releasing connection\n"); 
 597         { tsaDebug("unexpected message reply type %p\n", xtype
); } 
 599         dispatch_semaphore_signal(waitSemaphore
); 
 600         dispatch_release(waitSemaphore
); 
 603     { tsaDebug("waiting up to %d seconds for response from XPC\n", timeoutInSeconds
); } 
 604         dispatch_semaphore_wait(waitSemaphore
, finishTime
); 
 606         dispatch_release(waitSemaphore
); 
 607     xpc_release(tsaReqData
); 
 608     xpc_release(message
); 
 610     { tsaDebug("sendTSARequestWithXPC exit\n"); } 
 615 #pragma mark ----- TimeStamp request ----- 
 617 #include "tsaTemplates.h" 
 618 #include <security_asn1/SecAsn1Coder.h> 
 619 #include <Security/oidsalg.h> 
 620 #include <AssertMacros.h> 
 621 #include <libkern/OSByteOrder.h> 
 623 extern const SecAsn1Template kSecAsn1TSATimeStampReqTemplate
; 
 624 extern const SecAsn1Template kSecAsn1TSATimeStampRespTemplateDER
; 
 626 CFMutableDictionaryRef 
SecCmsTSAGetDefaultContext(CFErrorRef 
*error
) 
 628     // Caller responsible for retain/release 
 629     // <rdar://problem/11077440> Update SecCmsTSAGetDefaultContext with actual URL for Apple Timestamp server 
 630     //      URL will be in TimeStampingPrefs.plist 
 632     CFBundleRef secFWbundle 
= NULL
; 
 633     CFURLRef resourceURL 
= NULL
; 
 634     CFDataRef resourceData 
= NULL
; 
 635     CFPropertyListRef prefs 
= NULL
; 
 636     CFMutableDictionaryRef contextDict 
= NULL
; 
 637     SInt32 errorCode 
= 0; 
 638     CFOptionFlags options 
= 0; 
 639     CFPropertyListFormat format 
= 0; 
 640     OSStatus status 
= noErr
; 
 642     require_action(secFWbundle 
= CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")), xit
, status 
= errSecInternalError
); 
 643     require_action(resourceURL 
= CFBundleCopyResourceURL(secFWbundle
, CFSTR("TimeStampingPrefs"), CFSTR("plist"), NULL
), 
 644         xit
, status 
= errSecInvalidPrefsDomain
); 
 646     require(CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
, resourceURL
, &resourceData
, 
 647         NULL
, NULL
, &errorCode
), xit
); 
 648     require_action(resourceData
, xit
, status 
= errSecDataNotAvailable
); 
 650     prefs 
= CFPropertyListCreateWithData(kCFAllocatorDefault
, resourceData
, options
, &format
, error
); 
 651     require_action(prefs 
&& (CFGetTypeID(prefs
)==CFDictionaryGetTypeID()), xit
, status 
= errSecInvalidPrefsDomain
); 
 653     contextDict 
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, prefs
); 
 661         *error 
= CFErrorCreate(kCFAllocatorDefault
, kCFErrorDomainOSStatus
, status
, NULL
); 
 663         CFRelease(secFWbundle
); 
 665         CFRelease(resourceURL
); 
 667         CFRelease(resourceData
); 
 674 static CFDataRef 
_SecTSARequestCopyDEREncoding(SecAsn1TSAMessageImprint 
*messageImprint
, bool noCerts
, uint64_t nonce
) 
 676     // Returns DER encoded TimeStampReq 
 677     // Modeled on _SecOCSPRequestCopyDEREncoding 
 678     // The Timestamp Authority supports 64 bit nonces (or more possibly) 
 680     SecAsn1CoderRef             coder 
= NULL
; 
 682     SecAsn1Item                 vers 
= {1, &version
}; 
 683     uint8_t                     creq 
= noCerts
?0:1; 
 684     SecAsn1Item                 certReq 
= {1, &creq
};   //jch - to request or not? 
 685     SecAsn1TSATimeStampReq      tsreq 
= {}; 
 686     CFDataRef                   der 
= NULL
; 
 687     uint64_t                    nonceVal 
= OSSwapHostToBigConstInt64(nonce
); 
 688     SecAsn1Item                 nonceItem 
= {sizeof(uint64_t), (unsigned char *)&nonceVal
}; 
 690     uint8_t OID_FakePolicy_Data
[] = { 0x2A, 0x03, 0x04, 0x05, 0x06};     
 691     const CSSM_OID fakePolicyOID 
= {sizeof(OID_FakePolicy_Data
),OID_FakePolicy_Data
}; 
 693     tsreq
.version 
= vers
; 
 695     tsreq
.messageImprint 
= *messageImprint
; 
 696     tsreq
.certReq 
= certReq
; 
 698     // skip reqPolicy, extensions for now - FAKES - jch 
 699     tsreq
.reqPolicy 
= fakePolicyOID
;    //policyID; 
 701     tsreq
.nonce 
= nonceItem
; 
 703     // Encode the request 
 704     require_noerr(SecAsn1CoderCreate(&coder
), errOut
); 
 707     require_noerr(SecAsn1EncodeItem(coder
, &tsreq
, 
 708         &kSecAsn1TSATimeStampReqTemplate
, &encoded
), errOut
); 
 709     der 
= CFDataCreate(kCFAllocatorDefault
, encoded
.Data
, 
 714         SecAsn1CoderRelease(coder
); 
 719 OSStatus 
SecTSAResponseCopyDEREncoding(SecAsn1CoderRef coder
, const CSSM_DATA 
*tsaResponse
, SecAsn1TimeStampRespDER 
*respDER
) 
 721     // Partially decode the response 
 722     OSStatus status 
= paramErr
; 
 724     require(tsaResponse 
&& respDER
, errOut
); 
 725     require_noerr(SecAsn1DecodeData(coder
, tsaResponse
, 
 726         &kSecAsn1TSATimeStampRespTemplateDER
, respDER
), errOut
); 
 734 #pragma mark ----- TS Callback ----- 
 736 OSStatus 
SecCmsTSADefaultCallback(CFTypeRef context
, void *messageImprintV
, uint64_t nonce
, CSSM_DATA 
*signedDERBlob
) 
 738     OSStatus result 
= paramErr
; 
 739     const unsigned char *tsaReq 
= NULL
; 
 740     size_t tsaReqLength 
= 0; 
 741     CFDataRef cfreq 
= NULL
; 
 742     unsigned char *tsaURL 
= NULL
; 
 743     bool noCerts 
= false; 
 745     if (!context 
|| CFGetTypeID(context
)!=CFDictionaryGetTypeID()) 
 748     SecAsn1TSAMessageImprint 
*messageImprint 
= (SecAsn1TSAMessageImprint 
*)messageImprintV
; 
 749     if (!messageImprint 
|| !signedDERBlob
) 
 752     CFBooleanRef cfnocerts 
= (CFBooleanRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSAContextKeyNoCerts
); 
 755         tsaDebug("[TSA] Request noCerts\n"); 
 756         noCerts 
= CFBooleanGetValue(cfnocerts
); 
 759     // We must spoof the nonce here, before sending the request. 
 760     // If we tried to alter the reply, then the signature would break instead. 
 761     CFBooleanRef cfBadNonce 
= (CFBooleanRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSADebugContextKeyBadNonce
); 
 762     if (cfBadNonce 
&& CFBooleanGetValue(cfBadNonce
)) 
 764         tsaDebug("[TSA] Forcing bad TS Request by changing nonce\n"); 
 768     printDataAsHex("[TSA] hashToTimeStamp:", &messageImprint
->hashedMessage
,128); 
 769     cfreq 
= _SecTSARequestCopyDEREncoding(messageImprint
, noCerts
, nonce
); 
 772         tsaReq 
= CFDataGetBytePtr(cfreq
); 
 773         tsaReqLength 
= CFDataGetLength(cfreq
); 
 777         tsaWriteFileX("/tmp/tsareq.req", tsaReq
, tsaReqLength
); 
 781     CFStringRef url 
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSAContextKeyURL
); 
 784         tsaDebug("[TSA] missing URL for TSA (key: %s)\n", "kTSAContextKeyURL"); 
 789         If debugging, look at special values in the context to mess things up 
 792     CFBooleanRef cfBadReq 
= (CFBooleanRef
)CFDictionaryGetValue((CFDictionaryRef
)context
, kTSADebugContextKeyBadReq
); 
 793     if (cfBadReq 
&& CFBooleanGetValue(cfBadReq
)) 
 795         tsaDebug("[TSA] Forcing bad TS Request by truncating length from %ld to %ld\n", tsaReqLength
, (tsaReqLength
-4)); 
 799     // need to extract into buffer 
 800     CFIndex length 
= CFStringGetLength(url
);        // in 16-bit character units 
 801     tsaURL 
= malloc(6 * length 
+ 1);                // pessimistic 
 802     if (!CFStringGetCString(url
, (char *)tsaURL
, 6 * length 
+ 1, kCFStringEncodingUTF8
)) 
 805     tsaDebug("[TSA] URL for timestamp server: %s\n", tsaURL
); 
 807     unsigned char *tsaResp 
= NULL
; 
 808     size_t tsaRespLength 
= 0; 
 809     tsaDebug("calling sendTSARequestWithXPC with %ld bytes of request\n", tsaReqLength
); 
 811     require_noerr(result 
= sendTSARequestWithXPC(tsaReq
, tsaReqLength
, tsaURL
, &tsaResp
, &tsaRespLength
), xit
); 
 813     tsaDebug("sendTSARequestWithXPC copied: %ld bytes of response\n", tsaRespLength
); 
 815     signedDERBlob
->Data 
= tsaResp
; 
 816     signedDERBlob
->Length 
= tsaRespLength
; 
 822         free((void *)tsaURL
); 
 829 #pragma mark ----- TimeStamp Verification ----- 
 831 static OSStatus 
convertGeneralizedTimeToCFAbsoluteTime(const char *timeStr
, CFAbsoluteTime 
*ptime
) 
 834         See http://userguide.icu-project.org/formatparse/datetime for date/time format. 
 835         The "Z" signal a GMT time, but CFDateFormatterGetAbsoluteTimeFromString returns 
 836         values based on local time.  
 839     OSStatus result 
= noErr
; 
 840     CFDateFormatterRef formatter 
= NULL
; 
 841     CFStringRef time_string 
= NULL
; 
 842     CFTimeZoneRef gmt 
= NULL
; 
 843     CFLocaleRef locale 
= NULL
; 
 844     CFRange 
*rangep 
= NULL
; 
 846     require(timeStr 
&& timeStr
[0] && ptime
, xit
); 
 847     require(formatter 
= CFDateFormatterCreate(kCFAllocatorDefault
, locale
, kCFDateFormatterNoStyle
, kCFDateFormatterNoStyle
), xit
); 
 848 //    CFRetain(formatter); 
 849     CFDateFormatterSetFormat(formatter
, CFSTR("yyyyMMddHHmmss'Z'"));    // GeneralizedTime 
 850     gmt 
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0); 
 851         CFDateFormatterSetProperty(formatter
, kCFDateFormatterTimeZone
, gmt
); 
 853     time_string 
= CFStringCreateWithCString(kCFAllocatorDefault
, timeStr
, kCFStringEncodingUTF8
); 
 854     if (!time_string 
|| !CFDateFormatterGetAbsoluteTimeFromString(formatter
, time_string
, rangep
, ptime
)) 
 856         dtprintf("%s is not a valid date\n", timeStr
); 
 862         CFRelease(formatter
); 
 864         CFRelease(time_string
); 
 871 static OSStatus 
SecTSAValidateTimestamp(const SecAsn1TSATSTInfo 
*tstInfo
, CSSM_DATA 
**signingCerts
, CFAbsoluteTime 
*timestampTime
) 
 873     // See <rdar://problem/11077708> Properly handle revocation information of timestamping certificate 
 874     OSStatus result 
= paramErr
; 
 875     CFAbsoluteTime genTime 
= 0; 
 876     char timeStr
[32] = {0,}; 
 877     SecCertificateRef signingCertificate 
= NULL
; 
 879     require(tstInfo 
&& signingCerts 
&& (tstInfo
->genTime
.Length 
< 16), xit
); 
 881     // Find the leaf signingCert 
 882     require_noerr(result 
= SecCertificateCreateFromData(*signingCerts
,  
 883         CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_DER
, &signingCertificate
), xit
); 
 885     memcpy(timeStr
, tstInfo
->genTime
.Data
, tstInfo
->genTime
.Length
); 
 886     timeStr
[tstInfo
->genTime
.Length
] = 0; 
 887     require_noerr(convertGeneralizedTimeToCFAbsoluteTime(timeStr
, &genTime
), xit
); 
 888     if (SecCertificateIsValidX(signingCertificate
, genTime
)) // iOS? 
 891         result 
= errSecTimestampInvalid
; 
 893         *timestampTime 
= genTime
; 
 898 static OSStatus 
verifyTSTInfo(const CSSM_DATA_PTR content
, CSSM_DATA 
**signingCerts
, SecAsn1TSATSTInfo 
*tstInfo
, CFAbsoluteTime 
*timestampTime
, uint64_t expectedNonce
) 
 900     OSStatus status 
= paramErr
; 
 901     SecAsn1CoderRef coder 
= NULL
; 
 906     require_noerr(SecAsn1CoderCreate(&coder
), xit
); 
 907     require_noerr(SecAsn1Decode(coder
, content
->Data
, content
->Length
,  
 908                 kSecAsn1TSATSTInfoTemplate
, tstInfo
), xit
); 
 909     displayTSTInfo(tstInfo
); 
 912     if (tstInfo
->nonce
.Data 
&& expectedNonce
!=0) 
 914         uint64_t nonce 
= tsaDER_ToInt(&tstInfo
->nonce
); 
 915      //   if (expectedNonce!=nonce) 
 916             dtprintf("verifyTSTInfo nonce: actual: %lld, expected: %lld\n", nonce
, expectedNonce
); 
 917         require_action(expectedNonce
==nonce
, xit
, status 
= errSecTimestampRejection
); 
 920     status 
= SecTSAValidateTimestamp(tstInfo
, signingCerts
, timestampTime
); 
 921     dtprintf("SecTSAValidateTimestamp result: %ld\n", (long)status
); 
 925         SecAsn1CoderRelease(coder
); 
 929 static void debugShowExtendedTrustResult(int index
, CFDictionaryRef extendedResult
) 
 934         CFStringRef xresStr 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, 
 935         CFSTR("Extended trust result for signer #%d : %@"), index
, extendedResult
); 
 946 extern const char *cssmErrorString(CSSM_RETURN error
); 
 948 static void statusBitTest(CSSM_TP_APPLE_CERT_STATUS certStatus
, uint32 bit
, const char *str
) 
 950         if (certStatus 
& bit
) 
 951                 dtprintf("%s  ", str
); 
 955 static void debugShowCertEvidenceInfo(uint16_t certCount
, const CSSM_TP_APPLE_EVIDENCE_INFO 
*info
) 
 958         CSSM_TP_APPLE_CERT_STATUS cs
; 
 959 //      const CSSM_TP_APPLE_EVIDENCE_INFO *pinfo = info; 
 961         for (ix
=0; info 
&& (ix
<certCount
); ix
++, ++info
) 
 963                 cs 
= info
->StatusBits
; 
 964                 dtprintf("   cert %u:\n", ix
); 
 965                 dtprintf("      StatusBits     : 0x%x", (unsigned)cs
); 
 969                         statusBitTest(cs
, CSSM_CERT_STATUS_EXPIRED
, "EXPIRED"); 
 970                         statusBitTest(cs
, CSSM_CERT_STATUS_NOT_VALID_YET
,  
 972                         statusBitTest(cs
, CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
,  
 973                                 "IS_IN_INPUT_CERTS"); 
 974                         statusBitTest(cs
, CSSM_CERT_STATUS_IS_IN_ANCHORS
,  
 976                         statusBitTest(cs
, CSSM_CERT_STATUS_IS_ROOT
, "IS_ROOT"); 
 977                         statusBitTest(cs
, CSSM_CERT_STATUS_IS_FROM_NET
, "IS_FROM_NET"); 
 983                 dtprintf("      NumStatusCodes : %u ", info
->NumStatusCodes
); 
 984         CSSM_RETURN 
*pstatuscode 
= info
->StatusCodes
; 
 986         for (jx
=0; pstatuscode 
&& (jx
<info
->NumStatusCodes
); jx
++, ++pstatuscode
) 
 987             dtprintf("%s  ", cssmErrorString(*pstatuscode
)); 
 990                 dtprintf("      Index: %u\n", info
->Index
); 
 996 static const char *trustResultTypeString(SecTrustResultType trustResultType
) 
 999     switch (trustResultType
) 
1001     case kSecTrustResultProceed
:                    return "TrustResultProceed"; 
1002     case kSecTrustResultUnspecified
:                return "TrustResultUnspecified"; 
1003     case kSecTrustResultDeny
:                       return "TrustResultDeny";   // user reject 
1004     case kSecTrustResultInvalid
:                    return "TrustResultInvalid"; 
1005     case kSecTrustResultConfirm
:                    return "TrustResultConfirm"; 
1006     case kSecTrustResultRecoverableTrustFailure
:    return "TrustResultRecoverableTrustFailure"; 
1007     case kSecTrustResultFatalTrustFailure
:          return "TrustResultUnspecified"; 
1008     case kSecTrustResultOtherError
:                 return "TrustResultOtherError"; 
1009     default:                                        return "TrustResultUnknown"; 
1015 static OSStatus 
verifySigners(SecCmsSignedDataRef signedData
, int numberOfSigners
) 
1017     // See <rdar://problem/11077588> Bubble up SecTrustEvaluate of timestamp response to high level callers 
1018     // Also <rdar://problem/11077708> Properly handle revocation information of timestamping certificate 
1020     SecPolicyRef policy 
= NULL
; 
1023     require(policy 
= SecPolicyCreateWithOID(kSecPolicyAppleTimeStamping
), xit
); 
1025     for (jx 
= 0; jx 
< numberOfSigners
; ++jx
) 
1027         SecTrustResultType trustResultType
; 
1028         SecTrustRef trustRef 
= NULL
; 
1029         CFDictionaryRef extendedResult 
= NULL
; 
1030         CFArrayRef certChain 
= NULL
; 
1031         uint16_t certCount 
= 0; 
1033         CSSM_TP_APPLE_EVIDENCE_INFO 
*statusChain 
= NULL
; 
1035         // SecCmsSignedDataVerifySignerInfo returns trustRef, which we can call SecTrustEvaluate on 
1036         // usually (always?) if result is noErr, the SecTrust*Result calls will return errSecTrustNotAvailable 
1037         result 
= SecCmsSignedDataVerifySignerInfo (signedData
, jx
, NULL
, policy
, &trustRef
); 
1038         dtprintf("[%s] SecCmsSignedDataVerifySignerInfo: result: %d, signer: %d\n", 
1039             __FUNCTION__
, result
, jx
); 
1040          require_noerr(result
, xit
); 
1042         result 
= SecTrustEvaluate (trustRef
, &trustResultType
); 
1043         dtprintf("[%s] SecTrustEvaluate: result: %d, trustResult: %s (%d)\n", 
1044             __FUNCTION__
, result
, trustResultTypeString(trustResultType
), trustResultType
); 
1047         switch (trustResultType
) 
1049                 case kSecTrustResultProceed
: 
1050                 case kSecTrustResultUnspecified
: 
1052                 case kSecTrustResultDeny
:                   // user reject 
1053                         result 
= errSecTimestampNotTrusted
;     // SecCmsVSTimestampNotTrusted ? 
1055                 case kSecTrustResultInvalid
: 
1056                         assert(false);                          // should never happen 
1057                         result 
= errSecTimestampNotTrusted
;     // SecCmsVSTimestampNotTrusted ? 
1059         case kSecTrustResultConfirm
: 
1060         case kSecTrustResultRecoverableTrustFailure
: 
1061         case kSecTrustResultFatalTrustFailure
: 
1062         case kSecTrustResultOtherError
: 
1066                     There are two "errors" that need to be resolved externally: 
1067                     CSSMERR_TP_CERT_EXPIRED can be OK if the timestamp was made 
1068                     before the TSA chain expired; CSSMERR_TP_CERT_NOT_VALID_YET 
1069                     can happen in the case where the user's clock was set to 0. 
1070                     We don't want to prevent them using apps automatically, so 
1071                     return noErr and let codesign or whover decide. 
1073                                 OSStatus resultCode
; 
1074                                 require_action(SecTrustGetCssmResultCode(trustRef
, &resultCode
)==noErr
, xit
, result 
= errSecTimestampNotTrusted
); 
1075                                 result 
= (resultCode 
== CSSMERR_TP_CERT_EXPIRED 
|| resultCode 
== CSSMERR_TP_CERT_NOT_VALID_YET
)?noErr
:errSecTimestampNotTrusted
; 
1080         rx 
= SecTrustGetResult(trustRef
, &trustResultType
, &certChain
, &statusChain
); 
1081         dtprintf("[%s] SecTrustGetResult: result: %d, type: %d\n", __FUNCTION__
,rx
, trustResultType
); 
1082         certCount 
= certChain
?CFArrayGetCount(certChain
):0; 
1083         debugShowCertEvidenceInfo(certCount
, statusChain
); 
1085         rx 
= SecTrustCopyExtendedResult(trustRef
, &extendedResult
); 
1086         dtprintf("[%s] SecTrustCopyExtendedResult: result: %d\n", __FUNCTION__
, rx
); 
1089             debugShowExtendedTrustResult(jx
, extendedResult
); 
1090             CFRelease(extendedResult
); 
1094             CFRelease (trustRef
); 
1103 static OSStatus 
impExpImportCertUnCommon( 
1104         const CSSM_DATA         
*cdata
, 
1105         SecKeychainRef          importKeychain
, // optional 
1106         CFMutableArrayRef       outArray
)               // optional, append here  
1108     // The only difference between this and impExpImportCertCommon is that we append to outArray 
1109     // before attempting to add to the keychain 
1110         OSStatus status 
= noErr
; 
1111         SecCertificateRef certRef 
= NULL
; 
1113     require_action(cdata
, xit
, status 
= errSecUnsupportedFormat
); 
1115         /* Pass kCFAllocatorNull as bytesDeallocator to assure the bytes aren't freed */ 
1116         CFDataRef data 
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8 
*)cdata
->Data
, (CFIndex
)cdata
->Length
, kCFAllocatorNull
); 
1117     require_action(data
, xit
, status 
= errSecUnsupportedFormat
); 
1119         certRef 
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
); 
1120         CFRelease(data
); /* certRef has its own copy of the data now */ 
1122                 dtprintf("impExpHandleCert error\n"); 
1123                 return errSecUnsupportedFormat
; 
1127                 CFArrayAppendValue(outArray
, certRef
); 
1131                 status 
= SecCertificateAddToKeychain(certRef
, importKeychain
); 
1132                 if (status
!=noErr 
&& status
!=errSecDuplicateItem
) 
1133             { dtprintf("SecCertificateAddToKeychain error: %ld\n", (long)status
); } 
1142 static void saveTSACertificates(CSSM_DATA 
**signingCerts
, CFMutableArrayRef     outArray
) 
1144     SecKeychainRef defaultKeychain 
= NULL
; 
1145     // Don't save certificates in keychain to avoid securityd issues 
1146 //  if (SecKeychainCopyDefault(&defaultKeychain)) 
1147 //     defaultKeychain = NULL; 
1149     unsigned certCount 
= SecCmsArrayCount((void **)signingCerts
); 
1151     for (dex
=0; dex
<certCount
; dex
++) 
1153         OSStatus rx 
= impExpImportCertUnCommon(signingCerts
[dex
], defaultKeychain
, outArray
); 
1154         if (rx
!=noErr 
&& rx
!=errSecDuplicateItem
) 
1155             dtprintf("impExpImportCertCommon failed: %ld\n", (long)rx
); 
1157     if (defaultKeychain
) 
1158         CFRelease(defaultKeychain
); 
1161 static const char *cfabsoluteTimeToString(CFAbsoluteTime abstime
) 
1163     CFGregorianDate greg 
= CFAbsoluteTimeGetGregorianDate(abstime
, NULL
); 
1165     if (19 != snprintf(str
, 20, "%4.4d-%2.2d-%2.2d_%2.2d:%2.2d:%2.2d", 
1166         (int)greg
.year
, greg
.month
, greg
.day
, greg
.hour
, greg
.minute
, (int)greg
.second
)) 
1168     char *data 
= (char *)malloc(20); 
1169     strncpy(data
, str
, 20); 
1173 static OSStatus 
setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo
) 
1175     OSStatus status 
= noErr
; 
1177     if (!signerinfo
->timestampCertList 
|| (CFArrayGetCount(signerinfo
->timestampCertList
) == 0)) 
1178         return SecCmsVSSigningCertNotFound
; 
1180     SecCertificateRef tsaLeaf 
= (SecCertificateRef
)CFArrayGetValueAtIndex(signerinfo
->timestampCertList
, 0); 
1182         CFDataRef certData 
= SecCertificateCopyData(tsaLeaf
); 
1183     SecCertificateRefP certP 
= certData
?SecCertificateCreateWithDataP(kCFAllocatorDefault
, certData
):NULL
; 
1185     require_action(certP
, xit
, status 
= errSecCertificateCannotOperate
); 
1187     signerinfo
->tsaLeafNotBefore 
= SecCertificateNotValidBefore(certP
); /* Start date for Timestamp Authority leaf */ 
1188     signerinfo
->tsaLeafNotAfter 
= SecCertificateNotValidAfter(certP
);   /* Expiration date for Timestamp Authority leaf */ 
1190     const char *nbefore 
= cfabsoluteTimeToString(signerinfo
->tsaLeafNotBefore
); 
1191     const char *nafter 
= cfabsoluteTimeToString(signerinfo
->tsaLeafNotAfter
); 
1192     if (nbefore 
&& nafter
) 
1194         dtprintf("Timestamp Authority leaf valid from %s to %s\n", nbefore
, nafter
); 
1195         free((void *)nbefore
);free((void *)nafter
); 
1200                         status = errSecCertificateNotValidYet; 
1202                         status = errSecCertificateExpired; 
1206         if (certData
) CFRelease(certData
); 
1207         if (certP
) CFRelease(certP
); 
1213     From RFC 3161: Time-Stamp Protocol (TSP),August 2001, APPENDIX B: 
1215     B) The validity of the digital signature may then be verified in the 
1218         1)  The time-stamp token itself MUST be verified and it MUST be 
1219             verified that it applies to the signature of the signer. 
1221         2)  The date/time indicated by the TSA in the TimeStampToken 
1224         3)  The certificate used by the signer MUST be identified and 
1227         4)  The date/time indicated by the TSA MUST be within the 
1228             validity period of the signer's certificate. 
1230         5)  The revocation information about that certificate, at the 
1231             date/time of the Time-Stamping operation, MUST be retrieved. 
1233         6)  Should the certificate be revoked, then the date/time of 
1234             revocation shall be later than the date/time indicated by 
1237     If all these conditions are successful, then the digital signature 
1238     shall be declared as valid. 
1242 OSStatus 
decodeTimeStampToken(SecCmsSignerInfoRef signerinfo
, CSSM_DATA_PTR inData
, CSSM_DATA_PTR encDigest
, uint64_t expectedNonce
) 
1245         We update signerinfo with timestamp and tsa certificate chain. 
1246         encDigest is the original signed blob, which we must hash and compare. 
1247         inData comes from the unAuthAttr section of the CMS message 
1249         These are set in signerinfo as side effects: 
1254     SecCmsDecoderRef        decoderContext 
= NULL
; 
1255     SecCmsMessageRef        cmsMessage 
= NULL
; 
1256     SecCmsContentInfoRef    contentInfo
; 
1257     SecCmsSignedDataRef     signedData
; 
1258     SECOidTag               contentTypeTag
; 
1259     int                     contentLevelCount
; 
1261     OSStatus                result 
= errSecUnknownFormat
; 
1262     CSSM_DATA               
**signingCerts 
= NULL
; 
1264     OSStatus currentPORTErr 
= PORT_GetError(); 
1265     dtprintf("decodeTimeStampToken top: PORT_GetError() %d -----\n", (int)currentPORTErr
); 
1268     /* decode the message */ 
1269     require_noerr(result 
= SecCmsDecoderCreate (NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &decoderContext
), xit
); 
1270     result 
= SecCmsDecoderUpdate(decoderContext
, inData
->Data
, inData
->Length
); 
1273         result 
= errSecTimestampInvalid
; 
1274         SecCmsDecoderDestroy(decoderContext
); 
1278     require_noerr(result 
= SecCmsDecoderFinish(decoderContext
, &cmsMessage
), xit
); 
1280     // process the results 
1281     contentLevelCount 
= SecCmsMessageContentLevelCount(cmsMessage
); 
1284         printDataAsHex("encDigest",encDigest
, 0); 
1286     for (ix 
= 0; ix 
< contentLevelCount
; ++ix
) 
1288         dtprintf("\n----- Content Level %d -----\n", ix
); 
1289         // get content information 
1290         contentInfo 
= SecCmsMessageContentLevel (cmsMessage
, ix
); 
1291         contentTypeTag 
= SecCmsContentInfoGetContentTypeTag (contentInfo
); 
1293         // After 2nd round, contentInfo.content.data is the TSTInfo 
1295         debugShowContentTypeOID(contentInfo
); 
1297         switch (contentTypeTag
) 
1299         case SEC_OID_PKCS7_SIGNED_DATA
: 
1301             require((signedData 
= (SecCmsSignedDataRef
)SecCmsContentInfoGetContent(contentInfo
)) != NULL
, xit
); 
1303             debugShowSignerInfo(signedData
); 
1305             SECAlgorithmID 
**digestAlgorithms 
= SecCmsSignedDataGetDigestAlgs(signedData
); 
1306             unsigned digestAlgCount 
= SecCmsArrayCount((void **)digestAlgorithms
); 
1307             dtprintf("digestAlgCount: %d\n", digestAlgCount
); 
1308             if (signedData
->digests
) 
1312                 for (jx
=0;jx 
< digestAlgCount
;jx
++) 
1314                     sprintf(buffer
, " digest[%u]", jx
); 
1315                     printDataAsHex(buffer
,signedData
->digests
[jx
], 0); 
1320                 dtprintf("No digests\n"); 
1321                 CSSM_DATA_PTR innerContent 
= SecCmsContentInfoGetInnerContent(contentInfo
); 
1324                     dtprintf("inner content length: %ld\n", innerContent
->Length
); 
1325                     SecAsn1TSAMessageImprint fakeMessageImprint 
= {{{0}},}; 
1326                     OSStatus status 
= createTSAMessageImprint(signedData
, innerContent
, &fakeMessageImprint
); 
1328                         {    dtprintf("createTSAMessageImprint status: %d\n", (int)status
); } 
1329                     printDataAsHex("inner content hash",&fakeMessageImprint
.hashedMessage
, 0); 
1330                     CSSM_DATA_PTR digestdata 
= &fakeMessageImprint
.hashedMessage
; 
1331                     CSSM_DATA_PTR digests
[2] = {digestdata
, NULL
}; 
1332                     SecCmsSignedDataSetDigests(signedData
, digestAlgorithms
, (CSSM_DATA_PTR 
*)&digests
); 
1335                     dtprintf("no inner content\n"); 
1339                 Import the certificates. We leave this as a warning, since 
1340                 there are configurations where the certificates are not returned. 
1342             signingCerts 
= SecCmsSignedDataGetCertificateList(signedData
); 
1343             if (signingCerts 
== NULL
) 
1344             {    dtprintf("SecCmsSignedDataGetCertificateList returned NULL\n"); } 
1347                 if (!signerinfo
->timestampCertList
) 
1348                     signerinfo
->timestampCertList 
= CFArrayCreateMutable(kCFAllocatorDefault
, 10, &kCFTypeArrayCallBacks
); 
1349                 saveTSACertificates(signingCerts
, signerinfo
->timestampCertList
); 
1350                 require_noerr(result 
= setTSALeafValidityDates(signerinfo
), xit
); 
1351                 debugSaveCertificates(signingCerts
); 
1354             int numberOfSigners 
= SecCmsSignedDataSignerInfoCount (signedData
); 
1356             result 
= verifySigners(signedData
, numberOfSigners
); 
1358                 dtprintf("verifySigners failed: %ld\n", (long)result
);   // warning 
1361             if (result
)     // remap to SecCmsVSTimestampNotTrusted ? 
1366         case SEC_OID_PKCS9_SIGNING_CERTIFICATE
: 
1368             dtprintf("SEC_OID_PKCS9_SIGNING_CERTIFICATE seen\n"); 
1372         case SEC_OID_PKCS9_ID_CT_TSTInfo
: 
1374             SecAsn1TSATSTInfo tstInfo 
= {{0},}; 
1375             result 
= verifyTSTInfo(contentInfo
->rawContent
, signingCerts
, &tstInfo
, &signerinfo
->timestampTime
, expectedNonce
); 
1376             if (signerinfo
->timestampTime
) 
1378                 const char *tstamp 
= cfabsoluteTimeToString(signerinfo
->timestampTime
); 
1381                     dtprintf("Timestamp Authority timestamp: %s\n", tstamp
); 
1382                     free((void *)tstamp
); 
1389             char *otherContent 
= (char *)SecCmsContentInfoGetContent (contentInfo
); 
1390             dtprintf("otherContent : %p\n", otherContent
); 
1394             dtprintf("ContentTypeTag : %x\n", contentTypeTag
); 
1400                 SecCmsMessageDestroy(cmsMessage
);