提交 147f88d1 编写于 作者: V vit9696

OcAppleImageVerificationLib: Separate keys into OcAppleKeysLib and import...

OcAppleImageVerificationLib: Separate keys into OcAppleKeysLib and import tools from AppleSupportPkg
上级 aa490fa0
......@@ -13,6 +13,11 @@ TestsUser/Prelinked/Prelinked
TestsUser/Prelinked/Result.xml
TestsUser/Serialized/Serialized
TestsUser/Smbios/Smbios
Tools/EfiResTool/EfiResTool
Tools/AppleEfiSignTool/AppleEfiSignTool
Tools/AppleEfiSignTool/RsaPkGen/RsaTool
Tools/readlabel/readlabel
*.o
DICT
fuzz-*.log
crash-*
......
/** @file
Copyright (C) 2019, vit9696. All rights reserved.
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 OC_APPLE_KEYS_LIB_H
#define OC_APPLE_KEYS_LIB_H
#define NUM_OF_PK 2
typedef struct APPLE_PK_ENTRY_ {
UINT8 Hash[32];
UINT8 PublicKey[520];
} APPLE_PK_ENTRY;
extern CONST APPLE_PK_ENTRY PkDataBase[NUM_OF_PK];
#endif // OC_APPLE_KEYS_LIB_H
......@@ -33,11 +33,11 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include <Library/UefiLib.h>
#include <Library/OcCryptoLib.h>
#include <Library/OcAppleImageVerificationLib.h>
#include <Library/OcAppleKeysLib.h>
#include <Library/OcGuardLib.h>
#include <Protocol/DebugSupport.h>
#include <IndustryStandard/PeImage.h>
#include <Guid/AppleCertificate.h>
#include "ApplePublicKeyDb.h"
UINT16
GetPeHeaderMagicValue (
......
......@@ -44,6 +44,7 @@
BaseLib
UefiLib
DebugLib
OcAppleKeysLib
OcCryptoLib
OcGuardLib
......
/** @file
Copyright (C) 2019, vit9696. All rights reserved.
OcAppleImageVerificationLib
All rights reserved.
Copyright (c) 2018, savvas
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.
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_PK_DB_H
#define APPLE_PK_DB_H
#define NUM_OF_PK 2
#include <Library/OcAppleKeysLib.h>
typedef struct APPLE_PK_ENTRY_ {
UINT8 Hash[32];
UINT8 PublicKey[520];
} APPLE_PK_ENTRY;
APPLE_PK_ENTRY PkDataBase[NUM_OF_PK] = {
{
//
// PublicKey hash
//
{
CONST APPLE_PK_ENTRY PkDataBase[NUM_OF_PK] = {
{
//
// PublicKey hash
//
{
0xc7, 0xa1, 0xb9, 0x36, 0x28, 0x80, 0xde, 0x69, 0x57, 0x62, 0xb7, 0xb6,
0x5b, 0xec, 0x6b, 0xf1, 0x56, 0xa5, 0x5c, 0xf9, 0x24, 0x7f, 0x22, 0xef,
0x78, 0x62, 0x35, 0x53, 0x7f, 0x95, 0x2b, 0x45
},
},
//
// Original PublicKey
//
......@@ -52,10 +40,10 @@ APPLE_PK_ENTRY PkDataBase[NUM_OF_PK] = {
299E9EC8 6F5E85D4 79213FB1 F9DD5B93 8C57DE9A 52FF62A7
E1431EA9 250EE129 4338CDD9 CA48E7C3
**/
//
// PublicKey in format specified by Chromium project implementation
//
{
//
// PublicKey in format specified by Chromium project implementation
//
{
0x40, 0x00, 0x00, 0x00, 0xd1, 0x16, 0xcd, 0xe7, 0xcf, 0xfd, 0x3e, 0x6b,
0xfe, 0x66, 0xec, 0x75, 0xf4, 0x4b, 0x7e, 0x2e, 0x0e, 0xd2, 0x63, 0x98,
0x08, 0xa9, 0x8d, 0x10, 0xac, 0x37, 0x8e, 0x55, 0x1c, 0xaa, 0x0e, 0x1c,
......@@ -101,8 +89,8 @@ APPLE_PK_ENTRY PkDataBase[NUM_OF_PK] = {
0xe1, 0x72, 0xc3, 0x6c, 0x5f, 0xe4, 0x66, 0x0d, 0x4e, 0x08, 0x65, 0x9c,
0x46, 0xc5, 0x7b, 0x04
}
},
{
},
{
//
// PublicKey hash
//
......@@ -176,9 +164,5 @@ APPLE_PK_ENTRY PkDataBase[NUM_OF_PK] = {
0x37, 0x2e, 0x4c, 0x39, 0x19, 0xa0, 0x4e, 0x43, 0xe0, 0xcc, 0xd9, 0x87,
0x2c, 0xd1, 0x2c, 0x3e
}
}
}
};
extern APPLE_PK_ENTRY PkDataBase[NUM_OF_PK];
#endif //APPLE_PK_DB_H
## @file
# OcAppleVerificationLib
#
# Copyright (c) 2017-2018, savvas
#
# 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 = OcAppleKeysLib
FILE_GUID = 1CADD588-89D4-464C-92DC-FA0ABE350B16
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
LIBRARY_CLASS = OcAppleKeysLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SAL_DRIVER DXE_SMM_DRIVER SMM_CORE UEFI_APPLICATION UEFI_DRIVER
#
# VALID_ARCHITECTURES = X64
#
[Sources]
OcAppleKeysLib.c
[Packages]
MdePkg/MdePkg.dec
EfiPkg/EfiPkg.dec
OcSupportPkg/OcSupportPkg.dec
[LibraryClasses]
BaseLib
......@@ -25,7 +25,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
for computation.
**/
#ifdef EFIAPI
#include <Library/BaseMemoryLib.h>
#endif
#include <Library/OcCryptoLib.h>
/**
......
......@@ -30,7 +30,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
This implementation uses little endian byte order.
**/
#ifdef EFIAPI
#include <Library/BaseMemoryLib.h>
#endif
#include <Library/OcCryptoLib.h>
#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32-(b))))
......
......@@ -65,6 +65,9 @@
## @libraryclass
OcAppleKernelLib|Include/Library/OcAppleKernelLib.h
## @libraryclass
OcAppleKeysLib|Include/Library/OcAppleKeysLib.h
## @libraryclass
OcBootManagementLib|Include/Library/OcBootManagementLib.h
......
......@@ -53,6 +53,7 @@
OcAppleDiskImageLib|OcSupportPkg/Library/OcAppleDiskImageLib/OcAppleDiskImageLib.inf
OcAppleImageVerificationLib|OcSupportPkg/Library/OcAppleImageVerificationLib/OcAppleImageVerificationLib.inf
OcAppleKernelLib|OcSupportPkg/Library/OcAppleKernelLib/OcAppleKernelLib.inf
OcAppleKeysLib|OcSupportPkg/Library/OcAppleKeysLib/OcAppleKeysLib.inf
OcBootManagementLib|OcSupportPkg/Library/OcBootManagementLib/OcBootManagementLib.inf
OcCpuLib|OcSupportPkg/Library/OcCpuLib/OcCpuLib.inf
OcCryptoLib|OcSupportPkg/Library/OcCryptoLib/OcCryptoLib.inf
......
/** @file
AppleEfiSignTool – Tool for signing and verifying Apple EFI binaries.
Copyright (c) 2018, savvas
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 "AppleEfiPeImage.h"
#include "AppleEfiFatBinary.h"
#include <Library/OcAppleKeysLib.h>
#ifdef DEBUG
# define DEBUG_PRINT(x) printf x
#else
# define DEBUG_PRINT(x) do {} while (0)
#endif
uint16_t
GetPeHeaderMagicValue (
EFI_IMAGE_OPTIONAL_HEADER_UNION *Hdr
)
{
/**
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 returned value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
**/
if (Hdr->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 &&
Hdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
return EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
}
//
// Return the magic value from the PC/COFF Optional Header
//
return Hdr->Pe32.OptionalHeader.Magic;
}
int
BuildPeContext (
void *Image,
uint32_t *RealImageSize,
APPLE_PE_COFF_LOADER_IMAGE_CONTEXT *Context
)
{
EFI_IMAGE_DOS_HEADER *DosHdr = NULL;
EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHdr = NULL;
uint16_t PeHdrMagic = 0;
uint32_t HeaderWithoutDataDir = 0;
uint32_t SectionHeaderOffset = 0;
EFI_IMAGE_SECTION_HEADER *SectionCache = NULL;
uint32_t Index = 0;
uint32_t MaxHeaderSize = 0;
uint32_t ImageSize = 0;
ImageSize = *RealImageSize;
//
// Check context
//
if (Context == NULL) {
DEBUG_PRINT (("Null context\n"));
return -1;
}
//
// Verify Image size
//
if (sizeof (EFI_IMAGE_DOS_HEADER) >= sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION)) {
MaxHeaderSize = sizeof (EFI_IMAGE_DOS_HEADER);
} else {
MaxHeaderSize = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION);
}
if (ImageSize < MaxHeaderSize) {
DEBUG_PRINT (("Invalid image\n"));
return -1;
}
DosHdr = Image;
//
// Verify DosHdr magic
//
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
if (DosHdr->e_lfanew > ImageSize
|| DosHdr->e_lfanew < sizeof (EFI_IMAGE_DOS_HEADER)) {
DEBUG_PRINT (("Invalid PE offset\n"));
return -1;
}
PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *) ((uint8_t *) Image
+ DosHdr->e_lfanew);
if ((uint8_t *) Image + ImageSize -
sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION) < (uint8_t *) PeHdr) {
DEBUG_PRINT (("Invalid PE location\n"));
return -1;
}
//
// DosHdr is not relevant for UEFI, and it is not hashed by Apple Signature.
// Zero it to avoid potential harm!
//
memset ((uint8_t *) Image + sizeof (EFI_IMAGE_DOS_HEADER), 0,
DosHdr->e_lfanew - sizeof (EFI_IMAGE_DOS_HEADER));
} else {
//
// DosHdr truncated
//
PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *) Image;
}
PeHdrMagic = GetPeHeaderMagicValue (PeHdr);
if (PeHdrMagic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Pe32 part
//
//
// Check image header size
//
if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES <
PeHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes) {
DEBUG_PRINT (("Image header too small\n"));
return -1;
}
//
// Check image header aligment
//
HeaderWithoutDataDir = sizeof (EFI_IMAGE_OPTIONAL_HEADER32) -
sizeof (EFI_IMAGE_DATA_DIRECTORY) *
EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES;
if (PeHdr->Pe32.FileHeader.SizeOfOptionalHeader < HeaderWithoutDataDir
|| PeHdr->Pe32.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir
!= PeHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY)) {
DEBUG_PRINT (("Image header overflows data directory\n"));
return -1;
}
//
// Check image sections overflow
//
SectionHeaderOffset = DosHdr->e_lfanew + sizeof (uint32_t)
+ sizeof (EFI_IMAGE_FILE_HEADER)
+ PeHdr->Pe32.FileHeader.SizeOfOptionalHeader;
if (PeHdr->Pe32.OptionalHeader.SizeOfImage < SectionHeaderOffset ||
((PeHdr->Pe32.OptionalHeader.SizeOfImage - SectionHeaderOffset)
/ EFI_IMAGE_SIZEOF_SECTION_HEADER) <= PeHdr->Pe32.FileHeader.NumberOfSections) {
DEBUG_PRINT (("Image sections overflow image size\n"));
return -1;
}
if (PeHdr->Pe32.OptionalHeader.SizeOfHeaders < SectionHeaderOffset
|| ((PeHdr->Pe32.OptionalHeader.SizeOfHeaders - SectionHeaderOffset)
/ EFI_IMAGE_SIZEOF_SECTION_HEADER)
< (uint32_t) PeHdr->Pe32.FileHeader.NumberOfSections) {
DEBUG_PRINT (("Image sections overflow section headers\n"));
return -1;
}
} else if (PeHdrMagic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
//
// Pe32+ part
//
//
// Check image header size
//
if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES <
PeHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes) {
DEBUG_PRINT (("Image header too small\n"));
return -1;
}
//
// Check image header aligment
//
HeaderWithoutDataDir = sizeof (EFI_IMAGE_OPTIONAL_HEADER64) -
sizeof (EFI_IMAGE_DATA_DIRECTORY) *
EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES;
if (PeHdr->Pe32Plus.FileHeader.SizeOfOptionalHeader < HeaderWithoutDataDir
|| (PeHdr->Pe32Plus.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir) !=
PeHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY)) {
DEBUG_PRINT (("Image header overflows data directory\n"));
return -1;
}
//
// Check image sections overflow
//
SectionHeaderOffset = DosHdr->e_lfanew
+ sizeof (uint32_t)
+ sizeof (EFI_IMAGE_FILE_HEADER)
+ PeHdr->Pe32Plus.FileHeader.SizeOfOptionalHeader;
if (PeHdr->Pe32Plus.OptionalHeader.SizeOfImage < SectionHeaderOffset
|| ((PeHdr->Pe32Plus.OptionalHeader.SizeOfImage - SectionHeaderOffset)
/ EFI_IMAGE_SIZEOF_SECTION_HEADER) <= PeHdr->Pe32Plus.FileHeader.NumberOfSections) {
DEBUG_PRINT (("Image sections overflow image size\n"));
return -1;
}
if (PeHdr->Pe32Plus.OptionalHeader.SizeOfHeaders < SectionHeaderOffset
|| ((PeHdr->Pe32Plus.OptionalHeader.SizeOfHeaders - SectionHeaderOffset)
/ EFI_IMAGE_SIZEOF_SECTION_HEADER) < (uint32_t) PeHdr->Pe32Plus.FileHeader.NumberOfSections) {
DEBUG_PRINT (("Image sections overflow section headers\n"));
return -1;
}
} else {
DEBUG_PRINT (("Unsupported PE header magic\n"));
return -1;
}
if (PeHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) {
DEBUG_PRINT (("Unsupported image type\n"));
return -1;
}
if (PeHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) {
DEBUG_PRINT (("Unsupported image - Relocations have been stripped\n"));
return -1;
}
if (PeHdrMagic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Fill context for Pe32
//
Context->PeHdr = PeHdr;
Context->ImageAddress = PeHdr->Pe32.OptionalHeader.ImageBase;
Context->ImageSize = PeHdr->Pe32.OptionalHeader.SizeOfImage;
Context->SizeOfOptionalHeader = PeHdr->Pe32.FileHeader.SizeOfOptionalHeader;
Context->OptHdrChecksum= &Context->PeHdr->Pe32.OptionalHeader.CheckSum;
Context->SizeOfHeaders = PeHdr->Pe32.OptionalHeader.SizeOfHeaders;
Context->EntryPoint = PeHdr->Pe32.OptionalHeader.AddressOfEntryPoint;
Context->RelocDir = &PeHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
Context->NumberOfRvaAndSizes = PeHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes;
Context->NumberOfSections = PeHdr->Pe32.FileHeader.NumberOfSections;
Context->FirstSection = (EFI_IMAGE_SECTION_HEADER *) ((uint8_t *) PeHdr
+ PeHdr->Pe32.FileHeader.SizeOfOptionalHeader
+ sizeof (uint32_t)
+ sizeof (EFI_IMAGE_FILE_HEADER));
Context->SecDir = &PeHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
} else {
//
// Fill context for Pe32+
//
Context->PeHdr = PeHdr;
Context->ImageAddress = PeHdr->Pe32Plus.OptionalHeader.ImageBase;
Context->ImageSize = PeHdr->Pe32Plus.OptionalHeader.SizeOfImage;
Context->SizeOfOptionalHeader = PeHdr->Pe32.FileHeader.SizeOfOptionalHeader;
Context->OptHdrChecksum= &Context->PeHdr->Pe32Plus.OptionalHeader.CheckSum;
Context->SizeOfHeaders = PeHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
Context->EntryPoint = PeHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
Context->RelocDir = &PeHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
Context->NumberOfRvaAndSizes = PeHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
Context->NumberOfSections = PeHdr->Pe32.FileHeader.NumberOfSections;
Context->FirstSection = (EFI_IMAGE_SECTION_HEADER *) ((uint8_t *) PeHdr
+ PeHdr->Pe32.FileHeader.SizeOfOptionalHeader
+ sizeof (uint32_t)
+ sizeof (EFI_IMAGE_FILE_HEADER));
Context->SecDir = &PeHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
}
//
// Fill sections info
//
Context->PeHdrMagic = PeHdrMagic;
SectionCache = Context->FirstSection;
for (Index = 0, Context->SumOfSectionBytes = 0;
Index < Context->NumberOfSections; Index++, SectionCache++) {
if (Context->SumOfSectionBytes + SectionCache->SizeOfRawData
< Context->SumOfSectionBytes) {
DEBUG_PRINT (("Malformed binary: %x %x\n", (uint32_t) Context->SumOfSectionBytes, ImageSize));
return -1;
}
Context->SumOfSectionBytes += SectionCache->SizeOfRawData;
}
if (Context->SumOfSectionBytes >= ImageSize) {
DEBUG_PRINT (("Malformed binary: %x %x\n", (uint32_t) Context->SumOfSectionBytes, ImageSize));
return -1;
}
if (Context->ImageSize < Context->SizeOfHeaders) {
DEBUG_PRINT (("Invalid image\n"));
return -1;
}
if ((uint32_t) ((uint8_t *) Context->SecDir - (uint8_t *) Image) >
(ImageSize - sizeof (EFI_IMAGE_DATA_DIRECTORY))) {
DEBUG_PRINT (("Invalid image\n"));
return -1;
}
if (Context->SecDir->VirtualAddress >= ImageSize) {
DEBUG_PRINT (("Malformed security header\n"));
return -1;
}
//TODO: review the abobe checks, and truncate the image after
// Context->SecDir->VirtualAddress + APPLE_SIGNATURE_SECENTRY_SIZE.
return 0;
}
int
GetApplePeImageSignature (
void *Image,
APPLE_PE_COFF_LOADER_IMAGE_CONTEXT *Context,
uint8_t *PkLe,
uint8_t *PkBe,
uint8_t *SigLe,
uint8_t *SigBe
)
{
uint32_t SignatureDirectoryAddress = 0;
APPLE_SIGNATURE_DIRECTORY *SignatureDirectory = NULL;
//
// Get ptr and size of AppleSignatureDirectory
//
SignatureDirectoryAddress = Context->SecDir->VirtualAddress;
//
// Extract AppleSignatureDirectory
//
SignatureDirectory = (APPLE_SIGNATURE_DIRECTORY *)
((uint8_t *) Image + SignatureDirectoryAddress);
//
// Load PublicKey and Signature into memory
//
memcpy (PkLe, SignatureDirectory->PublicKey, 256);
memcpy (SigLe, SignatureDirectory->Signature, 256);
for (size_t i = 0; i < 256; i++) {
PkBe[256 - 1 - i] = PkLe[i];
SigBe[256 - 1 - i] = SigLe[i];
}
return 0;
}
int
GetApplePeImageSha256 (
void *Image,
APPLE_PE_COFF_LOADER_IMAGE_CONTEXT *Context,
uint8_t *CalcucatedHash
)
{
uint64_t HashSize = 0;
uint8_t *HashBase = NULL;
SHA256_CONTEXT Sha256Ctx;
//
// Apple EFI_IMAGE_DIRECTORY_ENTRY_SECURITY is always 8 bytes.
//
if (Context->SecDir->Size != APPLE_SIGNATURE_SECENTRY_SIZE) {
DEBUG_PRINT (("Invalid Apple signature size %u\n", (unsigned) Context->SecDir->Size));
return -1;
}
//
// Initialise a SHA hash context
//
Sha256Init (&Sha256Ctx);
//
// Hash DOS header and skip DOS stub
//
HashBase = (uint8_t *) Image;
HashSize = sizeof (EFI_IMAGE_DOS_HEADER);
Sha256Update (&Sha256Ctx, HashBase, HashSize);
//
// CheckSum field and SECURITY data directory (certificate) are excluded.
// Calculate the distance from the base of the image header to the image checksum address
// Hash the image header from its base to beginning of the image checksum
//
HashBase = (uint8_t *) Image + ((EFI_IMAGE_DOS_HEADER *) Image)->e_lfanew;
HashSize = (uint8_t *) Context->OptHdrChecksum - HashBase;
Sha256Update (&Sha256Ctx, HashBase, HashSize);
//
// Hash everything from the end of the checksum to the start of the Cert Directory.
//
HashBase = (uint8_t *) Context->OptHdrChecksum + sizeof (uint32_t);
HashSize = (uint8_t *) Context->SecDir - HashBase;
Sha256Update (&Sha256Ctx, HashBase, HashSize);
//
// Hash the rest of the file till SecDir data
//
HashBase = (uint8_t *) Context->SecDir + sizeof (EFI_IMAGE_DATA_DIRECTORY);
HashSize = (uint8_t *) Image + Context->SecDir->VirtualAddress - HashBase;
Sha256Update (&Sha256Ctx, HashBase, HashSize);
Sha256Final (&Sha256Ctx, CalcucatedHash);
return 0;
}
int
VerifyApplePeImageSignature (
void *PeImage,
uint32_t ImageSize
)
{
uint8_t PkLe[256];
uint8_t PkBe[256];
uint8_t SigLe[256];
uint8_t SigBe[256];
uint8_t CalcucatedHash[32];
uint8_t PkHash[32];
uint32_t WorkBuf32[RSANUMWORDS*3];
RSA_PUBLIC_KEY *Pk = NULL;
APPLE_PE_COFF_LOADER_IMAGE_CONTEXT *Context = NULL;
Context = malloc (sizeof (APPLE_PE_COFF_LOADER_IMAGE_CONTEXT));
if (Context == NULL) {
DEBUG_PRINT (("Pe context allocation failure\n"));
return -1;
}
if (BuildPeContext (PeImage, &ImageSize, Context) != 0) {
DEBUG_PRINT (("Malformed ApplePeImage\n"));
free (Context);
return -1;
}
//
// Extract AppleSignature from PEImage
//
if (GetApplePeImageSignature (PeImage, Context, PkLe, PkBe, SigLe, SigBe) != 0) {
DEBUG_PRINT (("AppleSignature broken or not present!\n"));
free (Context);
return -1;
}
//
// Calcucate PeImage hash via AppleAuthenticode algorithm
//
if (GetApplePeImageSha256 (PeImage, Context, CalcucatedHash) != 0) {
DEBUG_PRINT (("Couldn't calcuate hash of PeImage\n"));
free (Context);
return -1;
}
free (Context);
//
// Calculate Sha256 of extracted public key
//
SHA256_CONTEXT Sha256Ctx;
Sha256Init (&Sha256Ctx);
Sha256Update (&Sha256Ctx, PkLe, sizeof (PkLe));
Sha256Final (&Sha256Ctx, PkHash);
//
// Verify existence in DataBase
//
for (int Index = 0; Index < NUM_OF_PK; Index++) {
if (memcmp (PkDataBase[Index].Hash, PkHash, sizeof (PkHash)) == 0) {
//
// PublicKey valid. Extract prepared publickey from database
//
Pk = (RSA_PUBLIC_KEY *) PkDataBase[Index].PublicKey;
}
}
if (Pk == NULL) {
DEBUG_PRINT (("Unknown publickey or malformed AppleSignature directory!\n"));
return -1;
}
//
// Verify signature
//
if (RsaVerify (Pk, SigBe, CalcucatedHash, WorkBuf32) == 1 ) {
puts ("Signature verified!\n");
return 0;
}
return -1;
}
/**
Read Apple's EFI Fat binary and gather
position of each MZ image inside it and then
perform ImageVerification of each MZ image
**/
int
VerifyAppleImageSignature (
uint8_t *Image,
uint32_t ImageSize
)
{
EFIFatHeader *Hdr = NULL;
uint64_t Index = 0;
uint64_t SizeOfBinary = 0;
if (ImageSize < sizeof (EFIFatHeader)) {
DEBUG_PRINT (("Malformed binary\n"));
return -1;
}
//
// Get AppleEfiFatHeader
//
Hdr = (EFIFatHeader *) Image;
//
// Verify magic number
//
//
if (Hdr->Magic != EFI_FAT_MAGIC) {
DEBUG_PRINT (("Binary isn't EFIFat, verifying as single\n"));
return VerifyApplePeImageSignature (Image, ImageSize);
}
DEBUG_PRINT (("It is AppleEfiFatBinary\n"));
SizeOfBinary += sizeof (EFIFatHeader)
+ sizeof (EFIFatArchHeader)
* Hdr->NumArchs;
if (SizeOfBinary > ImageSize) {
DEBUG_PRINT (("Malformed AppleEfiFat header\n"));
return -1;
}
//
// Loop over number of arch's
//
for (Index = 0; Index < Hdr->NumArchs; Index++) {
//
// Only X86/X86_64 valid
//
if (Hdr->Archs[Index].CpuType == CPU_TYPE_X86
|| Hdr->Archs[Index].CpuType == CPU_TYPE_X86_64) {
DEBUG_PRINT (("ApplePeImage at offset %u\n", Hdr->Archs[Index].Offset));
//
// Check offset boundary and its size
//
if (Hdr->Archs[Index].Offset < SizeOfBinary
|| Hdr->Archs[Index].Offset >= ImageSize
|| ImageSize < ((uint64_t) Hdr->Archs[Index].Offset
+ Hdr->Archs[Index].Size)) {
DEBUG_PRINT(("Wrong offset of Image or it's size\n"));
return -1;
}
//
// Verify image with specified arch
//
if (VerifyApplePeImageSignature (Image + Hdr->Archs[Index].Offset,
Hdr->Archs[Index].Size) != 0) {
return -1;
}
}
SizeOfBinary = (uint64_t) Hdr->Archs[Index].Offset + Hdr->Archs[Index].Size;
}
if (SizeOfBinary != ImageSize) {
DEBUG_PRINT (("Malformed AppleEfiFatBinary\n"));
return -1;
}
return 0;
}
/** @file
AppleEfiSignTool – Tool for signing and verifying Apple EFI binaries.
Copyright (c) 2018, savvas
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_EFI_FAT_BINARY_H
#define APPLE_EFI_FAT_BINARY_H
#include <stdio.h>
#include <stdint.h>
#define EFI_FAT_MAGIC 0x0ef1fab9
#define CPU_ARCH_ABI64 0x01000000
#define CPU_TYPE_X86 7
#define CPU_TYPE_X86_64 (CPU_TYPE_X86 | CPU_ARCH_ABI64)
typedef struct {
//
// Probably 0x07 (CPU_TYPE_X86) or 0x01000007 (CPU_TYPE_X86_64)
//
uint32_t CpuType;
//
// Probably 3 (CPU_SUBTYPE_I386_ALL)
//
uint32_t CpuSubtype;
//
// Offset to beginning of architecture section
//
uint32_t Offset;
//
// Size of arch section
//
uint32_t Size;
//
// Alignment
//
uint32_t Align;
} EFIFatArchHeader;
typedef struct {
//
// Apple EFI fat binary magic number (0x0ef1fab9)
//
uint32_t Magic;
//
// Number of architectures
//
uint32_t NumArchs;
//
// Architecture headers
//
EFIFatArchHeader Archs[];
} EFIFatHeader;
//
// Functions prototypes
//
int
VerifyAppleImageSignature (
uint8_t *Image,
uint32_t ImageSize
);
#endif //APPLE_EFI_FAT_BINARY_H
/** @file
AppleEfiSignTool – Tool for signing and verifying Apple EFI binaries.
Copyright (c) 2018, savvas
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_EFI_PE_IMAGE_H
#define APPLE_EFI_PE_IMAGE_H
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <Library/OcCryptoLib.h>
#include "Edk2PeImage.h"
//
// The context structure used while PE/COFF image is being loaded and relocated.
//
typedef struct APPLE_PE_COFF_LOADER_IMAGE_CONTEXT_ {
uint64_t ImageAddress;
uint64_t ImageSize;
uint64_t EntryPoint;
uint64_t SizeOfHeaders;
uint16_t ImageType;
uint16_t NumberOfSections;
uint32_t *OptHdrChecksum;
uint32_t SizeOfOptionalHeader;
uint64_t SumOfSectionBytes;
EFI_IMAGE_SECTION_HEADER *FirstSection;
EFI_IMAGE_DATA_DIRECTORY *RelocDir;
EFI_IMAGE_DATA_DIRECTORY *SecDir;
uint64_t NumberOfRvaAndSizes;
uint16_t PeHdrMagic;
EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHdr;
} APPLE_PE_COFF_LOADER_IMAGE_CONTEXT;
typedef struct APPLE_SIGNATURE_DIRECTORY_ {
uint32_t ImageSize;
uint32_t SignatureDirectorySize;
uint32_t SignatureSize;
uint16_t CompressionType;
uint16_t EfiSignature;
EFI_GUID UnknownGuid;
EFI_GUID CertType;
uint8_t PublicKey[256];
uint8_t Signature[256];
} APPLE_SIGNATURE_DIRECTORY;
#define APPLE_SIGNATURE_SECENTRY_SIZE 8
//
// Function prototypes
//
uint16_t
GetPeHeaderMagicValue (
EFI_IMAGE_OPTIONAL_HEADER_UNION *Hdr
);
int
BuildPeContext (
void *Image,
uint32_t *RealImageSize,
APPLE_PE_COFF_LOADER_IMAGE_CONTEXT *Context
);
int
GetApplePeImageSignature (
void *Image,
APPLE_PE_COFF_LOADER_IMAGE_CONTEXT *Context,
uint8_t *PkLe,
uint8_t *PkBe,
uint8_t *SigLe,
uint8_t *SigBe
);
int
GetApplePeImageSha256 (
void *Image,
APPLE_PE_COFF_LOADER_IMAGE_CONTEXT *Context,
uint8_t *CalcucatedHash
);
int
VerifyApplePeImageSignature (
void *PeImage,
uint32_t ImageSize
);
int
VerifyAppleImageSignature (
uint8_t *Image,
uint32_t ImageSize
);
#endif //APPLE_EFI_PE_IMAGE_H
此差异已折叠。
CC ?= gcc
CFLAGS=-c -Wall -Wextra -pedantic -O3 -DDEBUG -I../../Include -include UefiCompat.h
OBJS=AppleEfiBinary.o Sha256.o Rsa2048Sha256.o OcAppleKeysLib.o main.o
all: AppleEfiSignTool
AppleEfiSignTool: $(OBJS)
$(CC) $(OBJS) -o AppleEfiSignTool
Sha256.o:
$(CC) $(CFLAGS) ../../Library/OcCryptoLib/Sha256.c -o $@
Rsa2048Sha256.o:
$(CC) $(CFLAGS) ../../Library/OcCryptoLib/Rsa2048Sha256.c -o $@
OcAppleKeysLib.o:
$(CC) $(CFLAGS) ../../Library/OcAppleKeysLib/OcAppleKeysLib.c -o $@
.c:
$(CC) $(CFLAGS) $< -o $@
clean:
rm -rf *.o AppleEfiSignTool
AppleEfiSignTool v1.0
=====================
Open source tool for signing and verifying Apple EFI binaries. It supports PE and AppleFat binaries.
## Capabilities
- Verifies the AppleFatBinary digital signature
- Verifies the ApplePEImage digital signature
#ifndef UEFI_COMPAT_H
#define UEFI_COMPAT_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
typedef int8_t INT8;
typedef int16_t INT16;
typedef int32_t INT32;
typedef int64_t INT64;
typedef uint8_t UINT8;
typedef uint16_t UINT16;
typedef uint32_t UINT32;
typedef uint64_t UINT64;
typedef bool BOOLEAN;
typedef void VOID;
typedef size_t UINTN;
#define CONST const
#define STATIC static
#define TRUE true
#define FALSE false
#define ZeroMem(Dst, Size) (memset)((Dst), 0, (Size))
#define CopyMem(Dst, Src, Size) (memcpy)((Dst), (Src), (Size))
#define CompareMem(One, Two, Size) (memcmp)((One),(Two),(Size))
#endif // UEFI_COMPAT_H
/** @file
AppleEfiSignTool – Tool for signing and verifying Apple EFI binaries.
Copyright (c) 2018, savvas
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 <errno.h>
#include <unistd.h>
#include "AppleEfiPeImage.h"
#ifdef DEBUG
# define DEBUG_PRINT(x) printf x
#else
# define DEBUG_PRINT(x) do {} while (0)
#endif
static uint8_t *Image = NULL;
static uint32_t ImageSize = 0;
static char UsageBanner[] = "AppleEfiSignTool v1.0 – Tool for signing and verifying\n"
"Apple EFI binaries. It supports PE and Fat binaries.\n"
"Usage:\n"
" -i : input file\n"
" -h : show this text\n"
"Example: ./AppleEfiSignTool -i apfs.efi\n";
void
OpenFile (
char *FileName
)
{
FILE *ImageFp;
ImageFp = fopen (FileName, "rb");
if (ImageFp == NULL) {
fprintf (stderr, "File not exist, errno = %d\n",errno);
exit (EXIT_FAILURE);
}
//CHECKME: does not guarantee that the file was read correctly!
fseek (ImageFp, 0, SEEK_END);
ImageSize = (uint32_t) ftell (ImageFp);
rewind (ImageFp);
Image = malloc (ImageSize + 1);
fread (Image, ImageSize, 1, ImageFp);
fclose (ImageFp);
}
int
main (
int argc,
char *argv[]
)
{
int Opt;
if (argc == 1){
puts(UsageBanner);
exit(EXIT_FAILURE);
}
while ((Opt = getopt (argc, argv, "i:vh")) != -1) {
switch (Opt) {
case 'i': {
//
// Open input file
//
OpenFile (optarg);
break;
}
case 'h': {
puts(UsageBanner);
exit(0);
break;
}
default:
puts(UsageBanner);
exit(EXIT_FAILURE);
}
}
for(int i = optind; i < argc; i++) {
printf("Unknown argument: %s\n", argv[i]);
puts(UsageBanner);
exit(EXIT_FAILURE);
}
int code = VerifyAppleImageSignature (Image, ImageSize);
free(Image);
return code;
}
/** @file
EfiResTool -- tool to work with APPL efires archives
Copyright (c) 2018, stek29
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
**/
#include <sys/types.h>
#include <unistd.h> // write
#include <fcntl.h> // open, close
#include <stdio.h> // fprintf
#include <string.h> // strerror, strdup, strchr
#include <stdlib.h> // free, EXIT_*
#include <sys/mman.h> // mmap, munmap
#include <sys/stat.h> // fstat
#include <errno.h> // errno
#include <dirent.h> // DIR, dirent, opendir, readdir
#include <stdint.h> // UINT32_MAX
typedef struct {
char name[64];
uint32_t offset;
uint32_t length;
}
__attribute__((packed, aligned(1)))
efires_file_t;
#define EFIRES_CURRENT_REVISION 2
typedef struct {
uint16_t revision; // EFIRES_CURRENT_REVISION
uint16_t nentries; // count of entries
efires_file_t entries[/* nentries */];
}
__attribute__((packed, aligned(1)))
efires_hdr_t;
#ifdef __APPLE__
#include <libkern/OSByteOrder.h>
#define le16toh(x) OSSwapLittleToHostInt16(x)
#define le32toh(x) OSSwapLittleToHostInt32(x)
#define htole16(x) OSSwapHostToLittleInt16(x)
#define htole32(x) OSSwapHostToLittleInt32(x)
#else
#include <endian.h>
#endif
typedef enum {
ONLY_LIST = 1,
} unpack_flag;
int unpack_efires(const char* fname, const char* destination, unpack_flag flags, char** filelist[]);
int pack_efires(const char* fname, const char* fromdir, const char* filelist[]);
int write_filelist(const char** filelist, const char* fname);
const char** parse_filelist(const char* fname);
void free_filelist(char** filelist);
#define ACTION_UNPACK "unpack"
#define ACTION_PACK "pack"
#define ACTION_LIST "list"
void print_usage(const char* prog) {
fprintf(stderr,
"efirestool -- tool to work with APPL efires archives\n"
"\n"
"Usage:\n"
" %s " ACTION_UNPACK " efires destination [filelist]\n"
" %s " ACTION_PACK " efires from [filelist]\n"
" %s " ACTION_LIST " efires [-f filelist]\n"
, prog, prog, prog);
}
int main(int argc, const char* argv[]) {
if (argc < 3) {
print_usage(argv[0]);
return EXIT_FAILURE;
}
const char* action = argv[1];
const char* efires = argv[2];
const char* directory = NULL;
const char* filelist_fname = NULL;
const char** filelist = NULL;
int retval = 0;
if (argc > 3) {
directory = argv[3];
}
if (argc > 4) {
filelist_fname = argv[4];
}
if ((strcmp(action, ACTION_UNPACK) == 0) || (strcmp(action, ACTION_LIST) == 0)) {
unpack_flag flags = 0;
if (strcmp(action, ACTION_LIST) == 0) flags |= ONLY_LIST;
retval = unpack_efires(efires, directory, flags, (char***) ((filelist_fname) ? &filelist : NULL));
if (!retval && filelist_fname) {
if (filelist == NULL) {
fprintf(stderr, "Failed to build filelist\n");
retval = 1;
} else {
retval = write_filelist(filelist, filelist_fname);
}
}
} else if (strcmp(action, ACTION_PACK) == 0) {
filelist = parse_filelist(filelist_fname);
if (filelist == NULL) {
fprintf(stderr, "Failed to parse filelist\n");
retval = 1;
} else {
retval = pack_efires(efires, directory, filelist);
}
} else {
print_usage(argv[0]);
retval = EXIT_FAILURE;
}
if (filelist) {
free_filelist((char**)filelist);
}
return retval;
}
void free_filelist(char** filelist) {
for (char** p = filelist; *p != NULL; ++p) {
free((char*)*p);
}
free(filelist);
}
const char** parse_filelist(const char* fname) {
FILE *f = fopen(fname, "r");
if (f == NULL) {
fprintf(stderr, "Cant open filelist (%s): %s\n", fname, strerror(errno));
return NULL;
}
// XXX realloc and grow
size_t res_size = sizeof(char*) * UINT32_MAX / sizeof(efires_file_t);
char** res = malloc(res_size);
if (res == NULL) {
fprintf(stderr, "Cant allocate memory for filelist\n");
fclose(f);
return NULL;
}
ssize_t linelen = 0;
char** itm = res;
size_t n = 0;
for (*itm = NULL, n = 0;
(itm < (res + res_size - 1)) && ((linelen = getline(itm, &n, f)) != -1);
++itm, *itm = NULL, n = 0, linelen = 0)
{
(*itm)[linelen - 1] = '\0';
}
if (linelen == 0) {
*itm = NULL;
} else if (*itm) {
free(*itm);
*itm = NULL;
}
fclose(f);
return (const char**)res;
}
int write_filelist(const char** filelist, const char* fname) {
if (filelist == NULL) {
fprintf(stderr, "Cant write NULL filelist\n");
return 1;
}
FILE *f = fopen(fname, "w");
if (f == NULL) {
fprintf(stderr, "Cant open filelist (%s): %s\n", fname, strerror(errno));
return 1;
}
for (const char** itm = filelist; *itm != NULL; ++itm) {
fprintf(f, "%s\n", *itm);
}
fclose(f);
return 0;
}
int unpack_efires(const char* fname, const char* destination, unpack_flag flags, char** filelist[]) {
int result = 1;
size_t file_size = 0;
const void *file_map = NULL;
if (filelist) *filelist = NULL;
if (((flags & ONLY_LIST) == 0) && (destination == NULL)) {
fprintf(stderr, "Cant determine destination\n");
goto out;
}
int fd = open(fname, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Cant open resource file (%s): %s\n", fname, strerror(errno));
goto out;
}
struct stat s;
if (fstat(fd, &s) != 0) {
fprintf(stderr, "fstat failed for (%s): %s\n", fname, strerror(errno));
goto out;
}
file_size = s.st_size;
if (file_size < sizeof(efires_hdr_t)) {
fprintf(stderr, "File is too short to be an efires file\n");
goto out;
}
file_map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (file_map == MAP_FAILED) {
fprintf(stderr, "Cant mmap file (%s): %s\n", fname, strerror(errno));
file_map = NULL;
goto out;
}
const efires_hdr_t *hdr = (const efires_hdr_t *) file_map;
if (le16toh(hdr->revision) != EFIRES_CURRENT_REVISION) {
fprintf(stderr, "Wrong efires revision: 0x%02x (expected 0x%02x)\n", le16toh(hdr->revision), EFIRES_CURRENT_REVISION);
goto out;
}
uint16_t nentries = le16toh(hdr->nentries);
fprintf(stderr, "File with 0x%x entries: %s\n", nentries, fname);
if (nentries * sizeof(efires_file_t) + sizeof(efires_hdr_t) > file_size) {
fprintf(stderr, "File is too small to have so many entries\n");
goto out;
}
char** filelist_iter = NULL;
if (filelist) {
*filelist = malloc((nentries + 1) * sizeof(char*));
filelist_iter = *filelist;
if (filelist_iter) {
*filelist_iter = NULL;
} else {
fprintf(stderr, "Cant allocate memory for filelist\n");
goto out;
}
}
if ((flags & ONLY_LIST) == 0) {
if (mkdir(destination, 0755) != 0) {
fprintf(stderr, "Cant create destination directory '%s': %s\n", destination, strerror(errno));
goto out;
}
if (chdir(destination) != 0) {
fprintf(stderr, "Cant chdir to destination directory '%s': %s\n", destination, strerror(errno));
goto out;
}
}
for (uint16_t i = 0; i != nentries; ++i) {
const efires_file_t *ent = &hdr->entries[i];
uint32_t off = le32toh(ent->offset);
uint32_t len = le32toh(ent->length);
printf("0x%04x (0x%08x - 0x%08x): %s\n", i, off, off + len, ent->name);
if (filelist_iter) *(filelist_iter++) = strdup(ent->name);
if (flags & ONLY_LIST) continue;
if (off + len > file_size) {
fprintf(stderr, "File 0x%04x: overflows efires file -- skipping\n", i);
continue;
}
int f = open(ent->name, O_WRONLY|O_CREAT|O_EXCL, 0755);
if (f == -1) {
fprintf(stderr, "File 0x%04x: Failed to create file: %s\n", i, strerror(errno));
continue;
}
int wrote = write(f, (void*) ((uintptr_t)file_map + off), len);
if ((uint32_t)wrote != len) {
fprintf(stderr, "File 0x%04x: Expected to write %d bytes, wrote %d: %s\n", i, len, wrote, strerror(errno));
}
close(f);
}
result = 0;
out:;
if (result && filelist && *filelist) {
free_filelist(*filelist);
*filelist = NULL;
}
if (file_map) {
munmap((void*)file_map, file_size);
}
return result;
}
int pack_efires(const char* fname, const char* fromdir, const char* filelist[]) {
int result = 1;
DIR *dir = NULL;
int dfd = -1;
int outfd = -1;
size_t file_size = 0;
void *file_map = NULL;
uint32_t nentries = 0;
dir = opendir(fromdir);
dfd = dirfd(dir);
if (dir == NULL || dfd == -1) {
fprintf(stderr, "Cant open directory to pack (%s) : %s\n", fromdir, strerror(errno));
goto out;
}
outfd = open(fname, O_RDWR | O_CREAT | O_EXCL, 0644);
if (outfd == -1) {
fprintf(stderr, "Cant open output file (%s) : %s\n", fname, strerror(errno));
goto out;
}
{
// write space for header
efires_hdr_t tmp;
write(outfd, &tmp, sizeof(tmp));
}
// header and one reserved zeroed entry
uint32_t full_file_len = sizeof(efires_hdr_t) + sizeof(efires_file_t);
efires_file_t cur_entr;
struct dirent *ep = NULL;
const char** itm = filelist;
while ((itm && *itm) || ((itm == NULL) && (ep = readdir(dir)) != NULL)) {
struct stat s;
const char* d_name = NULL;
if (itm) {
d_name = *(itm++);
} else {
d_name = ep->d_name;
}
if (fstatat(dfd, d_name, &s, 0) != 0) {
fprintf(stderr, "Cant stat file, skipping (%s/%s) : %s\n", fromdir, d_name, strerror(errno));
continue;
}
if ((s.st_mode & S_IFMT) != S_IFREG) {
fprintf(stderr, "Entry isn't regular file, skipping (%s/%s)\n", fromdir, d_name);
continue;
}
if (s.st_size > UINT32_MAX) {
fprintf(stderr, "File too big for efires, skipping (%s/%s)\n", fromdir, d_name);
continue;
}
cur_entr.length = (uint32_t) s.st_size;
if (full_file_len + cur_entr.length > UINT32_MAX) {
fprintf(stderr, "File too big to fit in current state, skipping (%u bytes left, %u bytes needed) (%s/%s)\n", UINT32_MAX - full_file_len, cur_entr.length, fromdir, d_name);
continue;
}
size_t e_name_len = strlen(d_name);
if (e_name_len > sizeof(cur_entr.name)) {
fprintf(stderr, "Filename too long, skipping (%s/%s)\n", fromdir, d_name);
continue;
}
++nentries;
memcpy(cur_entr.name, d_name, e_name_len);
if (e_name_len < sizeof(cur_entr.name)) {
memset(cur_entr.name + e_name_len, 0, sizeof(cur_entr.name) - e_name_len);
}
if (write(outfd, &cur_entr, sizeof(cur_entr)) != sizeof(cur_entr)) {
fprintf(stderr, "Write to result file failed: %s\n", strerror(errno));
goto out;
}
full_file_len += sizeof(cur_entr) + cur_entr.length;
if (nentries + 1 == UINT32_MAX) {
fprintf(stderr, "Too many entries, only packing 0x%08x\n", nentries);
break;
}
}
// reserved zeroed entry
memset(&cur_entr, 0, sizeof(cur_entr));
if (write(outfd, &cur_entr, sizeof(cur_entr)) != sizeof(cur_entr)) {
fprintf(stderr, "Write to result file failed: %s\n", strerror(errno));
goto out;
}
if (ftruncate(outfd, full_file_len) != 0) {
fprintf(stderr, "Failed to expand result file to needed size: %s\n", strerror(errno));
goto out;
}
file_map = mmap(NULL, full_file_len, PROT_READ | PROT_WRITE, MAP_SHARED, outfd, 0);
if (file_map == MAP_FAILED) {
fprintf(stderr, "Cant mmap result file: %s\n", strerror(errno));
file_map = NULL;
goto out;
}
efires_hdr_t *hdr = (efires_hdr_t *) file_map;
hdr->revision = htole16(EFIRES_CURRENT_REVISION);
hdr->nentries = htole16(nentries);
// header + nentries entries + reserved zeroed entry
uint32_t current_offset = sizeof(efires_hdr_t) + (nentries + 1) * sizeof(efires_file_t);
for (uint16_t i = 0; i != nentries; ++i) {
efires_file_t *ent = &hdr->entries[i];
uint32_t length = ent->length;
uint32_t offset = current_offset;
current_offset += length;
ent->length = htole32(length);
ent->offset = htole32(offset);
printf("0x%04x (0x%08x - 0x%08x): %s\n", i, offset, offset + length, ent->name);
int entfd = openat(dfd, ent->name, O_RDONLY);
if (entfd == -1) {
fprintf(stderr, "Cant open file, leaving zeroed (%s/%s): %s\n", fromdir, ent->name, strerror(errno));
continue;
}
if (read(entfd, (void*) ((uintptr_t)file_map + offset), length) != length) {
fprintf(stderr, "Cant read %u bytes from file, contents in efires undefined (%s/%s): %s\n", length, fromdir, ent->name, strerror(errno));
}
close(entfd);
}
result = 0;
out:;
if (dir != NULL) {
closedir(dir);
}
if (file_map != NULL) {
munmap(file_map, file_size);
}
if (outfd != -1) {
close(outfd);
// delete file if error occured
if (result) {
unlink(fname);
}
}
return result;
}
CC ?= gcc
CFLAGS=-c -Wall -Wextra -pedantic -O3 -DDEBUG
all: EfiResTool
EfiResTool: EfiResTool.o
$(CC) EfiResTool.o -o EfiResTool
.c:
$(CC) $(CFLAGS) $< -o $@
clean:
rm -rf *.o EfiResTool
EfiResTool
==============
Open source tool to work with APPL efires archives by stek29
https://gist.github.com/stek29/d13a34229a09020e0c1b0d897c42b433
\ No newline at end of file
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
CC ?= gcc
CFLAGS=-c -Wall -Wextra -pedantic -O3 -I/usr/local/opt/openssl/include -I/opt/local/include
LDFLAGS=-L/usr/local/opt/openssl/lib -L/opt/local/lib -lcrypto
all: RsaTool
RsaTool: RsaTool.o
$(CC) RsaTool.o -o RsaTool $(LDFLAGS)
.c:
$(CC) $(CFLAGS) $< -o $@
clean:
rm -rf *.o RsaTool
/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* C port of DumpPublicKey.java from the Android Open source project with
* support for additional RSA key sizes. (platform/system/core,git/libmincrypt
* /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library.
*/
#include <openssl/pem.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "openssl_compat.h"
/* Command line tool to extract RSA public keys from X.509 certificates
* and output a pre-processed version of keys for use by RSA verification
* routines.
*/
int check(RSA* key) {
const BIGNUM *n, *e;
int public_exponent, modulus;
RSA_get0_key(key, &n, &e, NULL);
public_exponent = BN_get_word(e);
modulus = BN_num_bits(n);
if (public_exponent != 3 && public_exponent != 65537) {
fprintf(stderr,
"WARNING: Public exponent should be 3 or 65537 (but is %d).\n",
public_exponent);
}
if (modulus != 1024 && modulus != 2048 && modulus != 3072 && modulus != 4096
&& modulus != 8192) {
fprintf(stderr, "ERROR: Unknown modulus length = %d.\n", modulus);
return 0;
}
return 1;
}
void native_to_big(unsigned char *data, size_t size) {
size_t i, tmp = 1;
if (*(unsigned char *)&tmp == 1) {
fprintf(stderr, "WARNING: Assuming little endian encoding.\n");
for (i = 0; i < size / 2; ++i) {
tmp = data[i];
data[i] = data[size - i - 1];
data[size - i - 1] = tmp;
}
} else {
fprintf(stderr, "WARNING: Assuming big endian encoding.\n");
}
}
void print_data(void *data, size_t size) {
size_t i;
static size_t block = 0;
if (data == NULL) {
if (block > 0)
puts("");
} else {
for (i = 0; i < size; ++i, ++block) {
if (block == 12) {
puts(",");
block = 0;
}
printf("%s0x%02x", block == 0 ? "" : ", ", ((uint8_t *)data)[i]);
}
}
}
/* Pre-processes and outputs RSA public key to standard out.
*/
void output(RSA* key) {
int i, nwords;
const BIGNUM *key_n;
BIGNUM *N = NULL;
BIGNUM *Big1 = NULL, *Big2 = NULL, *Big32 = NULL, *BigMinus1 = NULL;
BIGNUM *B = NULL;
BIGNUM *N0inv= NULL, *R = NULL, *RR = NULL, *RRTemp = NULL, *NnumBits = NULL;
BIGNUM *n = NULL, *rr = NULL;
BN_CTX *bn_ctx = BN_CTX_new();
uint32_t n0invout;
/* Output size of RSA key in 32-bit words */
nwords = RSA_size(key) / 4;
print_data(&nwords, sizeof(nwords));
/* Initialize BIGNUMs */
RSA_get0_key(key, &key_n, NULL, NULL);
N = BN_dup(key_n);
Big1 = BN_new();
Big2 = BN_new();
Big32 = BN_new();
BigMinus1 = BN_new();
N0inv= BN_new();
R = BN_new();
RR = BN_new();
RRTemp = BN_new();
NnumBits = BN_new();
n = BN_new();
rr = BN_new();
BN_set_word(Big1, 1L);
BN_set_word(Big2, 2L);
BN_set_word(Big32, 32L);
BN_sub(BigMinus1, Big1, Big2);
B = BN_new();
BN_exp(B, Big2, Big32, bn_ctx); /* B = 2^32 */
/* Calculate and output N0inv = -1 / N[0] mod 2^32 */
BN_mod_inverse(N0inv, N, B, bn_ctx);
BN_sub(N0inv, B, N0inv);
n0invout = BN_get_word(N0inv);
print_data(&n0invout, sizeof(n0invout));
/* Calculate R = 2^(# of key bits) */
BN_set_word(NnumBits, BN_num_bits(N));
BN_exp(R, Big2, NnumBits, bn_ctx);
/* Calculate RR = R^2 mod N */
BN_copy(RR, R);
BN_mul(RRTemp, RR, R, bn_ctx);
BN_mod(RR, RRTemp, N, bn_ctx);
/* Write out modulus as little endian array of integers. */
for (i = 0; i < nwords; ++i) {
uint32_t nout;
BN_mod(n, N, B, bn_ctx); /* n = N mod B */
nout = BN_get_word(n);
print_data(&nout, sizeof(nout));
BN_rshift(N, N, 32); /* N = N/B */
}
/* Write R^2 as little endian array of integers. */
for (i = 0; i < nwords; ++i) {
uint32_t rrout;
BN_mod(rr, RR, B, bn_ctx); /* rr = RR mod B */
rrout = BN_get_word(rr);
print_data(&rrout, sizeof(rrout));
BN_rshift(RR, RR, 32); /* RR = RR/B */
}
/* print terminator */
print_data(NULL, 0);
/* Free BIGNUMs. */
BN_free(N);
BN_free(Big1);
BN_free(Big2);
BN_free(Big32);
BN_free(BigMinus1);
BN_free(N0inv);
BN_free(R);
BN_free(RRTemp);
BN_free(NnumBits);
BN_free(n);
BN_free(rr);
}
enum {
INVALID_MODE,
CERT_MODE,
PEM_MODE,
RAW_MODE
};
int main(int argc, char* argv[]) {
int mode = INVALID_MODE;
FILE* fp;
X509* cert = NULL;
BIGNUM *mod = NULL;
BIGNUM *exp = NULL;
RSA* pubkey = NULL;
EVP_PKEY* key;
char *progname;
long size;
if (argc == 3) {
if (!strcmp(argv[1], "-cert"))
mode = CERT_MODE;
else if (!strcmp(argv[1], "-pub"))
mode = PEM_MODE;
else if (!strcmp(argv[1], "-raw"))
mode = RAW_MODE;
}
if (argc != 3 || mode == INVALID_MODE) {
progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
fprintf(stderr, "Usage: %s <-cert | -pub | -raw> <file>\n", progname);
return -1;
}
fp = fopen(argv[2], "r");
if (!fp) {
fprintf(stderr, "Couldn't open file %s!\n", argv[2]);
return -1;
}
if (mode == CERT_MODE) {
/* Read the certificate */
if (!PEM_read_X509(fp, &cert, NULL, NULL)) {
fprintf(stderr, "Couldn't read certificate.\n");
goto fail;
}
/* Get the public key from the certificate. */
key = X509_get_pubkey(cert);
/* Convert to a RSA_style key. */
if (!(pubkey = EVP_PKEY_get1_RSA(key))) {
fprintf(stderr, "Couldn't convert to a RSA style key.\n");
goto fail;
}
} else if (mode == PEM_MODE) {
/* Read the pubkey in .PEM format. */
if (!(pubkey = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL))) {
fprintf(stderr, "Couldn't read public key file.\n");
goto fail;
}
} else {
/* It is unimportant which exponent is used. */
static unsigned char expraw[4] = {0, 0, 0, 3};
static unsigned char modraw[1024];
if (fseek (fp, 0, SEEK_END) != 0
|| (size = ftell(fp)) < 0
|| fseek(fp, 0, SEEK_SET) != 0) {
fprintf(stderr, "Couldn't read modulus size.\n");
goto fail;
}
if ((size_t)size > sizeof(modraw)) {
fprintf(stderr, "Unsupported modulus size %ld.\n", size);
goto fail;
}
if (fread(modraw, size, 1, fp) != 1) {
fprintf(stderr, "Couldn't read modulus number.\n");
goto fail;
}
native_to_big(modraw, (size_t)size);
if (!(mod = BN_bin2bn(modraw, (int)size, NULL))) {
fprintf(stderr, "Couldn't create modulus number.\n");
goto fail;
}
if (!(exp = BN_bin2bn(expraw, sizeof(expraw), NULL))) {
fprintf(stderr, "Couldn't create public exp number.\n");
goto fail;
}
if (!(pubkey = RSA_new()) || !RSA_set0_key(pubkey, mod, exp, NULL)) {
fprintf(stderr, "Couldn't create rsa public key.\n");
goto fail;
}
/* RSA context owns its numbers. */
mod = exp = NULL;
}
if (check(pubkey)) {
output(pubkey);
}
fail:
X509_free(cert);
BN_free(mod);
BN_free(exp);
RSA_free(pubkey);
fclose(fp);
return 0;
}
/*
* Copyright (c) 2005 Darren Tucker <dtucker@zip.com.au>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _OPENSSL_COMPAT_H
#define _OPENSSL_COMPAT_H
#include <openssl/opensslv.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/dsa.h>
int ssh_compatible_openssl(long, long);
#if OPENSSL_VERSION_NUMBER < 0x10000001L
# define LIBCRYPTO_EVP_INL_TYPE unsigned int
#else
# define LIBCRYPTO_EVP_INL_TYPE size_t
#endif
#ifndef OPENSSL_RSA_MAX_MODULUS_BITS
# define OPENSSL_RSA_MAX_MODULUS_BITS 16384
#endif
#ifndef OPENSSL_DSA_MAX_MODULUS_BITS
# define OPENSSL_DSA_MAX_MODULUS_BITS 10000
#endif
#ifndef OPENSSL_HAVE_EVPCTR
# define EVP_aes_128_ctr evp_aes_128_ctr
# define EVP_aes_192_ctr evp_aes_128_ctr
# define EVP_aes_256_ctr evp_aes_128_ctr
const EVP_CIPHER *evp_aes_128_ctr(void);
void ssh_aes_ctr_iv(EVP_CIPHER_CTX *, int, u_char *, size_t);
#endif
/* Avoid some #ifdef. Code that uses these is unreachable without GCM */
#if !defined(OPENSSL_HAVE_EVPGCM) && !defined(EVP_CTRL_GCM_SET_IV_FIXED)
# define EVP_CTRL_GCM_SET_IV_FIXED -1
# define EVP_CTRL_GCM_IV_GEN -1
# define EVP_CTRL_GCM_SET_TAG -1
# define EVP_CTRL_GCM_GET_TAG -1
#endif
/* Replace missing EVP_CIPHER_CTX_ctrl() with something that returns failure */
#ifndef HAVE_EVP_CIPHER_CTX_CTRL
# ifdef OPENSSL_HAVE_EVPGCM
# error AES-GCM enabled without EVP_CIPHER_CTX_ctrl /* shouldn't happen */
# else
# define EVP_CIPHER_CTX_ctrl(a,b,c,d) (0)
# endif
#endif
#if defined(HAVE_EVP_RIPEMD160)
# if defined(OPENSSL_NO_RIPEMD) || defined(OPENSSL_NO_RMD160)
# undef HAVE_EVP_RIPEMD160
# endif
#endif
/*
* We overload some of the OpenSSL crypto functions with ssh_* equivalents
* to automatically handle OpenSSL engine initialisation.
*
* In order for the compat library to call the real functions, it must
* define SSH_DONT_OVERLOAD_OPENSSL_FUNCS before including this file and
* implement the ssh_* equivalents.
*/
#ifndef SSH_DONT_OVERLOAD_OPENSSL_FUNCS
# ifdef USE_OPENSSL_ENGINE
# ifdef OpenSSL_add_all_algorithms
# undef OpenSSL_add_all_algorithms
# endif
# define OpenSSL_add_all_algorithms() ssh_OpenSSL_add_all_algorithms()
# endif
void ssh_OpenSSL_add_all_algorithms(void);
#endif /* SSH_DONT_OVERLOAD_OPENSSL_FUNCS */
/**
* Get the RSA parameters
*
* @param rsa The RSA object
* @param n The @c n parameter
* @param e The @c e parameter
* @param d The @c d parameter
*/
static inline void
RSA_get0_key(const RSA *rsa, const BIGNUM **n,
const BIGNUM **e, const BIGNUM **d)
{
if (n != NULL)
{
*n = rsa ? rsa->n : NULL;
}
if (e != NULL)
{
*e = rsa ? rsa->e : NULL;
}
if (d != NULL)
{
*d = rsa ? rsa->d : NULL;
}
}
/**
* Set the RSA parameters
*
* @param rsa The RSA object
* @param n The @c n parameter
* @param e The @c e parameter
* @param d The @c d parameter
* @return 1 on success, 0 on error
*/
static inline int
RSA_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d)
{
if ((rsa->n == NULL && n == NULL)
|| (rsa->e == NULL && e == NULL))
{
return 0;
}
if (n != NULL)
{
BN_free(rsa->n);
rsa->n = n;
}
if (e != NULL)
{
BN_free(rsa->e);
rsa->e = e;
}
if (d != NULL)
{
BN_free(rsa->d);
rsa->d = d;
}
return 1;
}
#endif /* _OPENSSL_COMPAT_H */
CC ?= gcc
CFLAGS=-c -Wall -Wextra -pedantic -O3
all: readlabel
readlabel: readlabel.o
$(CC) readlabel.o -o readlabel
.c:
$(CC) $(CFLAGS) $< -o $@
clean:
rm -rf *.o readlabel
/** @file
Read macOS .disk_label (.disk_label_2x) file and convert to .ppm.
Reference: http://refit.sourceforge.net/info/vollabel.html
Copyright (c) 2019, vit9696
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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
static uint8_t palette[256] = {
[0x00] = 255,
[0xf6] = 238,
[0xf7] = 221,
[0x2a] = 204,
[0xf8] = 187,
[0xf9] = 170,
[0x55] = 153,
[0xfa] = 136,
[0xfb] = 119,
[0x80] = 102,
[0xfc] = 85,
[0xfd] = 68,
[0xab] = 51,
[0xfe] = 34,
[0xff] = 17,
[0xd6] = 0
};
static int read_file(const char *filename, uint8_t **buffer, size_t *size) {
FILE *fh = fopen(filename, "rb");
if (!fh) {
fprintf(stderr, "Missing file %s!\n", filename);
return -1;
}
if (fseek(fh, 0, SEEK_END)) {
fprintf(stderr, "Failed to find end of %s!\n", filename);
fclose(fh);
return -1;
}
long pos = ftell(fh);
if (pos <= 0) {
fprintf(stderr, "Invalid file size (%ld) of %s!\n", pos, filename);
fclose(fh);
return -1;
}
if (fseek(fh, 0, SEEK_SET)) {
fprintf(stderr, "Failed to rewind %s!\n", filename);
fclose(fh);
return -1;
}
*size = (size_t)pos;
*buffer = (uint8_t *)malloc(*size);
if (!*buffer) {
fprintf(stderr, "Failed to allocate %zu bytes for %s!\n", *size, filename);
fclose(fh);
return -1;
}
if (fread(*buffer, *size, 1, fh) != 1) {
fprintf(stderr, "Failed to read %zu bytes from %s!\n", *size, filename);
fclose(fh);
free(*buffer);
return -1;
}
fclose(fh);
return 0;
}
static int write_ppm(const char *filename, size_t width, size_t height, uint8_t *pixel) {
FILE *fh = fopen(filename, "wb");
if (!fh) {
fprintf(stderr, "Failed to open out file %s!\n", filename);
return -1;
}
if (fprintf(fh, "P6\n%zu %zu\n255\n", width, height) < 0) {
fprintf(stderr, "Failed to write ppm header to %s!\n", filename);
fclose(fh);
return -1;
}
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
uint8_t col[3];
col[0] = col[1] = col[2] = palette[*pixel];
if (fwrite(col, sizeof(col), 1, fh) != 1) {
fprintf(stderr, "Failed to write ppm pixel %zux%zu to %s!\n", x, y, filename);
fclose(fh);
return -1;
}
++pixel;
}
}
fclose(fh);
return 0;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
fprintf(stderr, "Pass file and outfile!\n");
return -1;
}
uint8_t *buffer;
size_t size;
if (read_file(argv[1], &buffer, &size)) {
return -1;
}
size_t header_size = sizeof(uint8_t) + sizeof(uint16_t)*2;
if (size < header_size) {
fprintf(stderr, "Too low size %zu!\n", size);
free(buffer);
return -1;
}
if (buffer[0] != 1) {
fprintf(stderr, "Invalid magic %02X!\n", buffer[0]);
free(buffer);
return -1;
}
size_t width = ((size_t) buffer[1] << 8U) | (size_t) buffer[2];
size_t height = ((size_t) buffer[3] << 8U) | (size_t) buffer[4];
if (width * height == 0 || size - header_size != width*height) {
fprintf(stderr, "Image mismatch %zux%zu with size %zu!\n", width, height, size);
free(buffer);
return -1;
}
(void)remove(argv[2]);
if (write_ppm(argv[2], width, height, buffer + header_size)) {
free(buffer);
return -1;
}
free(buffer);
return 0;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册