/** @file
  Implementation file for HECI Trusted Channnel Services

 @copyright
  INTEL CONFIDENTIAL
  Copyright 2016 - 2018 Intel Corporation.

  The source code contained or described herein and all documents related to the
  source code ("Material") are owned by Intel Corporation or its suppliers or
  licensors. Title to the Material remains with Intel Corporation or its suppliers
  and licensors. The Material may contain trade secrets and proprietary and
  confidential information of Intel Corporation and its suppliers and licensors,
  and is protected by worldwide copyright and trade secret laws and treaty
  provisions. No part of the Material may be used, copied, reproduced, modified,
  published, uploaded, posted, transmitted, distributed, or disclosed in any way
  without Intel's prior express written permission.

  No license under any patent, copyright, trade secret or other intellectual
  property right is granted to or conferred upon you by disclosure or delivery
  of the Materials, either expressly, by implication, inducement, estoppel or
  otherwise. Any license under such intellectual property rights must be
  express and approved by Intel in writing.

  Unless otherwise agreed by Intel in writing, you may not remove or alter
  this notice or any other notice embedded in Materials by Intel or
  Intel's suppliers or licensors in any way.

  This file contains an 'Intel Peripheral Driver' and is uniquely identified as
  "Intel Reference Module" and is licensed for Intel CPUs and chipsets under
  the terms of your license agreement with Intel or your vendor. This file may
  be modified by the user, subject to additional terms of the license agreement.

@par Specification Reference

**/

#include "TrustedChannelSmm.h"

#include  <Mbp.h>
#include <Library/HobLib.h>
#include <Protocol/TrustedChannel.h>
#include <Protocol/ComputeHmacSha256Protocol.h>

STATIC UINT32  mMonoCount         = 0;
STATIC UINT8   mHmacSha256Key[32] = {0};
COMPUTE_HMAC_SHA_256_PROTOCOL *mComputeHmacSha256Protocol;

HECI_TRUSTED_CHANNEL_PROTOCOL mHeciTrustedChannelProtocol = {
  HeciTrustedChannelIsTrustedChannelEnabled,
  HeciTrustedChannelUpdateTrustedHeader,
  HeciTrustedChannelVerifyTrustedHeader
};

/**
  Retrieve the SMM Trusted Key from CSE.

  @param[out] SmmTrustedKeyData    A pointer to a data buffer to hold the trusted key.

  @return     EFI_SUCCESS          Retrieved the SMM key successfully.
  @return     Others               Failed to retrieve the SMM key.
**/
EFI_STATUS
EFIAPI
GetSMMTrustedKey (
  OUT MBP_SMM_TRUSTED_KEY    *SmmTrustedKeyData
  )
{
  MBP_CMD_RESP_DATA          *MBPHeader;
  MBP_ITEM_HEADER            *MBPItem;
  MBP_SMM_TRUSTED_KEY        *SmmTrustedKey = NULL;
  UINT32                     MBPItemCount;
  EFI_HOB_GUID_TYPE          *MbpSensitiveDataHob;
  BOOLEAN                    MbpSensitiveDataHobInstances;

  if (SmmTrustedKeyData == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  DEBUG ((EFI_D_INFO, "Get SMMTrustedKey from HOB\n"));
  MbpSensitiveDataHob = GetFirstGuidHob (&gMeBiosPayloadSensitiveDataHobGuid);
  if (MbpSensitiveDataHob == NULL) {
    return EFI_NOT_FOUND;
  }
  MbpSensitiveDataHobInstances = FALSE;
  while (MbpSensitiveDataHob != NULL) {
    MBPHeader = (MBP_CMD_RESP_DATA *) ((UINT32*) GET_GUID_HOB_DATA (MbpSensitiveDataHob));
    MBPItem = (MBP_ITEM_HEADER *) (MBPHeader + 1);
    for (MBPItemCount = 0; MBPItemCount <= MBPHeader->ItemsNum; MBPItemCount++) {
      if (MBPItem->AppID == MBP_APP_ID_NVM && MBPItem->ItemID == MBP_ITEM_ID_SMM_TRUSTED_CHANNEL) {
        SmmTrustedKey = (MBP_SMM_TRUSTED_KEY *) (MBPItem + 1);
        if (MbpSensitiveDataHobInstances == FALSE) {
          CopyMem ((VOID *) SmmTrustedKeyData, (VOID *) SmmTrustedKey, sizeof (MBP_SMM_TRUSTED_KEY));
          MbpSensitiveDataHobInstances = TRUE;
        }
        ZeroMem ((VOID *) (SmmTrustedKey), sizeof (MBP_SMM_TRUSTED_KEY));
      }
    MBPItem = (MBP_ITEM_HEADER *) ((UINT32 *) MBPItem + MBPItem->Length);
    }
    MbpSensitiveDataHob = GET_NEXT_HOB (MbpSensitiveDataHob);
    MbpSensitiveDataHob = GetNextGuidHob (&gMeBiosPayloadSensitiveDataHobGuid, MbpSensitiveDataHob);
  }
  ASSERT (MbpSensitiveDataHobInstances == TRUE);
  return EFI_SUCCESS;
}

/**
  Returns whether trusted channel is enabled.

  @param[in]  None.

  @retval     TRUE if trusted channel is enabled.
              FALSE if trusted channel is disabled.
**/
BOOLEAN
EFIAPI
HeciTrustedChannelIsTrustedChannelEnabled (
  VOID
  )
{
  return (mMonoCount != 0);
}

/**
  Updates the SHA256 signature and monotonic counter fields of a HECI message header.

  @param[in,out]  MessageHeader   A pointer to the message header
  @param[in]      TotalHeaderSize The total header size
  @param[in]      TotalDataSize   The total data size

  @retval         EFI_SUCCESSS    The header was updated successfully
  @retval         Others          The header could not be updated
**/
EFI_STATUS
EFIAPI
HeciTrustedChannelUpdateTrustedHeader (
  IN OUT  UINT8       *MessageHeader,
  IN      UINT32      TotalHeaderSize,
  IN      UINT32      TotalDataSize
  )
{
  EFI_STATUS                         Status;
  MBP_SMM_TRUSTED_KEY                SmmTrustedKey;
  HECI2_TRUSTED_CHANNEL_BIOS_HEADER  *TrustedChannelHeader = NULL;
  UINT8                              Digest[64];

  if (MessageHeader == NULL || TotalHeaderSize < sizeof (HECI2_TRUSTED_CHANNEL_BIOS_HEADER)) {
    return EFI_INVALID_PARAMETER;
  }

  if (mMonoCount == 0) {
    //
    // Since the is the first message, get the SMM trusted key for this boot
    //
    Status = GetSMMTrustedKey (&SmmTrustedKey);

    if (!EFI_ERROR (Status)) {
      CopyMem (mHmacSha256Key, SmmTrustedKey.SmmTrustedKey, sizeof (mHmacSha256Key));
      mMonoCount = SmmTrustedKey.MonotonicCounter + 1;

      DEBUG ((EFI_D_INFO, "HMAC Monotonic Count = %d\n", mMonoCount));
    } else {
      DEBUG ((EFI_D_ERROR, "Unable to get the SMM trusted key. Cannot send HECI2 transactions.\n"));
      ASSERT_EFI_ERROR (Status);
      return Status;
    }
  }

  TrustedChannelHeader = (HECI2_TRUSTED_CHANNEL_BIOS_HEADER  *) MessageHeader;

  TrustedChannelHeader->MonotonicCounter = mMonoCount++;

  DEBUG ((EFI_D_INFO, "Current HMAC monotonic count = %d.\n", TrustedChannelHeader->MonotonicCounter));

  //
  // Compute the HMAC SHA-256 digest
  //
  // Includes all fields except the signature field
  //
  Status = mComputeHmacSha256Protocol->ComputeHmacSha256Signature (
                                         mHmacSha256Key,
                                         sizeof (mHmacSha256Key),
                                         (UINT8 *) MessageHeader + HECI2_HMAC_SHA256_SIGNATURE_SIZE,
                                         (TotalHeaderSize + TotalDataSize) - HECI2_HMAC_SHA256_SIGNATURE_SIZE,
                                         Digest
                                         );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  CopyMem (&TrustedChannelHeader->Signature, Digest, HECI2_HMAC_SHA256_SIGNATURE_SIZE);

  DEBUG ((EFI_D_INFO, "Trusted channel signature computed = 0x%x\n", Digest));

  return EFI_SUCCESS;
}

/**
  Verifies the SHA 256 HMAC signature of a HECI message.

  @param[in]        MessageHeader             A pointer to the message header
  @param[in]        TotalDataSize             The total data size
  @param[in]        IsVerificationSuccessful  Set to TRUE if the signature in the given message is valid

  @retval           EFI_SUCCESSS     The verification was successful
  @retval           Others           An error occurred during verification
**/
EFI_STATUS
EFIAPI
HeciTrustedChannelVerifyTrustedHeader (
  IN CONST UINT8     *MessageHeader,
  IN       UINT32    TotalDataSize,
  OUT      BOOLEAN   *IsVerificationSuccessful
  )
{
  EFI_STATUS                               Status;
  CONST HECI2_TRUSTED_CHANNEL_BIOS_HEADER  *TrustedChannelHeader = NULL;
  UINT8                                    Digest[64];

  ZeroMem (Digest, sizeof (Digest));

  if (mMonoCount == 0 || CompareMem (mHmacSha256Key, Digest, sizeof (mHmacSha256Key)) == 0) {
    // Verification should always occur after the monotonic counter and key have been initialized
    ASSERT (FALSE);
    return EFI_NOT_READY;
  }

  TrustedChannelHeader = (CONST HECI2_TRUSTED_CHANNEL_BIOS_HEADER  *) MessageHeader;
  DEBUG ((EFI_D_INFO, "HMAC monotonic count in verification = %d.\n", TrustedChannelHeader->MonotonicCounter));

  //
  // Compute the HMAC SHA-256 digest
  //
  // Includes all fields except the signature field
  //
  
  Status = mComputeHmacSha256Protocol->ComputeHmacSha256Signature (
                                         mHmacSha256Key,
                                         sizeof (mHmacSha256Key),
                                         (UINT8 *) MessageHeader + HECI2_HMAC_SHA256_SIGNATURE_SIZE,
                                         TotalDataSize - HECI2_HMAC_SHA256_SIGNATURE_SIZE,
                                         Digest
                                         );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (CompareMem (Digest, TrustedChannelHeader->Signature, HECI2_HMAC_SHA256_SIGNATURE_SIZE) == 0) {
    DEBUG ((EFI_D_INFO, "Trusted channel signature is valid.\n"));
    *IsVerificationSuccessful = TRUE;
  } else {
    DEBUG ((EFI_D_WARN, "Trusted channel signature is invalid!\n"));
    *IsVerificationSuccessful = FALSE;
  }

  return EFI_SUCCESS;
}

/**
  Entry point of the SMM Trusted Channel Driver.

  @param  ImageHandle    The firmware allocated handle for the EFI image.
  @param  SystemTable    A pointer to the EFI System Table.

  @retval EFI_SUCCESS    The entry point is executed successfully.

**/
EFI_STATUS
EFIAPI
TrustedChannelSmmEntry (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  EFI_HANDLE             Handle;
  EFI_STATUS             Status;
  MBP_SMM_TRUSTED_KEY    SmmTrustedKey;

  Handle = NULL;

  if (mMonoCount == 0) {
    //
    // Get the SMM Trusted Key for this boot
    //
    Status = GetSMMTrustedKey (&SmmTrustedKey);

    if (!EFI_ERROR (Status)) {
      CopyMem (mHmacSha256Key, SmmTrustedKey.SmmTrustedKey, sizeof (mHmacSha256Key));
      mMonoCount = SmmTrustedKey.MonotonicCounter + 1;

      DEBUG ((EFI_D_INFO, "HMAC Monotonic Count = %d\n", mMonoCount));
    } else {
      DEBUG ((EFI_D_ERROR, "Unable to get the SMM trusted key. Cannot send HECI2 transactions.\n"));
      ASSERT_EFI_ERROR (Status);
      return Status;
    }
  }

  //
  // Locate ComputeHmacSha256 Protocol
  //
  Status = gSmst->SmmLocateProtocol (
                    &gComputeHmacSha256ProtocolGuid,
                    NULL,
                    (VOID**)&mComputeHmacSha256Protocol
                    );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "ComputeHmacSha256Protocol is not found.\n"));
    ASSERT_EFI_ERROR (Status);
    return Status;
  }

  //
  // Install the Trusted Channel SMM Protocol
  //
  return gSmst->SmmInstallProtocolInterface (
                  &Handle,
                  &gEfiHeciTrustedChannelSmmProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &mHeciTrustedChannelProtocol
                  );
}