Update auth-variable and secure boot UI driver to support only time-based PK, KEK and Signature Database variable variable according to UEFI Spec requirement.
Signed-off-by: Fu Siyuan <siyuan.fu@intel.com> Reviewed-by: Dong Guo <guo.dong@intel.com> Reviewed-by: Ye Ting <ting.ye@intel.com> git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13310 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
parent
82a1e09c83
commit
8c1babfd28
|
@ -879,151 +879,49 @@ ProcessVarWithPk (
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
EFI_STATUS Status;
|
EFI_STATUS Status;
|
||||||
VARIABLE_POINTER_TRACK PkVariable;
|
|
||||||
EFI_SIGNATURE_LIST *OldPkList;
|
|
||||||
EFI_SIGNATURE_DATA *OldPkData;
|
|
||||||
EFI_VARIABLE_AUTHENTICATION *CertData;
|
|
||||||
BOOLEAN TimeBase;
|
|
||||||
BOOLEAN Del;
|
BOOLEAN Del;
|
||||||
UINT8 *Payload;
|
UINT8 *Payload;
|
||||||
UINTN PayloadSize;
|
UINTN PayloadSize;
|
||||||
UINT64 MonotonicCount;
|
|
||||||
EFI_TIME *TimeStamp;
|
|
||||||
|
|
||||||
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
|
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0 ||
|
||||||
|
(Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) {
|
||||||
//
|
//
|
||||||
// PK and KEK should set EFI_VARIABLE_NON_VOLATILE attribute.
|
// PK and KEK should set EFI_VARIABLE_NON_VOLATILE attribute and should be a time-based
|
||||||
|
// authenticated variable.
|
||||||
//
|
//
|
||||||
return EFI_INVALID_PARAMETER;
|
return EFI_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPlatformMode == USER_MODE && !(InCustomMode() && UserPhysicalPresent())) {
|
if (mPlatformMode == USER_MODE && !(InCustomMode() && UserPhysicalPresent())) {
|
||||||
|
//
|
||||||
if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
// Verify against X509 Cert PK.
|
||||||
|
//
|
||||||
|
Del = FALSE;
|
||||||
|
Status = VerifyTimeBasedPayload (
|
||||||
|
VariableName,
|
||||||
|
VendorGuid,
|
||||||
|
Data,
|
||||||
|
DataSize,
|
||||||
|
Variable,
|
||||||
|
Attributes,
|
||||||
|
AuthVarTypePk,
|
||||||
|
&Del
|
||||||
|
);
|
||||||
|
if (!EFI_ERROR (Status)) {
|
||||||
//
|
//
|
||||||
// EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute means time-based X509 Cert PK.
|
// If delete PK in user mode, need change to setup mode.
|
||||||
//
|
//
|
||||||
TimeBase = TRUE;
|
if (Del && IsPk) {
|
||||||
} else if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
Status = UpdatePlatformMode (SETUP_MODE);
|
||||||
//
|
|
||||||
// EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute means counter-based RSA-2048 Cert PK.
|
|
||||||
//
|
|
||||||
TimeBase = FALSE;
|
|
||||||
} else {
|
|
||||||
return EFI_INVALID_PARAMETER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TimeBase) {
|
|
||||||
//
|
|
||||||
// Verify against X509 Cert PK.
|
|
||||||
//
|
|
||||||
Del = FALSE;
|
|
||||||
Status = VerifyTimeBasedPayload (
|
|
||||||
VariableName,
|
|
||||||
VendorGuid,
|
|
||||||
Data,
|
|
||||||
DataSize,
|
|
||||||
Variable,
|
|
||||||
Attributes,
|
|
||||||
AuthVarTypePk,
|
|
||||||
&Del
|
|
||||||
);
|
|
||||||
if (!EFI_ERROR (Status)) {
|
|
||||||
//
|
|
||||||
// If delete PK in user mode, need change to setup mode.
|
|
||||||
//
|
|
||||||
if (Del && IsPk) {
|
|
||||||
Status = UpdatePlatformMode (SETUP_MODE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Status;
|
|
||||||
} else {
|
|
||||||
//
|
|
||||||
// Verify against RSA2048 Cert PK.
|
|
||||||
//
|
|
||||||
CertData = (EFI_VARIABLE_AUTHENTICATION *) Data;
|
|
||||||
if ((Variable->CurrPtr != NULL) && (CertData->MonotonicCount <= Variable->CurrPtr->MonotonicCount)) {
|
|
||||||
//
|
|
||||||
// Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION.
|
|
||||||
//
|
|
||||||
return EFI_SECURITY_VIOLATION;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Get platform key from variable.
|
|
||||||
//
|
|
||||||
Status = FindVariable (
|
|
||||||
EFI_PLATFORM_KEY_NAME,
|
|
||||||
&gEfiGlobalVariableGuid,
|
|
||||||
&PkVariable,
|
|
||||||
&mVariableModuleGlobal->VariableGlobal,
|
|
||||||
FALSE
|
|
||||||
);
|
|
||||||
ASSERT_EFI_ERROR (Status);
|
|
||||||
|
|
||||||
OldPkList = (EFI_SIGNATURE_LIST *) GetVariableDataPtr (PkVariable.CurrPtr);
|
|
||||||
OldPkData = (EFI_SIGNATURE_DATA *) ((UINT8 *) OldPkList + sizeof (EFI_SIGNATURE_LIST) + OldPkList->SignatureHeaderSize);
|
|
||||||
Status = VerifyCounterBasedPayload (Data, DataSize, OldPkData->SignatureData);
|
|
||||||
if (!EFI_ERROR (Status)) {
|
|
||||||
Status = CheckSignatureListFormat(
|
|
||||||
VariableName,
|
|
||||||
VendorGuid,
|
|
||||||
(UINT8*)Data + AUTHINFO_SIZE,
|
|
||||||
DataSize - AUTHINFO_SIZE);
|
|
||||||
if (EFI_ERROR (Status)) {
|
|
||||||
return Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status = UpdateVariable (
|
|
||||||
VariableName,
|
|
||||||
VendorGuid,
|
|
||||||
(UINT8*)Data + AUTHINFO_SIZE,
|
|
||||||
DataSize - AUTHINFO_SIZE,
|
|
||||||
Attributes,
|
|
||||||
0,
|
|
||||||
CertData->MonotonicCount,
|
|
||||||
Variable,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!EFI_ERROR (Status)) {
|
|
||||||
//
|
|
||||||
// If delete PK in user mode, need change to setup mode.
|
|
||||||
//
|
|
||||||
if ((DataSize == AUTHINFO_SIZE) && IsPk) {
|
|
||||||
Status = UpdatePlatformMode (SETUP_MODE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Status;
|
||||||
} else {
|
} else {
|
||||||
//
|
//
|
||||||
// Process PK or KEK in Setup mode or Custom Secure Boot mode.
|
// Process PK or KEK in Setup mode or Custom Secure Boot mode.
|
||||||
//
|
//
|
||||||
if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data);
|
||||||
//
|
PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
|
||||||
// Time-based Authentication descriptor.
|
|
||||||
//
|
|
||||||
MonotonicCount = 0;
|
|
||||||
TimeStamp = &((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp;
|
|
||||||
Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data);
|
|
||||||
PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
|
|
||||||
} else if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
|
||||||
//
|
|
||||||
// Counter-based Authentication descriptor.
|
|
||||||
//
|
|
||||||
MonotonicCount = ((EFI_VARIABLE_AUTHENTICATION *) Data)->MonotonicCount;
|
|
||||||
TimeStamp = NULL;
|
|
||||||
Payload = (UINT8*) Data + AUTHINFO_SIZE;
|
|
||||||
PayloadSize = DataSize - AUTHINFO_SIZE;
|
|
||||||
} else {
|
|
||||||
//
|
|
||||||
// No Authentication descriptor.
|
|
||||||
//
|
|
||||||
MonotonicCount = 0;
|
|
||||||
TimeStamp = NULL;
|
|
||||||
Payload = Data;
|
|
||||||
PayloadSize = DataSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status = CheckSignatureListFormat(VariableName, VendorGuid, Payload, PayloadSize);
|
Status = CheckSignatureListFormat(VariableName, VendorGuid, Payload, PayloadSize);
|
||||||
if (EFI_ERROR (Status)) {
|
if (EFI_ERROR (Status)) {
|
||||||
|
@ -1037,9 +935,9 @@ ProcessVarWithPk (
|
||||||
PayloadSize,
|
PayloadSize,
|
||||||
Attributes,
|
Attributes,
|
||||||
0,
|
0,
|
||||||
MonotonicCount,
|
0,
|
||||||
Variable,
|
Variable,
|
||||||
TimeStamp
|
&((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp
|
||||||
);
|
);
|
||||||
|
|
||||||
if (IsPk) {
|
if (IsPk) {
|
||||||
|
@ -1088,148 +986,39 @@ ProcessVarWithKek (
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
EFI_STATUS Status;
|
EFI_STATUS Status;
|
||||||
VARIABLE_POINTER_TRACK KekVariable;
|
|
||||||
EFI_SIGNATURE_LIST *KekList;
|
|
||||||
EFI_SIGNATURE_DATA *KekItem;
|
|
||||||
UINT32 KekCount;
|
|
||||||
EFI_VARIABLE_AUTHENTICATION *CertData;
|
|
||||||
EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlock;
|
|
||||||
BOOLEAN IsFound;
|
|
||||||
UINT32 Index;
|
|
||||||
UINT32 KekDataSize;
|
|
||||||
UINT8 *Payload;
|
UINT8 *Payload;
|
||||||
UINTN PayloadSize;
|
UINTN PayloadSize;
|
||||||
UINT64 MonotonicCount;
|
|
||||||
EFI_TIME *TimeStamp;
|
|
||||||
|
|
||||||
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
|
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0 ||
|
||||||
|
(Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) {
|
||||||
//
|
//
|
||||||
// DB and DBX should set EFI_VARIABLE_NON_VOLATILE attribute.
|
// DB and DBX should set EFI_VARIABLE_NON_VOLATILE attribute and should be a time-based
|
||||||
|
// authenticated variable.
|
||||||
//
|
//
|
||||||
return EFI_INVALID_PARAMETER;
|
return EFI_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status = EFI_SUCCESS;
|
Status = EFI_SUCCESS;
|
||||||
if (mPlatformMode == USER_MODE && !(InCustomMode() && UserPhysicalPresent())) {
|
if (mPlatformMode == USER_MODE && !(InCustomMode() && UserPhysicalPresent())) {
|
||||||
if (((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) &&
|
//
|
||||||
((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0)){
|
// Time-based, verify against X509 Cert KEK.
|
||||||
//
|
//
|
||||||
// In user mode, should set EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS or
|
return VerifyTimeBasedPayload (
|
||||||
// EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute.
|
VariableName,
|
||||||
//
|
VendorGuid,
|
||||||
return EFI_INVALID_PARAMETER;
|
Data,
|
||||||
}
|
DataSize,
|
||||||
|
Variable,
|
||||||
if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
Attributes,
|
||||||
//
|
AuthVarTypeKek,
|
||||||
// Time-based, verify against X509 Cert KEK.
|
NULL
|
||||||
//
|
);
|
||||||
return VerifyTimeBasedPayload (
|
|
||||||
VariableName,
|
|
||||||
VendorGuid,
|
|
||||||
Data,
|
|
||||||
DataSize,
|
|
||||||
Variable,
|
|
||||||
Attributes,
|
|
||||||
AuthVarTypeKek,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
} else if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
|
||||||
//
|
|
||||||
// Counter-based, verify against RSA2048 Cert KEK.
|
|
||||||
//
|
|
||||||
CertData = (EFI_VARIABLE_AUTHENTICATION *) Data;
|
|
||||||
CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) (CertData->AuthInfo.CertData);
|
|
||||||
if ((Variable->CurrPtr != NULL) && (CertData->MonotonicCount <= Variable->CurrPtr->MonotonicCount)) {
|
|
||||||
//
|
|
||||||
// Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION.
|
|
||||||
//
|
|
||||||
return EFI_SECURITY_VIOLATION;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Get KEK database from variable.
|
|
||||||
//
|
|
||||||
Status = FindVariable (
|
|
||||||
EFI_KEY_EXCHANGE_KEY_NAME,
|
|
||||||
&gEfiGlobalVariableGuid,
|
|
||||||
&KekVariable,
|
|
||||||
&mVariableModuleGlobal->VariableGlobal,
|
|
||||||
FALSE
|
|
||||||
);
|
|
||||||
ASSERT_EFI_ERROR (Status);
|
|
||||||
|
|
||||||
KekDataSize = KekVariable.CurrPtr->DataSize;
|
|
||||||
KekList = (EFI_SIGNATURE_LIST *) GetVariableDataPtr (KekVariable.CurrPtr);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Enumerate all Kek items in this list to verify the variable certificate data.
|
|
||||||
// If anyone is authenticated successfully, it means the variable is correct!
|
|
||||||
//
|
|
||||||
IsFound = FALSE;
|
|
||||||
while ((KekDataSize > 0) && (KekDataSize >= KekList->SignatureListSize)) {
|
|
||||||
if (CompareGuid (&KekList->SignatureType, &gEfiCertRsa2048Guid)) {
|
|
||||||
KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekList + sizeof (EFI_SIGNATURE_LIST) + KekList->SignatureHeaderSize);
|
|
||||||
KekCount = (KekList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - KekList->SignatureHeaderSize) / KekList->SignatureSize;
|
|
||||||
for (Index = 0; Index < KekCount; Index++) {
|
|
||||||
if (CompareMem (KekItem->SignatureData, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {
|
|
||||||
IsFound = TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekItem + KekList->SignatureSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
KekDataSize -= KekList->SignatureListSize;
|
|
||||||
KekList = (EFI_SIGNATURE_LIST *) ((UINT8 *) KekList + KekList->SignatureListSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsFound) {
|
|
||||||
return EFI_SECURITY_VIOLATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status = VerifyCounterBasedPayload (Data, DataSize, CertBlock->PublicKey);
|
|
||||||
if (!EFI_ERROR (Status)) {
|
|
||||||
Status = UpdateVariable (
|
|
||||||
VariableName,
|
|
||||||
VendorGuid,
|
|
||||||
(UINT8*)Data + AUTHINFO_SIZE,
|
|
||||||
DataSize - AUTHINFO_SIZE,
|
|
||||||
Attributes,
|
|
||||||
0,
|
|
||||||
CertData->MonotonicCount,
|
|
||||||
Variable,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
//
|
//
|
||||||
// If in setup mode or custom secure boot mode, no authentication needed.
|
// If in setup mode or custom secure boot mode, no authentication needed.
|
||||||
//
|
//
|
||||||
if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data);
|
||||||
//
|
PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
|
||||||
// Time-based Authentication descriptor.
|
|
||||||
//
|
|
||||||
MonotonicCount = 0;
|
|
||||||
TimeStamp = &((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp;
|
|
||||||
Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data);
|
|
||||||
PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
|
|
||||||
} else if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
|
||||||
//
|
|
||||||
// Counter-based Authentication descriptor.
|
|
||||||
//
|
|
||||||
MonotonicCount = ((EFI_VARIABLE_AUTHENTICATION *) Data)->MonotonicCount;
|
|
||||||
TimeStamp = NULL;
|
|
||||||
Payload = (UINT8*) Data + AUTHINFO_SIZE;
|
|
||||||
PayloadSize = DataSize - AUTHINFO_SIZE;
|
|
||||||
} else {
|
|
||||||
//
|
|
||||||
// No Authentication descriptor.
|
|
||||||
//
|
|
||||||
MonotonicCount = 0;
|
|
||||||
TimeStamp = NULL;
|
|
||||||
Payload = Data;
|
|
||||||
PayloadSize = DataSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status = UpdateVariable (
|
Status = UpdateVariable (
|
||||||
VariableName,
|
VariableName,
|
||||||
|
@ -1238,9 +1027,9 @@ ProcessVarWithKek (
|
||||||
PayloadSize,
|
PayloadSize,
|
||||||
Attributes,
|
Attributes,
|
||||||
0,
|
0,
|
||||||
MonotonicCount,
|
0,
|
||||||
Variable,
|
Variable,
|
||||||
TimeStamp
|
&((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
gEfiCustomModeEnableGuid
|
gEfiCustomModeEnableGuid
|
||||||
gEfiSecureBootEnableDisableGuid
|
gEfiSecureBootEnableDisableGuid
|
||||||
gSecureBootConfigFormSetGuid
|
gSecureBootConfigFormSetGuid
|
||||||
|
gEfiCertPkcs7Guid
|
||||||
gEfiCertRsa2048Guid ## CONSUMES
|
gEfiCertRsa2048Guid ## CONSUMES
|
||||||
gEfiCertX509Guid ## CONSUMES
|
gEfiCertX509Guid ## CONSUMES
|
||||||
gEfiCertSha1Guid ## CONSUMES
|
gEfiCertSha1Guid ## CONSUMES
|
||||||
|
|
|
@ -108,6 +108,90 @@ SaveSecureBootVariable (
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Create a time based data payload by concatenating the EFI_VARIABLE_AUTHENTICATION_2
|
||||||
|
descriptor with the input data. NO authentication is required in this function.
|
||||||
|
|
||||||
|
@param[in, out] DataSize On input, the size of Data buffer in bytes.
|
||||||
|
On output, the size of data returned in Data
|
||||||
|
buffer in bytes.
|
||||||
|
@param[in, out] Data On input, Pointer to data buffer to be wrapped or
|
||||||
|
pointer to NULL to wrap an empty payload.
|
||||||
|
On output, Pointer to the new payload date buffer allocated from pool,
|
||||||
|
it's caller's responsibility to free the memory when finish using it.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS Create time based payload successfully.
|
||||||
|
@retval EFI_OUT_OF_RESOURCES There are not enough memory resourses to create time based payload.
|
||||||
|
@retval EFI_INVALID_PARAMETER The parameter is invalid.
|
||||||
|
@retval Others Unexpected error happens.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
CreateTimeBasedPayload (
|
||||||
|
IN OUT UINTN *DataSize,
|
||||||
|
IN OUT UINT8 **Data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
UINT8 *NewData;
|
||||||
|
UINT8 *Payload;
|
||||||
|
UINTN PayloadSize;
|
||||||
|
EFI_VARIABLE_AUTHENTICATION_2 *DescriptorData;
|
||||||
|
UINTN DescriptorSize;
|
||||||
|
EFI_TIME Time;
|
||||||
|
|
||||||
|
if (Data == NULL || DataSize == NULL) {
|
||||||
|
return EFI_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// In Setup mode or Custom mode, the variable does not need to be signed but the
|
||||||
|
// parameters to the SetVariable() call still need to be prepared as authenticated
|
||||||
|
// variable. So we create EFI_VARIABLE_AUTHENTICATED_2 descriptor without certificate
|
||||||
|
// data in it.
|
||||||
|
//
|
||||||
|
Payload = *Data;
|
||||||
|
PayloadSize = *DataSize;
|
||||||
|
|
||||||
|
DescriptorSize = OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo) + OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData);
|
||||||
|
NewData = (UINT8*) AllocateZeroPool (DescriptorSize + PayloadSize);
|
||||||
|
if (NewData == NULL) {
|
||||||
|
return EFI_OUT_OF_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Payload != NULL) && (PayloadSize != 0)) {
|
||||||
|
CopyMem (NewData + DescriptorSize, Payload, PayloadSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorData = (EFI_VARIABLE_AUTHENTICATION_2 *) (NewData);
|
||||||
|
|
||||||
|
ZeroMem (&Time, sizeof (EFI_TIME));
|
||||||
|
Status = gRT->GetTime (&Time, NULL);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
FreePool(NewData);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
Time.Pad1 = 0;
|
||||||
|
Time.Nanosecond = 0;
|
||||||
|
Time.TimeZone = 0;
|
||||||
|
Time.Daylight = 0;
|
||||||
|
Time.Pad2 = 0;
|
||||||
|
CopyMem (&DescriptorData->TimeStamp, &Time, sizeof (EFI_TIME));
|
||||||
|
|
||||||
|
DescriptorData->AuthInfo.Hdr.dwLength = OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData);
|
||||||
|
DescriptorData->AuthInfo.Hdr.wRevision = 0x0200;
|
||||||
|
DescriptorData->AuthInfo.Hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
|
||||||
|
CopyGuid (&DescriptorData->AuthInfo.CertType, &gEfiCertPkcs7Guid);
|
||||||
|
|
||||||
|
if (Payload != NULL) {
|
||||||
|
FreePool(Payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
*DataSize = DescriptorSize + PayloadSize;
|
||||||
|
*Data = NewData;
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Internal helper function to delete a Variable given its name and GUID, NO authentication
|
Internal helper function to delete a Variable given its name and GUID, NO authentication
|
||||||
required.
|
required.
|
||||||
|
@ -127,130 +211,36 @@ DeleteVariable (
|
||||||
{
|
{
|
||||||
EFI_STATUS Status;
|
EFI_STATUS Status;
|
||||||
VOID* Variable;
|
VOID* Variable;
|
||||||
|
UINT8 *Data;
|
||||||
|
UINTN DataSize;
|
||||||
|
UINT32 Attr;
|
||||||
|
|
||||||
Variable = GetVariable (VariableName, VendorGuid);
|
Variable = GetVariable (VariableName, VendorGuid);
|
||||||
if (Variable == NULL) {
|
if (Variable == NULL) {
|
||||||
return EFI_SUCCESS;
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status = gRT->SetVariable (
|
Data = NULL;
|
||||||
VariableName,
|
DataSize = 0;
|
||||||
VendorGuid,
|
Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS
|
||||||
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
| EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
||||||
0,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
return Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
Status = CreateTimeBasedPayload (&DataSize, &Data);
|
||||||
Generate a PK signature list from the public key storing file (*.pbk).
|
if (EFI_ERROR (Status)) {
|
||||||
|
DEBUG ((EFI_D_ERROR, "Fail to create time-based data payload: %r", Status));
|
||||||
@param[in] PkKeyFile FileHandle of the public key storing file.
|
return Status;
|
||||||
@param[out] PkCert Point to the data buffer to store the signature list.
|
|
||||||
|
|
||||||
@return EFI_UNSUPPORTED Unsupported Key Length.
|
|
||||||
@return EFI_OUT_OF_RESOURCES There are not enough memory resourses to form the signature list.
|
|
||||||
|
|
||||||
**/
|
|
||||||
EFI_STATUS
|
|
||||||
CreatePkRsaSignatureList (
|
|
||||||
IN EFI_FILE_HANDLE PkKeyFile,
|
|
||||||
OUT EFI_SIGNATURE_LIST **PkCert
|
|
||||||
)
|
|
||||||
{
|
|
||||||
EFI_STATUS Status;
|
|
||||||
UINTN KeyBlobSize;
|
|
||||||
VOID *KeyBlob;
|
|
||||||
CPL_KEY_INFO *KeyInfo;
|
|
||||||
EFI_SIGNATURE_DATA *PkCertData;
|
|
||||||
VOID *KeyBuffer;
|
|
||||||
UINTN KeyLenInBytes;
|
|
||||||
|
|
||||||
PkCertData = NULL;
|
|
||||||
KeyBlob = NULL;
|
|
||||||
KeyBuffer = NULL;
|
|
||||||
Status = EFI_SUCCESS;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Get key from PK key file
|
|
||||||
//
|
|
||||||
Status = ReadFileContent (PkKeyFile, &KeyBlob, &KeyBlobSize, 0);
|
|
||||||
if (EFI_ERROR(Status)) {
|
|
||||||
DEBUG ((EFI_D_ERROR, "Can't Open the file for PK enrolling.\n"));
|
|
||||||
goto ON_EXIT;
|
|
||||||
}
|
|
||||||
ASSERT (KeyBlob != NULL);
|
|
||||||
|
|
||||||
KeyInfo = (CPL_KEY_INFO *)KeyBlob;
|
|
||||||
if (KeyInfo->KeyLengthInBits/8 != WIN_CERT_UEFI_RSA2048_SIZE) {
|
|
||||||
Status = EFI_UNSUPPORTED;
|
|
||||||
goto ON_EXIT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
Status = gRT->SetVariable (
|
||||||
// Convert the Public key to fix octet string format represented in RSA PKCS#1.
|
VariableName,
|
||||||
//
|
VendorGuid,
|
||||||
KeyLenInBytes = KeyInfo->KeyLengthInBits / 8;
|
Attr,
|
||||||
KeyBuffer = AllocateZeroPool(KeyLenInBytes);
|
DataSize,
|
||||||
if (KeyBuffer == NULL) {
|
Data
|
||||||
Status = EFI_OUT_OF_RESOURCES;
|
);
|
||||||
goto ON_EXIT;
|
if (Data != NULL) {
|
||||||
|
FreePool (Data);
|
||||||
}
|
}
|
||||||
Status = Int2OctStr (
|
|
||||||
(UINTN*) ((UINTN)KeyBlob + sizeof(CPL_KEY_INFO)),
|
|
||||||
KeyLenInBytes / sizeof (UINTN),
|
|
||||||
(UINT8*)KeyBuffer,
|
|
||||||
KeyLenInBytes
|
|
||||||
);
|
|
||||||
if (EFI_ERROR(Status)) {
|
|
||||||
goto ON_EXIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate space for PK certificate list and initialize the list.
|
|
||||||
// Create PK database entry with SignatureHeaderSize equals 0.
|
|
||||||
//
|
|
||||||
*PkCert = (EFI_SIGNATURE_LIST*)AllocateZeroPool(
|
|
||||||
sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1
|
|
||||||
+ WIN_CERT_UEFI_RSA2048_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
if (*PkCert == NULL) {
|
|
||||||
Status = EFI_OUT_OF_RESOURCES;
|
|
||||||
goto ON_EXIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*PkCert)->SignatureListSize = sizeof(EFI_SIGNATURE_LIST)
|
|
||||||
+ sizeof(EFI_SIGNATURE_DATA) - 1
|
|
||||||
+ WIN_CERT_UEFI_RSA2048_SIZE;
|
|
||||||
(*PkCert)->SignatureSize = sizeof(EFI_SIGNATURE_DATA) - 1 + WIN_CERT_UEFI_RSA2048_SIZE;
|
|
||||||
(*PkCert)->SignatureHeaderSize = 0;
|
|
||||||
CopyGuid (&(*PkCert)->SignatureType, &gEfiCertRsa2048Guid);
|
|
||||||
|
|
||||||
PkCertData = (EFI_SIGNATURE_DATA*)((UINTN)(*PkCert)
|
|
||||||
+ sizeof(EFI_SIGNATURE_LIST)
|
|
||||||
+ (*PkCert)->SignatureHeaderSize);
|
|
||||||
CopyGuid (&PkCertData->SignatureOwner, &gEfiGlobalVariableGuid);
|
|
||||||
//
|
|
||||||
// Fill the PK database with PKpub data from PKKeyFile.
|
|
||||||
//
|
|
||||||
CopyMem (&(PkCertData->SignatureData[0]), KeyBuffer, WIN_CERT_UEFI_RSA2048_SIZE);
|
|
||||||
|
|
||||||
ON_EXIT:
|
|
||||||
|
|
||||||
if (KeyBlob != NULL) {
|
|
||||||
FreePool (KeyBlob);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EFI_ERROR(Status) && *PkCert != NULL) {
|
|
||||||
FreePool (*PkCert);
|
|
||||||
*PkCert = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (KeyBuffer != NULL) {
|
|
||||||
FreePool (KeyBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,11 +347,11 @@ EnrollPlatformKey (
|
||||||
PkCert = NULL;
|
PkCert = NULL;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Parse the file's postfix. Only support *.pbk(RSA2048) and *.cer(X509) files.
|
// Parse the file's postfix. Only support *.cer(X509) files.
|
||||||
//
|
//
|
||||||
FilePostFix = Private->FileContext->FileName + StrLen (Private->FileContext->FileName) - 4;
|
FilePostFix = Private->FileContext->FileName + StrLen (Private->FileContext->FileName) - 4;
|
||||||
if (CompareMem (FilePostFix, L".pbk",4) && CompareMem (FilePostFix, L".cer",4)) {
|
if (CompareMem (FilePostFix, L".cer",4)) {
|
||||||
DEBUG ((EFI_D_ERROR, "Don't support the file, only *.pbk or *.cer.\n is supported."));
|
DEBUG ((EFI_D_ERROR, "Don't support the file, only *.cer is supported."));
|
||||||
return EFI_INVALID_PARAMETER;
|
return EFI_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
DEBUG ((EFI_D_INFO, "FileName= %s\n", Private->FileContext->FileName));
|
DEBUG ((EFI_D_INFO, "FileName= %s\n", Private->FileContext->FileName));
|
||||||
|
@ -370,22 +360,12 @@ EnrollPlatformKey (
|
||||||
//
|
//
|
||||||
// Prase the selected PK file and generature PK certificate list.
|
// Prase the selected PK file and generature PK certificate list.
|
||||||
//
|
//
|
||||||
if (!CompareMem (FilePostFix, L".pbk",4)) {
|
Status = CreatePkX509SignatureList (
|
||||||
Status = CreatePkRsaSignatureList (
|
Private->FileContext->FHandle,
|
||||||
Private->FileContext->FHandle,
|
&PkCert
|
||||||
&PkCert
|
);
|
||||||
);
|
if (EFI_ERROR (Status)) {
|
||||||
if (EFI_ERROR (Status)) {
|
goto ON_EXIT;
|
||||||
goto ON_EXIT;
|
|
||||||
}
|
|
||||||
} else if (!CompareMem (FilePostFix, L".cer",4)) {
|
|
||||||
Status = CreatePkX509SignatureList (
|
|
||||||
Private->FileContext->FHandle,
|
|
||||||
&PkCert
|
|
||||||
);
|
|
||||||
if (EFI_ERROR (Status)) {
|
|
||||||
goto ON_EXIT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ASSERT (PkCert != NULL);
|
ASSERT (PkCert != NULL);
|
||||||
|
|
||||||
|
@ -393,8 +373,14 @@ EnrollPlatformKey (
|
||||||
// Set Platform Key variable.
|
// Set Platform Key variable.
|
||||||
//
|
//
|
||||||
Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS
|
Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS
|
||||||
| EFI_VARIABLE_BOOTSERVICE_ACCESS;
|
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
||||||
DataSize = PkCert->SignatureListSize;
|
DataSize = PkCert->SignatureListSize;
|
||||||
|
Status = CreateTimeBasedPayload (&DataSize, (UINT8**) &PkCert);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
DEBUG ((EFI_D_ERROR, "Fail to create time-based data payload: %r", Status));
|
||||||
|
goto ON_EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
Status = gRT->SetVariable(
|
Status = gRT->SetVariable(
|
||||||
EFI_PLATFORM_KEY_NAME,
|
EFI_PLATFORM_KEY_NAME,
|
||||||
&gEfiGlobalVariableGuid,
|
&gEfiGlobalVariableGuid,
|
||||||
|
@ -437,8 +423,10 @@ DeletePlatformKey (
|
||||||
{
|
{
|
||||||
EFI_STATUS Status;
|
EFI_STATUS Status;
|
||||||
|
|
||||||
Status = DeleteVariable (EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid);
|
Status = DeleteVariable (
|
||||||
|
EFI_PLATFORM_KEY_NAME,
|
||||||
|
&gEfiGlobalVariableGuid
|
||||||
|
);
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,8 +539,14 @@ EnrollRsa2048ToKek (
|
||||||
// If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the
|
// If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the
|
||||||
// new KEK to original variable.
|
// new KEK to original variable.
|
||||||
//
|
//
|
||||||
Attr |= EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS;
|
Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS
|
||||||
DataSize = 0;
|
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
||||||
|
Status = CreateTimeBasedPayload (&KekSigListSize, (UINT8**) &KekSigList);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
DEBUG ((EFI_D_ERROR, "Fail to create time-based data payload: %r", Status));
|
||||||
|
goto ON_EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
Status = gRT->GetVariable(
|
Status = gRT->GetVariable(
|
||||||
EFI_KEY_EXCHANGE_KEY_NAME,
|
EFI_KEY_EXCHANGE_KEY_NAME,
|
||||||
&gEfiGlobalVariableGuid,
|
&gEfiGlobalVariableGuid,
|
||||||
|
@ -672,8 +666,13 @@ EnrollX509ToKek (
|
||||||
// new kek to original variable
|
// new kek to original variable
|
||||||
//
|
//
|
||||||
Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS
|
Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS
|
||||||
| EFI_VARIABLE_BOOTSERVICE_ACCESS;
|
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
||||||
|
Status = CreateTimeBasedPayload (&KekSigListSize, (UINT8**) &KekSigList);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
DEBUG ((EFI_D_ERROR, "Fail to create time-based data payload: %r", Status));
|
||||||
|
goto ON_EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
Status = gRT->GetVariable(
|
Status = gRT->GetVariable(
|
||||||
EFI_KEY_EXCHANGE_KEY_NAME,
|
EFI_KEY_EXCHANGE_KEY_NAME,
|
||||||
&gEfiGlobalVariableGuid,
|
&gEfiGlobalVariableGuid,
|
||||||
|
@ -826,7 +825,12 @@ EnrollX509toSigDB (
|
||||||
// new signature data to original variable
|
// new signature data to original variable
|
||||||
//
|
//
|
||||||
Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS
|
Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS
|
||||||
| EFI_VARIABLE_BOOTSERVICE_ACCESS;
|
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
||||||
|
Status = CreateTimeBasedPayload (&SigDBSize, (UINT8**) &Data);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
DEBUG ((EFI_D_ERROR, "Fail to create time-based data payload: %r", Status));
|
||||||
|
goto ON_EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
Status = gRT->GetVariable(
|
Status = gRT->GetVariable(
|
||||||
VariableName,
|
VariableName,
|
||||||
|
@ -1281,8 +1285,6 @@ EnrollImageSignatureToSigDB (
|
||||||
|
|
||||||
Data = NULL;
|
Data = NULL;
|
||||||
GuidCertData = NULL;
|
GuidCertData = NULL;
|
||||||
Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS
|
|
||||||
| EFI_VARIABLE_BOOTSERVICE_ACCESS;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Form the SigDB certificate list.
|
// Form the SigDB certificate list.
|
||||||
|
@ -1374,6 +1376,14 @@ EnrollImageSignatureToSigDB (
|
||||||
CopyGuid (&SigDBCertData->SignatureOwner, Private->SignatureGUID);
|
CopyGuid (&SigDBCertData->SignatureOwner, Private->SignatureGUID);
|
||||||
CopyMem (SigDBCertData->SignatureData, mImageDigest, mImageDigestSize);
|
CopyMem (SigDBCertData->SignatureData, mImageDigest, mImageDigestSize);
|
||||||
|
|
||||||
|
Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS
|
||||||
|
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
||||||
|
Status = CreateTimeBasedPayload (&SigDBSize, (UINT8**) &Data);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
DEBUG ((EFI_D_ERROR, "Fail to create time-based data payload: %r", Status));
|
||||||
|
goto ON_EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Check if SigDB variable has been already existed.
|
// Check if SigDB variable has been already existed.
|
||||||
// If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the
|
// If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the
|
||||||
|
@ -1799,8 +1809,14 @@ DeleteKeyExchangeKey (
|
||||||
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
|
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
CertList = (EFI_SIGNATURE_LIST*) OldData;
|
|
||||||
DataSize = Offset;
|
DataSize = Offset;
|
||||||
|
if ((Attr & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
||||||
|
Status = CreateTimeBasedPayload (&DataSize, &OldData);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
DEBUG ((EFI_D_ERROR, "Fail to create time-based data payload: %r", Status));
|
||||||
|
goto ON_EXIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Status = gRT->SetVariable(
|
Status = gRT->SetVariable(
|
||||||
EFI_KEY_EXCHANGE_KEY_NAME,
|
EFI_KEY_EXCHANGE_KEY_NAME,
|
||||||
|
@ -1986,8 +2002,14 @@ DeleteSignature (
|
||||||
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
|
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
CertList = (EFI_SIGNATURE_LIST*) OldData;
|
|
||||||
DataSize = Offset;
|
DataSize = Offset;
|
||||||
|
if ((Attr & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
||||||
|
Status = CreateTimeBasedPayload (&DataSize, &OldData);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
DEBUG ((EFI_D_ERROR, "Fail to create time-based data payload: %r", Status));
|
||||||
|
goto ON_EXIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Status = gRT->SetVariable(
|
Status = gRT->SetVariable(
|
||||||
VariableName,
|
VariableName,
|
||||||
|
@ -2465,7 +2487,7 @@ SecureBootCallback (
|
||||||
CreatePopUp (
|
CreatePopUp (
|
||||||
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
|
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
|
||||||
&Key,
|
&Key,
|
||||||
L"ERROR: The File Type is neither *.cer nor *.pbk!",
|
L"ERROR: Unsupported file type, only *.cer is supported!",
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue