From: "Judah Vang" <judah.vang@intel.com>
To: devel@edk2.groups.io
Cc: Jian J Wang <jian.j.wang@intel.com>,
Jiewen Yao <jiewen.yao@intel.com>,
Nishant C Mistry <nishant.c.mistry@intel.com>
Subject: [Patch v2 16/28] SecurityPkg: Add EncryptionVariable lib with AES
Date: Fri, 29 Apr 2022 11:04:18 -0700 [thread overview]
Message-ID: <20220429180430.3292-17-judah.vang@intel.com> (raw)
In-Reply-To: <20220429180430.3292-1-judah.vang@intel.com>
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2594
Add encryption/decryption of protected variable functionality.
Add functions to get/set cipher data of a protected variable.
This is use for supporting confidentiality for protected
variables.
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Nishant C Mistry <nishant.c.mistry@intel.com>
Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
Signed-off-by: Nishant C Mistry <nishant.c.mistry@intel.com>
Signed-off-by: Judah Vang <judah.vang@intel.com>
---
SecurityPkg/Library/EncryptionVariableLib/EncryptionVariableLib.inf | 43 ++
SecurityPkg/Library/EncryptionVariableLib/EncryptionVariable.h | 49 ++
SecurityPkg/Library/EncryptionVariableLib/EncryptionVariable.c | 728 ++++++++++++++++++++
3 files changed, 820 insertions(+)
diff --git a/SecurityPkg/Library/EncryptionVariableLib/EncryptionVariableLib.inf b/SecurityPkg/Library/EncryptionVariableLib/EncryptionVariableLib.inf
new file mode 100644
index 000000000000..7ece52f2fb58
--- /dev/null
+++ b/SecurityPkg/Library/EncryptionVariableLib/EncryptionVariableLib.inf
@@ -0,0 +1,43 @@
+## @file
+# Provides variable encryption/decryption services.
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010029
+ BASE_NAME = EncryptionVariableLib
+ FILE_GUID = 459E2CB0-AF4B-4415-B6A1-335E71FD8B85
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = EncryptionVariableLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ EncryptionVariable.c
+ EncryptionVariable.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ SecurityPkg/SecurityPkg.dec
+ CryptoPkg/CryptoPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ BaseCryptLib
+
+[Guids]
+ gEfiVariableGuid
+ gEfiAuthenticatedVariableGuid
diff --git a/SecurityPkg/Library/EncryptionVariableLib/EncryptionVariable.h b/SecurityPkg/Library/EncryptionVariableLib/EncryptionVariable.h
new file mode 100644
index 000000000000..f35f9f9e3ad7
--- /dev/null
+++ b/SecurityPkg/Library/EncryptionVariableLib/EncryptionVariable.h
@@ -0,0 +1,49 @@
+/** @file
+ Definitions used by this library implementation.
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef ENCRYPTION_VARIABLE_H_
+#define ENCRYPTION_VARIABLE_H_
+
+#define ENC_KEY_SEP L":"
+#define ENC_KEY_SEP_SIZE 2
+#define ENC_KEY_NAME L"VAR_ENC_KEY"
+#define ENC_KEY_NAME_SIZE 22
+
+#define ENC_KEY_SIZE (256/8)
+#define ENC_BLOCK_SIZE AES_BLOCK_SIZE
+#define ENC_IVEC_SIZE ENC_BLOCK_SIZE
+
+#define ENC_PADDING_BYTE 0x0F
+
+//
+// PKCS#5 padding
+//
+// #define AES_CIPHER_DATA_SIZE(PlainDataSize)
+// (AES_BLOCK_SIZE + (PlainDataSize)) & (~(AES_BLOCK_SIZE - 1))
+//
+#define AES_CIPHER_DATA_SIZE(PlainDataSize) ALIGN_VALUE (PlainDataSize, AES_BLOCK_SIZE)
+
+#define FREE_POOL(Address) \
+ if ((Address) != NULL) { \
+ FreePool (Address); \
+ (Address) = NULL; \
+ }
+
+#pragma pack(1)
+
+typedef struct {
+ UINT32 DataType; // SYM_TYPE_AES
+ UINT32 HeaderSize; // sizeof(VARIABLE_ENCRYPTION_HEADER)
+ UINT32 PlainDataSize; // Plain data size
+ UINT32 CipherDataSize; // Cipher data size
+ UINT8 KeyIvec[ENC_IVEC_SIZE];
+} VARIABLE_ENCRYPTION_HEADER;
+
+#pragma pack()
+
+#endif // _ENCRYPTION_VARIABLE_H_
diff --git a/SecurityPkg/Library/EncryptionVariableLib/EncryptionVariable.c b/SecurityPkg/Library/EncryptionVariableLib/EncryptionVariable.c
new file mode 100644
index 000000000000..58ffb09e0373
--- /dev/null
+++ b/SecurityPkg/Library/EncryptionVariableLib/EncryptionVariable.c
@@ -0,0 +1,728 @@
+/** @file
+ Implementation of EncryptionVariableLib with AES algorithm support.
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+
+#include <Guid/VariableFormat.h>
+#include <Library/EncryptionVariableLib.h>
+#include <Library/BaseCryptLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+
+#include "EncryptionVariable.h"
+
+/**
+ Derive encryption key for given variable from variable root key.
+
+ The derivation algorithm is depicted below
+
+ HKDF_Expand(SHA256, RootKey, Name||':'||Guid||':'||Attr||"VAR_ENC_KEY")
+
+ @param[in] VarEncInfo Pointer to structure containing detailed
+ information about a variable.
+ @param[in] EncKeySize Size of key requested.
+ @param[out] EncKey Buffer of key.
+
+ @retval TRUE The key was derived successfully.
+ @retval FALSE Failed to generate encryption key.
+
+**/
+STATIC
+BOOLEAN
+EncVarLibGenEncKey (
+ IN VARIABLE_ENCRYPTION_INFO *VarEncInfo,
+ IN UINTN EncKeySize,
+ OUT UINT8 *EncKey
+ )
+{
+ BOOLEAN Status;
+
+ struct {
+ VOID *Data;
+ UINTN Size;
+ } InfoGroup[6];
+ UINT8 *Info;
+ UINTN InfoSize;
+ UINTN Index;
+ UINT8 Salt[16];
+
+ //
+ // info: Name||':'||Guid||':'||Attr||"VAR_ENC_KEY"
+ //
+ InfoGroup[0].Size = VarEncInfo->Header.NameSize;
+ InfoGroup[0].Data = VarEncInfo->Header.VariableName;
+
+ InfoGroup[1].Size = ENC_KEY_SEP_SIZE;
+ InfoGroup[1].Data = ENC_KEY_SEP;
+
+ InfoGroup[2].Size = sizeof (*VarEncInfo->Header.VendorGuid);
+ InfoGroup[2].Data = VarEncInfo->Header.VendorGuid;
+
+ InfoGroup[3].Size = ENC_KEY_SEP_SIZE;
+ InfoGroup[3].Data = ENC_KEY_SEP;
+
+ InfoGroup[4].Size = sizeof (VarEncInfo->Header.Attributes);
+ InfoGroup[4].Data = &VarEncInfo->Header.Attributes;
+
+ InfoGroup[5].Size = ENC_KEY_NAME_SIZE;
+ InfoGroup[5].Data = ENC_KEY_NAME;
+
+ for (InfoSize = 0, Index = 0; Index < ARRAY_SIZE (InfoGroup); ++Index) {
+ InfoSize += InfoGroup[Index].Size;
+ }
+
+ Info = AllocateZeroPool (InfoSize);
+ if (Info == NULL) {
+ ASSERT (Info != NULL);
+ return FALSE;
+ }
+
+ for (InfoSize = 0, Index = 0; Index < ARRAY_SIZE (InfoGroup); ++Index) {
+ CopyMem (Info + InfoSize, InfoGroup[Index].Data, InfoGroup[Index].Size);
+ InfoSize += InfoGroup[Index].Size;
+ }
+
+ Status = HkdfSha256ExtractAndExpand (
+ VarEncInfo->Key,
+ VarEncInfo->KeySize,
+ Salt,
+ 0,
+ Info,
+ InfoSize,
+ EncKey,
+ EncKeySize
+ );
+
+ FreePool (Info);
+
+ return Status;
+}
+
+/**
+ Generate init-vector for AES encryption.
+
+ @param[out] InitVector IVEC buffer.
+ @param[in] Size Size of IVEC requested.
+
+ @retval TRUE IVEC was generated successfully.
+ @retval FALSE Failed to generate IVEC.
+
+**/
+STATIC
+BOOLEAN
+EncVarLibGenIvec (
+ OUT UINT8 *InitVector,
+ IN UINTN Size
+ )
+{
+ return RandomBytes (InitVector, Size);
+}
+
+/**
+ Check if there's valid variable information needed by encrypting or decrypting.
+
+ @param[in] VarEncInfo Buffer conveying details about a variable.
+ @param[in] CheckForEnc Flag indicating check for encrypting (TRUE) or
+ decrypting (FALSE).
+
+ @retval TRUE VarEncInfo is valid.
+ @retval FALSE VarEncInfo is invalid.
+
+**/
+STATIC
+BOOLEAN
+IsValidVariableInfo (
+ IN VARIABLE_ENCRYPTION_INFO *VarEncInfo,
+ IN BOOLEAN CheckForEnc
+ )
+{
+ BOOLEAN Valid;
+
+ if (CheckForEnc) {
+ Valid = (VarEncInfo->Header.Data != NULL && VarEncInfo->Header.DataSize > 0)
+ || (VarEncInfo->PlainData != NULL && VarEncInfo->PlainDataSize > 0);
+ if (!Valid) {
+ ASSERT (
+ (VarEncInfo->Header.Data != NULL && VarEncInfo->Header.DataSize > 0)
+ || (VarEncInfo->PlainData != NULL && VarEncInfo->PlainDataSize > 0)
+ );
+ }
+ } else {
+ Valid = (VarEncInfo->Header.Data != NULL && VarEncInfo->Header.DataSize > 0)
+ || (VarEncInfo->CipherData != NULL && VarEncInfo->CipherDataSize > 0);
+ if (!Valid) {
+ ASSERT (
+ (VarEncInfo->Header.Data != NULL && VarEncInfo->Header.DataSize > 0)
+ || (VarEncInfo->CipherData != NULL && VarEncInfo->CipherDataSize > 0)
+ );
+ }
+ }
+
+ Valid = Valid
+ && VarEncInfo->Header.VariableName != NULL
+ && VarEncInfo->Header.NameSize > 0
+ && VarEncInfo->Header.VendorGuid != NULL
+ && VarEncInfo->Key != NULL
+ && VarEncInfo->KeySize > 0;
+ if (!Valid) {
+ ASSERT (VarEncInfo->Header.VariableName != NULL);
+ ASSERT (VarEncInfo->Header.NameSize != 0);
+ ASSERT (VarEncInfo->Header.VendorGuid != NULL);
+ ASSERT (VarEncInfo->Key != NULL);
+ ASSERT (VarEncInfo->KeySize > 0);
+ }
+
+ return Valid;
+}
+
+/**
+ Sanity check of encrption header prefixed to encrypted data.
+
+ @param[in] EncHeader Pointer to VARIABLE_ENCRYPTION_HEADER.
+ @param[in] DataSize Size of variable data payload.
+
+ @retval TRUE EncHeader is valid.
+ @retval FALSE EncHeader is invalid.
+
+**/
+STATIC
+BOOLEAN
+IsValidEncrptionHeader (
+ IN VARIABLE_ENCRYPTION_HEADER *EncHeader,
+ IN UINT32 DataSize
+ )
+{
+ if ( (DataSize > sizeof (VARIABLE_ENCRYPTION_HEADER))
+ && ((EncHeader->DataType == ENC_TYPE_AES) || (EncHeader->DataType == ENC_TYPE_NULL))
+ && (EncHeader->HeaderSize >= sizeof (VARIABLE_ENCRYPTION_HEADER))
+ && (EncHeader->CipherDataSize > 0)
+ && ((EncHeader->CipherDataSize % ENC_BLOCK_SIZE) == 0)
+ && (EncHeader->PlainDataSize > 0)
+ && (EncHeader->PlainDataSize <= EncHeader->CipherDataSize)
+ && ((EncHeader->CipherDataSize + EncHeader->HeaderSize) <= DataSize))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Encrypt variable data.
+
+ If VarEncInfo->PlainData is not NULL, VarEncInfo->PlainData holds the plain
+ data. Otherwise, VarEncInfo->Headr.Data is supposed to be the plain data.
+
+ If VarEncInfo->CipherData is not NULL, The encrypted data is stored in
+ VarEncInfo->CipherData. Otherwise, the encrypted data is stored directly
+ in variable data buffer, i.e. VarEncInfo->Headr.Data.
+
+ @param[in, out] VarEncInfo Pointer to structure containing detailed
+ information about a variable.
+
+ @retval EFI_SUCCESS Variable was encrypted successfully.
+ @retval EFI_INVALID_PARAMETER Variable information in VarEncInfo is invalid.
+ @retval EFI_BUFFER_TOO_SMALL VarEncInfo->CipherData is not NULL but
+ VarEncInfo->CipherDataSize is too small.
+ @retval EFI_ABORTED Uknown error occurred during encrypting.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource.
+ @retval EFI_UNSUPPORTED Unsupported to encrypt variable.
+
+**/
+EFI_STATUS
+EFIAPI
+EncryptVariable (
+ IN OUT VARIABLE_ENCRYPTION_INFO *VarEncInfo
+ )
+{
+ EFI_STATUS Status;
+ VOID *AesContext;
+ UINT8 EncKey[ENC_KEY_SIZE];
+ UINT8 Ivec[ENC_IVEC_SIZE];
+ UINT8 *PlainBuffer;
+ UINT8 *CipherBuffer;
+ UINT8 *PlainData;
+ UINT32 PlainDataSize;
+ VARIABLE_ENCRYPTION_HEADER *CipherData;
+ UINT32 CipherDataSize;
+ UINT32 PaddingBytes;
+
+ Status = EFI_ABORTED;
+ AesContext = NULL;
+ PlainBuffer = NULL;
+ CipherBuffer = NULL;
+
+ if (!IsValidVariableInfo (VarEncInfo, TRUE)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (VarEncInfo->PlainData != NULL) {
+ PlainData = VarEncInfo->PlainData;
+ PlainDataSize = VarEncInfo->PlainDataSize;
+ } else {
+ PlainData = VarEncInfo->Header.Data;
+ PlainDataSize = (UINT32)VarEncInfo->Header.DataSize;
+ }
+
+ CipherDataSize = AES_CIPHER_DATA_SIZE (PlainDataSize);
+ if (VarEncInfo->CipherData != NULL) {
+ if (VarEncInfo->CipherDataSize
+ < (CipherDataSize + sizeof (VARIABLE_ENCRYPTION_HEADER)))
+ {
+ VarEncInfo->CipherDataSize = CipherDataSize
+ + sizeof (VARIABLE_ENCRYPTION_HEADER);
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CipherData = VarEncInfo->CipherData;
+ } else {
+ CipherData = VarEncInfo->Header.Data;
+ }
+
+ //
+ // Prepare buffer for encrypted data.
+ //
+ if ((UINTN)CipherData == (UINTN)PlainData) {
+ //
+ // Need buffer to store the encrypted data temporarily.
+ //
+ CipherBuffer = (UINT8 *)AllocateZeroPool (
+ CipherDataSize
+ + sizeof (VARIABLE_ENCRYPTION_HEADER)
+ );
+ if (CipherBuffer == NULL) {
+ ASSERT (CipherBuffer != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ CipherBuffer = (UINT8 *)CipherData;
+ }
+
+ //
+ // Plain variable data must also be multiple of ENC_BLOCK_SIZE.
+ //
+ PaddingBytes = ALIGN_VALUE (PlainDataSize, ENC_BLOCK_SIZE) - PlainDataSize;
+ if (PaddingBytes != 0) {
+ //
+ // Since the plain data size will be saved in the VARIABLE_ENCRYPTION_HEADER,
+ // there's no need to do PKCS way of padding. To save space, just padding
+ // the plain data to be of the nearest n*ENC_BLOCK_SIZE.
+ //
+ PlainBuffer = AllocateZeroPool (PlainDataSize + PaddingBytes);
+ if (PlainBuffer == NULL) {
+ ASSERT (PlainBuffer != NULL);
+ goto Done;
+ }
+
+ CopyMem (PlainBuffer, PlainData, PlainDataSize);
+ SetMem (PlainBuffer + PlainDataSize, PaddingBytes, ENC_PADDING_BYTE);
+ } else {
+ PlainBuffer = PlainData;
+ }
+
+ //
+ // Skip EFI_VARIABLE_APPEND_WRITE bit in generating encryption key.
+ //
+ VarEncInfo->Header.Attributes &= (~EFI_VARIABLE_APPEND_WRITE);
+ if (!EncVarLibGenEncKey (VarEncInfo, ENC_KEY_SIZE, EncKey)) {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ if (!EncVarLibGenIvec (Ivec, ENC_IVEC_SIZE)) {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ AesContext = AllocateZeroPool (AesGetContextSize ());
+ if (AesContext == NULL) {
+ ASSERT (AesContext != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!AesInit (AesContext, EncKey, ENC_KEY_SIZE * 8)) {
+ ASSERT (FALSE);
+ goto Done;
+ }
+
+ if (AesCbcEncrypt (
+ AesContext,
+ PlainBuffer,
+ PlainDataSize + PaddingBytes,
+ Ivec,
+ CipherBuffer + sizeof (VARIABLE_ENCRYPTION_HEADER)
+ ))
+ {
+ //
+ // Keep the IV for decryption.
+ //
+ CopyMem (CipherData->KeyIvec, Ivec, ENC_BLOCK_SIZE);
+
+ if ((UINTN)CipherBuffer != (UINTN)CipherData) {
+ CopyMem (
+ CipherData + 1,
+ CipherBuffer + sizeof (VARIABLE_ENCRYPTION_HEADER),
+ CipherDataSize
+ );
+ }
+
+ CipherData->CipherDataSize = CipherDataSize;
+ CipherData->PlainDataSize = PlainDataSize;
+ CipherData->DataType = ENC_TYPE_AES;
+ CipherData->HeaderSize = sizeof (VARIABLE_ENCRYPTION_HEADER);
+
+ VarEncInfo->CipherData = CipherData;
+ VarEncInfo->CipherDataSize = CipherDataSize + sizeof (VARIABLE_ENCRYPTION_HEADER);
+ VarEncInfo->CipherHeaderSize = sizeof (VARIABLE_ENCRYPTION_HEADER);
+ VarEncInfo->CipherDataType = ENC_TYPE_AES;
+
+ Status = EFI_SUCCESS;
+ } else {
+ VarEncInfo->CipherData = NULL;
+ VarEncInfo->CipherDataSize = 0;
+ VarEncInfo->CipherHeaderSize = 0;
+ VarEncInfo->CipherDataType = ENC_TYPE_NULL;
+
+ ASSERT (FALSE);
+ }
+
+Done:
+ FREE_POOL (AesContext);
+ if (PlainBuffer != PlainData) {
+ FREE_POOL (PlainBuffer);
+ }
+
+ if (CipherBuffer != (UINT8 *)CipherData) {
+ FREE_POOL (CipherBuffer);
+ }
+
+ return Status;
+}
+
+/**
+ Decrypt variable data.
+
+ If VarEncInfo->CipherData is not NULL, it must holds the cipher data to be
+ decrypted. Otherwise, assume the cipher data from variable data buffer, i.e.
+ VarEncInfo->Header.Data.
+
+ If VarEncInfo->Flags.DecryptInPlace is TRUE, the decrypted data will be put
+ back in the same buffer as cipher buffer got above, after encryption header,
+ which helps to identify later if the data in buffer is decrypted or not. This
+ can avoid repeat decryption when accessing the same variable more than once.
+
+ If VarEncInfo->Flags.DecryptInPlace is FALSE, VarEncInfo->PlainData must be
+ passed in with a valid buffer with VarEncInfo->PlainDataSize set correctly
+ with its size.
+
+ Note the VarEncInfo->PlainData is always pointing to the buffer address with
+ decrypted data without encryption header, and VarEncInfo->PlainDataSize is
+ always the size of original variable data, if this function returned
+ successfully.
+
+ @param[in, out] VarEncInfo Pointer to structure containing detailed
+ information about a variable.
+
+ @retval EFI_SUCCESS Variable was decrypted successfully.
+ @retval EFI_INVALID_PARAMETER Variable information in VarEncInfo is invalid.
+ @retval EFI_BUFFER_TOO_SMALL VarEncInfo->PlainData is not NULL but
+ VarEncInfo->PlainDataSize is too small.
+ @retval EFI_ABORTED Uknown error occurred during decrypting.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource.
+ @retval EFI_COMPROMISED_DATA The cipher header is not valid.
+ @retval EFI_UNSUPPORTED Unsupported to encrypt variable.
+
+**/
+EFI_STATUS
+EFIAPI
+DecryptVariable (
+ IN OUT VARIABLE_ENCRYPTION_INFO *VarEncInfo
+ )
+{
+ VOID *AesContext;
+ UINT8 EncKey[ENC_KEY_SIZE];
+ UINT8 *PlainBuffer;
+ UINT8 *PlainData;
+ VARIABLE_ENCRYPTION_HEADER *CipherData;
+ UINT32 CipherDataSize;
+ EFI_STATUS Status;
+
+ Status = EFI_ABORTED;
+ AesContext = NULL;
+ PlainBuffer = NULL;
+
+ if (!IsValidVariableInfo (VarEncInfo, FALSE)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (VarEncInfo->CipherData != NULL) {
+ CipherData = VarEncInfo->CipherData;
+ CipherDataSize = VarEncInfo->CipherDataSize;
+ } else {
+ CipherData = VarEncInfo->Header.Data;
+ CipherDataSize = (UINT32)VarEncInfo->Header.DataSize;
+ }
+
+ //
+ // Sanity check of cipher header.
+ //
+ if (!IsValidEncrptionHeader (CipherData, CipherDataSize)) {
+ return EFI_COMPROMISED_DATA;
+ }
+
+ if ( (VarEncInfo->PlainData != NULL)
+ && (VarEncInfo->PlainDataSize < CipherData->PlainDataSize))
+ {
+ VarEncInfo->PlainDataSize = CipherData->PlainDataSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ if (CipherData->DataType == ENC_TYPE_AES) {
+ if (VarEncInfo->Flags.DecryptInPlace) {
+ //
+ // Reusing cipher data buffer needs to keep the encryption header.
+ //
+ PlainData = (UINT8 *)CipherData + CipherData->HeaderSize;
+ } else {
+ PlainData = VarEncInfo->PlainData;
+ }
+
+ if (PlainData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Always need buffer to store the decrypted data temporarily, due to
+ // padding bytes or buffer reuse. Then the buffer must be larger than
+ // CipherData->PlainDataSize.
+ //
+ PlainBuffer = AllocatePages (EFI_SIZE_TO_PAGES (CipherDataSize));
+ if (PlainBuffer == NULL) {
+ ASSERT (PlainBuffer != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!EncVarLibGenEncKey (VarEncInfo, ENC_KEY_SIZE, EncKey)) {
+ ASSERT (FALSE);
+ goto Done;
+ }
+
+ AesContext = AllocatePages (EFI_SIZE_TO_PAGES (AesGetContextSize ()));
+ if (AesContext == NULL) {
+ ASSERT (AesContext != NULL);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+
+ if (!AesInit (AesContext, EncKey, ENC_KEY_SIZE * 8)) {
+ ASSERT (FALSE);
+ goto Done;
+ }
+
+ if (AesCbcDecrypt (
+ AesContext,
+ (UINT8 *)CipherData + CipherData->HeaderSize,
+ CipherDataSize - CipherData->HeaderSize,
+ CipherData->KeyIvec,
+ PlainBuffer
+ ))
+ {
+ Status = EFI_SUCCESS;
+ } else {
+ Status = EFI_COMPROMISED_DATA;
+ }
+ } else {
+ //
+ // The data has been decrypted already.
+ //
+ PlainBuffer = (UINT8 *)CipherData + CipherData->HeaderSize;
+
+ if (VarEncInfo->PlainData != NULL) {
+ PlainData = VarEncInfo->PlainData;
+ } else {
+ PlainData = PlainBuffer;
+ }
+
+ Status = EFI_SUCCESS;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ if (PlainBuffer != PlainData) {
+ CopyMem (PlainData, PlainBuffer, CipherData->PlainDataSize);
+ }
+
+ if (VarEncInfo->PlainData != NULL) {
+ if (VarEncInfo->PlainData != PlainBuffer) {
+ CopyMem (VarEncInfo->PlainData, PlainBuffer, CipherData->PlainDataSize);
+ }
+ } else {
+ VarEncInfo->PlainData = PlainData;
+ }
+
+ VarEncInfo->PlainDataSize = CipherData->PlainDataSize;
+ VarEncInfo->CipherHeaderSize = CipherData->HeaderSize;
+ VarEncInfo->CipherDataType = CipherData->DataType;
+
+ if (VarEncInfo->Flags.DecryptInPlace) {
+ CipherData->DataType = ENC_TYPE_NULL;
+ }
+ }
+
+Done:
+ FreePages (PlainBuffer, EFI_SIZE_TO_PAGES ((CipherDataSize)));
+ FreePages (AesContext, EFI_SIZE_TO_PAGES (AesGetContextSize ()));
+ return Status;
+}
+
+/**
+ Get cipher information about a variable, including plaindata size,
+ cipher algorithm type, etc.
+
+ For data passed in with VarEncInfo,
+
+ VarEncInfo->Header.Data
+ - The variable data in normal variable structure.
+ VarEncInfo->Header.DataSize
+ - The size of variable data.
+
+ For data passed out with VarEncInfo (valid only if EFI_SUCCESS is returned),
+
+ VarEncInfo->CipherDataType
+ - ENC_TYPE_NULL, if the variable is not encrypted or has been decrypted;
+ - ENC_TYPE_AES, if the variable is encrypted.
+ VarEncInfo->CipherHeaderSize
+ - Size of cipher header put before encrypted or decrypted data.
+ VarEncInfo->PlainData
+ - NULL, if the variable is encrypted; Or
+ - pointer to original variable data, if the variable has been decrypted.
+ VarEncInfo->PlainDataSize
+ - The size of original variable data
+ VarEncInfo->CipherData
+ - NULL, if the variable is decrypted; Or
+ - pointer to start of encrypted variable data, including encryption header;
+ VarEncInfo->CipherDataSize
+ - The size of encrypted variable data, including encryption header.
+
+ @param[in] VarEncInfo Pointer to structure containing detailed
+ information about a variable.
+
+ @retval EFI_SUCCESS The information was retrieved successfully.
+ @retval EFI_INVALID_PARAMETER Variable information in VarEncInfo is invalid.
+ @retval EFI_NOT_FOUND No cipher information recognized.
+ @retval EFI_UNSUPPORTED Unsupported interface.
+
+**/
+EFI_STATUS
+EFIAPI
+GetCipherDataInfo (
+ IN VARIABLE_ENCRYPTION_INFO *VarEncInfo
+ )
+{
+ VARIABLE_ENCRYPTION_HEADER *EncHeader;
+
+ if ((VarEncInfo->Header.Data == NULL) || (VarEncInfo->Header.DataSize == 0)) {
+ ASSERT (VarEncInfo->Header.Data != NULL);
+ ASSERT (VarEncInfo->Header.DataSize != 0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Validate encryption header.
+ //
+ EncHeader = (VARIABLE_ENCRYPTION_HEADER *)VarEncInfo->Header.Data;
+ if (!IsValidEncrptionHeader (EncHeader, (UINT32)VarEncInfo->Header.DataSize)) {
+ //
+ // Not an encrypted variable.
+ //
+ return EFI_NOT_FOUND;
+ }
+
+ if (EncHeader->DataType == ENC_TYPE_NULL) {
+ //
+ // The data must have been decrypted.
+ //
+ VarEncInfo->PlainData = (UINT8 *)VarEncInfo->Header.Data + EncHeader->HeaderSize;
+ VarEncInfo->CipherData = NULL;
+ VarEncInfo->Flags.DecryptInPlace = TRUE;
+ } else {
+ //
+ // The data is encrypted.
+ //
+ VarEncInfo->CipherData = VarEncInfo->Header.Data;
+ VarEncInfo->PlainData = NULL;
+ VarEncInfo->Flags.DecryptInPlace = FALSE;
+ }
+
+ VarEncInfo->PlainDataSize = EncHeader->PlainDataSize;
+ VarEncInfo->CipherDataSize = EncHeader->CipherDataSize + EncHeader->HeaderSize;
+ VarEncInfo->CipherDataType = EncHeader->DataType;
+ VarEncInfo->CipherHeaderSize = EncHeader->HeaderSize;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Force set cipher information for a variable, like plaindata size,
+ cipher algorithm type, cipher data etc.
+
+ The destination buffer must be passed via VarEncInfo->Header.Data.
+
+ This method is only used to update and/or change plain data information.
+
+ @param[in] VarEncInfo Pointer to structure containing detailed
+ information about a variable.
+
+ @retval EFI_SUCCESS The information was updated successfully.
+ @retval EFI_INVALID_PARAMETER Variable information in VarEncInfo is invalid.
+ @retval EFI_UNSUPPORTED If this method is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SetCipherDataInfo (
+ IN VARIABLE_ENCRYPTION_INFO *VarEncInfo
+ )
+{
+ VARIABLE_ENCRYPTION_HEADER *EncHeader;
+ UINT8 *Data;
+
+ if ( (VarEncInfo->Header.Data == NULL)
+ || (VarEncInfo->Header.DataSize < sizeof (VARIABLE_ENCRYPTION_HEADER))
+ || (VarEncInfo->CipherDataType != ENC_TYPE_NULL))
+ {
+ ASSERT (VarEncInfo->Header.Data != NULL);
+ ASSERT (VarEncInfo->Header.DataSize >= sizeof (VARIABLE_ENCRYPTION_HEADER));
+ ASSERT (VarEncInfo->CipherDataType == ENC_TYPE_NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Data = VarEncInfo->Header.Data;
+ EncHeader = (VARIABLE_ENCRYPTION_HEADER *)Data;
+
+ if ( !IsValidEncrptionHeader (EncHeader, (UINT32)VarEncInfo->Header.DataSize)
+ || (VarEncInfo->PlainDataSize > EncHeader->CipherDataSize))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((VarEncInfo->PlainData != NULL) && (VarEncInfo->PlainDataSize > 0)) {
+ CopyMem (
+ Data + EncHeader->HeaderSize,
+ VarEncInfo->PlainData,
+ VarEncInfo->PlainDataSize
+ );
+ }
+
+ EncHeader->DataType = VarEncInfo->CipherDataType;
+ if (VarEncInfo->PlainDataSize != 0) {
+ EncHeader->PlainDataSize = VarEncInfo->PlainDataSize;
+ }
+
+ return EFI_SUCCESS;
+}
--
2.35.1.windows.2
next prev parent reply other threads:[~2022-04-29 18:05 UTC|newest]
Thread overview: 49+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-04-29 18:04 [Patch v2 00/28] UEFI variable protection Judah Vang
2022-04-29 18:04 ` [Patch v2 01/28] MdeModulePkg: Add new GUID for Variable Store Info Judah Vang
2022-05-12 9:32 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 02/28] SecurityPkg: Add new GUIDs for Judah Vang
2022-05-12 9:33 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 03/28] MdeModulePkg: Update AUTH_VARIABLE_INFO struct Judah Vang
2022-05-12 9:33 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 04/28] MdeModulePkg: Add reference to new Ppi Guid Judah Vang
2022-05-12 9:32 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 05/28] MdeModulePkg: Add new ProtectedVariable GUIDs Judah Vang
2022-05-12 9:32 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 06/28] MdeModulePkg: Add new include files Judah Vang
2022-05-12 9:31 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 07/28] MdeModulePkg: Add Null ProtectedVariable Library Judah Vang
2022-05-22 8:38 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 08/28] MdeModulePkg: Add new Variable functionality Judah Vang
2022-05-22 10:24 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 09/28] MdeModulePkg: Add support for Protected Variables Judah Vang
2022-05-22 14:03 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 10/28] SecurityPkg: Add new KeyService types and defines Judah Vang
2022-05-22 14:06 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 11/28] SecurityPkg: Update RPMC APIs with index Judah Vang
2022-05-22 14:07 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 12/28] SecurityPkg: Add new variable types and functions Judah Vang
2022-05-22 14:12 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 13/28] SecurityPkg: Fix GetVariableKey API Judah Vang
2022-05-22 14:15 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 14/28] SecurityPkg: Add null encryption variable libs Judah Vang
2022-05-22 14:20 ` Wang, Jian J
2022-04-29 18:04 ` [Patch v2 15/28] SecurityPkg: Add VariableKey library function Judah Vang
2022-04-29 18:04 ` Judah Vang [this message]
2022-04-29 18:04 ` [Patch v2 17/28] SecurityPkg: Add Protected Variable Services Judah Vang
2022-04-29 18:04 ` [Patch v2 18/28] MdeModulePkg: Reference Null ProtectedVariableLib Judah Vang
2022-04-29 18:04 ` [Patch v2 19/28] SecurityPkg: Add references to new *.inf files Judah Vang
2022-04-29 18:04 ` [Patch v2 20/28] ArmVirtPkg: Add reference to ProtectedVariableNull Judah Vang
2022-05-03 7:29 ` Ard Biesheuvel
2022-04-29 18:04 ` [Patch v2 21/28] UefiPayloadPkg: Add ProtectedVariable reference Judah Vang
2022-04-29 21:03 ` Guo Dong
2022-04-29 18:04 ` [Patch v2 22/28] EmulatorPkg: " Judah Vang
2022-04-29 18:04 ` [Patch v2 23/28] OvmfPkg: " Judah Vang
2022-04-29 18:04 ` [Patch v2 24/28] OvmfPkg: Add ProtectedVariableLib reference Judah Vang
2022-04-29 18:04 ` [Patch v2 25/28] " Judah Vang
2022-04-29 18:04 ` [Patch v2 26/28] " Judah Vang
2022-04-29 18:04 ` [Patch v2 27/28] OvmfPkg: Add ProtectedVariable reference Judah Vang
2022-05-03 7:30 ` [edk2-devel] " Ard Biesheuvel
2022-04-29 18:04 ` [Patch v2 28/28] CryptoPkg: Enable cypto HMAC KDF library Judah Vang
2022-05-17 2:48 ` [edk2-devel] [Patch v2 00/28] UEFI variable protection Michael Kubacki
[not found] ` <16EFC4965F71DFB8.20068@groups.io>
2022-06-16 19:13 ` Michael Kubacki
2022-08-23 3:35 ` Michael Kubacki
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220429180430.3292-17-judah.vang@intel.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox