/** @file
  RFC3161 Timestamp Countersignature Verification Wrapper Implementation which does
  not provide real capabilities.

Copyright (c) 2024, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "InternalCryptLib.h"
#include <mbedtls/asn1.h>

//
// OID ASN.1 Value for SPC_RFC3161_OBJID ("1.3.6.1.4.1.311.3.3.1")
//
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8  mSpcRFC3161OidValue[] = {
  0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x03, 0x03, 0x01
};

/**
  Convert ASN.1 GeneralizedTime to EFI Time.

  @param[in]  Ptr          Pointer to the ASN.1 GeneralizedTime to be converted.
  @param[out] EfiTime      Return the corresponding EFI Time.

  @retval  TRUE   The time conversion succeeds.
  @retval  FALSE  Invalid parameters.

**/
STATIC
BOOLEAN
ConvertAsn1TimeToEfiTime (
  IN  UINT8     *Ptr,
  OUT EFI_TIME  *EfiTime
  )
{
  CONST CHAR8  *Str;
  UINTN        Index;

  if ((Ptr == NULL) || (EfiTime == NULL)) {
    return FALSE;
  }

  Str = (CONST CHAR8 *)Ptr;
  SetMem (EfiTime, sizeof (EFI_TIME), 0);

  Index = 0;

  /* 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;
}

/**
  Verifies the validity of a RFC3161 Timestamp CounterSignature embedded in PE/COFF Authenticode
  signature.

  Return FALSE to indicate this interface is not supported.

  @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  FALSE  This interface is not supported.

**/
BOOLEAN
EFIAPI
ImageTimestampVerify (
  IN  CONST UINT8  *AuthData,
  IN  UINTN        DataSize,
  IN  CONST UINT8  *TsaCert,
  IN  UINTN        CertSize,
  OUT EFI_TIME     *SigningTime
  )
{
  BOOLEAN  Status;
  UINT8    *Ptr;
  UINT8    *End;
  INT32    Len;
  UINTN    ObjLen;
  UINT8    *TempPtr;

  //
  // Initializations
  //
  if (SigningTime != NULL) {
    SetMem (SigningTime, sizeof (EFI_TIME), 0);
  }

  //
  // Input Parameters Checking.
  //
  if ((AuthData == NULL) || (TsaCert == NULL)) {
    return FALSE;
  }

  if ((DataSize > INT_MAX) || (CertSize > INT_MAX)) {
    return FALSE;
  }

  Ptr = (UINT8 *)(UINTN)AuthData;
  Len = (UINT32)DataSize;
  End = Ptr + Len;

  // ContentInfo
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  // ContentType
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_OID) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;
  // content
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
    return FALSE;
  }

  End = Ptr + ObjLen;
  // signedData
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  // version
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_INTEGER) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;
  // digestAlgo
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  // encapContentInfo
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  // cert
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  TempPtr = Ptr;
  // OPTIONAL CRLs
  if (mbedtls_asn1_get_tag (&TempPtr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) == 0) {
    Ptr = TempPtr + ObjLen;
  }

  // signerInfo
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET) != 0) {
    return FALSE;
  }

  // sub parse
  // signerInfo
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  End = Ptr + ObjLen;

  // version
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_INTEGER) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  // sid
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  // digestalgo
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  // OPTIONAL AuthenticatedAttributes
  TempPtr = Ptr;
  if (mbedtls_asn1_get_tag (&TempPtr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) == 0) {
    Ptr = TempPtr + ObjLen;
  }

  // signaturealgo
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  // signature
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_OCTET_STRING) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  // OPTIONAL UnauthenticatedAttributes
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, 0xA1) != 0) {
    return FALSE;
  }

  // Attribute
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  // type
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_OID) != 0) {
    return FALSE;
  }

  if (CompareMem (Ptr, mSpcRFC3161OidValue, sizeof (mSpcRFC3161OidValue)) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  // values
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET) != 0) {
    return FALSE;
  }

  // values
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  // signedData OID
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_OID) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  // [0]
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
    return FALSE;
  }

  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  // integer
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_INTEGER) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;
  // SET
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  // tST OID
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_OID) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
    return FALSE;
  }

  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_OCTET_STRING) != 0) {
    return FALSE;
  }

  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  // Integer
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_INTEGER) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;
  // policy OID
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_OID) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;
  // sequence
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;
  // Integer
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_INTEGER) != 0) {
    return FALSE;
  }

  Ptr += ObjLen;

  // GeneralizedTime
  if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_GENERALIZED_TIME) != 0) {
    return FALSE;
  }

  //
  // Retrieve the signing time from TS_TST_INFO structure.
  //
  if (SigningTime != NULL) {
    SetMem (SigningTime, sizeof (EFI_TIME), 0);
    Status = ConvertAsn1TimeToEfiTime (Ptr, SigningTime);
  }

  return Status;
}
