| /** @file | |
| RFC3161 Timestamp Countersignature Verification over OpenSSL. | |
| The timestamp is generated by a TimeStamping Authority (TSA) and asserts that a | |
| publisher's signature existed before the specified time. The timestamp extends | |
| the lifetime of the signature when a signing certificate expires or is later | |
| revoked. | |
| Copyright (c) 2014 - 2017, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "InternalCryptLib.h" | |
| #include <openssl/asn1.h> | |
| #include <openssl/asn1t.h> | |
| #include <openssl/x509.h> | |
| #include <openssl/x509v3.h> | |
| #include <openssl/pkcs7.h> | |
| // | |
| // OID ASN.1 Value for SPC_RFC3161_OBJID ("1.3.6.1.4.1.311.3.3.1") | |
| // | |
| UINT8 mSpcRFC3161OidValue[] = { | |
| 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x03, 0x03, 0x01 | |
| }; | |
| /// | |
| /// The messageImprint field SHOULD contain the hash of the datum to be | |
| /// time-stamped. The hash is represented as an OCTET STRING. Its | |
| /// length MUST match the length of the hash value for that algorithm | |
| /// (e.g., 20 bytes for SHA-1 or 16 bytes for MD5). | |
| /// | |
| /// MessageImprint ::= SEQUENCE { | |
| /// hashAlgorithm AlgorithmIdentifier, | |
| /// hashedMessage OCTET STRING } | |
| /// | |
| typedef struct { | |
| X509_ALGOR *HashAlgorithm; | |
| ASN1_OCTET_STRING *HashedMessage; | |
| } TS_MESSAGE_IMPRINT; | |
| // | |
| // ASN.1 Functions for TS_MESSAGE_IMPRINT | |
| // | |
| DECLARE_ASN1_FUNCTIONS (TS_MESSAGE_IMPRINT) | |
| ASN1_SEQUENCE (TS_MESSAGE_IMPRINT) = { | |
| ASN1_SIMPLE (TS_MESSAGE_IMPRINT, HashAlgorithm, X509_ALGOR), | |
| ASN1_SIMPLE (TS_MESSAGE_IMPRINT, HashedMessage, ASN1_OCTET_STRING) | |
| } ASN1_SEQUENCE_END (TS_MESSAGE_IMPRINT) | |
| IMPLEMENT_ASN1_FUNCTIONS (TS_MESSAGE_IMPRINT) | |
| /// | |
| /// Accuracy represents the time deviation around the UTC time contained | |
| /// in GeneralizedTime of time-stamp token. | |
| /// | |
| /// Accuracy ::= SEQUENCE { | |
| /// seconds INTEGER OPTIONAL, | |
| /// millis [0] INTEGER (1..999) OPTIONAL, | |
| /// micros [1] INTEGER (1..999) OPTIONAL } | |
| /// | |
| typedef struct { | |
| ASN1_INTEGER *Seconds; | |
| ASN1_INTEGER *Millis; | |
| ASN1_INTEGER *Micros; | |
| } TS_ACCURACY; | |
| // | |
| // ASN.1 Functions for TS_ACCURACY | |
| // | |
| DECLARE_ASN1_FUNCTIONS (TS_ACCURACY) | |
| ASN1_SEQUENCE (TS_ACCURACY) = { | |
| ASN1_OPT (TS_ACCURACY, Seconds, ASN1_INTEGER), | |
| ASN1_IMP_OPT (TS_ACCURACY, Millis, ASN1_INTEGER, 0), | |
| ASN1_IMP_OPT (TS_ACCURACY, Micros, ASN1_INTEGER, 1) | |
| } ASN1_SEQUENCE_END (TS_ACCURACY) | |
| IMPLEMENT_ASN1_FUNCTIONS (TS_ACCURACY) | |
| /// | |
| /// The timestamp token info resulting from a successful timestamp request, | |
| /// as defined in RFC 3161. | |
| /// | |
| /// TSTInfo ::= SEQUENCE { | |
| /// version INTEGER { v1(1) }, | |
| /// policy TSAPolicyId, | |
| /// messageImprint MessageImprint, | |
| /// -- MUST have the same value as the similar field in | |
| /// -- TimeStampReq | |
| /// serialNumber INTEGER, | |
| /// -- Time-Stamping users MUST be ready to accommodate integers | |
| /// -- up to 160 bits. | |
| /// genTime GeneralizedTime, | |
| /// accuracy Accuracy OPTIONAL, | |
| /// ordering BOOLEAN DEFAULT FALSE, | |
| /// nonce INTEGER OPTIONAL, | |
| /// -- MUST be present if the similar field was present | |
| /// -- in TimeStampReq. In that case it MUST have the same value. | |
| /// tsa [0] GeneralName OPTIONAL, | |
| /// extensions [1] IMPLICIT Extensions OPTIONAL } | |
| /// | |
| typedef struct { | |
| ASN1_INTEGER *Version; | |
| ASN1_OBJECT *Policy; | |
| TS_MESSAGE_IMPRINT *MessageImprint; | |
| ASN1_INTEGER *SerialNumber; | |
| ASN1_GENERALIZEDTIME *GenTime; | |
| TS_ACCURACY *Accuracy; | |
| ASN1_BOOLEAN Ordering; | |
| ASN1_INTEGER *Nonce; | |
| GENERAL_NAME *Tsa; | |
| STACK_OF(X509_EXTENSION) *Extensions; | |
| } TS_TST_INFO; | |
| // | |
| // ASN.1 Functions for TS_TST_INFO | |
| // | |
| DECLARE_ASN1_FUNCTIONS (TS_TST_INFO) | |
| ASN1_SEQUENCE (TS_TST_INFO) = { | |
| ASN1_SIMPLE (TS_TST_INFO, Version, ASN1_INTEGER), | |
| ASN1_SIMPLE (TS_TST_INFO, Policy, ASN1_OBJECT), | |
| ASN1_SIMPLE (TS_TST_INFO, MessageImprint, TS_MESSAGE_IMPRINT), | |
| ASN1_SIMPLE (TS_TST_INFO, SerialNumber, ASN1_INTEGER), | |
| ASN1_SIMPLE (TS_TST_INFO, GenTime, ASN1_GENERALIZEDTIME), | |
| ASN1_OPT (TS_TST_INFO, Accuracy, TS_ACCURACY), | |
| ASN1_OPT (TS_TST_INFO, Ordering, ASN1_FBOOLEAN), | |
| ASN1_OPT (TS_TST_INFO, Nonce, ASN1_INTEGER), | |
| ASN1_EXP_OPT(TS_TST_INFO, Tsa, GENERAL_NAME, 0), | |
| ASN1_IMP_SEQUENCE_OF_OPT (TS_TST_INFO, Extensions, X509_EXTENSION, 1) | |
| } ASN1_SEQUENCE_END (TS_TST_INFO) | |
| IMPLEMENT_ASN1_FUNCTIONS (TS_TST_INFO) | |
| /** | |
| Convert ASN.1 GeneralizedTime to EFI Time. | |
| @param[in] Asn1Time Pointer to the ASN.1 GeneralizedTime to be converted. | |
| @param[out] SigningTime Return the corresponding EFI Time. | |
| @retval TRUE The time convertion succeeds. | |
| @retval FALSE Invalid parameters. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| ConvertAsn1TimeToEfiTime ( | |
| IN ASN1_TIME *Asn1Time, | |
| OUT EFI_TIME *EfiTime | |
| ) | |
| { | |
| CONST CHAR8 *Str; | |
| UINTN Index; | |
| if ((Asn1Time == NULL) || (EfiTime == NULL)) { | |
| return FALSE; | |
| } | |
| Str = (CONST CHAR8*)Asn1Time->data; | |
| SetMem (EfiTime, 0, sizeof (EFI_TIME)); | |
| Index = 0; | |
| if (Asn1Time->type == V_ASN1_UTCTIME) { /* two digit year */ | |
| EfiTime->Year = (Str[Index++] - '0') * 10; | |
| EfiTime->Year += (Str[Index++] - '0'); | |
| if (EfiTime->Year < 70) { | |
| EfiTime->Year += 100; | |
| } | |
| } else if (Asn1Time->type == V_ASN1_GENERALIZEDTIME) { /* four digit year */ | |
| EfiTime->Year = (Str[Index++] - '0') * 1000; | |
| EfiTime->Year += (Str[Index++] - '0') * 100; | |
| EfiTime->Year += (Str[Index++] - '0') * 10; | |
| EfiTime->Year += (Str[Index++] - '0'); | |
| if ((EfiTime->Year < 1900) || (EfiTime->Year > 9999)) { | |
| return FALSE; | |
| } | |
| } | |
| EfiTime->Month = (Str[Index++] - '0') * 10; | |
| EfiTime->Month += (Str[Index++] - '0'); | |
| if ((EfiTime->Month < 1) || (EfiTime->Month > 12)) { | |
| return FALSE; | |
| } | |
| EfiTime->Day = (Str[Index++] - '0') * 10; | |
| EfiTime->Day += (Str[Index++] - '0'); | |
| if ((EfiTime->Day < 1) || (EfiTime->Day > 31)) { | |
| return FALSE; | |
| } | |
| EfiTime->Hour = (Str[Index++] - '0') * 10; | |
| EfiTime->Hour += (Str[Index++] - '0'); | |
| if (EfiTime->Hour > 23) { | |
| return FALSE; | |
| } | |
| EfiTime->Minute = (Str[Index++] - '0') * 10; | |
| EfiTime->Minute += (Str[Index++] - '0'); | |
| if (EfiTime->Minute > 59) { | |
| return FALSE; | |
| } | |
| EfiTime->Second = (Str[Index++] - '0') * 10; | |
| EfiTime->Second += (Str[Index++] - '0'); | |
| if (EfiTime->Second > 59) { | |
| return FALSE; | |
| } | |
| /* Note: we did not adjust the time based on time zone information */ | |
| return TRUE; | |
| } | |
| /** | |
| Check the validity of TimeStamp Token Information. | |
| @param[in] TstInfo Pointer to the TS_TST_INFO structure. | |
| @param[in] TimestampedData Pointer to the data to be time-stamped. | |
| @param[in] DataSize Size of timestamped data in bytes. | |
| @retval TRUE The TimeStamp Token Information is valid. | |
| @retval FALSE Invalid TimeStamp Token Information. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| CheckTSTInfo ( | |
| IN CONST TS_TST_INFO *TstInfo, | |
| IN CONST UINT8 *TimestampedData, | |
| IN UINTN DataSize | |
| ) | |
| { | |
| BOOLEAN Status; | |
| TS_MESSAGE_IMPRINT *Imprint; | |
| X509_ALGOR *HashAlgo; | |
| CONST EVP_MD *Md; | |
| EVP_MD_CTX *MdCtx; | |
| UINTN MdSize; | |
| UINT8 *HashedMsg; | |
| // | |
| // Initialization | |
| // | |
| Status = FALSE; | |
| HashAlgo = NULL; | |
| HashedMsg = NULL; | |
| MdCtx = NULL; | |
| // | |
| // -- Check version number of Timestamp: | |
| // The version field (currently v1) describes the version of the time-stamp token. | |
| // Conforming time-stamping servers MUST be able to provide version 1 time-stamp tokens. | |
| // | |
| if ((ASN1_INTEGER_get (TstInfo->Version)) != 1) { | |
| return FALSE; | |
| } | |
| // | |
| // -- Check Policies | |
| // The policy field MUST indicate the TSA's policy under which the response was produced. | |
| // | |
| if (TstInfo->Policy == NULL) { | |
| /// NOTE: Need to check if the requested and returned policies. | |
| /// We have no information about the Requested TSA Policy. | |
| return FALSE; | |
| } | |
| // | |
| // -- Compute & Check Message Imprint | |
| // | |
| Imprint = TstInfo->MessageImprint; | |
| HashAlgo = X509_ALGOR_dup (Imprint->HashAlgorithm); | |
| Md = EVP_get_digestbyobj (HashAlgo->algorithm); | |
| if (Md == NULL) { | |
| goto _Exit; | |
| } | |
| MdSize = EVP_MD_size (Md); | |
| HashedMsg = AllocateZeroPool (MdSize); | |
| if (HashedMsg == NULL) { | |
| goto _Exit; | |
| } | |
| MdCtx = EVP_MD_CTX_new (); | |
| if (MdCtx == NULL) { | |
| goto _Exit; | |
| } | |
| if ((EVP_DigestInit_ex (MdCtx, Md, NULL) != 1) || | |
| (EVP_DigestUpdate (MdCtx, TimestampedData, DataSize) != 1) || | |
| (EVP_DigestFinal (MdCtx, HashedMsg, NULL) != 1)) { | |
| goto _Exit; | |
| } | |
| if ((MdSize == (UINTN)ASN1_STRING_length (Imprint->HashedMessage)) && | |
| (CompareMem (HashedMsg, ASN1_STRING_get0_data (Imprint->HashedMessage), MdSize) != 0)) { | |
| goto _Exit; | |
| } | |
| // | |
| // -- Check Nonces | |
| // | |
| if (TstInfo->Nonce != NULL) { | |
| // | |
| // Nonces is optional, No error if no nonce is returned; | |
| // | |
| } | |
| // | |
| // -- Check if the TSA name and signer certificate is matched. | |
| // | |
| if (TstInfo->Tsa != NULL) { | |
| // | |
| // Ignored the optional Tsa field checking. | |
| // | |
| } | |
| Status = TRUE; | |
| _Exit: | |
| X509_ALGOR_free (HashAlgo); | |
| EVP_MD_CTX_free (MdCtx); | |
| if (HashedMsg != NULL) { | |
| FreePool (HashedMsg); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Verifies the validity of a TimeStamp Token as described in RFC 3161 ("Internet | |
| X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)"). | |
| If TSToken is NULL, then return FALSE. | |
| If TimestampedData is NULL, then return FALSE. | |
| @param[in] TSToken Pointer to the RFC3161 TimeStamp Token, which is generated | |
| by a TSA and located in the software publisher's SignerInfo | |
| structure. | |
| @param[in] TokenSize Size of the TimeStamp Token in bytes. | |
| @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER. | |
| @param[in] CertSize Size of the trusted TSA certificate in bytes. | |
| @param[in] TimestampedData Pointer to the data to be time-stamped. | |
| @param[in] DataSize Size of timestamped data in bytes. | |
| @param[out] SigningTime Return the time of timestamp generation time if the timestamp | |
| signature is valid. | |
| @retval TRUE The specified timestamp token is valid. | |
| @retval FALSE Invalid timestamp token. | |
| **/ | |
| BOOLEAN | |
| 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 | |
| ) | |
| { | |
| BOOLEAN Status; | |
| CONST UINT8 *TokenTemp; | |
| PKCS7 *Pkcs7; | |
| X509 *Cert; | |
| CONST UINT8 *CertTemp; | |
| X509_STORE *CertStore; | |
| BIO *OutBio; | |
| UINT8 *TstData; | |
| UINTN TstSize; | |
| CONST UINT8 *TstTemp; | |
| TS_TST_INFO *TstInfo; | |
| Status = FALSE; | |
| // | |
| // Check input parameters | |
| // | |
| if ((TSToken == NULL) || (TsaCert == NULL) || (TimestampedData == NULL) || | |
| (TokenSize > INT_MAX) || (CertSize > INT_MAX) || (DataSize > INT_MAX)) { | |
| return FALSE; | |
| } | |
| // | |
| // Initializations | |
| // | |
| if (SigningTime != NULL) { | |
| SetMem (SigningTime, sizeof (EFI_TIME), 0); | |
| } | |
| Pkcs7 = NULL; | |
| Cert = NULL; | |
| CertStore = NULL; | |
| OutBio = NULL; | |
| TstData = NULL; | |
| TstInfo = NULL; | |
| // | |
| // TimeStamp Token should contain one valid DER-encoded ASN.1 PKCS#7 structure. | |
| // | |
| TokenTemp = TSToken; | |
| Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &TokenTemp, (int) TokenSize); | |
| if (Pkcs7 == NULL) { | |
| goto _Exit; | |
| } | |
| // | |
| // The timestamp signature (TSA's response) will be one PKCS#7 signed data. | |
| // | |
| if (!PKCS7_type_is_signed (Pkcs7)) { | |
| goto _Exit; | |
| } | |
| // | |
| // Read the trusted TSA certificate (DER-encoded), and Construct X509 Certificate. | |
| // | |
| CertTemp = TsaCert; | |
| Cert = d2i_X509 (NULL, &CertTemp, (long) CertSize); | |
| if (Cert == NULL) { | |
| goto _Exit; | |
| } | |
| // | |
| // Setup X509 Store for trusted certificate. | |
| // | |
| CertStore = X509_STORE_new (); | |
| if ((CertStore == NULL) || !(X509_STORE_add_cert (CertStore, Cert))) { | |
| goto _Exit; | |
| } | |
| // | |
| // Allow partial certificate chains, terminated by a non-self-signed but | |
| // still trusted intermediate certificate. Also disable time checks. | |
| // | |
| X509_STORE_set_flags (CertStore, | |
| X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME); | |
| X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY); | |
| // | |
| // Verifies the PKCS#7 signedData structure, and output the signed contents. | |
| // | |
| OutBio = BIO_new (BIO_s_mem ()); | |
| if (OutBio == NULL) { | |
| goto _Exit; | |
| } | |
| if (!PKCS7_verify (Pkcs7, NULL, CertStore, NULL, OutBio, PKCS7_BINARY)) { | |
| goto _Exit; | |
| } | |
| // | |
| // Read the signed contents detached in timestamp signature. | |
| // | |
| TstData = AllocateZeroPool (2048); | |
| if (TstData == NULL) { | |
| goto _Exit; | |
| } | |
| TstSize = BIO_read (OutBio, (void *) TstData, 2048); | |
| // | |
| // Construct TS_TST_INFO structure from the signed contents. | |
| // | |
| TstTemp = TstData; | |
| TstInfo = d2i_TS_TST_INFO (NULL, (const unsigned char **) &TstTemp, | |
| (int)TstSize); | |
| if (TstInfo == NULL) { | |
| goto _Exit; | |
| } | |
| // | |
| // Check TS_TST_INFO structure. | |
| // | |
| Status = CheckTSTInfo (TstInfo, TimestampedData, DataSize); | |
| if (!Status) { | |
| goto _Exit; | |
| } | |
| // | |
| // Retrieve the signing time from TS_TST_INFO structure. | |
| // | |
| if (SigningTime != NULL) { | |
| SetMem (SigningTime, sizeof (EFI_TIME), 0); | |
| Status = ConvertAsn1TimeToEfiTime (TstInfo->GenTime, SigningTime); | |
| } | |
| _Exit: | |
| // | |
| // Release Resources | |
| // | |
| PKCS7_free (Pkcs7); | |
| X509_free (Cert); | |
| X509_STORE_free (CertStore); | |
| BIO_free (OutBio); | |
| TS_TST_INFO_free (TstInfo); | |
| if (TstData != NULL) { | |
| FreePool (TstData); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Verifies the validity of a RFC3161 Timestamp CounterSignature embedded in PE/COFF Authenticode | |
| signature. | |
| If AuthData is NULL, then return FALSE. | |
| @param[in] AuthData Pointer to the Authenticode Signature retrieved from signed | |
| PE/COFF image to be verified. | |
| @param[in] DataSize Size of the Authenticode Signature in bytes. | |
| @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER, which | |
| is used for TSA certificate chain verification. | |
| @param[in] CertSize Size of the trusted certificate in bytes. | |
| @param[out] SigningTime Return the time of timestamp generation time if the timestamp | |
| signature is valid. | |
| @retval TRUE The specified Authenticode includes a valid RFC3161 Timestamp CounterSignature. | |
| @retval FALSE No valid RFC3161 Timestamp CounterSignature in the specified Authenticode data. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| ImageTimestampVerify ( | |
| IN CONST UINT8 *AuthData, | |
| IN UINTN DataSize, | |
| IN CONST UINT8 *TsaCert, | |
| IN UINTN CertSize, | |
| OUT EFI_TIME *SigningTime | |
| ) | |
| { | |
| BOOLEAN Status; | |
| PKCS7 *Pkcs7; | |
| CONST UINT8 *Temp; | |
| STACK_OF(PKCS7_SIGNER_INFO) *SignerInfos; | |
| PKCS7_SIGNER_INFO *SignInfo; | |
| UINTN Index; | |
| STACK_OF(X509_ATTRIBUTE) *Sk; | |
| X509_ATTRIBUTE *Xa; | |
| ASN1_OBJECT *XaObj; | |
| ASN1_TYPE *Asn1Type; | |
| ASN1_OCTET_STRING *EncDigest; | |
| UINT8 *TSToken; | |
| UINTN TokenSize; | |
| // | |
| // Input Parameters Checking. | |
| // | |
| if ((AuthData == NULL) || (TsaCert == NULL)) { | |
| return FALSE; | |
| } | |
| if ((DataSize > INT_MAX) || (CertSize > INT_MAX)) { | |
| return FALSE; | |
| } | |
| // | |
| // Register & Initialize necessary digest algorithms for PKCS#7 Handling. | |
| // | |
| if ((EVP_add_digest (EVP_md5 ()) == 0) || (EVP_add_digest (EVP_sha1 ()) == 0) || | |
| (EVP_add_digest (EVP_sha256 ()) == 0) || (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA)) == 0) { | |
| return FALSE; | |
| } | |
| // | |
| // Initialization. | |
| // | |
| Status = FALSE; | |
| Pkcs7 = NULL; | |
| SignInfo = NULL; | |
| // | |
| // Decode ASN.1-encoded Authenticode data into PKCS7 structure. | |
| // | |
| Temp = AuthData; | |
| Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) DataSize); | |
| if (Pkcs7 == NULL) { | |
| goto _Exit; | |
| } | |
| // | |
| // Check if there is one and only one signer. | |
| // | |
| SignerInfos = PKCS7_get_signer_info (Pkcs7); | |
| if (!SignerInfos || (sk_PKCS7_SIGNER_INFO_num (SignerInfos) != 1)) { | |
| goto _Exit; | |
| } | |
| // | |
| // Locate the TimeStamp CounterSignature. | |
| // | |
| SignInfo = sk_PKCS7_SIGNER_INFO_value (SignerInfos, 0); | |
| if (SignInfo == NULL) { | |
| goto _Exit; | |
| } | |
| // | |
| // Locate Message Digest which will be the data to be time-stamped. | |
| // | |
| EncDigest = SignInfo->enc_digest; | |
| if (EncDigest == NULL) { | |
| goto _Exit; | |
| } | |
| // | |
| // The RFC3161 timestamp counterSignature is contained in unauthenticatedAttributes field | |
| // of SignerInfo. | |
| // | |
| Sk = SignInfo->unauth_attr; | |
| if (Sk == NULL) { // No timestamp counterSignature. | |
| goto _Exit; | |
| } | |
| Asn1Type = NULL; | |
| for (Index = 0; Index < (UINTN) sk_X509_ATTRIBUTE_num (Sk); Index++) { | |
| // | |
| // Search valid RFC3161 timestamp counterSignature based on OBJID. | |
| // | |
| Xa = sk_X509_ATTRIBUTE_value (Sk, (int)Index); | |
| if (Xa == NULL) { | |
| continue; | |
| } | |
| XaObj = X509_ATTRIBUTE_get0_object(Xa); | |
| if (XaObj == NULL) { | |
| continue; | |
| } | |
| if ((OBJ_length(XaObj) != sizeof (mSpcRFC3161OidValue)) || | |
| (CompareMem (OBJ_get0_data(XaObj), mSpcRFC3161OidValue, sizeof (mSpcRFC3161OidValue)) != 0)) { | |
| continue; | |
| } | |
| Asn1Type = X509_ATTRIBUTE_get0_type(Xa, 0); | |
| } | |
| if (Asn1Type == NULL) { | |
| Status = FALSE; | |
| goto _Exit; | |
| } | |
| TSToken = Asn1Type->value.octet_string->data; | |
| TokenSize = Asn1Type->value.octet_string->length; | |
| // | |
| // TimeStamp counterSignature (Token) verification. | |
| // | |
| Status = TimestampTokenVerify ( | |
| TSToken, | |
| TokenSize, | |
| TsaCert, | |
| CertSize, | |
| EncDigest->data, | |
| EncDigest->length, | |
| SigningTime | |
| ); | |
| _Exit: | |
| // | |
| // Release Resources | |
| // | |
| PKCS7_free (Pkcs7); | |
| return Status; | |
| } |