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