]> git.proxmox.com Git - mirror_edk2.git/blobdiff - SecurityPkg/VariableAuthenticated/EsalVariableDxeSal/AuthService.c
Add security package to repository.
[mirror_edk2.git] / SecurityPkg / VariableAuthenticated / EsalVariableDxeSal / AuthService.c
diff --git a/SecurityPkg/VariableAuthenticated/EsalVariableDxeSal/AuthService.c b/SecurityPkg/VariableAuthenticated/EsalVariableDxeSal/AuthService.c
new file mode 100644 (file)
index 0000000..38f4626
--- /dev/null
@@ -0,0 +1,882 @@
+/** @file\r
+  Implement authentication services for the authenticated variable\r
+  service in UEFI2.2.\r
+\r
+Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials \r
+are licensed and made available under the terms and conditions of the BSD License \r
+which accompanies this distribution.  The full text of the license may be found at \r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Variable.h"\r
+#include "AuthService.h"\r
+\r
+///\r
+/// Global database array for scratch\r
+///\r
+UINT32   mPubKeyNumber;\r
+UINT32   mPlatformMode;\r
+EFI_GUID mSignatureSupport[SIGSUPPORT_NUM] = {EFI_CERT_RSA2048_SHA256_GUID, EFI_CERT_RSA2048_SHA1_GUID};\r
+//\r
+// Public Exponent of RSA Key.\r
+//\r
+CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 };\r
+\r
+/**\r
+  Initializes for authenticated varibale service.\r
+\r
+  @retval EFI_SUCCESS           The function successfully executed.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate enough memory resources.\r
+\r
+**/\r
+EFI_STATUS\r
+AutenticatedVariableServiceInitialize (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  VARIABLE_POINTER_TRACK  Variable;\r
+  UINT8                   VarValue;\r
+  UINT32                  VarAttr;\r
+  UINTN                   DataSize;\r
+  UINTN                   CtxSize;\r
+  VARIABLE_HEADER         VariableHeader;\r
+  BOOLEAN                 Valid;\r
+\r
+  mVariableModuleGlobal->AuthenticatedVariableGuid[Physical] = &gEfiAuthenticatedVariableGuid;\r
+  mVariableModuleGlobal->CertRsa2048Sha256Guid[Physical]     = &gEfiCertRsa2048Sha256Guid;\r
+  mVariableModuleGlobal->ImageSecurityDatabaseGuid[Physical] = &gEfiImageSecurityDatabaseGuid;\r
+\r
+  //\r
+  // Initialize hash context.\r
+  //\r
+  CtxSize   = Sha256GetContextSize ();\r
+  mVariableModuleGlobal->HashContext[Physical] = AllocateRuntimePool (CtxSize);\r
+  ASSERT (mVariableModuleGlobal->HashContext[Physical] != NULL);\r
+  //\r
+  // Check "AuthVarKeyDatabase" variable's existence. \r
+  // If it doesn't exist, create a new one with initial value of 0 and EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. \r
+  //\r
+  Status = FindVariable (\r
+             mVariableModuleGlobal->VariableName[Physical][VAR_AUTH_KEY_DB], \r
+             &gEfiAuthenticatedVariableGuid, \r
+             &Variable, \r
+             &mVariableModuleGlobal->VariableGlobal[Physical],\r
+             mVariableModuleGlobal->FvbInstance\r
+             );\r
+\r
+  if (Variable.CurrPtr == 0x0) {\r
+    VarAttr       = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;\r
+    VarValue      = 0;\r
+    mPubKeyNumber = 0;\r
+    Status        = UpdateVariable (\r
+                      mVariableModuleGlobal->VariableName[Physical][VAR_AUTH_KEY_DB],\r
+                      &gEfiAuthenticatedVariableGuid,\r
+                      &VarValue,\r
+                      sizeof(UINT8),\r
+                      VarAttr,\r
+                      0,\r
+                      0,\r
+                      FALSE,\r
+                      mVariableModuleGlobal,\r
+                      &Variable\r
+                      );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  } else {\r
+    //\r
+    // Load database in global variable for cache.\r
+    //\r
+    Valid = IsValidVariableHeader (\r
+              Variable.CurrPtr, \r
+              Variable.Volatile, \r
+              &mVariableModuleGlobal->VariableGlobal[Physical], \r
+              mVariableModuleGlobal->FvbInstance, \r
+              &VariableHeader\r
+              );\r
+    ASSERT (Valid);\r
+\r
+    DataSize  = DataSizeOfVariable (&VariableHeader);\r
+    ASSERT (DataSize <= MAX_KEYDB_SIZE);\r
+    GetVariableDataPtr (\r
+      Variable.CurrPtr,\r
+      Variable.Volatile,\r
+      &mVariableModuleGlobal->VariableGlobal[Physical],\r
+      mVariableModuleGlobal->FvbInstance,\r
+      (CHAR16 *) mVariableModuleGlobal->PubKeyStore\r
+      );\r
+\r
+    mPubKeyNumber = (UINT32) (DataSize / EFI_CERT_TYPE_RSA2048_SIZE);\r
+  }\r
+  //\r
+  // Check "SetupMode" variable's existence. \r
+  // If it doesn't exist, check PK database's existence to determine the value.\r
+  // Then create a new one with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. \r
+  //\r
+  Status = FindVariable (\r
+             mVariableModuleGlobal->VariableName[Physical][VAR_SETUP_MODE], \r
+             &gEfiGlobalVariableGuid, \r
+             &Variable, \r
+             &mVariableModuleGlobal->VariableGlobal[Physical],\r
+             mVariableModuleGlobal->FvbInstance\r
+             );\r
+\r
+  if (Variable.CurrPtr == 0x0) {\r
+    Status = FindVariable (\r
+               mVariableModuleGlobal->VariableName[Physical][VAR_PLATFORM_KEY], \r
+               &gEfiGlobalVariableGuid, \r
+               &Variable, \r
+               &mVariableModuleGlobal->VariableGlobal[Physical],\r
+               mVariableModuleGlobal->FvbInstance\r
+               );\r
+    if (Variable.CurrPtr == 0x0) {\r
+      mPlatformMode = SETUP_MODE;\r
+    } else {\r
+      mPlatformMode = USER_MODE;\r
+    }\r
+\r
+    VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;\r
+    Status  = UpdateVariable (\r
+                mVariableModuleGlobal->VariableName[Physical][VAR_SETUP_MODE],\r
+                &gEfiGlobalVariableGuid,\r
+                &mPlatformMode,\r
+                sizeof(UINT8),\r
+                VarAttr,\r
+                0,\r
+                0,\r
+                FALSE,\r
+                mVariableModuleGlobal,\r
+                &Variable\r
+                );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  } else {\r
+    GetVariableDataPtr (\r
+      Variable.CurrPtr,\r
+      Variable.Volatile,\r
+      &mVariableModuleGlobal->VariableGlobal[Physical],\r
+      mVariableModuleGlobal->FvbInstance,\r
+      (CHAR16 *) &mPlatformMode\r
+      );\r
+  }\r
+  //\r
+  // Check "SignatureSupport" variable's existence. \r
+  // If it doesn't exist, then create a new one with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. \r
+  //\r
+  Status = FindVariable (\r
+             EFI_SIGNATURE_SUPPORT_NAME, \r
+             &gEfiGlobalVariableGuid, \r
+             &Variable, \r
+             &mVariableModuleGlobal->VariableGlobal[Physical],\r
+             mVariableModuleGlobal->FvbInstance\r
+             );\r
+\r
+  if (Variable.CurrPtr == 0x0) {\r
+    VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;\r
+    Status  = UpdateVariable (\r
+                EFI_SIGNATURE_SUPPORT_NAME,\r
+                &gEfiGlobalVariableGuid,\r
+                mSignatureSupport,\r
+                SIGSUPPORT_NUM * sizeof(EFI_GUID),\r
+                VarAttr,\r
+                0,\r
+                0,\r
+                FALSE,\r
+                mVariableModuleGlobal,\r
+                &Variable\r
+                );\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Add public key in store and return its index.\r
+\r
+  @param[in]  VirtualMode             The current calling mode for this function.\r
+  @param[in]  Global                  The context of this Extended SAL Variable Services Class call.\r
+  @param[in]  PubKey                  The input pointer to Public Key data.\r
+\r
+  @return                             The index of new added item.\r
+\r
+**/\r
+UINT32\r
+AddPubKeyInStore (\r
+  IN  BOOLEAN                   VirtualMode,\r
+  IN  ESAL_VARIABLE_GLOBAL      *Global,\r
+  IN  UINT8                     *PubKey\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  BOOLEAN                 IsFound;\r
+  UINT32                  Index;\r
+  VARIABLE_POINTER_TRACK  Variable;\r
+  UINT8                   *Ptr;\r
+\r
+  if (PubKey == NULL) {\r
+    return 0;\r
+  }\r
+\r
+  Status = FindVariable (\r
+             Global->VariableName[VirtualMode][VAR_AUTH_KEY_DB],\r
+             Global->AuthenticatedVariableGuid[VirtualMode],\r
+             &Variable,\r
+             &Global->VariableGlobal[VirtualMode],\r
+             Global->FvbInstance\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+  //\r
+  // Check whether the public key entry does exist.\r
+  //\r
+  IsFound = FALSE;\r
+  for (Ptr = Global->PubKeyStore, Index = 1; Index <= mPubKeyNumber; Index++) {\r
+    if (CompareMem (Ptr, PubKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {\r
+      IsFound = TRUE;\r
+      break;\r
+    }\r
+    Ptr += EFI_CERT_TYPE_RSA2048_SIZE;\r
+  }\r
+\r
+  if (!IsFound) {\r
+    //\r
+    // Add public key in database.\r
+    //\r
+    if (mPubKeyNumber == MAX_KEY_NUM) {\r
+      //\r
+      // Notes: Database is full, need enhancement here, currently just return 0.\r
+      //\r
+      return 0;\r
+    }\r
+\r
+    CopyMem (Global->PubKeyStore + mPubKeyNumber * EFI_CERT_TYPE_RSA2048_SIZE, PubKey, EFI_CERT_TYPE_RSA2048_SIZE);\r
+    Index = ++mPubKeyNumber;\r
+    //\r
+    // Update public key database variable.\r
+    //\r
+    Status = UpdateVariable (\r
+               Global->VariableName[VirtualMode][VAR_AUTH_KEY_DB],\r
+               Global->AuthenticatedVariableGuid[VirtualMode],\r
+               Global->PubKeyStore,\r
+               mPubKeyNumber * EFI_CERT_TYPE_RSA2048_SIZE,\r
+               EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS,\r
+               0,\r
+               0,\r
+               VirtualMode,\r
+               Global,\r
+               &Variable\r
+               );\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
+\r
+  return Index;\r
+}\r
+\r
+/**\r
+  Verify data payload with AuthInfo in EFI_CERT_TYPE_RSA2048_SHA256 type.\r
+  Follow the steps in UEFI2.2.\r
+\r
+  @param[in]  VirtualMode             The current calling mode for this function.\r
+  @param[in]  Global                  The context of this Extended SAL Variable Services Class call.\r
+  @param[in]  Data                    The pointer to data with AuthInfo.\r
+  @param[in]  DataSize                The size of Data.\r
+  @param[in]  PubKey                  The public key used for verification.\r
+\r
+  @retval EFI_INVALID_PARAMETER       Invalid parameter.\r
+  @retval EFI_SECURITY_VIOLATION      Authentication failed.\r
+  @retval EFI_SUCCESS                 Authentication successful.\r
+\r
+**/\r
+EFI_STATUS\r
+VerifyDataPayload (\r
+  IN  BOOLEAN                   VirtualMode,\r
+  IN  ESAL_VARIABLE_GLOBAL      *Global,\r
+  IN  UINT8                     *Data,\r
+  IN  UINTN                     DataSize,\r
+  IN  UINT8                     *PubKey\r
+  )\r
+{\r
+  BOOLEAN                         Status;\r
+  EFI_VARIABLE_AUTHENTICATION     *CertData;\r
+  EFI_CERT_BLOCK_RSA_2048_SHA256  *CertBlock;\r
+  UINT8                           Digest[SHA256_DIGEST_SIZE];\r
+  VOID                            *Rsa;\r
+  VOID                            *HashContext;\r
+\r
+  Rsa         = NULL;\r
+  CertData    = NULL;\r
+  CertBlock   = NULL;\r
+\r
+  if (Data == NULL || PubKey == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  CertData  = (EFI_VARIABLE_AUTHENTICATION *) Data;\r
+  CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) (CertData->AuthInfo.CertData);\r
+\r
+  //\r
+  // wCertificateType should be WIN_CERT_TYPE_EFI_GUID.\r
+  // Cert type should be EFI_CERT_TYPE_RSA2048_SHA256.\r
+  //\r
+  if ((CertData->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) ||\r
+      !CompareGuid (&CertData->AuthInfo.CertType, Global->CertRsa2048Sha256Guid[VirtualMode])\r
+        ) {\r
+    //\r
+    // Invalid AuthInfo type, return EFI_SECURITY_VIOLATION.\r
+    //\r
+    return EFI_SECURITY_VIOLATION;\r
+  }\r
+\r
+  //\r
+  // Hash data payload with SHA256.\r
+  //\r
+  ZeroMem (Digest, SHA256_DIGEST_SIZE);\r
+  HashContext = Global->HashContext[VirtualMode];\r
+  Status  = Sha256Init (HashContext);\r
+  if (!Status) {\r
+    goto Done;\r
+  }\r
+  Status  = Sha256Update (HashContext, Data + AUTHINFO_SIZE, (UINTN) (DataSize - AUTHINFO_SIZE));\r
+  if (!Status) {\r
+    goto Done;\r
+  }\r
+  //\r
+  // Hash Monotonic Count.\r
+  //\r
+  Status  = Sha256Update (HashContext, &CertData->MonotonicCount, sizeof (UINT64));\r
+  if (!Status) {\r
+    goto Done;\r
+  }\r
+  Status  = Sha256Final (HashContext, Digest);\r
+  if (!Status) {\r
+    goto Done;\r
+  }\r
+  //\r
+  // Generate & Initialize RSA Context.\r
+  //\r
+  Rsa = RsaNew ();\r
+  ASSERT (Rsa != NULL);\r
+  // \r
+  // Set RSA Key Components.\r
+  // NOTE: Only N and E are needed to be set as RSA public key for signature verification.\r
+  //\r
+  Status = RsaSetKey (Rsa, RsaKeyN, PubKey, EFI_CERT_TYPE_RSA2048_SIZE);\r
+  if (!Status) {\r
+    goto Done;\r
+  }\r
+  Status = RsaSetKey (Rsa, RsaKeyE, mRsaE, sizeof (mRsaE));\r
+  if (!Status) {\r
+    goto Done;\r
+  }\r
+  //\r
+  // Verify the signature.\r
+  //\r
+  Status = RsaPkcs1Verify (\r
+             Rsa, \r
+             Digest, \r
+             SHA256_DIGEST_SIZE, \r
+             CertBlock->Signature, \r
+             EFI_CERT_TYPE_RSA2048_SHA256_SIZE\r
+             );\r
+\r
+Done:\r
+  if (Rsa != NULL) {\r
+    RsaFree (Rsa);\r
+  }\r
+  if (Status) {\r
+    return EFI_SUCCESS;\r
+  } else {\r
+    return EFI_SECURITY_VIOLATION;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Update platform mode.\r
+\r
+  @param[in]  VirtualMode             The current calling mode for this function.\r
+  @param[in]  Global                  The context of this Extended SAL Variable Services Class call.\r
+  @param[in]  Mode                    SETUP_MODE or USER_MODE.\r
+\r
+**/\r
+VOID\r
+UpdatePlatformMode (\r
+  IN  BOOLEAN                   VirtualMode,\r
+  IN  ESAL_VARIABLE_GLOBAL      *Global,\r
+  IN  UINT32                    Mode\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  VARIABLE_POINTER_TRACK  Variable;\r
+  UINT32                  VarAttr;\r
+\r
+  Status = FindVariable (\r
+             Global->VariableName[VirtualMode][VAR_SETUP_MODE], \r
+             Global->GlobalVariableGuid[VirtualMode], \r
+             &Variable, \r
+             &Global->VariableGlobal[VirtualMode],\r
+             Global->FvbInstance\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  mPlatformMode  = Mode;\r
+  VarAttr        = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;\r
+  Status         = UpdateVariable (\r
+                     Global->VariableName[VirtualMode][VAR_SETUP_MODE],\r
+                     Global->GlobalVariableGuid[VirtualMode],\r
+                     &mPlatformMode,\r
+                     sizeof(UINT8),\r
+                     VarAttr,\r
+                     0,\r
+                     0,\r
+                     VirtualMode,\r
+                     Global,\r
+                     &Variable\r
+                     );\r
+  ASSERT_EFI_ERROR (Status);\r
+}\r
+\r
+/**\r
+  Process variable with platform key for verification.\r
+\r
+  @param[in]  VariableName                The name of Variable to be found.\r
+  @param[in]  VendorGuid                  The variable vendor GUID.\r
+  @param[in]  Data                        The data pointer.\r
+  @param[in]  DataSize                    The size of Data found. If size is less than the\r
+                                          data, this value contains the required size.\r
+  @param[in]  VirtualMode                 The current calling mode for this function.\r
+  @param[in]  Global                      The context of this Extended SAL Variable Services Class call.\r
+  @param[in]  Variable                    The variable information which is used to keep track of variable usage.\r
+  @param[in]  Attributes                  The attribute value of the variable.\r
+  @param[in]  IsPk                        Indicates whether to process pk.\r
+\r
+  @retval EFI_INVALID_PARAMETER           Invalid parameter.\r
+  @retval EFI_SECURITY_VIOLATION          The variable does NOT pass the validation \r
+                                          check carried out by the firmware. \r
+  @retval EFI_SUCCESS                     The variable passed validation successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+ProcessVarWithPk (\r
+  IN  CHAR16                    *VariableName,\r
+  IN  EFI_GUID                  *VendorGuid,\r
+  IN  VOID                      *Data,\r
+  IN  UINTN                     DataSize,\r
+  IN  BOOLEAN                   VirtualMode,\r
+  IN  ESAL_VARIABLE_GLOBAL      *Global,\r
+  IN  VARIABLE_POINTER_TRACK    *Variable,\r
+  IN  UINT32                    Attributes OPTIONAL,\r
+  IN  BOOLEAN                   IsPk\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  VARIABLE_POINTER_TRACK      PkVariable;\r
+  EFI_SIGNATURE_LIST          *OldPkList;\r
+  EFI_SIGNATURE_DATA          *OldPkData;\r
+  EFI_VARIABLE_AUTHENTICATION *CertData;\r
+  VARIABLE_HEADER             VariableHeader;\r
+  BOOLEAN                     Valid;\r
+\r
+  OldPkList = NULL;\r
+\r
+  if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {\r
+    //\r
+    // PK and KEK should set EFI_VARIABLE_NON_VOLATILE attribute.\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (mPlatformMode == USER_MODE) {\r
+    if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) {\r
+      //\r
+      // In user mode, PK and KEK should set EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    CertData = (EFI_VARIABLE_AUTHENTICATION *) Data;\r
+\r
+    if (Variable->CurrPtr != 0x0) {\r
+      Valid = IsValidVariableHeader (\r
+                Variable->CurrPtr, \r
+                Variable->Volatile, \r
+                &Global->VariableGlobal[VirtualMode], \r
+                Global->FvbInstance, \r
+                &VariableHeader\r
+                );\r
+      ASSERT (Valid);\r
+\r
+      if (CertData->MonotonicCount <= VariableHeader.MonotonicCount) {\r
+        //\r
+        // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION.\r
+        //\r
+        return EFI_SECURITY_VIOLATION;\r
+      }\r
+    }\r
+    //\r
+    // Get platform key from variable.\r
+    //\r
+    Status = FindVariable (\r
+               Global->VariableName[VirtualMode][VAR_PLATFORM_KEY], \r
+               Global->GlobalVariableGuid[VirtualMode], \r
+               &PkVariable, \r
+               &Global->VariableGlobal[VirtualMode],\r
+               Global->FvbInstance\r
+               );\r
+    ASSERT_EFI_ERROR (Status);\r
+\r
+    ZeroMem (Global->KeyList, MAX_KEYDB_SIZE);\r
+    GetVariableDataPtr (\r
+      PkVariable.CurrPtr,\r
+      PkVariable.Volatile,\r
+      &Global->VariableGlobal[VirtualMode],\r
+      Global->FvbInstance,\r
+      (CHAR16 *) Global->KeyList\r
+      );\r
+\r
+    OldPkList = (EFI_SIGNATURE_LIST *) Global->KeyList;\r
+    OldPkData = (EFI_SIGNATURE_DATA *) ((UINT8 *) OldPkList + sizeof (EFI_SIGNATURE_LIST) + OldPkList->SignatureHeaderSize);\r
+    Status    = VerifyDataPayload (VirtualMode, Global, Data, DataSize, OldPkData->SignatureData);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = UpdateVariable (\r
+                 VariableName, \r
+                 VendorGuid, \r
+                 (UINT8*)Data + AUTHINFO_SIZE, \r
+                 DataSize - AUTHINFO_SIZE, \r
+                 Attributes, \r
+                 0, \r
+                 CertData->MonotonicCount, \r
+                 VirtualMode, \r
+                 Global,\r
+                 Variable\r
+                 );\r
+\r
+      if (!EFI_ERROR (Status)) {\r
+        //\r
+        // If delete PK in user mode, need change to setup mode.\r
+        //\r
+        if ((DataSize == AUTHINFO_SIZE) && IsPk) {\r
+          UpdatePlatformMode (VirtualMode, Global, SETUP_MODE);\r
+        }\r
+      }\r
+    }\r
+  } else {\r
+    Status = UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, VirtualMode, Global, Variable);\r
+    //\r
+    // If enroll PK in setup mode, need change to user mode.\r
+    //\r
+    if ((DataSize != 0) && IsPk) {\r
+      UpdatePlatformMode (VirtualMode, Global, USER_MODE);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Process variable with key exchange key for verification.\r
+\r
+  @param[in]  VariableName                The name of Variable to be found.\r
+  @param[in]  VendorGuid                  The variable vendor GUID.\r
+  @param[in]  Data                        The data pointer.\r
+  @param[in]  DataSize                    The size of Data found. If size is less than the\r
+                                          data, this value contains the required size.\r
+  @param[in]  VirtualMode                 The current calling mode for this function.\r
+  @param[in]  Global                      The context of this Extended SAL Variable Services Class call.\r
+  @param[in]  Variable                    The variable information which is used to keep track of variable usage.\r
+  @param[in]  Attributes                  The attribute value of the variable.\r
+\r
+  @retval EFI_INVALID_PARAMETER           Invalid parameter.\r
+  @retval EFI_SECURITY_VIOLATION          The variable did NOT pass the validation \r
+                                          check carried out by the firmware. \r
+  @retval EFI_SUCCESS                     The variable passed validation successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+ProcessVarWithKek (\r
+  IN  CHAR16                               *VariableName,\r
+  IN  EFI_GUID                             *VendorGuid,\r
+  IN  VOID                                 *Data,\r
+  IN  UINTN                                DataSize,\r
+  IN  BOOLEAN                              VirtualMode,\r
+  IN  ESAL_VARIABLE_GLOBAL                 *Global,\r
+  IN  VARIABLE_POINTER_TRACK               *Variable,\r
+  IN  UINT32                               Attributes OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  VARIABLE_POINTER_TRACK          KekVariable;\r
+  EFI_SIGNATURE_LIST              *KekList;\r
+  EFI_SIGNATURE_DATA              *KekItem;\r
+  UINT32                          KekCount;\r
+  EFI_VARIABLE_AUTHENTICATION     *CertData;\r
+  EFI_CERT_BLOCK_RSA_2048_SHA256  *CertBlock;\r
+  BOOLEAN                         IsFound;\r
+  UINT32                          Index;\r
+  VARIABLE_HEADER                 VariableHeader;\r
+  BOOLEAN                         Valid;\r
+\r
+  KekList = NULL;\r
+\r
+  if (mPlatformMode == USER_MODE) {\r
+    if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) {\r
+      //\r
+      // In user mode, should set EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    CertData  = (EFI_VARIABLE_AUTHENTICATION *) Data;\r
+    CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) (CertData->AuthInfo.CertData);\r
+    if (Variable->CurrPtr != 0x0) {\r
+      Valid = IsValidVariableHeader (\r
+                Variable->CurrPtr, \r
+                Variable->Volatile, \r
+                &Global->VariableGlobal[VirtualMode], \r
+                Global->FvbInstance, \r
+                &VariableHeader\r
+                );\r
+      ASSERT (Valid);\r
+\r
+      if (CertData->MonotonicCount <= VariableHeader.MonotonicCount) {\r
+        //\r
+        // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION.\r
+        //\r
+        return EFI_SECURITY_VIOLATION;\r
+      }\r
+    }\r
+    //\r
+    // Get KEK database from variable.\r
+    //\r
+    Status = FindVariable (\r
+               Global->VariableName[VirtualMode][VAR_KEY_EXCHANGE_KEY], \r
+               Global->GlobalVariableGuid[VirtualMode], \r
+               &KekVariable, \r
+               &Global->VariableGlobal[VirtualMode],\r
+               Global->FvbInstance\r
+               );\r
+    ASSERT_EFI_ERROR (Status);\r
+\r
+    ZeroMem (Global->KeyList, MAX_KEYDB_SIZE);\r
+    GetVariableDataPtr (\r
+      KekVariable.CurrPtr,\r
+      KekVariable.Volatile,\r
+      &Global->VariableGlobal[VirtualMode],\r
+      Global->FvbInstance,\r
+      (CHAR16 *) Global->KeyList\r
+      );\r
+    //\r
+    // Enumerate all Kek items in this list to verify the variable certificate data.\r
+    // If anyone is authenticated successfully, it means the variable is correct!\r
+    //\r
+    KekList   = (EFI_SIGNATURE_LIST *) Global->KeyList;\r
+    IsFound   = FALSE;\r
+    KekCount  = (KekList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - KekList->SignatureHeaderSize) / KekList->SignatureSize;\r
+    KekItem   = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekList + sizeof (EFI_SIGNATURE_LIST) + KekList->SignatureHeaderSize);\r
+    for (Index = 0; Index < KekCount; Index++) {\r
+      if (CompareMem (KekItem->SignatureData, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {\r
+        IsFound = TRUE;\r
+        break;\r
+      }\r
+      KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekItem + KekList->SignatureSize);\r
+    }\r
+\r
+    if (!IsFound) {\r
+      return EFI_SECURITY_VIOLATION;\r
+    }\r
+\r
+    Status = VerifyDataPayload (VirtualMode, Global, Data, DataSize, CertBlock->PublicKey);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = UpdateVariable (\r
+                 VariableName, \r
+                 VendorGuid, \r
+                 (UINT8*)Data + AUTHINFO_SIZE, \r
+                 DataSize - AUTHINFO_SIZE, \r
+                 Attributes, \r
+                 0, \r
+                 CertData->MonotonicCount, \r
+                 VirtualMode,\r
+                 Global,\r
+                 Variable\r
+                 );\r
+    }\r
+  } else {\r
+    //\r
+    // If in setup mode, no authentication needed.\r
+    //\r
+    Status = UpdateVariable (\r
+               VariableName, \r
+               VendorGuid, \r
+               Data, \r
+               DataSize, \r
+               Attributes, \r
+               0, \r
+               0, \r
+               VirtualMode,\r
+               Global,\r
+               Variable\r
+               );\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Process variable with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set, and return the index of associated public key.\r
+\r
+  @param[in]  Data                        The data pointer.\r
+  @param[in]  DataSize                    The size of Data found. If size is less than the\r
+                                          data, this value contains the required size.\r
+  @param[in]  VirtualMode                 The current calling mode for this function.\r
+  @param[in]  Global                      The context of this Extended SAL Variable Services Class call.\r
+  @param[in]  Variable                    The variable information which is used to keep track of variable usage.\r
+  @param[in]  Attributes                  The attribute value of the variable.\r
+  @param[out] KeyIndex                    The output index of corresponding public key in database.\r
+  @param[out] MonotonicCount              The output value of corresponding Monotonic Count.\r
+\r
+  @retval EFI_INVALID_PARAMETER           Invalid parameter.\r
+  @retval EFI_WRITE_PROTECTED             The variable is write-protected and needs authentication with\r
+                                          EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set.\r
+  @retval EFI_SECURITY_VIOLATION          The variable is with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS\r
+                                          set, but the AuthInfo does NOT pass the validation \r
+                                          check carried out by the firmware. \r
+  @retval EFI_SUCCESS                     The variable is not write-protected, or passed validation successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+VerifyVariable (\r
+  IN  VOID                      *Data,\r
+  IN  UINTN                     DataSize,\r
+  IN  BOOLEAN                   VirtualMode,\r
+  IN  ESAL_VARIABLE_GLOBAL      *Global,\r
+  IN  VARIABLE_POINTER_TRACK    *Variable,\r
+  IN  UINT32                    Attributes OPTIONAL,\r
+  OUT UINT32                    *KeyIndex OPTIONAL,\r
+  OUT UINT64                    *MonotonicCount OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  BOOLEAN                         IsDeletion;\r
+  BOOLEAN                         IsFirstTime;\r
+  UINT8                           *PubKey;\r
+  EFI_VARIABLE_AUTHENTICATION     *CertData;\r
+  EFI_CERT_BLOCK_RSA_2048_SHA256  *CertBlock;\r
+  VARIABLE_HEADER                 VariableHeader;\r
+  BOOLEAN                         Valid;\r
+\r
+  CertData    = NULL;\r
+  CertBlock   = NULL;\r
+  PubKey      = NULL;\r
+  IsDeletion  = FALSE;\r
+  Valid       = FALSE;\r
+\r
+  if (KeyIndex != NULL) {\r
+    *KeyIndex = 0;\r
+  }\r
+  //\r
+  // Determine if first time SetVariable with the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS.\r
+  //\r
+  ZeroMem (&VariableHeader, sizeof (VARIABLE_HEADER));\r
+  if (Variable->CurrPtr != 0x0) {\r
+    Valid = IsValidVariableHeader (\r
+              Variable->CurrPtr, \r
+              Variable->Volatile, \r
+              &Global->VariableGlobal[VirtualMode], \r
+              Global->FvbInstance, \r
+              &VariableHeader\r
+              );\r
+    ASSERT (Valid);\r
+  }\r
+\r
+  if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {\r
+    if (KeyIndex == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Determine current operation type.\r
+    //\r
+    if (DataSize == AUTHINFO_SIZE) {\r
+      IsDeletion = TRUE;\r
+    }\r
+    //\r
+    // Determine whether this is the first time with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set.\r
+    //\r
+    if (Variable->CurrPtr == 0x0) {\r
+      IsFirstTime = TRUE;\r
+    } else if (Valid &&(VariableHeader.Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) {\r
+      IsFirstTime = TRUE;\r
+    } else {\r
+      *KeyIndex   = VariableHeader.PubKeyIndex;\r
+      IsFirstTime = FALSE;\r
+    }\r
+  } else if (Valid && (VariableHeader.Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) { \r
+      //\r
+      // If the variable is already write-protected, it always needs authentication before update.\r
+      //\r
+      return EFI_WRITE_PROTECTED;\r
+  } else {\r
+    //\r
+    // If without EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, set and attributes collision.\r
+    // That means it is not authenticated variable, just return EFI_SUCCESS.\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Get PubKey and check Monotonic Count value corresponding to the variable.\r
+  //\r
+  CertData  = (EFI_VARIABLE_AUTHENTICATION *) Data;\r
+  CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) (CertData->AuthInfo.CertData);\r
+  PubKey    = CertBlock->PublicKey;\r
+\r
+  if (MonotonicCount != NULL) {\r
+    //\r
+    // Update Monotonic Count value.\r
+    //\r
+    *MonotonicCount = CertData->MonotonicCount;\r
+  }\r
+\r
+  if (!IsFirstTime) {\r
+    //\r
+    // Check input PubKey.\r
+    //\r
+    if (CompareMem (PubKey, Global->PubKeyStore + (*KeyIndex - 1) * EFI_CERT_TYPE_RSA2048_SIZE, EFI_CERT_TYPE_RSA2048_SIZE) != 0) {\r
+      return EFI_SECURITY_VIOLATION;\r
+    }\r
+    //\r
+    // Compare the current monotonic count and ensure that it is greater than the last SetVariable\r
+    // operation with the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute set.\r
+    //\r
+    if (CertData->MonotonicCount <= VariableHeader.MonotonicCount) {\r
+      //\r
+      // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION.\r
+      //\r
+      return EFI_SECURITY_VIOLATION;\r
+    }\r
+  } \r
+  //\r
+  // Verify the certificate in Data payload.\r
+  //\r
+  Status = VerifyDataPayload (VirtualMode, Global, Data, DataSize, PubKey);\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Now, the signature has been verified!\r
+    //\r
+    if (IsFirstTime && !IsDeletion) {\r
+      //\r
+      // Update public key database variable if need and return the index.\r
+      //\r
+      *KeyIndex = AddPubKeyInStore (VirtualMode, Global, PubKey);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r