mirror of https://github.com/acidanthera/audk.git
730 lines
21 KiB
C
730 lines
21 KiB
C
/** @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 - 2015, 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)
|
|
|
|
|
|
/**
|
|
Verification callback function to override any existing callbacks in OpenSSL
|
|
for intermediate TSA certificate supports.
|
|
|
|
@param[in] Status Original status before calling this callback.
|
|
@param[in] Context X509 store context.
|
|
|
|
@retval 1 Current X509 certificate is verified successfully.
|
|
@retval 0 Verification failed.
|
|
|
|
**/
|
|
int
|
|
TSVerifyCallback (
|
|
IN int Status,
|
|
IN X509_STORE_CTX *Context
|
|
)
|
|
{
|
|
X509_OBJECT *Obj;
|
|
INTN Error;
|
|
INTN Index;
|
|
INTN Count;
|
|
|
|
Obj = NULL;
|
|
Error = (INTN) X509_STORE_CTX_get_error (Context);
|
|
|
|
//
|
|
// X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_
|
|
// CERT_LOCALLY mean a X509 certificate is not self signed and its issuer
|
|
// can not be found in X509_verify_cert of X509_vfy.c.
|
|
// In order to support intermediate certificate node, we override the
|
|
// errors if the certification is obtained from X509 store, i.e. it is
|
|
// a trusted ceritifcate node that is enrolled by user.
|
|
// Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
|
|
// are also ignored to enable such feature.
|
|
//
|
|
if ((Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) ||
|
|
(Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)) {
|
|
Obj = (X509_OBJECT *) malloc (sizeof (X509_OBJECT));
|
|
if (Obj == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
Obj->type = X509_LU_X509;
|
|
Obj->data.x509 = Context->current_cert;
|
|
|
|
CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE);
|
|
|
|
if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {
|
|
Status = 1;
|
|
} else {
|
|
//
|
|
// If any certificate in the chain is enrolled as trusted certificate,
|
|
// pass the certificate verification.
|
|
//
|
|
if (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {
|
|
Count = (INTN) sk_X509_num (Context->chain);
|
|
for (Index = 0; Index < Count; Index++) {
|
|
Obj->data.x509 = sk_X509_value (Context->chain, (int) Index);
|
|
if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {
|
|
Status = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE);
|
|
}
|
|
|
|
if ((Error == X509_V_ERR_CERT_UNTRUSTED) ||
|
|
(Error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) {
|
|
Status = 1;
|
|
}
|
|
|
|
if (Obj != NULL) {
|
|
OPENSSL_free (Obj);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
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;
|
|
|
|
//
|
|
// -- 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;
|
|
}
|
|
EVP_DigestInit (&MdCtx, Md);
|
|
EVP_DigestUpdate (&MdCtx, TimestampedData, DataSize);
|
|
EVP_DigestFinal (&MdCtx, HashedMsg, NULL);
|
|
if ((MdSize == (UINTN)ASN1_STRING_length (Imprint->HashedMessage)) &&
|
|
(CompareMem (HashedMsg, ASN1_STRING_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);
|
|
if (HashedMsg != NULL) {
|
|
FreePool (HashedMsg);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Verifies the validility 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;
|
|
}
|
|
|
|
//
|
|
// Register customized X509 verification callback function to support
|
|
// trusted intermediate TSA certificate anchor.
|
|
//
|
|
CertStore->verify_cb = TSVerifyCallback;
|
|
|
|
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 validility 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_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->object->length != sizeof (mSpcRFC3161OidValue)) ||
|
|
(CompareMem (Xa->object->data, mSpcRFC3161OidValue, sizeof (mSpcRFC3161OidValue)) != 0)) {
|
|
continue;
|
|
}
|
|
Asn1Type = sk_ASN1_TYPE_value (Xa->value.set, 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;
|
|
}
|