/** Single function calculates SHA1 digest value for all raw data. It combines Sha1Init(), Sha1Update() and Sha1Final(). @param[in] Data Raw data to be digested. @param[in] DataLen Size of the raw data. @param[out] Digest Pointer to a buffer that stores the final digest. @retval EFI_SUCCESS Always successfully calculate the final digest. **/ EFI_STATUS EFIAPI TpmCommHashAll ( IN CONST UINT8 *Data, IN UINTN DataLen, OUT TPM_DIGEST *Digest ) { VOID *Sha1Ctx; UINTN CtxSize; CtxSize = Sha1GetContextSize (); Sha1Ctx = AllocatePool (CtxSize); ASSERT (Sha1Ctx != NULL); Sha1Init (Sha1Ctx); Sha1Update (Sha1Ctx, Data, DataLen); Sha1Final (Sha1Ctx, (UINT8 *)Digest); FreePool (Sha1Ctx); return EFI_SUCCESS; }
/** Calculates the hash of the given data based on the specified hash GUID. @param[in] Data Pointer to the data buffer to be hashed. @param[in] DataSize The size of data buffer in bytes. @param[in] CertGuid The GUID to identify the hash algorithm to be used. @param[out] HashValue Pointer to a buffer that receives the hash result. @retval TRUE Data hash calculation succeeded. @retval FALSE Data hash calculation failed. **/ BOOLEAN CalculateDataHash ( IN VOID *Data, IN UINTN DataSize, IN EFI_GUID *CertGuid, OUT UINT8 *HashValue ) { BOOLEAN Status; VOID *HashCtx; UINTN CtxSize; Status = FALSE; HashCtx = NULL; if (CompareGuid (CertGuid, &gEfiCertSha1Guid)) { // // SHA-1 Hash // CtxSize = Sha1GetContextSize (); HashCtx = AllocatePool (CtxSize); if (HashCtx == NULL) { goto _Exit; } Status = Sha1Init (HashCtx); Status = Sha1Update (HashCtx, Data, DataSize); Status = Sha1Final (HashCtx, HashValue); } else if (CompareGuid (CertGuid, &gEfiCertSha256Guid)) { // // SHA256 Hash // CtxSize = Sha256GetContextSize (); HashCtx = AllocatePool (CtxSize); if (HashCtx == NULL) { goto _Exit; } Status = Sha256Init (HashCtx); Status = Sha256Update (HashCtx, Data, DataSize); Status = Sha256Final (HashCtx, HashValue); } else if (CompareGuid (CertGuid, &gEfiCertSha384Guid)) { // // SHA384 Hash // CtxSize = Sha384GetContextSize (); HashCtx = AllocatePool (CtxSize); if (HashCtx == NULL) { goto _Exit; } Status = Sha384Init (HashCtx); Status = Sha384Update (HashCtx, Data, DataSize); Status = Sha384Final (HashCtx, HashValue); } else if (CompareGuid (CertGuid, &gEfiCertSha512Guid)) { // // SHA512 Hash // CtxSize = Sha512GetContextSize (); HashCtx = AllocatePool (CtxSize); if (HashCtx == NULL) { goto _Exit; } Status = Sha512Init (HashCtx); Status = Sha512Update (HashCtx, Data, DataSize); Status = Sha512Final (HashCtx, HashValue); } _Exit: if (HashCtx != NULL) { FreePool (HashCtx); } return Status; }
/** Measure PE image into TPM log based on the authenticode image hashing in PE/COFF Specification 8.0 Appendix A. Caution: This function may receive untrusted input. PE/COFF image is external input, so this function will validate its data structure within this image buffer before use. @param[in] TcgProtocol Pointer to the located TCG protocol instance. @param[in] ImageAddress Start address of image buffer. @param[in] ImageSize Image size @param[in] LinkTimeBase Address that the image is loaded into memory. @param[in] ImageType Image subsystem type. @param[in] FilePath File path is corresponding to the input image. @retval EFI_SUCCESS Successfully measure image. @retval EFI_OUT_OF_RESOURCES No enough resource to measure image. @retval EFI_UNSUPPORTED ImageType is unsupported or PE image is mal-format. @retval other error value **/ EFI_STATUS EFIAPI TcgMeasurePeImage ( IN EFI_TCG_PROTOCOL *TcgProtocol, IN EFI_PHYSICAL_ADDRESS ImageAddress, IN UINTN ImageSize, IN UINTN LinkTimeBase, IN UINT16 ImageType, IN EFI_DEVICE_PATH_PROTOCOL *FilePath ) { EFI_STATUS Status; TCG_PCR_EVENT *TcgEvent; EFI_IMAGE_LOAD_EVENT *ImageLoad; UINT32 FilePathSize; VOID *Sha1Ctx; UINTN CtxSize; EFI_IMAGE_DOS_HEADER *DosHdr; UINT32 PeCoffHeaderOffset; EFI_IMAGE_SECTION_HEADER *Section; UINT8 *HashBase; UINTN HashSize; UINTN SumOfBytesHashed; EFI_IMAGE_SECTION_HEADER *SectionHeader; UINTN Index; UINTN Pos; UINT16 Magic; UINT32 EventSize; UINT32 EventNumber; EFI_PHYSICAL_ADDRESS EventLogLastEntry; EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; UINT32 NumberOfRvaAndSizes; BOOLEAN HashStatus; UINT32 CertSize; Status = EFI_UNSUPPORTED; ImageLoad = NULL; SectionHeader = NULL; Sha1Ctx = NULL; FilePathSize = (UINT32) GetDevicePathSize (FilePath); // // Determine destination PCR by BootPolicy // EventSize = sizeof (*ImageLoad) - sizeof (ImageLoad->DevicePath) + FilePathSize; TcgEvent = AllocateZeroPool (EventSize + sizeof (TCG_PCR_EVENT)); if (TcgEvent == NULL) { return EFI_OUT_OF_RESOURCES; } TcgEvent->EventSize = EventSize; ImageLoad = (EFI_IMAGE_LOAD_EVENT *) TcgEvent->Event; switch (ImageType) { case EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION: TcgEvent->EventType = EV_EFI_BOOT_SERVICES_APPLICATION; TcgEvent->PCRIndex = 4; break; case EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: TcgEvent->EventType = EV_EFI_BOOT_SERVICES_DRIVER; TcgEvent->PCRIndex = 2; break; case EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER: TcgEvent->EventType = EV_EFI_RUNTIME_SERVICES_DRIVER; TcgEvent->PCRIndex = 2; break; default: DEBUG (( EFI_D_ERROR, "TcgMeasurePeImage: Unknown subsystem type %d", ImageType )); goto Finish; } ImageLoad->ImageLocationInMemory = ImageAddress; ImageLoad->ImageLengthInMemory = ImageSize; ImageLoad->ImageLinkTimeAddress = LinkTimeBase; ImageLoad->LengthOfDevicePath = FilePathSize; if ((FilePath != NULL) && (FilePathSize != 0)) { CopyMem (ImageLoad->DevicePath, FilePath, FilePathSize); } // // Check PE/COFF image // DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageAddress; PeCoffHeaderOffset = 0; if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { PeCoffHeaderOffset = DosHdr->e_lfanew; } Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset); if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { goto Finish; } // // PE/COFF Image Measurement // // NOTE: The following codes/steps are based upon the authenticode image hashing in // PE/COFF Specification 8.0 Appendix A. // // // 1. Load the image header into memory. // 2. Initialize a SHA hash context. CtxSize = Sha1GetContextSize (); Sha1Ctx = AllocatePool (CtxSize); if (Sha1Ctx == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Finish; } HashStatus = Sha1Init (Sha1Ctx); if (!HashStatus) { goto Finish; } // // Measuring PE/COFF Image Header; // But CheckSum field and SECURITY data directory (certificate) are excluded // if (Hdr.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC // then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC // Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC; } else { // // Get the magic value from the PE/COFF Optional Header // Magic = Hdr.Pe32->OptionalHeader.Magic; } // // 3. Calculate the distance from the base of the image header to the image checksum address. // 4. Hash the image header from its base to beginning of the image checksum. // HashBase = (UINT8 *) (UINTN) ImageAddress; if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset // NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes; HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32->OptionalHeader.CheckSum) - HashBase); } else { // // Use PE32+ offset // NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32Plus->OptionalHeader.CheckSum) - HashBase); } HashStatus = Sha1Update (Sha1Ctx, HashBase, HashSize); if (!HashStatus) { goto Finish; } // // 5. Skip over the image checksum (it occupies a single ULONG). // if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { // // 6. Since there is no Cert Directory in optional header, hash everything // from the end of the checksum to the end of image header. // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); } else { // // Use PE32+ offset. // HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); } if (HashSize != 0) { HashStatus = Sha1Update (Sha1Ctx, HashBase, HashSize); if (!HashStatus) { goto Finish; } } } else { // // 7. Hash everything from the end of the checksum to the start of the Cert Directory. // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset // HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase); } else { // // Use PE32+ offset // HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase); } if (HashSize != 0) { HashStatus = Sha1Update (Sha1Ctx, HashBase, HashSize); if (!HashStatus) { goto Finish; } } // // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.) // 9. Hash everything from the end of the Cert Directory to the end of image header. // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset // HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); } else { // // Use PE32+ offset // HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); } if (HashSize != 0) { HashStatus = Sha1Update (Sha1Ctx, HashBase, HashSize); if (!HashStatus) { goto Finish; } } } // // 10. Set the SUM_OF_BYTES_HASHED to the size of the header // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset // SumOfBytesHashed = Hdr.Pe32->OptionalHeader.SizeOfHeaders; } else { // // Use PE32+ offset // SumOfBytesHashed = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders; } // // 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER // structures in the image. The 'NumberOfSections' field of the image // header indicates how big the table should be. Do not include any // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero. // SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * Hdr.Pe32->FileHeader.NumberOfSections); if (SectionHeader == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Finish; } // // 12. Using the 'PointerToRawData' in the referenced section headers as // a key, arrange the elements in the table in ascending order. In other // words, sort the section headers according to the disk-file offset of // the section. // Section = (EFI_IMAGE_SECTION_HEADER *) ( (UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER) + Hdr.Pe32->FileHeader.SizeOfOptionalHeader ); for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { Pos = Index; while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) { CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof(EFI_IMAGE_SECTION_HEADER)); Pos--; } CopyMem (&SectionHeader[Pos], Section, sizeof(EFI_IMAGE_SECTION_HEADER)); Section += 1; } // // 13. Walk through the sorted table, bring the corresponding section // into memory, and hash the entire section (using the 'SizeOfRawData' // field in the section header to determine the amount of data to hash). // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED . // 15. Repeat steps 13 and 14 for all the sections in the sorted table. // for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { Section = (EFI_IMAGE_SECTION_HEADER *) &SectionHeader[Index]; if (Section->SizeOfRawData == 0) { continue; } HashBase = (UINT8 *) (UINTN) ImageAddress + Section->PointerToRawData; HashSize = (UINTN) Section->SizeOfRawData; HashStatus = Sha1Update (Sha1Ctx, HashBase, HashSize); if (!HashStatus) { goto Finish; } SumOfBytesHashed += HashSize; } // // 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra // data in the file that needs to be added to the hash. This data begins // at file offset SUM_OF_BYTES_HASHED and its length is: // FileSize - (CertDirectory->Size) // if (ImageSize > SumOfBytesHashed) { HashBase = (UINT8 *) (UINTN) ImageAddress + SumOfBytesHashed; if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { CertSize = 0; } else { if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // CertSize = Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; } else { // // Use PE32+ offset. // CertSize = Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; } } if (ImageSize > CertSize + SumOfBytesHashed) { HashSize = (UINTN) (ImageSize - CertSize - SumOfBytesHashed); HashStatus = Sha1Update (Sha1Ctx, HashBase, HashSize); if (!HashStatus) { goto Finish; } } else if (ImageSize < CertSize + SumOfBytesHashed) { goto Finish; } } // // 17. Finalize the SHA hash. // HashStatus = Sha1Final (Sha1Ctx, (UINT8 *) &TcgEvent->Digest); if (!HashStatus) { goto Finish; } // // Log the PE data // EventNumber = 1; Status = TcgProtocol->HashLogExtendEvent ( TcgProtocol, (EFI_PHYSICAL_ADDRESS) (UINTN) (VOID *) NULL, 0, TPM_ALG_SHA, TcgEvent, &EventNumber, &EventLogLastEntry ); if (Status == EFI_OUT_OF_RESOURCES) { // // Out of resource here means the image is hashed and its result is extended to PCR. // But the event log cann't be saved since log area is full. // Just return EFI_SUCCESS in order not to block the image load. // Status = EFI_SUCCESS; } Finish: FreePool (TcgEvent); if (SectionHeader != NULL) { FreePool (SectionHeader); } if (Sha1Ctx != NULL ) { FreePool (Sha1Ctx); } return Status; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TestLargeVector // // Tests AES CTR against a known large vector (of 1 million bytes). We check it against a known SHA-1 hash of // the output. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static bool TestLargeVector ( void ) { //dd if=/dev/zero iflag=count_bytes count=1000000 status=none | openssl enc -aes-128-ctr -K 00001111222233334444555566667777 -iv 88889999aaaabbbb | openssl sha1 //(stdin)= 6227c0192b110133fadd6d229790bbdf13c068ab uint8_t const* key = (uint8_t const*)"\x00\x00\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77"; uint8_t const* iv = (uint8_t const*)"\x88\x88\x99\x99\xaa\xaa\xbb\xbb"; uint8_t const* sha1Hash = (uint8_t const*)"\xe1\x63\x5f\xa4\xf5\x7c\x98\x54\xf6\x18\xec\x0c\x8f\x18\x7f\x04\x34\xa2\xe1\x72"; uint32_t const numBytesToGenerate = 1000000; uint8_t* buffer = malloc( numBytesToGenerate ); uint32_t amountLeft = numBytesToGenerate; uint32_t chunkSize; Sha1Context sha1Context; AesCtrContext aesCtrContext; SHA1_HASH calcSha1; // Encrypt in one go first. memset( buffer, 0, numBytesToGenerate ); AesCtrXorWithKey( key, AES_KEY_SIZE_128, iv, buffer, buffer, numBytesToGenerate ); Sha1Initialise( &sha1Context ); Sha1Update( &sha1Context, buffer, numBytesToGenerate ); Sha1Finalise( &sha1Context, &calcSha1 ); if( 0 != memcmp( &calcSha1, sha1Hash, SHA1_HASH_SIZE ) ) { printf( "Large test vector failed\n" ); return false; } memset( buffer, 0, numBytesToGenerate ); // Now encrypt in smaller pieces (10000 bytes at a time) Sha1Initialise( &sha1Context ); AesCtrInitialiseWithKey( &aesCtrContext, key, AES_KEY_SIZE_128, iv ); while( amountLeft > 0 ) { memset( buffer, 0, numBytesToGenerate ); chunkSize = MIN( amountLeft, 10000 ); AesCtrOutput( &aesCtrContext, buffer, chunkSize ); Sha1Update( &sha1Context, buffer, chunkSize ); amountLeft -= chunkSize; } Sha1Finalise( &sha1Context, &calcSha1 ); if( 0 != memcmp( &calcSha1, sha1Hash, SHA1_HASH_SIZE ) ) { printf( "Large test vector failed\n" ); return false; } return true; }