/** Authenticate data with the certificate. @param[in] InData Pointer to the Data to be signed. @param[in] InDataSize InData size in bytes. @param[in] PrivateKey Pointer to the private key. @param[in] PrivateKeySize The size of Private Key in bytes. @param[in] KeyPassWord Pointer to the password for retrieving private key. @param[in] KeyPwdSize The size of Key Password in bytes. @param[out] OutData The pointer to the signed data. @param[in, out] OutDataSize Pointer to contain the size of out data. **/ VOID IpSecCryptoIoAuthDataWithCertificate ( IN UINT8 *InData, IN UINTN InDataSize, IN UINT8 *PrivateKey, IN UINTN PrivateKeySize, IN UINT8 *KeyPassWord, IN UINTN KeyPwdSize, OUT UINT8 **OutData, IN OUT UINTN *OutDataSize ) { UINT8 *RsaContext; UINT8 *Signature; UINTN SigSize; SigSize = 0; RsaContext = NULL; // // Retrieve RSA Private Key from password-protected PEM data // RsaGetPrivateKeyFromPem ( (CONST UINT8 *)PrivateKey, PrivateKeySize, (CONST CHAR8 *)KeyPassWord, (VOID **) &RsaContext ); if (RsaContext == NULL) { return; } // // Sign data // Signature = NULL; if (!RsaPkcs1Sign (RsaContext, InData, InDataSize, Signature, &SigSize)) { Signature = AllocateZeroPool (SigSize); } else { return; } RsaPkcs1Sign (RsaContext, InData, InDataSize, Signature, &SigSize); *OutData = Signature; *OutDataSize = SigSize; if (RsaContext != NULL) { RsaFree (RsaContext); } }
/** Verify the singed data with the public key which is contained in a certificate. @param[in] InCert Pointer to the Certificate which contains the public key. @param[in] CertLen The size of Certificate in bytes. @param[in] InCa Pointer to the CA certificate @param[in] CaLen The size of CA certificate in bytes. @param[in] InData Pointer to octect message hash to be checked. @param[in] InDataSize Size of the message hash in bytes. @param[in] Singnature The pointer to the RSA PKCS1-V1_5 signature to be verifed. @param[in] SigSize Size of signature in bytes. @retval TRUE Valid signature encoded in PKCS1-v1_5. @retval FALSE Invalid signature or invalid RSA context. **/ BOOLEAN IpSecCryptoIoVerifySignDataByCertificate ( IN UINT8 *InCert, IN UINTN CertLen, IN UINT8 *InCa, IN UINTN CaLen, IN UINT8 *InData, IN UINTN InDataSize, IN UINT8 *Singnature, IN UINTN SigSize ) { UINT8 *RsaContext; BOOLEAN Status; // // Create the RSA Context // RsaContext = RsaNew (); if (RsaContext == NULL) { return FALSE; } // // Verify the validity of X509 Certificate // if (!X509VerifyCert (InCert, CertLen, InCa, CaLen)) { return FALSE; } // // Retrieve the RSA public Key from Certificate // RsaGetPublicKeyFromX509 ((CONST UINT8 *)InCert, CertLen, (VOID **)&RsaContext); // // Verify data // Status = RsaPkcs1Verify (RsaContext, InData, InDataSize, Singnature, SigSize); if (RsaContext != NULL) { RsaFree (RsaContext); } return Status; }
/** Retrieves the RSA Public Key from one X509 certificate (DER format only). @param[in] InCert Pointer to the certificate. @param[in] CertLen The size of the certificate in bytes. @param[out] PublicKey Pointer to the retrieved public key. @param[out] PublicKeyLen Size of Public Key in bytes. @retval EFI_SUCCESS Successfully get the public Key. @retval EFI_INVALID_PARAMETER The certificate is malformed. **/ EFI_STATUS IpSecCryptoIoGetPublicKeyFromCert ( IN UINT8 *InCert, IN UINTN CertLen, OUT UINT8 **PublicKey, OUT UINTN *PublicKeyLen ) { UINT8 *RsaContext; EFI_STATUS Status; Status = EFI_SUCCESS; // // Create the RSA Context // RsaContext = RsaNew (); // // Retrieve the RSA public key from CA Certificate // if (!RsaGetPublicKeyFromX509 ((CONST UINT8 *)InCert, CertLen, (VOID **) &RsaContext)) { Status = EFI_INVALID_PARAMETER; goto EXIT; } *PublicKeyLen = 0; RsaGetKey (RsaContext, RsaKeyN, NULL, PublicKeyLen); *PublicKey = AllocateZeroPool (*PublicKeyLen); ASSERT (*PublicKey != NULL); if (!RsaGetKey (RsaContext, RsaKeyN, *PublicKey, PublicKeyLen)) { Status = EFI_INVALID_PARAMETER; } EXIT: if (RsaContext != NULL) { RsaFree (RsaContext); } return Status; }
/** Verify data payload with AuthInfo in EFI_CERT_TYPE_RSA2048_SHA256 type. Follow the steps in UEFI2.2. @param[in] VirtualMode The current calling mode for this function. @param[in] Global The context of this Extended SAL Variable Services Class call. @param[in] Data The pointer to data with AuthInfo. @param[in] DataSize The size of Data. @param[in] PubKey The public key used for verification. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_SECURITY_VIOLATION Authentication failed. @retval EFI_SUCCESS Authentication successful. **/ EFI_STATUS VerifyDataPayload ( IN BOOLEAN VirtualMode, IN ESAL_VARIABLE_GLOBAL *Global, IN UINT8 *Data, IN UINTN DataSize, IN UINT8 *PubKey ) { BOOLEAN Status; EFI_VARIABLE_AUTHENTICATION *CertData; EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlock; UINT8 Digest[SHA256_DIGEST_SIZE]; VOID *Rsa; VOID *HashContext; Rsa = NULL; CertData = NULL; CertBlock = NULL; if (Data == NULL || PubKey == NULL) { return EFI_INVALID_PARAMETER; } CertData = (EFI_VARIABLE_AUTHENTICATION *) Data; CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) (CertData->AuthInfo.CertData); // // wCertificateType should be WIN_CERT_TYPE_EFI_GUID. // Cert type should be EFI_CERT_TYPE_RSA2048_SHA256. // if ((CertData->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) || !CompareGuid (&CertData->AuthInfo.CertType, Global->CertRsa2048Sha256Guid[VirtualMode]) ) { // // Invalid AuthInfo type, return EFI_SECURITY_VIOLATION. // return EFI_SECURITY_VIOLATION; } // // Hash data payload with SHA256. // ZeroMem (Digest, SHA256_DIGEST_SIZE); HashContext = Global->HashContext[VirtualMode]; Status = Sha256Init (HashContext); if (!Status) { goto Done; } Status = Sha256Update (HashContext, Data + AUTHINFO_SIZE, (UINTN) (DataSize - AUTHINFO_SIZE)); if (!Status) { goto Done; } // // Hash Monotonic Count. // Status = Sha256Update (HashContext, &CertData->MonotonicCount, sizeof (UINT64)); if (!Status) { goto Done; } Status = Sha256Final (HashContext, Digest); if (!Status) { goto Done; } // // Generate & Initialize RSA Context. // Rsa = RsaNew (); ASSERT (Rsa != NULL); // // Set RSA Key Components. // NOTE: Only N and E are needed to be set as RSA public key for signature verification. // Status = RsaSetKey (Rsa, RsaKeyN, PubKey, EFI_CERT_TYPE_RSA2048_SIZE); if (!Status) { goto Done; } Status = RsaSetKey (Rsa, RsaKeyE, mRsaE, sizeof (mRsaE)); if (!Status) { goto Done; } // // Verify the signature. // Status = RsaPkcs1Verify ( Rsa, Digest, SHA256_DIGEST_SIZE, CertBlock->Signature, EFI_CERT_TYPE_RSA2048_SHA256_SIZE ); Done: if (Rsa != NULL) { RsaFree (Rsa); } if (Status) { return EFI_SUCCESS; } else { return EFI_SECURITY_VIOLATION; } }
/** Extraction handler tries to extract raw data from the input guided section. It also does authentication check for RSA 2048 SHA 256 signature in the input guided section. It first checks whether the input guid section is supported. If not, EFI_INVALID_PARAMETER will return. @param InputSection Buffer containing the input GUIDed section to be processed. @param OutputBuffer Buffer to contain the output raw data allocated by the caller. @param ScratchBuffer A pointer to a caller-allocated buffer for function internal use. @param AuthenticationStatus A pointer to a caller-allocated UINT32 that indicates the authentication status of the output buffer. @retval EFI_SUCCESS Section Data and Auth Status is extracted successfully. @retval EFI_INVALID_PARAMETER The GUID in InputSection does not match this instance guid. **/ EFI_STATUS EFIAPI Rsa2048Sha256GuidedSectionHandler ( IN CONST VOID *InputSection, OUT VOID **OutputBuffer, IN VOID *ScratchBuffer, OPTIONAL OUT UINT32 *AuthenticationStatus ) { EFI_STATUS Status; UINT32 OutputBufferSize; VOID *DummyInterface; EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlockRsa2048Sha256; BOOLEAN CryptoStatus; UINT8 Digest[SHA256_DIGEST_SIZE]; UINT8 *PublicKey; UINTN PublicKeyBufferSize; VOID *HashContext; VOID *Rsa; HashContext = NULL; Rsa = NULL; if (IS_SECTION2 (InputSection)) { // // Check whether the input guid section is recognized. // if (!CompareGuid ( &gEfiCertTypeRsa2048Sha256Guid, &(((EFI_GUID_DEFINED_SECTION2 *)InputSection)->SectionDefinitionGuid))) { return EFI_INVALID_PARAMETER; } // // Get the RSA 2048 SHA 256 information. // CertBlockRsa2048Sha256 = &((RSA_2048_SHA_256_SECTION2_HEADER *) InputSection)->CertBlockRsa2048Sha256; OutputBufferSize = SECTION2_SIZE (InputSection) - sizeof (RSA_2048_SHA_256_SECTION2_HEADER); if ((((EFI_GUID_DEFINED_SECTION *)InputSection)->Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) != 0) { PERF_START (NULL, "RsaCopy", "DXE", 0); CopyMem (*OutputBuffer, (UINT8 *)InputSection + sizeof (RSA_2048_SHA_256_SECTION2_HEADER), OutputBufferSize); PERF_END (NULL, "RsaCopy", "DXE", 0); } else { *OutputBuffer = (UINT8 *)InputSection + sizeof (RSA_2048_SHA_256_SECTION2_HEADER); } // // Implicitly RSA 2048 SHA 256 GUIDed section should have STATUS_VALID bit set // ASSERT ((((EFI_GUID_DEFINED_SECTION2 *)InputSection)->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) != 0); *AuthenticationStatus = EFI_AUTH_STATUS_IMAGE_SIGNED; } else { // // Check whether the input guid section is recognized. // if (!CompareGuid ( &gEfiCertTypeRsa2048Sha256Guid, &(((EFI_GUID_DEFINED_SECTION *)InputSection)->SectionDefinitionGuid))) { return EFI_INVALID_PARAMETER; } // // Get the RSA 2048 SHA 256 information. // CertBlockRsa2048Sha256 = &((RSA_2048_SHA_256_SECTION_HEADER *)InputSection)->CertBlockRsa2048Sha256; OutputBufferSize = SECTION_SIZE (InputSection) - sizeof (RSA_2048_SHA_256_SECTION_HEADER); if ((((EFI_GUID_DEFINED_SECTION *)InputSection)->Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) != 0) { PERF_START (NULL, "RsaCopy", "DXE", 0); CopyMem (*OutputBuffer, (UINT8 *)InputSection + sizeof (RSA_2048_SHA_256_SECTION_HEADER), OutputBufferSize); PERF_END (NULL, "RsaCopy", "DXE", 0); } else { *OutputBuffer = (UINT8 *)InputSection + sizeof (RSA_2048_SHA_256_SECTION_HEADER); } // // Implicitly RSA 2048 SHA 256 GUIDed section should have STATUS_VALID bit set // ASSERT ((((EFI_GUID_DEFINED_SECTION *) InputSection)->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) != 0); *AuthenticationStatus = EFI_AUTH_STATUS_IMAGE_SIGNED; } // // Check whether there exists EFI_SECURITY_POLICY_PROTOCOL_GUID. // Status = gBS->LocateProtocol (&gEfiSecurityPolicyProtocolGuid, NULL, &DummyInterface); if (!EFI_ERROR (Status)) { // // If SecurityPolicy Protocol exist, AUTH platform override bit is set. // *AuthenticationStatus |= EFI_AUTH_STATUS_PLATFORM_OVERRIDE; return EFI_SUCCESS; } // // All paths from here return EFI_SUCESS and result is returned in AuthenticationStatus // Status = EFI_SUCCESS; // // Fail if the HashType is not SHA 256 // if (!CompareGuid (&gEfiHashAlgorithmSha256Guid, &CertBlockRsa2048Sha256->HashType)) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: HASH type of section is not supported\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } // // Allocate hash context buffer required for SHA 256 // HashContext = AllocatePool (Sha256GetContextSize ()); if (HashContext == NULL) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: Can not allocate hash context\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } // // Hash public key from data payload with SHA256. // ZeroMem (Digest, SHA256_DIGEST_SIZE); CryptoStatus = Sha256Init (HashContext); if (!CryptoStatus) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: Sha256Init() failed\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } CryptoStatus = Sha256Update (HashContext, &CertBlockRsa2048Sha256->PublicKey, sizeof(CertBlockRsa2048Sha256->PublicKey)); if (!CryptoStatus) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: Sha256Update() failed\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } CryptoStatus = Sha256Final (HashContext, Digest); if (!CryptoStatus) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: Sha256Final() failed\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } // // Fail if the PublicKey is not one of the public keys in PcdRsa2048Sha256PublicKeyBuffer // PublicKey = (UINT8 *)PcdGetPtr (PcdRsa2048Sha256PublicKeyBuffer); DEBUG ((DEBUG_VERBOSE, "DxePcdRsa2048Sha256: PublicKeyBuffer = %p\n", PublicKey)); ASSERT (PublicKey != NULL); DEBUG ((DEBUG_VERBOSE, "DxePcdRsa2048Sha256: PublicKeyBuffer Token = %08x\n", PcdToken (PcdRsa2048Sha256PublicKeyBuffer))); PublicKeyBufferSize = LibPcdGetExSize (&gEfiSecurityPkgTokenSpaceGuid, PcdToken (PcdRsa2048Sha256PublicKeyBuffer)); DEBUG ((DEBUG_VERBOSE, "DxePcdRsa2048Sha256: PublicKeyBuffer Size = %08x\n", PublicKeyBufferSize)); ASSERT ((PublicKeyBufferSize % SHA256_DIGEST_SIZE) == 0); CryptoStatus = FALSE; while (PublicKeyBufferSize != 0) { if (CompareMem (Digest, PublicKey, SHA256_DIGEST_SIZE) == 0) { CryptoStatus = TRUE; break; } PublicKey = PublicKey + SHA256_DIGEST_SIZE; PublicKeyBufferSize = PublicKeyBufferSize - SHA256_DIGEST_SIZE; } if (!CryptoStatus) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: Public key in section is not supported\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } // // Generate & Initialize RSA Context. // Rsa = RsaNew (); if (Rsa == NULL) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: RsaNew() failed\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } // // Set RSA Key Components. // NOTE: Only N and E are needed to be set as RSA public key for signature verification. // CryptoStatus = RsaSetKey (Rsa, RsaKeyN, CertBlockRsa2048Sha256->PublicKey, sizeof(CertBlockRsa2048Sha256->PublicKey)); if (!CryptoStatus) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: RsaSetKey(RsaKeyN) failed\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } CryptoStatus = RsaSetKey (Rsa, RsaKeyE, mRsaE, sizeof (mRsaE)); if (!CryptoStatus) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: RsaSetKey(RsaKeyE) failed\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } // // Hash data payload with SHA256. // ZeroMem (Digest, SHA256_DIGEST_SIZE); CryptoStatus = Sha256Init (HashContext); if (!CryptoStatus) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: Sha256Init() failed\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } PERF_START (NULL, "RsaShaData", "DXE", 0); CryptoStatus = Sha256Update (HashContext, *OutputBuffer, OutputBufferSize); PERF_END (NULL, "RsaShaData", "DXE", 0); if (!CryptoStatus) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: Sha256Update() failed\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } CryptoStatus = Sha256Final (HashContext, Digest); if (!CryptoStatus) { DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: Sha256Final() failed\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; goto Done; } // // Verify the RSA 2048 SHA 256 signature. // PERF_START (NULL, "RsaVerify", "DXE", 0); CryptoStatus = RsaPkcs1Verify ( Rsa, Digest, SHA256_DIGEST_SIZE, CertBlockRsa2048Sha256->Signature, sizeof (CertBlockRsa2048Sha256->Signature) ); PERF_END (NULL, "RsaVerify", "DXE", 0); if (!CryptoStatus) { // // If RSA 2048 SHA 256 signature verification fails, AUTH tested failed bit is set. // DEBUG ((DEBUG_ERROR, "DxeRsa2048Sha256: RsaPkcs1Verify() failed\n")); *AuthenticationStatus |= EFI_AUTH_STATUS_TEST_FAILED; } Done: // // Free allocated resources used to perform RSA 2048 SHA 256 signature verification // if (Rsa != NULL) { RsaFree (Rsa); } if (HashContext != NULL) { FreePool (HashContext); } DEBUG ((DEBUG_VERBOSE, "DxeRsa2048Sha256: Status = %r AuthenticationStatus = %08x\n", Status, *AuthenticationStatus)); return Status; }
/** Validate UEFI-OpenSSL RSA Key Retrieving & Signature Interfaces. @retval EFI_SUCCESS Validation succeeded. @retval EFI_ABORTED Validation failed. **/ EFI_STATUS ValidateCryptRsa2 ( VOID ) { BOOLEAN Status; VOID *RsaPrivKey; VOID *RsaPubKey; UINT8 *Signature; UINTN SigSize; UINT8 *Subject; UINTN SubjectSize; Print (L"\nUEFI-OpenSSL RSA Key Retrieving Testing: "); // // Retrieve RSA private key from encrypted PEM data. // Print (L"\n- Retrieve RSA Private Key for PEM ..."); Status = RsaGetPrivateKeyFromPem (TestKeyPem, sizeof (TestKeyPem), PemPass, &RsaPrivKey); if (!Status) { Print (L"[Fail]"); return EFI_ABORTED; } else { Print (L"[Pass]"); } // // Retrieve RSA public key from X509 Certificate. // Print (L"\n- Retrieve RSA Public Key from X509 ... "); RsaPubKey = NULL; Status = RsaGetPublicKeyFromX509 (TestCert, sizeof (TestCert), &RsaPubKey); if (!Status) { Print (L"[Fail]"); return EFI_ABORTED; } else { Print (L"[Pass]"); } // // Generate RSA PKCS#1 Signature. // Print (L"\n- PKCS#1 Signature ... "); SigSize = 0; Status = RsaPkcs1Sign (RsaPrivKey, MsgHash, SHA1_DIGEST_SIZE, NULL, &SigSize); if (Status || SigSize == 0) { Print (L"[Fail]"); return EFI_ABORTED; } Signature = AllocatePool (SigSize); Status = RsaPkcs1Sign (RsaPrivKey, MsgHash, SHA1_DIGEST_SIZE, Signature, &SigSize); if (!Status) { Print (L"[Fail]"); return EFI_ABORTED; } else { Print (L"[Pass]"); } // // Verify RSA PKCS#1-encoded Signature. // Print (L"\n- PKCS#1 Signature Verification ... "); Status = RsaPkcs1Verify (RsaPubKey, MsgHash, SHA1_DIGEST_SIZE, Signature, SigSize); if (!Status) { Print (L"[Fail]"); return EFI_ABORTED; } else { Print (L"[Pass]"); } // // X509 Certificate Subject Retrieving. // Print (L"\n- X509 Certificate Subject Bytes Retrieving ... "); SubjectSize = 0; Status = X509GetSubjectName (TestCert, sizeof (TestCert), NULL, &SubjectSize); Subject = (UINT8 *)AllocatePool (SubjectSize); Status = X509GetSubjectName (TestCert, sizeof (TestCert), Subject, &SubjectSize); if (!Status) { Print (L"[Fail]"); return EFI_ABORTED; } else { Print (L"[Pass]"); } // // X509 Certificate Verification. // Print (L"\n- X509 Certificate Verification with Trusted CA ..."); Status = X509VerifyCert (TestCert, sizeof (TestCert), TestCACert, sizeof (TestCACert)); if (!Status) { Print (L"[Fail]\n"); return EFI_ABORTED; } else { Print (L"[Pass]\n"); } // // Release Resources. // RsaFree (RsaPubKey); RsaFree (RsaPrivKey); FreePool (Signature); FreePool (Subject); return EFI_SUCCESS; }