123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 |
- /** @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>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #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 conversion 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;
- }
|