1 /** @file
2   RFC3161 Timestamp Countersignature Verification over OpenSSL.
3   The timestamp is generated by a TimeStamping Authority (TSA) and asserts that a
4   publisher's signature existed before the specified time. The timestamp extends
5   the lifetime of the signature when a signing certificate expires or is later
6   revoked.
7 
8 Copyright (c) 2014 - 2017, Intel Corporation. All rights reserved.<BR>
9 SPDX-License-Identifier: BSD-2-Clause-Patent
10 
11 **/
12 
13 #include "InternalCryptLib.h"
14 
15 #include <openssl/asn1.h>
16 #include <openssl/asn1t.h>
17 #include <openssl/x509.h>
18 #include <openssl/x509v3.h>
19 #include <openssl/pkcs7.h>
20 
21 //
22 // OID ASN.1 Value for SPC_RFC3161_OBJID ("1.3.6.1.4.1.311.3.3.1")
23 //
24 UINT8 mSpcRFC3161OidValue[] = {
25   0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x03, 0x03, 0x01
26   };
27 
28 ///
29 /// The messageImprint field SHOULD contain the hash of the datum to be
30 /// time-stamped.  The hash is represented as an OCTET STRING.  Its
31 /// length MUST match the length of the hash value for that algorithm
32 /// (e.g., 20 bytes for SHA-1 or 16 bytes for MD5).
33 ///
34 /// MessageImprint ::= SEQUENCE  {
35 ///   hashAlgorithm                AlgorithmIdentifier,
36 ///   hashedMessage                OCTET STRING  }
37 ///
38 typedef struct {
39   X509_ALGOR         *HashAlgorithm;
40   ASN1_OCTET_STRING  *HashedMessage;
41 } TS_MESSAGE_IMPRINT;
42 
43 //
44 // ASN.1 Functions for TS_MESSAGE_IMPRINT
45 //
46 DECLARE_ASN1_FUNCTIONS (TS_MESSAGE_IMPRINT)
47 ASN1_SEQUENCE (TS_MESSAGE_IMPRINT) = {
48   ASN1_SIMPLE (TS_MESSAGE_IMPRINT, HashAlgorithm, X509_ALGOR),
49   ASN1_SIMPLE (TS_MESSAGE_IMPRINT, HashedMessage, ASN1_OCTET_STRING)
50 } ASN1_SEQUENCE_END (TS_MESSAGE_IMPRINT)
51 IMPLEMENT_ASN1_FUNCTIONS (TS_MESSAGE_IMPRINT)
52 
53 ///
54 /// Accuracy represents the time deviation around the UTC time contained
55 /// in GeneralizedTime of time-stamp token.
56 ///
57 /// Accuracy ::= SEQUENCE {
58 ///       seconds        INTEGER              OPTIONAL,
59 ///       millis     [0] INTEGER  (1..999)    OPTIONAL,
60 ///       micros     [1] INTEGER  (1..999)    OPTIONAL  }
61 ///
62 typedef struct {
63   ASN1_INTEGER  *Seconds;
64   ASN1_INTEGER  *Millis;
65   ASN1_INTEGER  *Micros;
66 } TS_ACCURACY;
67 
68 //
69 // ASN.1 Functions for TS_ACCURACY
70 //
71 DECLARE_ASN1_FUNCTIONS (TS_ACCURACY)
72 ASN1_SEQUENCE (TS_ACCURACY) = {
73   ASN1_OPT     (TS_ACCURACY, Seconds, ASN1_INTEGER),
74   ASN1_IMP_OPT (TS_ACCURACY, Millis,  ASN1_INTEGER, 0),
75   ASN1_IMP_OPT (TS_ACCURACY, Micros,  ASN1_INTEGER, 1)
76 } ASN1_SEQUENCE_END (TS_ACCURACY)
77 IMPLEMENT_ASN1_FUNCTIONS (TS_ACCURACY)
78 
79 ///
80 /// The timestamp token info resulting from a successful timestamp request,
81 /// as defined in RFC 3161.
82 ///
83 ///  TSTInfo ::= SEQUENCE  {
84 ///     version                      INTEGER  { v1(1) },
85 ///     policy                       TSAPolicyId,
86 ///     messageImprint               MessageImprint,
87 ///       -- MUST have the same value as the similar field in
88 ///       -- TimeStampReq
89 ///     serialNumber                 INTEGER,
90 ///       -- Time-Stamping users MUST be ready to accommodate integers
91 ///       -- up to 160 bits.
92 ///     genTime                      GeneralizedTime,
93 ///     accuracy                     Accuracy                 OPTIONAL,
94 ///     ordering                     BOOLEAN             DEFAULT FALSE,
95 ///     nonce                        INTEGER                  OPTIONAL,
96 ///       -- MUST be present if the similar field was present
97 ///       -- in TimeStampReq.  In that case it MUST have the same value.
98 ///     tsa                          [0] GeneralName          OPTIONAL,
99 ///     extensions                   [1] IMPLICIT Extensions   OPTIONAL  }
100 ///
101 typedef struct {
102   ASN1_INTEGER              *Version;
103   ASN1_OBJECT               *Policy;
104   TS_MESSAGE_IMPRINT        *MessageImprint;
105   ASN1_INTEGER              *SerialNumber;
106   ASN1_GENERALIZEDTIME      *GenTime;
107   TS_ACCURACY               *Accuracy;
108   ASN1_BOOLEAN              Ordering;
109   ASN1_INTEGER              *Nonce;
110   GENERAL_NAME              *Tsa;
111   STACK_OF(X509_EXTENSION)  *Extensions;
112 } TS_TST_INFO;
113 
114 //
115 // ASN.1 Functions for TS_TST_INFO
116 //
117 DECLARE_ASN1_FUNCTIONS (TS_TST_INFO)
118 ASN1_SEQUENCE (TS_TST_INFO) = {
119   ASN1_SIMPLE (TS_TST_INFO, Version, ASN1_INTEGER),
120   ASN1_SIMPLE (TS_TST_INFO, Policy, ASN1_OBJECT),
121   ASN1_SIMPLE (TS_TST_INFO, MessageImprint, TS_MESSAGE_IMPRINT),
122   ASN1_SIMPLE (TS_TST_INFO, SerialNumber, ASN1_INTEGER),
123   ASN1_SIMPLE (TS_TST_INFO, GenTime, ASN1_GENERALIZEDTIME),
124   ASN1_OPT    (TS_TST_INFO, Accuracy, TS_ACCURACY),
125   ASN1_OPT    (TS_TST_INFO, Ordering, ASN1_FBOOLEAN),
126   ASN1_OPT    (TS_TST_INFO, Nonce, ASN1_INTEGER),
127   ASN1_EXP_OPT(TS_TST_INFO, Tsa, GENERAL_NAME, 0),
128   ASN1_IMP_SEQUENCE_OF_OPT (TS_TST_INFO, Extensions, X509_EXTENSION, 1)
129 } ASN1_SEQUENCE_END (TS_TST_INFO)
130 IMPLEMENT_ASN1_FUNCTIONS (TS_TST_INFO)
131 
132 
133 /**
134   Convert ASN.1 GeneralizedTime to EFI Time.
135 
136   @param[in]  Asn1Time         Pointer to the ASN.1 GeneralizedTime to be converted.
137   @param[out] SigningTime      Return the corresponding EFI Time.
138 
139   @retval  TRUE   The time conversion succeeds.
140   @retval  FALSE  Invalid parameters.
141 
142 **/
143 BOOLEAN
144 EFIAPI
145 ConvertAsn1TimeToEfiTime (
146   IN  ASN1_TIME  *Asn1Time,
147   OUT EFI_TIME   *EfiTime
148   )
149 {
150   CONST CHAR8  *Str;
151   UINTN        Index;
152 
153   if ((Asn1Time == NULL) || (EfiTime == NULL)) {
154     return FALSE;
155   }
156 
157   Str = (CONST CHAR8*)Asn1Time->data;
158   SetMem (EfiTime, 0, sizeof (EFI_TIME));
159 
160   Index = 0;
161   if (Asn1Time->type == V_ASN1_UTCTIME) {               /* two digit year */
162     EfiTime->Year  = (Str[Index++] - '0') * 10;
163     EfiTime->Year += (Str[Index++] - '0');
164     if (EfiTime->Year < 70) {
165       EfiTime->Year += 100;
166     }
167   } else if (Asn1Time->type == V_ASN1_GENERALIZEDTIME) { /* four digit year */
168     EfiTime->Year  = (Str[Index++] - '0') * 1000;
169     EfiTime->Year += (Str[Index++] - '0') * 100;
170     EfiTime->Year += (Str[Index++] - '0') * 10;
171     EfiTime->Year += (Str[Index++] - '0');
172     if ((EfiTime->Year < 1900) || (EfiTime->Year > 9999)) {
173       return FALSE;
174     }
175   }
176 
177   EfiTime->Month   = (Str[Index++] - '0') * 10;
178   EfiTime->Month  += (Str[Index++] - '0');
179   if ((EfiTime->Month < 1) || (EfiTime->Month > 12)) {
180     return FALSE;
181   }
182 
183   EfiTime->Day     = (Str[Index++] - '0') * 10;
184   EfiTime->Day    += (Str[Index++] - '0');
185   if ((EfiTime->Day < 1) || (EfiTime->Day > 31)) {
186     return FALSE;
187   }
188 
189   EfiTime->Hour    = (Str[Index++] - '0') * 10;
190   EfiTime->Hour   += (Str[Index++] - '0');
191   if (EfiTime->Hour > 23) {
192     return FALSE;
193   }
194 
195   EfiTime->Minute  = (Str[Index++] - '0') * 10;
196   EfiTime->Minute += (Str[Index++] - '0');
197   if (EfiTime->Minute > 59) {
198     return FALSE;
199   }
200 
201   EfiTime->Second  = (Str[Index++] - '0') * 10;
202   EfiTime->Second += (Str[Index++] - '0');
203   if (EfiTime->Second > 59) {
204     return FALSE;
205   }
206 
207   /* Note: we did not adjust the time based on time zone information */
208 
209   return TRUE;
210 }
211 
212 /**
213 
214   Check the validity of TimeStamp Token Information.
215 
216   @param[in]  TstInfo          Pointer to the TS_TST_INFO structure.
217   @param[in]  TimestampedData  Pointer to the data to be time-stamped.
218   @param[in]  DataSize         Size of timestamped data in bytes.
219 
220   @retval  TRUE   The TimeStamp Token Information is valid.
221   @retval  FALSE  Invalid TimeStamp Token Information.
222 
223 **/
224 BOOLEAN
225 EFIAPI
CheckTSTInfo(IN CONST TS_TST_INFO * TstInfo,IN CONST UINT8 * TimestampedData,IN UINTN DataSize)226 CheckTSTInfo (
227   IN  CONST TS_TST_INFO  *TstInfo,
228   IN  CONST UINT8        *TimestampedData,
229   IN  UINTN              DataSize
230   )
231 {
232   BOOLEAN             Status;
233   TS_MESSAGE_IMPRINT  *Imprint;
234   X509_ALGOR          *HashAlgo;
235   CONST EVP_MD        *Md;
236   EVP_MD_CTX          *MdCtx;
237   UINTN               MdSize;
238   UINT8               *HashedMsg;
239 
240   //
241   // Initialization
242   //
243   Status    = FALSE;
244   HashAlgo  = NULL;
245   HashedMsg = NULL;
246   MdCtx     = NULL;
247 
248   //
249   // -- Check version number of Timestamp:
250   //   The version field (currently v1) describes the version of the time-stamp token.
251   //   Conforming time-stamping servers MUST be able to provide version 1 time-stamp tokens.
252   //
253   if ((ASN1_INTEGER_get (TstInfo->Version)) != 1) {
254     return FALSE;
255   }
256 
257   //
258   // -- Check Policies
259   //   The policy field MUST indicate the TSA's policy under which the response was produced.
260   //
261   if (TstInfo->Policy == NULL) {
262     /// NOTE: Need to check if the requested and returned policies.
263     ///       We have no information about the Requested TSA Policy.
264     return FALSE;
265   }
266 
267   //
268   // -- Compute & Check Message Imprint
269   //
270   Imprint  = TstInfo->MessageImprint;
271   HashAlgo = X509_ALGOR_dup (Imprint->HashAlgorithm);
272 
273   Md = EVP_get_digestbyobj (HashAlgo->algorithm);
274   if (Md == NULL) {
275     goto _Exit;
276   }
277 
278   MdSize = EVP_MD_size (Md);
279   HashedMsg = AllocateZeroPool (MdSize);
280   if (HashedMsg == NULL) {
281     goto _Exit;
282   }
283   MdCtx = EVP_MD_CTX_new ();
284   if (MdCtx == NULL) {
285     goto _Exit;
286   }
287   if ((EVP_DigestInit_ex (MdCtx, Md, NULL) != 1) ||
288       (EVP_DigestUpdate (MdCtx, TimestampedData, DataSize) != 1) ||
289       (EVP_DigestFinal (MdCtx, HashedMsg, NULL) != 1)) {
290     goto _Exit;
291   }
292   if ((MdSize == (UINTN)ASN1_STRING_length (Imprint->HashedMessage)) &&
293       (CompareMem (HashedMsg, ASN1_STRING_get0_data (Imprint->HashedMessage), MdSize) != 0)) {
294     goto _Exit;
295   }
296 
297   //
298   // -- Check Nonces
299   //
300   if (TstInfo->Nonce != NULL) {
301     //
302     // Nonces is optional, No error if no nonce is returned;
303     //
304   }
305 
306   //
307   // -- Check if the TSA name and signer certificate is matched.
308   //
309   if (TstInfo->Tsa != NULL) {
310     //
311     //  Ignored the optional Tsa field checking.
312     //
313   }
314 
315   Status = TRUE;
316 
317 _Exit:
318   X509_ALGOR_free (HashAlgo);
319   EVP_MD_CTX_free (MdCtx);
320   if (HashedMsg != NULL) {
321     FreePool (HashedMsg);
322   }
323 
324   return Status;
325 }
326 
327 /**
328   Verifies the validity of a TimeStamp Token as described in RFC 3161 ("Internet
329   X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)").
330 
331   If TSToken is NULL, then return FALSE.
332   If TimestampedData is NULL, then return FALSE.
333 
334   @param[in]  TSToken          Pointer to the RFC3161 TimeStamp Token, which is generated
335                                by a TSA and located in the software publisher's SignerInfo
336                                structure.
337   @param[in]  TokenSize        Size of the TimeStamp Token in bytes.
338   @param[in]  TsaCert          Pointer to a trusted/root TSA certificate encoded in DER.
339   @param[in]  CertSize         Size of the trusted TSA certificate in bytes.
340   @param[in]  TimestampedData  Pointer to the data to be time-stamped.
341   @param[in]  DataSize         Size of timestamped data in bytes.
342   @param[out] SigningTime      Return the time of timestamp generation time if the timestamp
343                                signature is valid.
344 
345   @retval  TRUE   The specified timestamp token is valid.
346   @retval  FALSE  Invalid timestamp token.
347 
348 **/
349 BOOLEAN
350 EFIAPI
TimestampTokenVerify(IN CONST UINT8 * TSToken,IN UINTN TokenSize,IN CONST UINT8 * TsaCert,IN UINTN CertSize,IN CONST UINT8 * TimestampedData,IN UINTN DataSize,OUT EFI_TIME * SigningTime)351 TimestampTokenVerify (
352   IN  CONST UINT8  *TSToken,
353   IN  UINTN        TokenSize,
354   IN  CONST UINT8  *TsaCert,
355   IN  UINTN        CertSize,
356   IN  CONST UINT8  *TimestampedData,
357   IN  UINTN        DataSize,
358   OUT EFI_TIME     *SigningTime
359   )
360 {
361   BOOLEAN      Status;
362   CONST UINT8  *TokenTemp;
363   PKCS7        *Pkcs7;
364   X509         *Cert;
365   CONST UINT8  *CertTemp;
366   X509_STORE   *CertStore;
367   BIO          *OutBio;
368   UINT8        *TstData;
369   UINTN        TstSize;
370   CONST UINT8  *TstTemp;
371   TS_TST_INFO  *TstInfo;
372 
373   Status = FALSE;
374 
375   //
376   // Check input parameters
377   //
378   if ((TSToken == NULL) || (TsaCert == NULL) || (TimestampedData == NULL) ||
379       (TokenSize > INT_MAX) || (CertSize > INT_MAX) || (DataSize > INT_MAX)) {
380     return FALSE;
381   }
382 
383   //
384   // Initializations
385   //
386   if (SigningTime != NULL) {
387     SetMem (SigningTime, sizeof (EFI_TIME), 0);
388   }
389   Pkcs7     = NULL;
390   Cert      = NULL;
391   CertStore = NULL;
392   OutBio    = NULL;
393   TstData   = NULL;
394   TstInfo   = NULL;
395 
396   //
397   // TimeStamp Token should contain one valid DER-encoded ASN.1 PKCS#7 structure.
398   //
399   TokenTemp = TSToken;
400   Pkcs7     = d2i_PKCS7 (NULL, (const unsigned char **) &TokenTemp, (int) TokenSize);
401   if (Pkcs7 == NULL) {
402     goto _Exit;
403   }
404 
405   //
406   // The timestamp signature (TSA's response) will be one PKCS#7 signed data.
407   //
408   if (!PKCS7_type_is_signed (Pkcs7)) {
409     goto _Exit;
410   }
411 
412   //
413   // Read the trusted TSA certificate (DER-encoded), and Construct X509 Certificate.
414   //
415   CertTemp = TsaCert;
416   Cert = d2i_X509 (NULL, &CertTemp, (long) CertSize);
417   if (Cert == NULL) {
418     goto _Exit;
419   }
420 
421   //
422   // Setup X509 Store for trusted certificate.
423   //
424   CertStore = X509_STORE_new ();
425   if ((CertStore == NULL) || !(X509_STORE_add_cert (CertStore, Cert))) {
426     goto _Exit;
427   }
428 
429   //
430   // Allow partial certificate chains, terminated by a non-self-signed but
431   // still trusted intermediate certificate. Also disable time checks.
432   //
433   X509_STORE_set_flags (CertStore,
434                         X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME);
435 
436   X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY);
437 
438   //
439   // Verifies the PKCS#7 signedData structure, and output the signed contents.
440   //
441   OutBio = BIO_new (BIO_s_mem ());
442   if (OutBio == NULL) {
443     goto _Exit;
444   }
445   if (!PKCS7_verify (Pkcs7, NULL, CertStore, NULL, OutBio, PKCS7_BINARY)) {
446     goto _Exit;
447   }
448 
449   //
450   // Read the signed contents detached in timestamp signature.
451   //
452   TstData = AllocateZeroPool (2048);
453   if (TstData == NULL) {
454     goto _Exit;
455   }
456   TstSize = BIO_read (OutBio, (void *) TstData, 2048);
457 
458   //
459   // Construct TS_TST_INFO structure from the signed contents.
460   //
461   TstTemp = TstData;
462   TstInfo = d2i_TS_TST_INFO (NULL, (const unsigned char **) &TstTemp,
463               (int)TstSize);
464   if (TstInfo == NULL) {
465     goto _Exit;
466   }
467 
468   //
469   // Check TS_TST_INFO structure.
470   //
471   Status = CheckTSTInfo (TstInfo, TimestampedData, DataSize);
472   if (!Status) {
473     goto _Exit;
474   }
475 
476   //
477   // Retrieve the signing time from TS_TST_INFO structure.
478   //
479   if (SigningTime != NULL) {
480     SetMem (SigningTime, sizeof (EFI_TIME), 0);
481     Status = ConvertAsn1TimeToEfiTime (TstInfo->GenTime, SigningTime);
482   }
483 
484 _Exit:
485   //
486   // Release Resources
487   //
488   PKCS7_free (Pkcs7);
489   X509_free (Cert);
490   X509_STORE_free (CertStore);
491   BIO_free (OutBio);
492   TS_TST_INFO_free (TstInfo);
493 
494   if (TstData != NULL) {
495     FreePool (TstData);
496   }
497 
498   return Status;
499 }
500 
501 /**
502   Verifies the validity of a RFC3161 Timestamp CounterSignature embedded in PE/COFF Authenticode
503   signature.
504 
505   If AuthData is NULL, then return FALSE.
506 
507   @param[in]  AuthData     Pointer to the Authenticode Signature retrieved from signed
508                            PE/COFF image to be verified.
509   @param[in]  DataSize     Size of the Authenticode Signature in bytes.
510   @param[in]  TsaCert      Pointer to a trusted/root TSA certificate encoded in DER, which
511                            is used for TSA certificate chain verification.
512   @param[in]  CertSize     Size of the trusted certificate in bytes.
513   @param[out] SigningTime  Return the time of timestamp generation time if the timestamp
514                            signature is valid.
515 
516   @retval  TRUE   The specified Authenticode includes a valid RFC3161 Timestamp CounterSignature.
517   @retval  FALSE  No valid RFC3161 Timestamp CounterSignature in the specified Authenticode data.
518 
519 **/
520 BOOLEAN
521 EFIAPI
ImageTimestampVerify(IN CONST UINT8 * AuthData,IN UINTN DataSize,IN CONST UINT8 * TsaCert,IN UINTN CertSize,OUT EFI_TIME * SigningTime)522 ImageTimestampVerify (
523   IN  CONST UINT8  *AuthData,
524   IN  UINTN        DataSize,
525   IN  CONST UINT8  *TsaCert,
526   IN  UINTN        CertSize,
527   OUT EFI_TIME     *SigningTime
528   )
529 {
530   BOOLEAN                      Status;
531   PKCS7                        *Pkcs7;
532   CONST UINT8                  *Temp;
533   STACK_OF(PKCS7_SIGNER_INFO)  *SignerInfos;
534   PKCS7_SIGNER_INFO            *SignInfo;
535   UINTN                        Index;
536   STACK_OF(X509_ATTRIBUTE)     *Sk;
537   X509_ATTRIBUTE               *Xa;
538   ASN1_OBJECT                  *XaObj;
539   ASN1_TYPE                    *Asn1Type;
540   ASN1_OCTET_STRING            *EncDigest;
541   UINT8                        *TSToken;
542   UINTN                        TokenSize;
543 
544   //
545   // Input Parameters Checking.
546   //
547   if ((AuthData == NULL) || (TsaCert == NULL)) {
548     return FALSE;
549   }
550 
551   if ((DataSize > INT_MAX) || (CertSize > INT_MAX)) {
552     return FALSE;
553   }
554 
555   //
556   // Register & Initialize necessary digest algorithms for PKCS#7 Handling.
557   //
558   if ((EVP_add_digest (EVP_md5 ()) == 0) || (EVP_add_digest (EVP_sha1 ()) == 0) ||
559       (EVP_add_digest (EVP_sha256 ()) == 0) || (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA)) == 0) {
560     return FALSE;
561   }
562 
563   //
564   // Initialization.
565   //
566   Status    = FALSE;
567   Pkcs7     = NULL;
568   SignInfo  = NULL;
569 
570   //
571   // Decode ASN.1-encoded Authenticode data into PKCS7 structure.
572   //
573   Temp  = AuthData;
574   Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) DataSize);
575   if (Pkcs7 == NULL) {
576     goto _Exit;
577   }
578 
579   //
580   // Check if there is one and only one signer.
581   //
582   SignerInfos = PKCS7_get_signer_info (Pkcs7);
583   if (!SignerInfos || (sk_PKCS7_SIGNER_INFO_num (SignerInfos) != 1)) {
584     goto _Exit;
585   }
586 
587   //
588   // Locate the TimeStamp CounterSignature.
589   //
590   SignInfo = sk_PKCS7_SIGNER_INFO_value (SignerInfos, 0);
591   if (SignInfo == NULL) {
592     goto _Exit;
593   }
594 
595   //
596   // Locate Message Digest which will be the data to be time-stamped.
597   //
598   EncDigest = SignInfo->enc_digest;
599   if (EncDigest == NULL) {
600     goto _Exit;
601   }
602 
603   //
604   // The RFC3161 timestamp counterSignature is contained in unauthenticatedAttributes field
605   // of SignerInfo.
606   //
607   Sk = SignInfo->unauth_attr;
608   if (Sk == NULL) {             // No timestamp counterSignature.
609     goto _Exit;
610   }
611 
612   Asn1Type = NULL;
613   for (Index = 0; Index < (UINTN) sk_X509_ATTRIBUTE_num (Sk); Index++) {
614     //
615     // Search valid RFC3161 timestamp counterSignature based on OBJID.
616     //
617     Xa = sk_X509_ATTRIBUTE_value (Sk, (int)Index);
618     if (Xa == NULL) {
619       continue;
620     }
621     XaObj = X509_ATTRIBUTE_get0_object(Xa);
622     if (XaObj == NULL) {
623       continue;
624     }
625     if ((OBJ_length(XaObj) != sizeof (mSpcRFC3161OidValue)) ||
626         (CompareMem (OBJ_get0_data(XaObj), mSpcRFC3161OidValue, sizeof (mSpcRFC3161OidValue)) != 0)) {
627       continue;
628     }
629     Asn1Type = X509_ATTRIBUTE_get0_type(Xa, 0);
630   }
631 
632   if (Asn1Type == NULL) {
633     Status = FALSE;
634     goto _Exit;
635   }
636   TSToken   = Asn1Type->value.octet_string->data;
637   TokenSize = Asn1Type->value.octet_string->length;
638 
639   //
640   // TimeStamp counterSignature (Token) verification.
641   //
642   Status = TimestampTokenVerify (
643              TSToken,
644              TokenSize,
645              TsaCert,
646              CertSize,
647              EncDigest->data,
648              EncDigest->length,
649              SigningTime
650              );
651 
652 _Exit:
653   //
654   // Release Resources
655   //
656   PKCS7_free (Pkcs7);
657 
658   return Status;
659 }
660