]> git.proxmox.com Git - mirror_edk2.git/blob - CryptoPkg/Library/BaseCryptLib/Pk/CryptTs.c
CryptoPkg: Replace BSD License with BSD+Patent License
[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 - 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
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
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
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 }