They will do basic validation for authentication data structure, then call crypto library\r
to verify the signature.\r
\r
-Copyright (c) 2009 - 2016, 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
+Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
//\r
CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 };\r
\r
+CONST UINT8 mSha256OidValue[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 };\r
+\r
//\r
// Requirement for different signature type which have been defined in UEFI spec.\r
// These data are used to perform SignatureList format check while setting PK/KEK variable.\r
);\r
}\r
\r
-/**\r
- Update the variable region with Variable information.\r
-\r
- @param[in] VariableName Name of variable.\r
- @param[in] VendorGuid Guid of variable.\r
- @param[in] Data Data pointer.\r
- @param[in] DataSize Size of Data.\r
- @param[in] Attributes Attribute value of the variable.\r
- @param[in] KeyIndex Index of associated public key.\r
- @param[in] MonotonicCount Value of associated monotonic count.\r
-\r
- @retval EFI_SUCCESS The update operation is success.\r
- @retval EFI_INVALID_PARAMETER Invalid parameter.\r
- @retval EFI_WRITE_PROTECTED Variable is write-protected.\r
- @retval EFI_OUT_OF_RESOURCES There is not enough resource.\r
-\r
-**/\r
-EFI_STATUS\r
-AuthServiceInternalUpdateVariableWithMonotonicCount (\r
- IN CHAR16 *VariableName,\r
- IN EFI_GUID *VendorGuid,\r
- IN VOID *Data,\r
- IN UINTN DataSize,\r
- IN UINT32 Attributes,\r
- IN UINT32 KeyIndex,\r
- IN UINT64 MonotonicCount\r
- )\r
-{\r
- AUTH_VARIABLE_INFO AuthVariableInfo;\r
-\r
- ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));\r
- AuthVariableInfo.VariableName = VariableName;\r
- AuthVariableInfo.VendorGuid = VendorGuid;\r
- AuthVariableInfo.Data = Data;\r
- AuthVariableInfo.DataSize = DataSize;\r
- AuthVariableInfo.Attributes = Attributes;\r
- AuthVariableInfo.PubKeyIndex = KeyIndex;\r
- AuthVariableInfo.MonotonicCount = MonotonicCount;\r
-\r
- return mAuthVarLibContextIn->UpdateVariable (\r
- &AuthVariableInfo\r
- );\r
-}\r
-\r
/**\r
Update the variable region with Variable information.\r
\r
return FALSE;\r
}\r
\r
-/**\r
- Get available public key index.\r
-\r
- @param[in] PubKey Pointer to Public Key data.\r
-\r
- @return Public key index, 0 if no any public key index available.\r
-\r
-**/\r
-UINT32\r
-GetAvailableKeyIndex (\r
- IN UINT8 *PubKey\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINT8 *Data;\r
- UINTN DataSize;\r
- UINT8 *Ptr;\r
- UINT32 Index;\r
- BOOLEAN IsFound;\r
- EFI_GUID VendorGuid;\r
- CHAR16 Name[1];\r
- AUTH_VARIABLE_INFO AuthVariableInfo;\r
- UINT32 KeyIndex;\r
-\r
- Status = AuthServiceInternalFindVariable (\r
- AUTHVAR_KEYDB_NAME,\r
- &gEfiAuthenticatedVariableGuid,\r
- (VOID **) &Data,\r
- &DataSize\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((EFI_D_ERROR, "Get public key database variable failure, Status = %r\n", Status));\r
- return 0;\r
- }\r
-\r
- if (mPubKeyNumber == mMaxKeyNumber) {\r
- Name[0] = 0;\r
- AuthVariableInfo.VariableName = Name;\r
- ZeroMem (&VendorGuid, sizeof (VendorGuid));\r
- AuthVariableInfo.VendorGuid = &VendorGuid;\r
- mPubKeyNumber = 0;\r
- //\r
- // Collect valid key data.\r
- //\r
- do {\r
- Status = mAuthVarLibContextIn->FindNextVariable (AuthVariableInfo.VariableName, AuthVariableInfo.VendorGuid, &AuthVariableInfo);\r
- if (!EFI_ERROR (Status)) {\r
- if (AuthVariableInfo.PubKeyIndex != 0) {\r
- for (Ptr = Data; Ptr < (Data + DataSize); Ptr += sizeof (AUTHVAR_KEY_DB_DATA)) {\r
- if (ReadUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) Ptr)->KeyIndex)) == AuthVariableInfo.PubKeyIndex) {\r
- //\r
- // Check if the key data has been collected.\r
- //\r
- for (Index = 0; Index < mPubKeyNumber; Index++) {\r
- if (ReadUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + Index)->KeyIndex)) == AuthVariableInfo.PubKeyIndex) {\r
- break;\r
- }\r
- }\r
- if (Index == mPubKeyNumber) {\r
- //\r
- // New key data.\r
- //\r
- CopyMem ((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + mPubKeyNumber, Ptr, sizeof (AUTHVAR_KEY_DB_DATA));\r
- mPubKeyNumber++;\r
- }\r
- break;\r
- }\r
- }\r
- }\r
- }\r
- } while (Status != EFI_NOT_FOUND);\r
-\r
- //\r
- // No available space to add new public key.\r
- //\r
- if (mPubKeyNumber == mMaxKeyNumber) {\r
- return 0;\r
- }\r
- }\r
-\r
- //\r
- // Find available public key index.\r
- //\r
- for (KeyIndex = 1; KeyIndex <= mMaxKeyNumber; KeyIndex++) {\r
- IsFound = FALSE;\r
- for (Ptr = mPubKeyStore; Ptr < (mPubKeyStore + mPubKeyNumber * sizeof (AUTHVAR_KEY_DB_DATA)); Ptr += sizeof (AUTHVAR_KEY_DB_DATA)) {\r
- if (ReadUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) Ptr)->KeyIndex)) == KeyIndex) {\r
- IsFound = TRUE;\r
- break;\r
- }\r
- }\r
- if (!IsFound) {\r
- break;\r
- }\r
- }\r
-\r
- return KeyIndex;\r
-}\r
-\r
-/**\r
- Add public key in store and return its index.\r
-\r
- @param[in] PubKey Input pointer to Public Key data.\r
- @param[in] VariableDataEntry The variable data entry.\r
-\r
- @return Index of new added public key.\r
-\r
-**/\r
-UINT32\r
-AddPubKeyInStore (\r
- IN UINT8 *PubKey,\r
- IN VARIABLE_ENTRY_CONSISTENCY *VariableDataEntry\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINT32 Index;\r
- VARIABLE_ENTRY_CONSISTENCY PublicKeyEntry;\r
- UINT32 Attributes;\r
- UINT32 KeyIndex;\r
-\r
- if (PubKey == NULL) {\r
- return 0;\r
- }\r
-\r
- //\r
- // Check whether the public key entry does exist.\r
- //\r
- for (Index = 0; Index < mPubKeyNumber; Index++) {\r
- if (CompareMem (((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + Index)->KeyData, PubKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {\r
- return ReadUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + Index)->KeyIndex));\r
- }\r
- }\r
-\r
- KeyIndex = GetAvailableKeyIndex (PubKey);\r
- if (KeyIndex == 0) {\r
- return 0;\r
- }\r
-\r
- //\r
- // Check the variable space for both public key and variable data.\r
- //\r
- PublicKeyEntry.VariableSize = (mPubKeyNumber + 1) * sizeof (AUTHVAR_KEY_DB_DATA);\r
- PublicKeyEntry.Guid = &gEfiAuthenticatedVariableGuid;\r
- PublicKeyEntry.Name = AUTHVAR_KEYDB_NAME;\r
- Attributes = VARIABLE_ATTRIBUTE_NV_BS_RT | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;\r
-\r
- if (!mAuthVarLibContextIn->CheckRemainingSpaceForConsistency (Attributes, &PublicKeyEntry, VariableDataEntry, NULL)) {\r
- //\r
- // No enough variable space.\r
- //\r
- return 0;\r
- }\r
-\r
- WriteUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + mPubKeyNumber)->KeyIndex), KeyIndex);\r
- CopyMem (((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + mPubKeyNumber)->KeyData, PubKey, EFI_CERT_TYPE_RSA2048_SIZE);\r
- mPubKeyNumber++;\r
-\r
- //\r
- // Update public key database variable.\r
- //\r
- Status = AuthServiceInternalUpdateVariable (\r
- AUTHVAR_KEYDB_NAME,\r
- &gEfiAuthenticatedVariableGuid,\r
- mPubKeyStore,\r
- mPubKeyNumber * sizeof (AUTHVAR_KEY_DB_DATA),\r
- Attributes\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((EFI_D_ERROR, "Update public key database variable failure, Status = %r\n", Status));\r
- return 0;\r
- }\r
-\r
- return KeyIndex;\r
-}\r
-\r
-/**\r
- Verify data payload with AuthInfo in EFI_CERT_TYPE_RSA2048_SHA256_GUID type.\r
- Follow the steps in UEFI2.2.\r
-\r
- Caution: This function may receive untrusted input.\r
- This function may be invoked in SMM mode, and datasize and data are external input.\r
- This function will do basic validation, before parse the data.\r
- This function will parse the authentication carefully to avoid security issues, like\r
- buffer overflow, integer overflow.\r
-\r
- @param[in] Data Pointer to data with AuthInfo.\r
- @param[in] DataSize Size of Data.\r
- @param[in] PubKey Public key used for verification.\r
-\r
- @retval EFI_INVALID_PARAMETER Invalid parameter.\r
- @retval EFI_SECURITY_VIOLATION If authentication failed.\r
- @retval EFI_SUCCESS Authentication successful.\r
-\r
-**/\r
-EFI_STATUS\r
-VerifyCounterBasedPayload (\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
- UINTN PayloadSize;\r
-\r
- PayloadSize = DataSize - AUTHINFO_SIZE;\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_GUID.\r
- //\r
- if ((CertData->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) ||\r
- !CompareGuid (&CertData->AuthInfo.CertType, &gEfiCertTypeRsa2048Sha256Guid)) {\r
- //\r
- // Invalid AuthInfo type, return EFI_SECURITY_VIOLATION.\r
- //\r
- return EFI_SECURITY_VIOLATION;\r
- }\r
- //\r
- // Hash data payload with SHA256.\r
- //\r
- ZeroMem (Digest, SHA256_DIGEST_SIZE);\r
- Status = Sha256Init (mHashCtx);\r
- if (!Status) {\r
- goto Done;\r
- }\r
- Status = Sha256Update (mHashCtx, Data + AUTHINFO_SIZE, PayloadSize);\r
- if (!Status) {\r
- goto Done;\r
- }\r
- //\r
- // Hash Size.\r
- //\r
- Status = Sha256Update (mHashCtx, &PayloadSize, sizeof (UINTN));\r
- if (!Status) {\r
- goto Done;\r
- }\r
- //\r
- // Hash Monotonic Count.\r
- //\r
- Status = Sha256Update (mHashCtx, &CertData->MonotonicCount, sizeof (UINT64));\r
- if (!Status) {\r
- goto Done;\r
- }\r
- Status = Sha256Final (mHashCtx, 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
Update platform mode.\r
\r
}\r
\r
/**\r
- Process variable with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS/EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set\r
+ Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set\r
\r
Caution: This function may receive untrusted input.\r
This function may be invoked in SMM mode, and datasize and data are external input.\r
\r
@return EFI_INVALID_PARAMETER Invalid parameter.\r
@return EFI_WRITE_PROTECTED Variable is write-protected and needs authentication with\r
- EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set.\r
+ EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set.\r
@return EFI_OUT_OF_RESOURCES The Database to save the public key is full.\r
- @return EFI_SECURITY_VIOLATION The variable is with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS\r
+ @return EFI_SECURITY_VIOLATION The variable is with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS\r
set, but the AuthInfo does NOT pass the validation\r
check carried out by the firmware.\r
@return EFI_SUCCESS Variable is not write-protected or pass validation successfully.\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
- UINT32 KeyIndex;\r
- UINT64 MonotonicCount;\r
- VARIABLE_ENTRY_CONSISTENCY VariableDataEntry;\r
- UINT32 Index;\r
AUTH_VARIABLE_INFO OrgVariableInfo;\r
\r
- KeyIndex = 0;\r
- CertData = NULL;\r
- CertBlock = NULL;\r
- PubKey = NULL;\r
- IsDeletion = FALSE;\r
Status = EFI_SUCCESS;\r
\r
ZeroMem (&OrgVariableInfo, sizeof (OrgVariableInfo));\r
\r
if ((!EFI_ERROR (Status)) && IsDeleteAuthVariable (OrgVariableInfo.Attributes, Data, DataSize, Attributes) && UserPhysicalPresent()) {\r
//\r
- // Allow the delete operation of common authenticated variable at user physical presence.\r
+ // Allow the delete operation of common authenticated variable(AT or AW) at user physical presence.\r
//\r
Status = AuthServiceInternalUpdateVariable (\r
VariableName,\r
}\r
\r
//\r
- // A time-based authenticated variable and a count-based authenticated variable\r
- // can't be updated by each other.\r
- //\r
- if (OrgVariableInfo.Data != NULL) {\r
- if (((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) &&\r
- ((OrgVariableInfo.Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0)) {\r
- return EFI_SECURITY_VIOLATION;\r
- }\r
-\r
- if (((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) &&\r
- ((OrgVariableInfo.Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0)) {\r
- return EFI_SECURITY_VIOLATION;\r
- }\r
- }\r
-\r
- //\r
- // Process Time-based Authenticated variable.\r
- //\r
- if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {\r
+ if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {\r
+ //\r
+ // Reject Counter Based Auth Variable processing request.\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+ } else if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {\r
+ //\r
+ // Process Time-based Authenticated variable.\r
+ //\r
return VerifyTimeBasedPayloadAndUpdate (\r
VariableName,\r
VendorGuid,\r
);\r
}\r
\r
- //\r
- // Determine if first time SetVariable with the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS.\r
- //\r
- if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {\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 (OrgVariableInfo.Data == NULL) {\r
- IsFirstTime = TRUE;\r
- } else if ((OrgVariableInfo.Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) {\r
- IsFirstTime = TRUE;\r
- } else {\r
- KeyIndex = OrgVariableInfo.PubKeyIndex;\r
- IsFirstTime = FALSE;\r
- }\r
- } else if ((OrgVariableInfo.Data != NULL) &&\r
- ((OrgVariableInfo.Attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) != 0)\r
- ) {\r
+ if ((OrgVariableInfo.Data != NULL) &&\r
+ ((OrgVariableInfo.Attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_TIME_BASED_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 update variable as usual.\r
- //\r
- Status = AuthServiceInternalUpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes);\r
- return Status;\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
- //\r
- // Update Monotonic Count value.\r
- //\r
- MonotonicCount = CertData->MonotonicCount;\r
-\r
- if (!IsFirstTime) {\r
- //\r
- // 2 cases need to check here\r
- // 1. Internal PubKey variable. PubKeyIndex is always 0\r
- // 2. Other counter-based AuthVariable. Check input PubKey.\r
- //\r
- if (KeyIndex == 0) {\r
- return EFI_SECURITY_VIOLATION;\r
- }\r
- for (Index = 0; Index < mPubKeyNumber; Index++) {\r
- if (ReadUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + Index)->KeyIndex)) == KeyIndex) {\r
- if (CompareMem (((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + Index)->KeyData, PubKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {\r
- break;\r
- } else {\r
- return EFI_SECURITY_VIOLATION;\r
- }\r
- }\r
- }\r
- if (Index == mPubKeyNumber) {\r
- return EFI_SECURITY_VIOLATION;\r
- }\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 (MonotonicCount <= OrgVariableInfo.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 = VerifyCounterBasedPayload (Data, DataSize, PubKey);\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
}\r
\r
//\r
- // Now, the signature has been verified!\r
+ // Not authenticated variable, just update variable as usual.\r
//\r
- if (IsFirstTime && !IsDeletion) {\r
- VariableDataEntry.VariableSize = DataSize - AUTHINFO_SIZE;\r
- VariableDataEntry.Guid = VendorGuid;\r
- VariableDataEntry.Name = VariableName;\r
-\r
- //\r
- // Update public key database variable if need.\r
- //\r
- KeyIndex = AddPubKeyInStore (PubKey, &VariableDataEntry);\r
- if (KeyIndex == 0) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
- }\r
+ Status = AuthServiceInternalUpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes);\r
+ return Status;\r
\r
- //\r
- // Verification pass.\r
- //\r
- return AuthServiceInternalUpdateVariableWithMonotonicCount (VariableName, VendorGuid, (UINT8*)Data + AUTHINFO_SIZE, DataSize - AUTHINFO_SIZE, Attributes, KeyIndex, MonotonicCount);\r
}\r
\r
/**\r
return (BOOLEAN) (FirstTime->Second <= SecondTime->Second);\r
}\r
\r
+/**\r
+ Calculate SHA256 digest of SignerCert CommonName + ToplevelCert tbsCertificate\r
+ SignerCert and ToplevelCert are inside the signer certificate chain.\r
+\r
+ @param[in] SignerCert A pointer to SignerCert data.\r
+ @param[in] SignerCertSize Length of SignerCert data.\r
+ @param[in] TopLevelCert A pointer to TopLevelCert data.\r
+ @param[in] TopLevelCertSize Length of TopLevelCert data.\r
+ @param[out] Sha256Digest Sha256 digest calculated.\r
+\r
+ @return EFI_ABORTED Digest process failed.\r
+ @return EFI_SUCCESS SHA256 Digest is succesfully calculated.\r
+\r
+**/\r
+EFI_STATUS\r
+CalculatePrivAuthVarSignChainSHA256Digest(\r
+ IN UINT8 *SignerCert,\r
+ IN UINTN SignerCertSize,\r
+ IN UINT8 *TopLevelCert,\r
+ IN UINTN TopLevelCertSize,\r
+ OUT UINT8 *Sha256Digest\r
+ )\r
+{\r
+ UINT8 *TbsCert;\r
+ UINTN TbsCertSize;\r
+ CHAR8 CertCommonName[128];\r
+ UINTN CertCommonNameSize;\r
+ BOOLEAN CryptoStatus;\r
+ EFI_STATUS Status;\r
+\r
+ CertCommonNameSize = sizeof(CertCommonName);\r
+\r
+ //\r
+ // Get SignerCert CommonName\r
+ //\r
+ Status = X509GetCommonName(SignerCert, SignerCertSize, CertCommonName, &CertCommonNameSize);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG((DEBUG_INFO, "%a Get SignerCert CommonName failed with status %x\n", __FUNCTION__, Status));\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ //\r
+ // Get TopLevelCert tbsCertificate\r
+ //\r
+ if (!X509GetTBSCert(TopLevelCert, TopLevelCertSize, &TbsCert, &TbsCertSize)) {\r
+ DEBUG((DEBUG_INFO, "%a Get Top-level Cert tbsCertificate failed!\n", __FUNCTION__));\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ //\r
+ // Digest SignerCert CN + TopLevelCert tbsCertificate\r
+ //\r
+ ZeroMem (Sha256Digest, SHA256_DIGEST_SIZE);\r
+ CryptoStatus = Sha256Init (mHashCtx);\r
+ if (!CryptoStatus) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ //\r
+ // '\0' is forced in CertCommonName. No overflow issue\r
+ //\r
+ CryptoStatus = Sha256Update (\r
+ mHashCtx,\r
+ CertCommonName,\r
+ AsciiStrLen (CertCommonName)\r
+ );\r
+ if (!CryptoStatus) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ CryptoStatus = Sha256Update (mHashCtx, TbsCert, TbsCertSize);\r
+ if (!CryptoStatus) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ CryptoStatus = Sha256Final (mHashCtx, Sha256Digest);\r
+ if (!CryptoStatus) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
/**\r
Find matching signer's certificates for common authenticated variable\r
by corresponding VariableName and VendorGuid from "certdb" or "certdbv".\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
- \r
+\r
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {\r
//\r
// Get variable "certdb".\r
\r
/**\r
Delete matching signer's certificates when deleting common authenticated\r
- variable by corresponding VariableName and VendorGuid from "certdb" or \r
+ variable by corresponding VariableName and VendorGuid from "certdb" or\r
"certdbv" according to authenticated variable attributes.\r
\r
@param[in] VariableName Name of authenticated Variable.\r
/**\r
Insert signer's certificates for common authenticated variable with VariableName\r
and VendorGuid in AUTH_CERT_DB_DATA to "certdb" or "certdbv" according to\r
- time based authenticated variable attributes.\r
+ time based authenticated variable attributes. CertData is the SHA256 digest of\r
+ SignerCert CommonName + TopLevelCert tbsCertificate.\r
\r
- @param[in] VariableName Name of authenticated Variable.\r
- @param[in] VendorGuid Vendor GUID of authenticated Variable.\r
- @param[in] Attributes Attributes of authenticated variable.\r
- @param[in] CertData Pointer to signer's certificates.\r
- @param[in] CertDataSize Length of CertData in bytes.\r
+ @param[in] VariableName Name of authenticated Variable.\r
+ @param[in] VendorGuid Vendor GUID of authenticated Variable.\r
+ @param[in] Attributes Attributes of authenticated variable.\r
+ @param[in] SignerCert Signer certificate data.\r
+ @param[in] SignerCertSize Length of signer certificate.\r
+ @param[in] TopLevelCert Top-level certificate data.\r
+ @param[in] TopLevelCertSize Length of top-level certificate.\r
\r
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
@retval EFI_ACCESS_DENIED An AUTH_CERT_DB_DATA entry with same VariableName\r
IN CHAR16 *VariableName,\r
IN EFI_GUID *VendorGuid,\r
IN UINT32 Attributes,\r
- IN UINT8 *CertData,\r
- IN UINTN CertDataSize\r
+ IN UINT8 *SignerCert,\r
+ IN UINTN SignerCertSize,\r
+ IN UINT8 *TopLevelCert,\r
+ IN UINTN TopLevelCertSize\r
)\r
{\r
EFI_STATUS Status;\r
UINT32 NewCertDbSize;\r
UINT32 CertNodeSize;\r
UINT32 NameSize;\r
+ UINT32 CertDataSize;\r
AUTH_CERT_DB_DATA *Ptr;\r
CHAR16 *DbName;\r
+ UINT8 Sha256Digest[SHA256_DIGEST_SIZE];\r
\r
- if ((VariableName == NULL) || (VendorGuid == NULL) || (CertData == NULL)) {\r
+ if ((VariableName == NULL) || (VendorGuid == NULL) || (SignerCert == NULL) ||(TopLevelCert == NULL)) {\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
// Construct new data content of variable "certdb" or "certdbv".\r
//\r
NameSize = (UINT32) StrLen (VariableName);\r
+ CertDataSize = sizeof(Sha256Digest);\r
CertNodeSize = sizeof (AUTH_CERT_DB_DATA) + (UINT32) CertDataSize + NameSize * sizeof (CHAR16);\r
NewCertDbSize = (UINT32) DataSize + CertNodeSize;\r
if (NewCertDbSize > mMaxCertDbSize) {\r
return EFI_OUT_OF_RESOURCES;\r
}\r
+\r
+ Status = CalculatePrivAuthVarSignChainSHA256Digest(\r
+ SignerCert,\r
+ SignerCertSize,\r
+ TopLevelCert,\r
+ TopLevelCertSize,\r
+ Sha256Digest\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
NewCertDb = (UINT8*) mCertDbStore;\r
\r
//\r
\r
CopyMem (\r
(UINT8 *) Ptr + sizeof (AUTH_CERT_DB_DATA) + NameSize * sizeof (CHAR16),\r
- CertData,\r
+ Sha256Digest,\r
CertDataSize\r
);\r
\r
&AuthVariableInfo\r
);\r
\r
- if (EFI_ERROR(Status)) {\r
+ if (EFI_ERROR(Status) || (AuthVariableInfo.Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) {\r
Status = DeleteCertsFromDb(\r
VariableName,\r
&AuthVarGuid,\r
UINTN NewDataSize;\r
UINT8 *Buffer;\r
UINTN Length;\r
- UINT8 *RootCert;\r
- UINTN RootCertSize;\r
+ UINT8 *TopLevelCert;\r
+ UINTN TopLevelCertSize;\r
+ UINT8 *TrustedCert;\r
+ UINTN TrustedCertSize;\r
UINT8 *SignerCerts;\r
UINTN CertStackSize;\r
UINT8 *CertsInCertDb;\r
UINT32 CertsSizeinDb;\r
+ UINT8 Sha256Digest[SHA256_DIGEST_SIZE];\r
+ EFI_CERT_DATA *CertDataPtr;\r
\r
+ //\r
+ // 1. TopLevelCert is the top-level issuer certificate in signature Signer Cert Chain\r
+ // 2. TrustedCert is the certificate which firmware trusts. It could be saved in protected\r
+ // storage or PK payload on PK init\r
+ //\r
VerifyStatus = FALSE;\r
CertData = NULL;\r
NewData = NULL;\r
Attr = Attributes;\r
SignerCerts = NULL;\r
- RootCert = NULL;\r
+ TopLevelCert = NULL;\r
CertsInCertDb = NULL;\r
+ CertDataPtr = NULL;\r
\r
//\r
// When the attribute EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS is\r
SigData = CertData->AuthInfo.CertData;\r
SigDataSize = CertData->AuthInfo.Hdr.dwLength - (UINT32) (OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData));\r
\r
+ //\r
+ // SignedData.digestAlgorithms shall contain the digest algorithm used when preparing the\r
+ // signature. Only a digest algorithm of SHA-256 is accepted.\r
+ //\r
+ // According to PKCS#7 Definition:\r
+ // SignedData ::= SEQUENCE {\r
+ // version Version,\r
+ // digestAlgorithms DigestAlgorithmIdentifiers,\r
+ // contentInfo ContentInfo,\r
+ // .... }\r
+ // The DigestAlgorithmIdentifiers can be used to determine the hash algorithm\r
+ // in VARIABLE_AUTHENTICATION_2 descriptor.\r
+ // This field has the fixed offset (+13) and be calculated based on two bytes of length encoding.\r
+ //\r
+ if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {\r
+ if (SigDataSize >= (13 + sizeof (mSha256OidValue))) {\r
+ if (((*(SigData + 1) & TWO_BYTE_ENCODE) != TWO_BYTE_ENCODE) ||\r
+ (CompareMem (SigData + 13, &mSha256OidValue, sizeof (mSha256OidValue)) != 0)) {\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+ }\r
+ }\r
+\r
//\r
// Find out the new data payload which follows Pkcs7 SignedData directly.\r
//\r
SigDataSize,\r
&SignerCerts,\r
&CertStackSize,\r
- &RootCert,\r
- &RootCertSize\r
+ &TopLevelCert,\r
+ &TopLevelCertSize\r
);\r
if (!VerifyStatus) {\r
goto Exit;\r
}\r
CertList = (EFI_SIGNATURE_LIST *) Data;\r
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
- if ((RootCertSize != (CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1))) ||\r
- (CompareMem (Cert->SignatureData, RootCert, RootCertSize) != 0)) {\r
+ if ((TopLevelCertSize != (CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1))) ||\r
+ (CompareMem (Cert->SignatureData, TopLevelCert, TopLevelCertSize) != 0)) {\r
VerifyStatus = FALSE;\r
goto Exit;\r
}\r
VerifyStatus = Pkcs7Verify (\r
SigData,\r
SigDataSize,\r
- RootCert,\r
- RootCertSize,\r
+ TopLevelCert,\r
+ TopLevelCertSize,\r
NewData,\r
NewDataSize\r
);\r
//\r
// Iterate each Signature Data Node within this CertList for a verify\r
//\r
- RootCert = Cert->SignatureData;\r
- RootCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1);\r
+ TrustedCert = Cert->SignatureData;\r
+ TrustedCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1);\r
\r
//\r
// Verify Pkcs7 SignedData via Pkcs7Verify library.\r
VerifyStatus = Pkcs7Verify (\r
SigData,\r
SigDataSize,\r
- RootCert,\r
- RootCertSize,\r
+ TrustedCert,\r
+ TrustedCertSize,\r
NewData,\r
NewDataSize\r
);\r
SigDataSize,\r
&SignerCerts,\r
&CertStackSize,\r
- &RootCert,\r
- &RootCertSize\r
+ &TopLevelCert,\r
+ &TopLevelCertSize\r
);\r
if (!VerifyStatus) {\r
goto Exit;\r
goto Exit;\r
}\r
\r
- if ((CertStackSize != CertsSizeinDb) ||\r
- (CompareMem (SignerCerts, CertsInCertDb, CertsSizeinDb) != 0)) {\r
- goto Exit;\r
+ if (CertsSizeinDb == SHA256_DIGEST_SIZE) {\r
+ //\r
+ // Check hash of signer cert CommonName + Top-level issuer tbsCertificate against data in CertDb\r
+ //\r
+ CertDataPtr = (EFI_CERT_DATA *)(SignerCerts + 1);\r
+ Status = CalculatePrivAuthVarSignChainSHA256Digest(\r
+ CertDataPtr->CertDataBuffer,\r
+ ReadUnaligned32 ((UINT32 *)&(CertDataPtr->CertDataLength)),\r
+ TopLevelCert,\r
+ TopLevelCertSize,\r
+ Sha256Digest\r
+ );\r
+ if (EFI_ERROR(Status) || CompareMem (Sha256Digest, CertsInCertDb, CertsSizeinDb) != 0){\r
+ goto Exit;\r
+ }\r
+ } else {\r
+ //\r
+ // Keep backward compatible with previous solution which saves whole signer certs stack in CertDb\r
+ //\r
+ if ((CertStackSize != CertsSizeinDb) ||\r
+ (CompareMem (SignerCerts, CertsInCertDb, CertsSizeinDb) != 0)) {\r
+ goto Exit;\r
+ }\r
}\r
}\r
\r
VerifyStatus = Pkcs7Verify (\r
SigData,\r
SigDataSize,\r
- RootCert,\r
- RootCertSize,\r
+ TopLevelCert,\r
+ TopLevelCertSize,\r
NewData,\r
NewDataSize\r
);\r
\r
if ((OrgTimeStamp == NULL) && (PayloadSize != 0)) {\r
//\r
- // Insert signer's certificates when adding a new common authenticated variable.\r
+ // When adding a new common authenticated variable, always save Hash of cn of signer cert + tbsCertificate of Top-level issuer\r
//\r
- Status = InsertCertsToDb (VariableName, VendorGuid, Attributes, SignerCerts, CertStackSize);\r
+ CertDataPtr = (EFI_CERT_DATA *)(SignerCerts + 1);\r
+ Status = InsertCertsToDb (\r
+ VariableName,\r
+ VendorGuid,\r
+ Attributes,\r
+ CertDataPtr->CertDataBuffer,\r
+ ReadUnaligned32 ((UINT32 *)&(CertDataPtr->CertDataLength)),\r
+ TopLevelCert,\r
+ TopLevelCertSize\r
+ );\r
if (EFI_ERROR (Status)) {\r
VerifyStatus = FALSE;\r
goto Exit;\r
} else if (AuthVarType == AuthVarTypePayload) {\r
CertList = (EFI_SIGNATURE_LIST *) PayloadPtr;\r
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
- RootCert = Cert->SignatureData;\r
- RootCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1);\r
+ TrustedCert = Cert->SignatureData;\r
+ TrustedCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1);\r
//\r
// Verify Pkcs7 SignedData via Pkcs7Verify library.\r
//\r
VerifyStatus = Pkcs7Verify (\r
SigData,\r
SigDataSize,\r
- RootCert,\r
- RootCertSize,\r
+ TrustedCert,\r
+ TrustedCertSize,\r
NewData,\r
NewDataSize\r
);\r
Exit:\r
\r
if (AuthVarType == AuthVarTypePk || AuthVarType == AuthVarTypePriv) {\r
- Pkcs7FreeSigners (RootCert);\r
+ Pkcs7FreeSigners (TopLevelCert);\r
Pkcs7FreeSigners (SignerCerts);\r
}\r
\r