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
8Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR>
9This program and the accompanying materials
10are licensed and made available under the terms and conditions of the BSD License
11which accompanies this distribution.  The full text of the license may be found at
12http://opensource.org/licenses/bsd-license.php
13
14THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
15WITHOUT 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//
30UINT8 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///
44typedef 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//
52DECLARE_ASN1_FUNCTIONS (TS_MESSAGE_IMPRINT)
53ASN1_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)
57IMPLEMENT_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///
68typedef 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//
77DECLARE_ASN1_FUNCTIONS (TS_ACCURACY)
78ASN1_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)
83IMPLEMENT_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///
107typedef 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//
123DECLARE_ASN1_FUNCTIONS (TS_TST_INFO)
124ASN1_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)
136IMPLEMENT_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**/
149BOOLEAN
150EFIAPI
151ConvertAsn1TimeToEfiTime (
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**/
230BOOLEAN
231EFIAPI
232CheckTSTInfo (
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**/
347BOOLEAN
348EFIAPI
349TimestampTokenVerify (
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. Also disable time checks.
430  //
431  X509_STORE_set_flags (CertStore,
432                        X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME);
433
434  X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY);
435
436  //
437  // Verifies the PKCS#7 signedData structure, and output the signed contents.
438  //
439  OutBio = BIO_new (BIO_s_mem ());
440  if (OutBio == NULL) {
441    goto _Exit;
442  }
443  if (!PKCS7_verify (Pkcs7, NULL, CertStore, NULL, OutBio, PKCS7_BINARY)) {
444    goto _Exit;
445  }
446
447  //
448  // Read the signed contents detached in timestamp signature.
449  //
450  TstData = AllocateZeroPool (2048);
451  if (TstData == NULL) {
452    goto _Exit;
453  }
454  TstSize = BIO_read (OutBio, (void *) TstData, 2048);
455
456  //
457  // Construct TS_TST_INFO structure from the signed contents.
458  //
459  TstTemp = TstData;
460  TstInfo = d2i_TS_TST_INFO (NULL, (const unsigned char **) &TstTemp,
461              (int)TstSize);
462  if (TstInfo == NULL) {
463    goto _Exit;
464  }
465
466  //
467  // Check TS_TST_INFO structure.
468  //
469  Status = CheckTSTInfo (TstInfo, TimestampedData, DataSize);
470  if (!Status) {
471    goto _Exit;
472  }
473
474  //
475  // Retrieve the signing time from TS_TST_INFO structure.
476  //
477  if (SigningTime != NULL) {
478    SetMem (SigningTime, sizeof (EFI_TIME), 0);
479    Status = ConvertAsn1TimeToEfiTime (TstInfo->GenTime, SigningTime);
480  }
481
482_Exit:
483  //
484  // Release Resources
485  //
486  PKCS7_free (Pkcs7);
487  X509_free (Cert);
488  X509_STORE_free (CertStore);
489  BIO_free (OutBio);
490  TS_TST_INFO_free (TstInfo);
491
492  if (TstData != NULL) {
493    FreePool (TstData);
494  }
495
496  return Status;
497}
498
499/**
500  Verifies the validility of a RFC3161 Timestamp CounterSignature embedded in PE/COFF Authenticode
501  signature.
502
503  If AuthData is NULL, then return FALSE.
504
505  @param[in]  AuthData     Pointer to the Authenticode Signature retrieved from signed
506                           PE/COFF image to be verified.
507  @param[in]  DataSize     Size of the Authenticode Signature in bytes.
508  @param[in]  TsaCert      Pointer to a trusted/root TSA certificate encoded in DER, which
509                           is used for TSA certificate chain verification.
510  @param[in]  CertSize     Size of the trusted certificate in bytes.
511  @param[out] SigningTime  Return the time of timestamp generation time if the timestamp
512                           signature is valid.
513
514  @retval  TRUE   The specified Authenticode includes a valid RFC3161 Timestamp CounterSignature.
515  @retval  FALSE  No valid RFC3161 Timestamp CounterSignature in the specified Authenticode data.
516
517**/
518BOOLEAN
519EFIAPI
520ImageTimestampVerify (
521  IN  CONST UINT8  *AuthData,
522  IN  UINTN        DataSize,
523  IN  CONST UINT8  *TsaCert,
524  IN  UINTN        CertSize,
525  OUT EFI_TIME     *SigningTime
526  )
527{
528  BOOLEAN                      Status;
529  PKCS7                        *Pkcs7;
530  CONST UINT8                  *Temp;
531  STACK_OF(PKCS7_SIGNER_INFO)  *SignerInfos;
532  PKCS7_SIGNER_INFO            *SignInfo;
533  UINTN                        Index;
534  STACK_OF(X509_ATTRIBUTE)     *Sk;
535  X509_ATTRIBUTE               *Xa;
536  ASN1_OBJECT                  *XaObj;
537  ASN1_TYPE                    *Asn1Type;
538  ASN1_OCTET_STRING            *EncDigest;
539  UINT8                        *TSToken;
540  UINTN                        TokenSize;
541
542  //
543  // Input Parameters Checking.
544  //
545  if ((AuthData == NULL) || (TsaCert == NULL)) {
546    return FALSE;
547  }
548
549  if ((DataSize > INT_MAX) || (CertSize > INT_MAX)) {
550    return FALSE;
551  }
552
553  //
554  // Register & Initialize necessary digest algorithms for PKCS#7 Handling.
555  //
556  if ((EVP_add_digest (EVP_md5 ()) == 0) || (EVP_add_digest (EVP_sha1 ()) == 0) ||
557      (EVP_add_digest (EVP_sha256 ()) == 0) || (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA)) == 0) {
558    return FALSE;
559  }
560
561  //
562  // Initialization.
563  //
564  Status    = FALSE;
565  Pkcs7     = NULL;
566  SignInfo  = NULL;
567
568  //
569  // Decode ASN.1-encoded Authenticode data into PKCS7 structure.
570  //
571  Temp  = AuthData;
572  Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) DataSize);
573  if (Pkcs7 == NULL) {
574    goto _Exit;
575  }
576
577  //
578  // Check if there is one and only one signer.
579  //
580  SignerInfos = PKCS7_get_signer_info (Pkcs7);
581  if (!SignerInfos || (sk_PKCS7_SIGNER_INFO_num (SignerInfos) != 1)) {
582    goto _Exit;
583  }
584
585  //
586  // Locate the TimeStamp CounterSignature.
587  //
588  SignInfo = sk_PKCS7_SIGNER_INFO_value (SignerInfos, 0);
589  if (SignInfo == NULL) {
590    goto _Exit;
591  }
592
593  //
594  // Locate Message Digest which will be the data to be time-stamped.
595  //
596  EncDigest = SignInfo->enc_digest;
597  if (EncDigest == NULL) {
598    goto _Exit;
599  }
600
601  //
602  // The RFC3161 timestamp counterSignature is contained in unauthenticatedAttributes field
603  // of SignerInfo.
604  //
605  Sk = SignInfo->unauth_attr;
606  if (Sk == NULL) {             // No timestamp counterSignature.
607    goto _Exit;
608  }
609
610  Asn1Type = NULL;
611  for (Index = 0; Index < (UINTN) sk_X509_ATTRIBUTE_num (Sk); Index++) {
612    //
613    // Search valid RFC3161 timestamp counterSignature based on OBJID.
614    //
615    Xa = sk_X509_ATTRIBUTE_value (Sk, (int)Index);
616    if (Xa == NULL) {
617      continue;
618    }
619    XaObj = X509_ATTRIBUTE_get0_object(Xa);
620    if (XaObj == NULL) {
621      continue;
622    }
623    if ((OBJ_length(XaObj) != sizeof (mSpcRFC3161OidValue)) ||
624        (CompareMem (OBJ_get0_data(XaObj), mSpcRFC3161OidValue, sizeof (mSpcRFC3161OidValue)) != 0)) {
625      continue;
626    }
627    Asn1Type = X509_ATTRIBUTE_get0_type(Xa, 0);
628  }
629
630  if (Asn1Type == NULL) {
631    Status = FALSE;
632    goto _Exit;
633  }
634  TSToken   = Asn1Type->value.octet_string->data;
635  TokenSize = Asn1Type->value.octet_string->length;
636
637  //
638  // TimeStamp counterSignature (Token) verification.
639  //
640  Status = TimestampTokenVerify (
641             TSToken,
642             TokenSize,
643             TsaCert,
644             CertSize,
645             EncDigest->data,
646             EncDigest->length,
647             SigningTime
648             );
649
650_Exit:
651  //
652  // Release Resources
653  //
654  PKCS7_free (Pkcs7);
655
656  return Status;
657}
658