]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_smime/lib/tsaSupport.c
Security-58286.260.20.tar.gz
[apple/security.git] / OSX / libsecurity_smime / lib / tsaSupport.c
index 46c3eee37a5ffcc54447a4d0fa2ae8f5b09b4364..ac6c155f5b6e94340d3d5653abac464f33fa44ab 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2012-2016 Apple Inc. All Rights Reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
 #include "tsaTemplates.h"
 #include <Security/SecAsn1Coder.h>
 #include <AssertMacros.h>
+#include <Security/SecBasePriv.h>
 #include <Security/SecPolicy.h>
 #include <Security/SecTrustPriv.h>
 #include <Security/SecImportExport.h>
 #include <Security/SecCertificatePriv.h>
-#include <security_keychain/SecCertificateP.h>
-#include <security_keychain/SecCertificatePrivP.h>
 #include <utilities/SecCFRelease.h>
+#include <utilities/SecDispatchRelease.h>
+#include <utilities/debugging.h>
 
 #include "tsaSupport.h"
 #include "tsaSupportPriv.h"
 #include "tsaTemplates.h"
 #include "cmslocal.h"
+#include "cert.h"
 
 #include "secoid.h"
 #include "secitem.h"
@@ -90,16 +92,16 @@ extern OSStatus impExpImportCertCommon(
                     fprintf(stderr, "%s " fmt, buf, ## __VA_ARGS__); \
                     syslog(LOG_ERR, " " fmt, ## __VA_ARGS__); \
                     } } while (0)
-    #define tsa_secdebug(scope, format...) \
+    #define tsa_secinfo(scope, format...) \
     { \
         syslog(LOG_NOTICE, format); \
-        secdebug(scope, format); \
+        secinfo(scope, format); \
         printf(format); \
     }
 #else
-    #define tsaDebug(args...)                  tsa_secdebug("tsa", ## args)
-#define tsa_secdebug(scope, format...) \
-        secdebug(scope, format)
+    #define tsaDebug(args...)                  tsa_secinfo("tsa", ## args)
+#define tsa_secinfo(scope, format...) \
+        secinfo(scope, format)
 #endif
 
 #ifndef NDEBUG
@@ -165,8 +167,7 @@ static void printDataAsHex(const char *title, const CSSM_DATA *d, unsigned maxTo
     int offset, sz = 0;
     const int wrapwid = 24;     // large enough so SHA-1 hashes fit on one line...
 
-    if ((maxToPrint != 0) && (len > maxToPrint))
-    {
+    if ((maxToPrint != 0) && (len > maxToPrint)) {
         len = maxToPrint;
         more = true;
     }
@@ -178,22 +179,19 @@ static void printDataAsHex(const char *title, const CSSM_DATA *d, unsigned maxTo
     dtprintf("%s", buffer);
     offset = 0;
 
-    for (i=0; (i < len) && (offset+3 < bufferSize); i++, offset += sz)
-    {
+    for (i=0; (i < len) && (offset+3 < bufferSize); i++, offset += sz) {
         sz = sprintf(buffer + offset, " %02x", (unsigned int)cp[i] & 0xff);
-        if ((i % wrapwid) == (wrapwid-1))
-        {
-            dtprintf("%s", buffer);
+        if ((i % wrapwid) == (wrapwid-1)) {
+            dtprintf("%s\n", buffer);
             offset = 0;
             sz = 0;
         }
     }
 
     sz=sprintf(buffer + offset, more?" ...\n":"\n");
-        offset += sz;
+    offset += sz;
     buffer[offset+1]=0;
 
-//    fprintf(stderr, "%s", buffer);
     dtprintf("%s", buffer);
 
     free(buffer);
@@ -207,8 +205,9 @@ int tsaWriteFileX(const char *fileName, const unsigned char *bytes, size_t numBy
     int fd;
 
     fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
-    if (fd <= 0)
+    if (fd == -1) {
         return errno;
+    }
 
     rtn = (int)write(fd, bytes, numBytes);
     if(rtn != (int)numBytes)
@@ -524,9 +523,9 @@ static OSStatus sendTSARequestWithXPC(const unsigned char *tsaReq, size_t tsaReq
     xpc_connection_send_message_with_reply(con, message, xpc_queue, ^(xpc_object_t reply)
     {
         tsaDebug("xpc_connection_send_message_with_reply handler called back\n");
-        dispatch_retain(waitSemaphore);
+        dispatch_retain_safe(waitSemaphore);
 
-    xpc_type_t xtype = xpc_get_type(reply);
+        xpc_type_t xtype = xpc_get_type(reply);
         if (XPC_TYPE_ERROR == xtype)
             {   tsaDebug("message error: %s\n", xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION)); }
         else if (XPC_TYPE_CONNECTION == xtype)
@@ -595,15 +594,13 @@ static OSStatus sendTSARequestWithXPC(const unsigned char *tsaReq, size_t tsaReq
     }
     else
         { tsaDebug("unexpected message reply type %p\n", xtype); }
-
-        dispatch_semaphore_signal(waitSemaphore);
-        dispatch_release(waitSemaphore);
+        if (waitSemaphore) { dispatch_semaphore_signal(waitSemaphore); }
+        dispatch_release_null(waitSemaphore);
     });
-
     { tsaDebug("waiting up to %d seconds for response from XPC\n", timeoutInSeconds); }
-       dispatch_semaphore_wait(waitSemaphore, finishTime);
+    if (waitSemaphore) { dispatch_semaphore_wait(waitSemaphore, finishTime); }
 
-       dispatch_release(waitSemaphore);
+    dispatch_release_null(waitSemaphore);
     xpc_release(tsaReqData);
     xpc_release(message);
 
@@ -780,13 +777,22 @@ OSStatus SecCmsTSADefaultCallback(CFTypeRef context, void *messageImprintV, uint
 #endif
     }
 
-    CFStringRef url = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSAContextKeyURL);
+    CFTypeRef url = CFDictionaryGetValue((CFDictionaryRef)context, kTSAContextKeyURL);
     if (!url)
     {
         tsaDebug("[TSA] missing URL for TSA (key: %s)\n", "kTSAContextKeyURL");
         goto xit;
     }
 
+    CFStringRef urlStr = NULL;
+    if (CFURLGetTypeID() == CFGetTypeID(url)) {
+        urlStr = CFURLGetString(url);
+    } else {
+        require_quiet(CFStringGetTypeID() == CFGetTypeID(url), xit);
+        urlStr = url;
+    }
+    require_quiet(urlStr, xit);
+
     /*
         If debugging, look at special values in the context to mess things up
     */
@@ -799,9 +805,9 @@ OSStatus SecCmsTSADefaultCallback(CFTypeRef context, void *messageImprintV, uint
     }
 
     // need to extract into buffer
-    CFIndex length = CFStringGetLength(url);        // in 16-bit character units
+    CFIndex length = CFStringGetLength(urlStr);        // in 16-bit character units
     tsaURL = malloc(6 * length + 1);                // pessimistic
-    if (!CFStringGetCString(url, (char *)tsaURL, 6 * length + 1, kCFStringEncodingUTF8))
+    if (!CFStringGetCString(urlStr, (char *)tsaURL, 6 * length + 1, kCFStringEncodingUTF8))
         goto xit;
 
     tsaDebug("[TSA] URL for timestamp server: %s\n", tsaURL);
@@ -870,36 +876,29 @@ xit:
     return result;
 }
 
-static OSStatus SecTSAValidateTimestamp(const SecAsn1TSATSTInfo *tstInfo, CSSM_DATA **signingCerts, CFAbsoluteTime *timestampTime)
+static OSStatus SecTSAValidateTimestamp(const SecAsn1TSATSTInfo *tstInfo, SecCertificateRef signerCert, CFAbsoluteTime *timestampTime)
 {
     // See <rdar://problem/11077708> Properly handle revocation information of timestamping certificate
     OSStatus result = paramErr;
     CFAbsoluteTime genTime = 0;
     char timeStr[32] = {0,};
-    SecCertificateRef signingCertificate = NULL;
 
-    require(tstInfo && signingCerts && (tstInfo->genTime.Length < 16), xit);
-
-    // Find the leaf signingCert
-    require_noerr(result = SecCertificateCreateFromData(*signingCerts,
-        CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &signingCertificate), xit);
+    require(tstInfo && signerCert && (tstInfo->genTime.Length < 16), xit);;
 
     memcpy(timeStr, tstInfo->genTime.Data, tstInfo->genTime.Length);
     timeStr[tstInfo->genTime.Length] = 0;
     require_noerr(convertGeneralizedTimeToCFAbsoluteTime(timeStr, &genTime), xit);
-    if (SecCertificateIsValidX(signingCertificate, genTime)) // iOS?
+    if (SecCertificateIsValidX(signerCert, genTime)) // iOS?
         result = noErr;
     else
         result = errSecTimestampInvalid;
     if (timestampTime)
         *timestampTime = genTime;
 xit:
-    if (signingCertificate)
-        CFReleaseNull(signingCertificate);
     return result;
 }
 
-static OSStatus verifyTSTInfo(const CSSM_DATA_PTR content, CSSM_DATA **signingCerts, SecAsn1TSATSTInfo *tstInfo, CFAbsoluteTime *timestampTime, uint64_t expectedNonce)
+static OSStatus verifyTSTInfo(const CSSM_DATA_PTR content, SecCmsSignerInfoRef signerinfo, SecAsn1TSATSTInfo *tstInfo, CFAbsoluteTime *timestampTime, uint64_t expectedNonce, CSSM_DATA_PTR encDigest)
 {
     OSStatus status = paramErr;
     SecAsn1CoderRef coder = NULL;
@@ -907,6 +906,9 @@ static OSStatus verifyTSTInfo(const CSSM_DATA_PTR content, CSSM_DATA **signingCe
     if (!tstInfo)
         return SECFailure;
 
+    SecCertificateRef signerCert = SecCmsSignerInfoGetTimestampSigningCert(signerinfo);
+    SecAsn1TSAMessageImprint expectedMessageImprint;
+
     require_noerr(SecAsn1CoderCreate(&coder), xit);
     require_noerr(SecAsn1Decode(coder, content->Data, content->Length,
                kSecAsn1TSATSTInfoTemplate, tstInfo), xit);
@@ -921,9 +923,18 @@ static OSStatus verifyTSTInfo(const CSSM_DATA_PTR content, CSSM_DATA **signingCe
         require_action(expectedNonce==nonce, xit, status = errSecTimestampRejection);
     }
 
-    status = SecTSAValidateTimestamp(tstInfo, signingCerts, timestampTime);
+    // Check the times in the timestamp
+    require_noerr(status = SecTSAValidateTimestamp(tstInfo, signerCert, timestampTime), xit);
     dtprintf("SecTSAValidateTimestamp result: %ld\n", (long)status);
 
+    // Check the message imprint against the encDigest from the signerInfo containing this timestamp
+    SECOidTag hashAlg = SECOID_GetAlgorithmTag(&tstInfo->messageImprint.hashAlgorithm);
+    require_action(hashAlg == SEC_OID_SHA256 || hashAlg == SEC_OID_SHA1, xit, status = errSecInvalidDigestAlgorithm);
+    require_noerr(status = createTSAMessageImprint(signerinfo, &tstInfo->messageImprint.hashAlgorithm,
+                                                   encDigest, &expectedMessageImprint), xit);
+    require_action(CERT_CompareCssmData(&expectedMessageImprint.hashedMessage, &tstInfo->messageImprint.hashedMessage), xit,
+                   status = errSecTimestampInvalid; secerror("Timestamp MessageImprint did not match the signature's hash"));
+
 xit:
     if (coder)
         SecAsn1CoderRelease(coder);
@@ -1006,7 +1017,6 @@ static const char *trustResultTypeString(SecTrustResultType trustResultType)
     case kSecTrustResultUnspecified:                return "TrustResultUnspecified";
     case kSecTrustResultDeny:                       return "TrustResultDeny";   // user reject
     case kSecTrustResultInvalid:                    return "TrustResultInvalid";
-    case kSecTrustResultConfirm:                    return "TrustResultConfirm";
     case kSecTrustResultRecoverableTrustFailure:    return "TrustResultRecoverableTrustFailure";
     case kSecTrustResultFatalTrustFailure:          return "TrustResultUnspecified";
     case kSecTrustResultOtherError:                 return "TrustResultOtherError";
@@ -1064,7 +1074,6 @@ static OSStatus verifySigners(SecCmsSignedDataRef signedData, int numberOfSigner
                        assert(false);                          // should never happen
                        result = errSecTimestampNotTrusted;     // SecCmsVSTimestampNotTrusted ?
             break;
-        case kSecTrustResultConfirm:
         case kSecTrustResultRecoverableTrustFailure:
         case kSecTrustResultFatalTrustFailure:
         case kSecTrustResultOtherError:
@@ -1183,14 +1192,11 @@ static const char *cfabsoluteTimeToString(CFAbsoluteTime abstime)
 
 static OSStatus setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo)
 {
-    OSStatus status = noErr;
+    SecCertificateRef tsaLeaf = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL);
 
-    if (!signerinfo->timestampCertList || (CFArrayGetCount(signerinfo->timestampCertList) == 0))
+    if (!tsaLeaf)
         return SecCmsVSSigningCertNotFound;
 
-    SecCertificateRef tsaLeaf = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->timestampCertList, 0);
-    require_action(tsaLeaf, xit, status = errSecCertificateCannotOperate);
-
     signerinfo->tsaLeafNotBefore = SecCertificateNotValidBefore(tsaLeaf); /* Start date for Timestamp Authority leaf */
     signerinfo->tsaLeafNotAfter = SecCertificateNotValidAfter(tsaLeaf);   /* Expiration date for Timestamp Authority leaf */
 
@@ -1202,15 +1208,7 @@ static OSStatus setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo)
         free((void *)nbefore);free((void *)nafter);
     }
 
-/*
-               if(at < nb)
-                       status = errSecCertificateNotValidYet;
-               else if (at > na)
-                       status = errSecCertificateExpired;
-*/
-
-xit:
-    return status;
+    return errSecSuccess;
 }
 
 /*
@@ -1251,14 +1249,15 @@ OSStatus decodeTimeStampToken(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR inDa
 OSStatus decodeTimeStampTokenWithPolicy(SecCmsSignerInfoRef signerinfo, CFTypeRef timeStampPolicy, CSSM_DATA_PTR inData, CSSM_DATA_PTR encDigest, uint64_t expectedNonce)
 {
     /*
-        We update signerinfo with timestamp and tsa certificate chain.
-        encDigest is the original signed blob, which we must hash and compare.
-        inData comes from the unAuthAttr section of the CMS message
+     We update signerinfo with timestamp and tsa certificate chain.
+     encDigest is the original signed blob, which we must hash and compare.
+     inData comes from the unAuthAttr section of the CMS message
 
-        These are set in signerinfo as side effects:
-            timestampTime -
-            timestampCertList
-    */
+     These are set in signerinfo as side effects:
+        timestampTime
+        timestampCertList
+        timestampCert
+     */
 
     SecCmsDecoderRef        decoderContext = NULL;
     SecCmsMessageRef        cmsMessage = NULL;
@@ -1276,23 +1275,22 @@ OSStatus decodeTimeStampTokenWithPolicy(SecCmsSignerInfoRef signerinfo, CFTypeRe
     /* decode the message */
     require_noerr(result = SecCmsDecoderCreate (NULL, NULL, NULL, NULL, NULL, NULL, NULL, &decoderContext), xit);
     result = SecCmsDecoderUpdate(decoderContext, inData->Data, inData->Length);
-       if (result)
-    {
+    if (result) {
         result = errSecTimestampInvalid;
         SecCmsDecoderDestroy(decoderContext);
         goto xit;
-       }
+    }
 
     require_noerr(result = SecCmsDecoderFinish(decoderContext, &cmsMessage), xit);
 
     // process the results
     contentLevelCount = SecCmsMessageContentLevelCount(cmsMessage);
 
-    if (encDigest)
+    if (encDigest) {
         printDataAsHex("encDigest",encDigest, 0);
+    }
 
-    for (ix = 0; ix < contentLevelCount; ++ix)
-    {
+    for (ix = 0; ix < contentLevelCount; ++ix) {
         dtprintf("\n----- Content Level %d -----\n", ix);
         // get content information
         contentInfo = SecCmsMessageContentLevel (cmsMessage, ix);
@@ -1302,109 +1300,102 @@ OSStatus decodeTimeStampTokenWithPolicy(SecCmsSignerInfoRef signerinfo, CFTypeRe
 
         debugShowContentTypeOID(contentInfo);
 
-        switch (contentTypeTag)
-        {
-        case SEC_OID_PKCS7_SIGNED_DATA:
-        {
-            require((signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(contentInfo)) != NULL, xit);
-
-            debugShowSignerInfo(signedData);
-
-            SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(signedData);
-            unsigned digestAlgCount = SecCmsArrayCount((void **)digestAlgorithms);
-            dtprintf("digestAlgCount: %d\n", digestAlgCount);
-            if (signedData->digests)
-            {
-                int jx;
-                char buffer[128];
-                for (jx=0;jx < digestAlgCount;jx++)
-                {
-                    sprintf(buffer, " digest[%u]", jx);
-                    printDataAsHex(buffer,signedData->digests[jx], 0);
-                }
-            }
-            else
-            {
-                dtprintf("No digests\n");
-                CSSM_DATA_PTR innerContent = SecCmsContentInfoGetInnerContent(contentInfo);
-                if (innerContent)
-                {
-                    dtprintf("inner content length: %ld\n", innerContent->Length);
-                    SecAsn1TSAMessageImprint fakeMessageImprint = {{{0}},};
-                    OSStatus status = createTSAMessageImprint(signedData, innerContent, &fakeMessageImprint);
-                    if (status)
-                        {    dtprintf("createTSAMessageImprint status: %d\n", (int)status); }
-                    printDataAsHex("inner content hash",&fakeMessageImprint.hashedMessage, 0);
-                    CSSM_DATA_PTR digestdata = &fakeMessageImprint.hashedMessage;
-                    CSSM_DATA_PTR digests[2] = {digestdata, NULL};
-                    SecCmsSignedDataSetDigests(signedData, digestAlgorithms, (CSSM_DATA_PTR *)&digests);
+        switch (contentTypeTag) {
+            case SEC_OID_PKCS7_SIGNED_DATA: {
+                require((signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(contentInfo)) != NULL, xit);
+
+                debugShowSignerInfo(signedData);
+
+                SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(signedData);
+                unsigned digestAlgCount = SecCmsArrayCount((void **)digestAlgorithms);
+                dtprintf("digestAlgCount: %d\n", digestAlgCount);
+                if (signedData->digests) {
+                    int jx;
+                    char buffer[128];
+                    for (jx=0;jx < digestAlgCount;jx++) {
+                        sprintf(buffer, " digest[%u]", jx);
+                        printDataAsHex(buffer,signedData->digests[jx], 0);
+                    }
+                } else {
+                    dtprintf("digests not yet computed\n");
+                    CSSM_DATA_PTR innerContent = SecCmsContentInfoGetInnerContent(contentInfo);
+                    if (innerContent)
+                    {
+                        dtprintf("inner content length: %ld\n", innerContent->Length);
+                        SecAsn1TSAMessageImprint fakeMessageImprint = {{{0}},};
+                        SecCmsSignerInfoRef tsaSigner = SecCmsSignedDataGetSignerInfo(signedData, 0);
+                        OSStatus status = createTSAMessageImprint(tsaSigner, &tsaSigner->digestAlg, innerContent, &fakeMessageImprint);
+                        require_noerr_action(status, xit, dtprintf("createTSAMessageImprint status: %d\n", (int)status); result = status);
+                        printDataAsHex("inner content hash",&fakeMessageImprint.hashedMessage, 0);
+                        CSSM_DATA_PTR digestdata = &fakeMessageImprint.hashedMessage;
+                        CSSM_DATA_PTR digests[2] = {digestdata, NULL};
+                        status = SecCmsSignedDataSetDigests(signedData, digestAlgorithms, (CSSM_DATA_PTR *)&digests);
+                        require_noerr_action(status, xit, dtprintf("createTSAMessageImprint status: %d\n", (int)status); result = status);
+                    } else {
+                        dtprintf("no inner content\n");
+                    }
                 }
-                else
-                    dtprintf("no inner content\n");
-            }
-
-            /*
-                Import the certificates. We leave this as a warning, since
-                there are configurations where the certificates are not returned.
-            */
-            signingCerts = SecCmsSignedDataGetCertificateList(signedData);
-            if (signingCerts == NULL)
-            {    dtprintf("SecCmsSignedDataGetCertificateList returned NULL\n"); }
-            else
-            {
-                if (!signerinfo->timestampCertList)
-                    signerinfo->timestampCertList = CFArrayCreateMutable(kCFAllocatorDefault, 10, &kCFTypeArrayCallBacks);
-                saveTSACertificates(signingCerts, signerinfo->timestampCertList);
-                require_noerr(result = setTSALeafValidityDates(signerinfo), xit);
-                debugSaveCertificates(signingCerts);
-            }
 
-            int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData);
-
-            result = verifySigners(signedData, numberOfSigners, timeStampPolicy);
-            if (result)
-                dtprintf("verifySigners failed: %ld\n", (long)result);   // warning
+                /*
+                 Import the certificates. We leave this as a warning, since
+                 there are configurations where the certificates are not returned.
+                 */
+                signingCerts = SecCmsSignedDataGetCertificateList(signedData);
+                if (signingCerts == NULL) {
+                    dtprintf("SecCmsSignedDataGetCertificateList returned NULL\n");
+                } else {
+                    if (!signerinfo->timestampCertList) {
+                        signerinfo->timestampCertList = CFArrayCreateMutable(kCFAllocatorDefault, 10, &kCFTypeArrayCallBacks);
+                    }
+                    saveTSACertificates(signingCerts, signerinfo->timestampCertList);
+                    require_noerr(result = setTSALeafValidityDates(signerinfo), xit);
+                    debugSaveCertificates(signingCerts);
+                }
 
+                int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData);
 
-            if (result)     // remap to SecCmsVSTimestampNotTrusted ?
-                goto xit;
+                if (numberOfSigners > 0) {
+                    /* @@@ assume there's only one signer since SecCms can't handle multiple signers anyway */
+                    signerinfo->timestampCert = CFRetainSafe(SecCmsSignerInfoGetSigningCertificate(signedData->signerInfos[0], NULL));
+                }
 
-            break;
-        }
-        case SEC_OID_PKCS9_SIGNING_CERTIFICATE:
-        {
-            dtprintf("SEC_OID_PKCS9_SIGNING_CERTIFICATE seen\n");
-            break;
-        }
+                result = verifySigners(signedData, numberOfSigners, timeStampPolicy);
+                if (result) {
+                    dtprintf("verifySigners failed: %ld\n", (long)result);   // warning
+                    goto xit; // remap to SecCmsVSTimestampNotTrusted ?
+                }
 
-        case SEC_OID_PKCS9_ID_CT_TSTInfo:
-        {
-            SecAsn1TSATSTInfo tstInfo = {{0},};
-            result = verifyTSTInfo(contentInfo->rawContent, signingCerts, &tstInfo, &signerinfo->timestampTime, expectedNonce);
-            if (signerinfo->timestampTime)
-            {
-                const char *tstamp = cfabsoluteTimeToString(signerinfo->timestampTime);
-                if (tstamp)
-                {
-                    dtprintf("Timestamp Authority timestamp: %s\n", tstamp);
-                    free((void *)tstamp);
+                break;
+            }
+            case SEC_OID_PKCS9_SIGNING_CERTIFICATE: {
+                dtprintf("SEC_OID_PKCS9_SIGNING_CERTIFICATE seen\n");
+                break;
+            }
+            case SEC_OID_PKCS9_ID_CT_TSTInfo: {
+                SecAsn1TSATSTInfo tstInfo = {{0},};
+                result = verifyTSTInfo(contentInfo->rawContent, signerinfo, &tstInfo, &signerinfo->timestampTime, expectedNonce, encDigest);
+                if (signerinfo->timestampTime) {
+                    const char *tstamp = cfabsoluteTimeToString(signerinfo->timestampTime);
+                    if (tstamp) {
+                        dtprintf("Timestamp Authority timestamp: %s\n", tstamp);
+                        free((void *)tstamp);
+                    }
                 }
+                break;
             }
-            break;
-        }
-        case SEC_OID_OTHER:
-        {
-            dtprintf("otherContent : %p\n", (char *)SecCmsContentInfoGetContent (contentInfo));
-            break;
-        }
-        default:
-            dtprintf("ContentTypeTag : %x\n", contentTypeTag);
-            break;
+            case SEC_OID_OTHER: {
+                dtprintf("otherContent : %p\n", (char *)SecCmsContentInfoGetContent (contentInfo));
+                break;
+            }
+            default:
+                dtprintf("ContentTypeTag : %x\n", contentTypeTag);
+                break;
         }
     }
 xit:
-       if (cmsMessage)
-               SecCmsMessageDestroy(cmsMessage);
+    if (cmsMessage) {
+        SecCmsMessageDestroy(cmsMessage);
+    }
 
     return result;
 }