Last active
February 6, 2022 14:50
-
-
Save MCJack123/943eaca762730ca4b7ae460b731b68e7 to your computer and use it in GitHub Desktop.
A tool to read/write BSD/macOS chunklist files, such as BaseSystem.dmg.chunklist. (Signing is not supported yet.)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Requires C99 or C++14 or later */ | |
/* Chunklist file format */ | |
#include <stdint.h> | |
#define CHUNKLIST_MAGIC 0x4C4B4E43 | |
#define CHUNKLIST_FILE_VERSION_10 1 | |
#define CHUNKLIST_CHUNK_METHOD_10 1 | |
#define CHUNKLIST_SIGNATURE_METHOD_10 1 | |
#define CHUNKLIST_SIG_LEN 256 | |
#define CHUNKLIST_PUBKEY_LEN (2048/8) | |
#define SHA256_DIGEST_LENGTH 32 | |
struct chunklist_hdr { | |
uint32_t cl_magic; | |
uint32_t cl_header_size; | |
uint8_t cl_file_ver; | |
uint8_t cl_chunk_method; | |
uint8_t cl_sig_method; | |
uint8_t __unused1; | |
uint64_t cl_chunk_count; | |
uint64_t cl_chunk_offset; | |
uint64_t cl_sig_offset; | |
} __attribute__((packed)); | |
struct chunklist_chunk { | |
uint32_t chunk_size; | |
uint8_t chunk_sha256[SHA256_DIGEST_LENGTH]; | |
} __attribute__((packed)); | |
#define strequ(s, t) strcmp(s, t) == 0 | |
#include "sha256.h" | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <math.h> | |
int main(int argc, const char * argv[]) { | |
if (argc < 4) { | |
printf("Usage: %s <create|check> <chunklist> <file> [-v]\n", argv[0]); | |
return -1; | |
} | |
int verbose = argc > 4; | |
if (strequ(argv[1], "check")) { | |
FILE * cl = fopen(argv[2], "r"); | |
if (cl == NULL) { | |
printf("Error: %s could not be opened.\n", argv[2]); | |
return -2; | |
} | |
struct chunklist_hdr header; | |
fread(&header, sizeof(struct chunklist_hdr), 1, cl); | |
if (header.cl_magic != CHUNKLIST_MAGIC) { | |
printf("Error: %s is not a chunklist file.\n", argv[2]); | |
return -3; | |
} | |
if (header.cl_file_ver != CHUNKLIST_FILE_VERSION_10 || | |
header.cl_sig_method != CHUNKLIST_SIGNATURE_METHOD_10 || | |
header.cl_chunk_method != CHUNKLIST_CHUNK_METHOD_10) { | |
printf("Error: %s is not supported.\n", argv[2]); | |
return -4; | |
} | |
fseek(cl, (long int)header.cl_chunk_offset, SEEK_SET); | |
FILE * fp = fopen(argv[3], "r"); | |
if (fp == NULL) { | |
printf("Error: %s could not be opened.\n", argv[3]); | |
return -2; | |
} | |
for (int i = 0; i < header.cl_chunk_count; i++) { | |
struct chunklist_chunk chunk; | |
fread(&chunk, sizeof(struct chunklist_chunk), 1, cl); | |
if (verbose) printf("Checking chunk %d (size %d)...\n", i, chunk.chunk_size); | |
BYTE * data = (BYTE*)malloc(chunk.chunk_size); | |
fread(data, chunk.chunk_size, 1, fp); | |
SHA256_CTX ctx; | |
BYTE hash[SHA256_DIGEST_LENGTH]; | |
sha256_init(&ctx); | |
sha256_update(&ctx, data, chunk.chunk_size); | |
sha256_final(&ctx, hash); | |
free(data); | |
if (memcmp(chunk.chunk_sha256, hash, SHA256_DIGEST_LENGTH) != 0) { | |
printf("Verify failed.\n"); | |
fclose(fp); | |
fclose(cl); | |
return 1; | |
} | |
} | |
fclose(fp); | |
fclose(cl); | |
} else if (strequ(argv[1], "create")) { | |
struct chunklist_hdr header; | |
FILE * fp = fopen(argv[3], "r"); | |
if (fp == NULL) { | |
printf("Error: %s could not be opened.\n", argv[3]); | |
return -2; | |
} | |
header.cl_magic = CHUNKLIST_MAGIC; | |
header.cl_header_size = 36; | |
header.cl_file_ver = CHUNKLIST_FILE_VERSION_10; | |
header.cl_chunk_method = CHUNKLIST_CHUNK_METHOD_10; | |
header.cl_sig_method = CHUNKLIST_SIGNATURE_METHOD_10; | |
header.cl_chunk_offset = 36; | |
fseek(fp, 0, SEEK_END); | |
uint32_t file_size = ftell(fp); | |
fseek(fp, 0, SEEK_SET); | |
header.cl_chunk_count = (uint64_t)ceil(file_size / 10485760); | |
struct chunklist_chunk chunks[header.cl_chunk_count]; | |
for (int i = 0; i*10485760 < file_size && !feof(fp) && i < header.cl_chunk_count; i++) { | |
chunks[i].chunk_size = (i == header.cl_chunk_count - 1 ? file_size % 10485760 : 10485760); | |
if (verbose) printf("Creating chunk %d (size %d)...\n", i, chunks[i].chunk_size); | |
BYTE * data = malloc(chunks[i].chunk_size); | |
fread(data, chunks[i].chunk_size, 1, fp); | |
SHA256_CTX ctx; | |
sha256_init(&ctx); | |
sha256_update(&ctx, data, chunks[i].chunk_size); | |
sha256_final(&ctx, chunks[i].chunk_sha256); | |
free(data); | |
} | |
fclose(fp); | |
FILE * cl = fopen(argv[2], "w"); | |
fwrite(&header, sizeof(struct chunklist_hdr), 1, cl); | |
fwrite(chunks, sizeof(struct chunklist_chunk)*header.cl_chunk_count, 1, cl); | |
fclose(fp); | |
printf("Wrote chunklist to %s\n", argv[2]); | |
return 0; | |
} else { | |
printf("Usage: %s <create|check> <chunklist> <file> [-v]\n", argv[0]); | |
return -1; | |
} | |
printf("Verify succeeded.\n"); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************************************* | |
* Filename: sha256.c | |
* Author: Brad Conte (brad AT bradconte.com) | |
* Copyright: | |
* Disclaimer: This code is presented "as is" without any guarantees. | |
* Details: Implementation of the SHA-256 hashing algorithm. | |
SHA-256 is one of the three algorithms in the SHA2 | |
specification. The others, SHA-384 and SHA-512, are not | |
offered in this implementation. | |
Algorithm specification can be found here: | |
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf | |
This implementation uses little endian byte order. | |
*********************************************************************/ | |
/*************************** HEADER FILES ***************************/ | |
#include <stdlib.h> | |
#include <memory.h> | |
#include "sha256.h" | |
/****************************** MACROS ******************************/ | |
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) | |
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) | |
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) | |
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) | |
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) | |
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) | |
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) | |
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) | |
/**************************** VARIABLES *****************************/ | |
static const WORD k[64] = { | |
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, | |
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, | |
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, | |
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, | |
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, | |
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, | |
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, | |
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 | |
}; | |
/*********************** FUNCTION DEFINITIONS ***********************/ | |
void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) | |
{ | |
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; | |
for (i = 0, j = 0; i < 16; ++i, j += 4) | |
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); | |
for ( ; i < 64; ++i) | |
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; | |
a = ctx->state[0]; | |
b = ctx->state[1]; | |
c = ctx->state[2]; | |
d = ctx->state[3]; | |
e = ctx->state[4]; | |
f = ctx->state[5]; | |
g = ctx->state[6]; | |
h = ctx->state[7]; | |
for (i = 0; i < 64; ++i) { | |
t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; | |
t2 = EP0(a) + MAJ(a,b,c); | |
h = g; | |
g = f; | |
f = e; | |
e = d + t1; | |
d = c; | |
c = b; | |
b = a; | |
a = t1 + t2; | |
} | |
ctx->state[0] += a; | |
ctx->state[1] += b; | |
ctx->state[2] += c; | |
ctx->state[3] += d; | |
ctx->state[4] += e; | |
ctx->state[5] += f; | |
ctx->state[6] += g; | |
ctx->state[7] += h; | |
} | |
void sha256_init(SHA256_CTX *ctx) | |
{ | |
ctx->datalen = 0; | |
ctx->bitlen = 0; | |
ctx->state[0] = 0x6a09e667; | |
ctx->state[1] = 0xbb67ae85; | |
ctx->state[2] = 0x3c6ef372; | |
ctx->state[3] = 0xa54ff53a; | |
ctx->state[4] = 0x510e527f; | |
ctx->state[5] = 0x9b05688c; | |
ctx->state[6] = 0x1f83d9ab; | |
ctx->state[7] = 0x5be0cd19; | |
} | |
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) | |
{ | |
WORD i; | |
for (i = 0; i < len; ++i) { | |
ctx->data[ctx->datalen] = data[i]; | |
ctx->datalen++; | |
if (ctx->datalen == 64) { | |
sha256_transform(ctx, ctx->data); | |
ctx->bitlen += 512; | |
ctx->datalen = 0; | |
} | |
} | |
} | |
void sha256_final(SHA256_CTX *ctx, BYTE hash[]) | |
{ | |
WORD i; | |
i = ctx->datalen; | |
// Pad whatever data is left in the buffer. | |
if (ctx->datalen < 56) { | |
ctx->data[i++] = 0x80; | |
while (i < 56) | |
ctx->data[i++] = 0x00; | |
} | |
else { | |
ctx->data[i++] = 0x80; | |
while (i < 64) | |
ctx->data[i++] = 0x00; | |
sha256_transform(ctx, ctx->data); | |
memset(ctx->data, 0, 56); | |
} | |
// Append to the padding the total message's length in bits and transform. | |
ctx->bitlen += ctx->datalen * 8; | |
ctx->data[63] = ctx->bitlen; | |
ctx->data[62] = ctx->bitlen >> 8; | |
ctx->data[61] = ctx->bitlen >> 16; | |
ctx->data[60] = ctx->bitlen >> 24; | |
ctx->data[59] = ctx->bitlen >> 32; | |
ctx->data[58] = ctx->bitlen >> 40; | |
ctx->data[57] = ctx->bitlen >> 48; | |
ctx->data[56] = ctx->bitlen >> 56; | |
sha256_transform(ctx, ctx->data); | |
// Since this implementation uses little endian byte ordering and SHA uses big endian, | |
// reverse all the bytes when copying the final state to the output hash. | |
for (i = 0; i < 4; ++i) { | |
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; | |
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; | |
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; | |
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; | |
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; | |
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; | |
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; | |
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************************************* | |
* Filename: sha256.h | |
* Author: Brad Conte (brad AT bradconte.com) | |
* Copyright: | |
* Disclaimer: This code is presented "as is" without any guarantees. | |
* Details: Defines the API for the corresponding SHA1 implementation. | |
*********************************************************************/ | |
#ifndef SHA256_H | |
#define SHA256_H | |
/*************************** HEADER FILES ***************************/ | |
#include <stddef.h> | |
/****************************** MACROS ******************************/ | |
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest | |
/**************************** DATA TYPES ****************************/ | |
typedef unsigned char BYTE; // 8-bit byte | |
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines | |
typedef struct { | |
BYTE data[64]; | |
WORD datalen; | |
unsigned long long bitlen; | |
WORD state[8]; | |
} SHA256_CTX; | |
/*********************** FUNCTION DECLARATIONS **********************/ | |
void sha256_init(SHA256_CTX *ctx); | |
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); | |
void sha256_final(SHA256_CTX *ctx, BYTE hash[]); | |
#endif // SHA256_H |
zhangyoufu
commented
Oct 21, 2019
•
https://github.com/sadponyguerillaboy/Apple-Chunklist-Verification-Creation-Toolkit (MIT licensed Python) can verify chunklist signatures and create signed chunklists.
Edit: wow @zhangyoufu, thanks!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment