diff --git a/Include/IndustryStandard/AppleChunklist.h b/Include/IndustryStandard/AppleChunklist.h new file mode 100644 index 00000000..74e611c8 --- /dev/null +++ b/Include/IndustryStandard/AppleChunklist.h @@ -0,0 +1,65 @@ +/** @file + Copyright (C) 2019, Goldfish64. All rights reserved. + + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#ifndef APPLE_CHUNKLIST_H_ +#define APPLE_CHUNKLIST_H_ + +#pragma pack(1) + +// +// Magic number used to identify chunklist. +// +#define APPLE_CHUNKLIST_MAGIC 0x4C4B4E43 // "CNKL" + +// +// Supported chunklist versions. +// +#define APPLE_CHUNKLIST_FILE_VERSION_10 0x1 +#define APPLE_CHUNKLIST_CHUNK_METHOD_10 0x1 +#define APPLE_CHUNKLIST_SIG_METHOD_10 0x1 +#define APPLE_CHUNKLIST_CHECKSUM_LENGTH 32 +#define APPLE_CHUNKLIST_SIG_LENGTH 256 + +// +// Chunklist chunk. +// +typedef struct { + UINT32 Length; + UINT8 Checksum[APPLE_CHUNKLIST_CHECKSUM_LENGTH]; +} APPLE_CHUNKLIST_CHUNK; + +// +// Chunklist signature. +// +typedef struct { + UINT8 Signature[APPLE_CHUNKLIST_SIG_LENGTH]; +} APPLE_CHUNKLIST_SIG; + +// +// Chunklist header. +// +typedef struct { + UINT32 Magic; + UINT32 Length; + UINT8 FileVersion; + UINT8 ChunkMethod; + UINT8 SigMethod; + UINT8 Unused; + + UINT64 ChunkCount; + UINT64 ChunkOffset; + UINT64 SigOffset; +} APPLE_CHUNKLIST_HEADER; + +#pragma pack() + +#endif diff --git a/Include/Library/OcAppleChunklistLib.h b/Include/Library/OcAppleChunklistLib.h new file mode 100644 index 00000000..f2e848cc --- /dev/null +++ b/Include/Library/OcAppleChunklistLib.h @@ -0,0 +1,69 @@ +/** @file + Copyright (C) 2019, Goldfish64. All rights reserved. + + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#ifndef APPLE_CHUNKLIST_LIB_H_ +#define APPLE_CHUNKLIST_LIB_H_ + +#include + +// +// Chunklist context. +// +typedef struct { + APPLE_CHUNKLIST_HEADER *Header; + UINTN FileSize; + APPLE_CHUNKLIST_CHUNK *Chunks; + APPLE_CHUNKLIST_SIG *Signature; +} OC_APPLE_CHUNKLIST_CONTEXT; + +// +// Chunklist functions. +// + +/** + Initializes a chunklist context. + + @param[in] Buffer A pointer to a buffer containing the chunklist data. + @param[in] Length The length of the buffer specified in Buffer. + @param[out] Context The Context to initialize. + + @retval EFI_SUCCESS The Context was intialized successfully. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED The chunklist is unsupported. +**/ +EFI_STATUS +EFIAPI +OcAppleChunklistInitializeContext( + IN VOID *Buffer, + IN UINTN Length, + OUT OC_APPLE_CHUNKLIST_CONTEXT *Context); + +/** + Verifies the specified data against a chunklist context. + + @param[in] Context The Context to verify against. + @param[in] Buffer A pointer to a buffer containing the data to be verified. + @param[in] Length The length of the buffer specified in Buffer. + + @retval EFI_SUCCESS The data was verified successfully. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_END_OF_FILE The end of Buffer was reached. + @retval EFI_COMPROMISED_DATA The data failed verification. +**/ +EFI_STATUS +EFIAPI +OcAppleChunklistVerifyData( + IN OC_APPLE_CHUNKLIST_CONTEXT *Context, + IN VOID *Buffer, + IN UINTN Length); + +#endif diff --git a/Library/OcAppleChunklistLib/OcAppleChunklistLib.c b/Library/OcAppleChunklistLib/OcAppleChunklistLib.c new file mode 100644 index 00000000..26efabaa --- /dev/null +++ b/Library/OcAppleChunklistLib/OcAppleChunklistLib.c @@ -0,0 +1,98 @@ +/** @file + Copyright (C) 2019, Goldfish64. All rights reserved. + + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include +#include +#include +#include +#include +#include +#include + +EFI_STATUS +EFIAPI +OcAppleChunklistInitializeContext( + IN VOID *Buffer, + IN UINTN Length, + OUT OC_APPLE_CHUNKLIST_CONTEXT *Context) { + + // Create variables. + APPLE_CHUNKLIST_HEADER *ChunklistHeader = NULL; + + // Check if parameters are valid. + if (!Buffer || !Length || !Context) + return EFI_INVALID_PARAMETER; + if (Length < sizeof(APPLE_CHUNKLIST_HEADER)) + return EFI_INVALID_PARAMETER; + + // Get header and ensure it is valid. + ChunklistHeader = (APPLE_CHUNKLIST_HEADER*)Buffer; + if ((ChunklistHeader->Magic != APPLE_CHUNKLIST_MAGIC) || + (ChunklistHeader->Length != sizeof(APPLE_CHUNKLIST_HEADER)) || + (ChunklistHeader->FileVersion != APPLE_CHUNKLIST_FILE_VERSION_10) || + (ChunklistHeader->ChunkMethod != APPLE_CHUNKLIST_CHUNK_METHOD_10) || + (ChunklistHeader->SigMethod != APPLE_CHUNKLIST_SIG_METHOD_10)) + return EFI_UNSUPPORTED; + + // Get chunklist data. + ZeroMem(Context, sizeof(OC_APPLE_CHUNKLIST_CONTEXT)); + Context->Header = ChunklistHeader; + Context->FileSize = Length; + Context->Chunks = (APPLE_CHUNKLIST_CHUNK*)(((UINT8*)Buffer) + ReadUnaligned64(&(ChunklistHeader->ChunkOffset))); + Context->Signature = (APPLE_CHUNKLIST_SIG*)(((UINT8*)Buffer) + ReadUnaligned64(&(ChunklistHeader->SigOffset))); + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +OcAppleChunklistVerifyData( + IN OC_APPLE_CHUNKLIST_CONTEXT *Context, + IN VOID *Buffer, + IN UINTN Length) { + + // Create variables. + UINT8 *BufferCurrent = NULL; + UINTN RemainingLength = 0; + APPLE_CHUNKLIST_CHUNK *Chunks; + UINT64 ChunkCount; + UINT8 ChunkHash[SHA256_DIGEST_SIZE]; + + // Check if parameters are valid. + if (!Context || !Buffer || !Length) + return EFI_INVALID_PARAMETER; + + // Get chunklist info. + Chunks = Context->Chunks; + ChunkCount = ReadUnaligned64(&(Context->Header->ChunkCount)); + + // Hash each chunk and validate checksums are the same. + BufferCurrent = (UINT8*)Buffer; + RemainingLength = Length; + for (UINT64 i = 0; i < ChunkCount; i++) { + // Ensure length of chunk is valid. + if (Chunks[i].Length > RemainingLength) + return EFI_END_OF_FILE; + + // Calculate checksum of data and ensure they match. + DEBUG((DEBUG_INFO, "AppleChunklistVerifyData(): Validating chunk %lu of %lu\n", i, ChunkCount)); + Sha256(ChunkHash, BufferCurrent, Chunks[i].Length); + if (CompareMem(ChunkHash, Chunks[i].Checksum, SHA256_DIGEST_SIZE)) + return EFI_COMPROMISED_DATA; + + // Move to next chunk. + BufferCurrent += Chunks[i].Length; + RemainingLength -= Chunks[i].Length; + } + + // Success. + return EFI_SUCCESS; +} diff --git a/Library/OcAppleChunklistLib/OcAppleChunklistLib.inf b/Library/OcAppleChunklistLib/OcAppleChunklistLib.inf new file mode 100644 index 00000000..f83b567f --- /dev/null +++ b/Library/OcAppleChunklistLib/OcAppleChunklistLib.inf @@ -0,0 +1,33 @@ +## @file +# Copyright (C) 2019, Goldfish64. All rights reserved. +# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = OcAppleChunklistLib + FILE_GUID = D891DF81-0C83-47FF-ABAD-546050E1A07F + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = OcAppleChunklistLib|PEIM DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_DRIVER UEFI_APPLICATION DXE_SMM_DRIVER + +[Packages] + MdePkg/MdePkg.dec + OcSupportPkg/OcSupportPkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + OcCryptoLib + UefiLib + +[Sources] + OcAppleChunklistLib.c diff --git a/OcSupportPkg.dec b/OcSupportPkg.dec index 90d32807..f1f6b3a1 100644 --- a/OcSupportPkg.dec +++ b/OcSupportPkg.dec @@ -57,6 +57,9 @@ ## @libraryclass OcAppleBootPolicyLib|Include/Library/OcAppleBootPolicyLib.h + ## @libraryclass + OcAppleChunklistLib|Include/Library/OcAppleChunklistLib.h + ## @libraryclass OcAppleImageVerificationLib|Include/Library/OcAppleImageVerificationLib.h diff --git a/OcSupportPkg.dsc b/OcSupportPkg.dsc index fc3ed043..c653d6e2 100644 --- a/OcSupportPkg.dsc +++ b/OcSupportPkg.dsc @@ -49,6 +49,7 @@ [Components] OcSupportPkg/Library/OcAppleBootPolicyLib/OcAppleBootPolicyLib.inf + OcSupportPkg/Library/OcAppleChunklistLib/OcAppleChunklistLib.inf OcSupportPkg/Library/OcAppleImageVerificationLib/OcAppleImageVerificationLib.inf OcSupportPkg/Library/OcCryptoLib/OcCryptoLib.inf OcSupportPkg/Library/OcGuardLib/OcGuardLib.inf