From c09c07b33038cc333daf1e15f3abbbe522b1de62 Mon Sep 17 00:00:00 2001
From: Nikias Bassen
Date: Thu, 29 Aug 2019 10:00:09 +0200
Subject: img4: Add new function img4_create_local_manifest()

---
 src/img4.c | 502 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/img4.h |   5 +-
 2 files changed, 482 insertions(+), 25 deletions(-)

diff --git a/src/img4.c b/src/img4.c
index db33f15..9bc2bf8 100644
--- a/src/img4.c
+++ b/src/img4.c
@@ -1,8 +1,8 @@
 /*
  * img4.c
- * Functions for handling the new IMG4 format
+ * Functions for handling the IMG4 format
  *
- * Copyright (c) 2013 Nikias Bassen. All Rights Reserved.
+ * Copyright (c) 2013-2019 Nikias Bassen. All Rights Reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -25,53 +25,104 @@
 #include "common.h"
 #include "img4.h"
 
+#define ASN1_PRIVATE 0xc0
+#define ASN1_PRIMITIVE_TAG 0x1f
 #define ASN1_CONSTRUCTED 0x20
 #define ASN1_SEQUENCE 0x10
+#define ASN1_SET 0x11
 #define ASN1_CONTEXT_SPECIFIC 0x80
 #define ASN1_IA5_STRING 0x16
+#define ASN1_OCTET_STRING 0x04
+#define ASN1_INTEGER 0x02
+#define ASN1_BOOLEAN 0x01
 
 #define IMG4_MAGIC "IMG4"
 #define IMG4_MAGIC_SIZE 4
 
-static unsigned char* asn1_create_element_header(unsigned char type, unsigned int size, unsigned char** data, unsigned int *data_size)
+static int asn1_calc_int_size(uint64_t value)
 {
-	unsigned char buf[6];
-	unsigned int off = 0;
+	int i = 1;
+	while ((value >>= 7) != 0) i++;
+	return i;
+}
 
-	if (!type || size == 0 || !data || !data_size) {
-		return NULL;
+static void asn1_write_int_value(unsigned char **p, uint64_t value, int size)
+{
+	int value_size = (size > 0) ? size : asn1_calc_int_size(value);
+	int i = 0;
+	for (i = 1; i <= value_size; i++) {
+		(*p)[value_size-i] = value & 0xFF;
+		value >>= 8;
 	}
+	*p += value_size;
+}
 
-	buf[off++] = type;
+static void asn1_write_size(unsigned int size, unsigned char** data, unsigned int *data_size)
+{
+	unsigned int off = 0;
 
 	// first, calculate the size
 	if (size >= 0x1000000) {
 		// 1+4 bytes length
-		buf[off++] = 0x84;
-		buf[off++] = (size >> 24) & 0xFF;
-		buf[off++] = (size >> 16) & 0xFF;
-		buf[off++] = (size >> 8) & 0xFF;
-		buf[off++] = size & 0xFF;
+		(*data)[off++] = 0x84;
+		(*data)[off++] = (size >> 24) & 0xFF;
+		(*data)[off++] = (size >> 16) & 0xFF;
+		(*data)[off++] = (size >> 8) & 0xFF;
+		(*data)[off++] = size & 0xFF;
 	} else if (size >= 0x10000) {
 		// 1+3 bytes length
-		buf[off++] = 0x83;
-		buf[off++] = (size >> 16) & 0xFF;
-		buf[off++] = (size >> 8) & 0xFF;
-		buf[off++] = size & 0xFF;
+		(*data)[off++] = 0x83;
+		(*data)[off++] = (size >> 16) & 0xFF;
+		(*data)[off++] = (size >> 8) & 0xFF;
+		(*data)[off++] = size & 0xFF;
 	} else if (size >= 0x100) {
 		// 1+2 bytes length
-		buf[off++] = 0x82;
-		buf[off++] = (size >> 8) & 0xFF;
-		buf[off++] = (size & 0xFF);
+		(*data)[off++] = 0x82;
+		(*data)[off++] = (size >> 8) & 0xFF;
+		(*data)[off++] = (size & 0xFF);
 	} else if (size >= 0x80) {
 		// 1+1 byte length
-		buf[off++] = 0x81;
-		buf[off++] = (size & 0xFF);
+		(*data)[off++] = 0x81;
+		(*data)[off++] = (size & 0xFF);
 	} else {
 		// 1 byte length
-		buf[off++] = size & 0xFF;
+		(*data)[off++] = size & 0xFF;
+	}
+
+	*data += off;
+	*data_size += off;
+}
+
+static void asn1_write_element_header(unsigned char type, unsigned int size, unsigned char** data, unsigned int *data_size)
+{
+	unsigned int off = 0;
+
+	if (!type || size == 0 || !data || !data_size) {
+		return;
+	}
+
+	(*data)[off++] = type;
+	*data += off;
+
+	asn1_write_size(size, data, &off);
+
+	*data_size += off;
+}
+
+static unsigned char* asn1_create_element_header(unsigned char type, unsigned int size, unsigned char** data, unsigned int *data_size)
+{
+	unsigned char buf[6];
+	unsigned int off = 0;
+
+	if (!type || size == 0 || !data || !data_size) {
+		return NULL;
 	}
 
+	buf[off++] = type;
+
+	unsigned char* p = &buf[off];
+	asn1_write_size(size, &p, &off);
+
 	*data = malloc(off);
 	memcpy(*data, buf, off);
 	*data_size = off;
@@ -79,6 +130,82 @@ static unsigned char* asn1_create_element_header(unsigned char type, unsigned in
 	return *data;
 }
 
+static void asn1_write_priv_element(unsigned char **p, unsigned int *length, unsigned int value)
+{
+	int i = 0;
+	int ttag = 0;
+	int tag = value;
+
+	i = ASN1_CONSTRUCTED;
+	i |= (0xFF & ASN1_PRIVATE);
+
+	(*p)[0] = i | ASN1_PRIMITIVE_TAG;
+	*p += 1;
+	*length += 1;
+
+	for (i = 0, ttag = tag; ttag > 0; i++)
+            ttag >>= 7;
+        ttag = i;
+        while (i-- > 0) {
+            (*p)[i] = tag & 0x7f;
+            if (i != (ttag - 1))
+                (*p)[i] |= 0x80;
+            tag >>= 7;
+        }
+        *p += ttag;
+	*length += ttag;
+}
+
+static void asn1_write_element(unsigned char **p, unsigned int *length, unsigned char type, void *data, int data_len)
+{
+	unsigned int this_len = 0;
+	switch (type) {
+	case ASN1_IA5_STRING: {
+		char *str = (char*)data;
+		size_t len = (data_len < 0) ? strlen(str) : data_len;
+		asn1_write_element_header(type, len, p, &this_len);
+		*length += this_len;
+		memcpy(*p, str, len);
+		*p += len;
+		*length += len;
+	}	break;
+	case ASN1_OCTET_STRING: {
+		asn1_write_element_header(type, data_len, p, &this_len);
+		*length += this_len;
+		memcpy(*p, data, data_len);
+		*p += data_len;
+		*length += data_len;
+	}	break;
+	case ASN1_INTEGER: {
+		uint64_t value = *(uint64_t*)data;
+		int value_size = asn1_calc_int_size(value);
+		asn1_write_element_header(type, value_size, p, &this_len);
+		*length += this_len;
+		asn1_write_int_value(p, value, value_size);
+		*length += value_size;
+	}	break;
+	case ASN1_BOOLEAN: {
+		unsigned int value = *(unsigned int*)data;
+		asn1_write_element_header(type, 1, p, &this_len);
+		*length += this_len;
+		asn1_write_int_value(p, value ? 0xFF : 0x00, 1);
+		*length += 1;
+	}	break;
+	case (ASN1_SET | ASN1_CONSTRUCTED): {
+		asn1_write_element_header(type, data_len, p, &this_len);
+		*length += this_len;
+		if (data && data_len > 0) {
+			memcpy(*p, data, data_len);
+			*p += data_len;
+			*length += data_len;
+		}
+	}	break;
+	default:
+		fprintf(stderr, "ERROR: %s: type %02x is not implemented", __func__, type);
+		return;
+	}
+}
+
 static unsigned int asn1_get_element(const unsigned char* data, unsigned char* type, unsigned char* size)
 {
 	unsigned int off = 0;
@@ -137,6 +264,97 @@ static const unsigned char *asn1_find_element(unsigned int index, unsigned char
 	return &data[off];
 }
 
+const char *_img4_get_component_tag(const char *compname)
+{
+	struct comp_tags {
+		const char *comp;
+		const char *tag;
+	};
+	const struct comp_tags component_tags[] = {
+		{ "ACIBT", "acib" },
+		{ "ACIWIFI", "aciw" },
+		{ "Alamo", "almo" },
+		{ "ANE", "anef" },
+		{ "AOP", "aopf" },
+		{ "Ap,HapticAssets", "hpas" },
+		{ "AppleLogo", "logo" },
+		{ "AudioCodecFirmware", "acfw" },
+		{ "AVE", "avef" },
+		{ "BatteryCharging", "glyC" },
+		{ "BatteryCharging0", "chg0" },
+		{ "BatteryCharging1", "chg1" },
+		{ "BatteryFull", "batF" },
+		{ "BatteryLow0", "bat0" },
+		{ "BatteryLow1", "bat1" },
+		{ "BatteryPlugin", "glyP" },
+		{ "CFELoader", "cfel" },
+		{ "Dali", "dali" },
+		{ "DCP", "dcpf" },
+		{ "DeviceTree", "dtre" },
+		{ "Diags", "diag" },
+		{ "EngineeringTrustCache", "dtrs" },
+		{ "ftap", "ftap" },
+		{ "ftsp", "ftsp" },
+		{ "GFX", "gfxf" },
+		{ "Hamm", "hamf" },
+		{ "Homer", "homr" },
+		{ "iBEC", "ibec" },
+		{ "iBoot", "ibot" },
+		{ "iBootData", "ibdt" },
+		{ "iBootTest", "itst" },
+		{ "iBSS", "ibss" },
+		{ "InputDevice", "ipdf" },
+		{ "ISP", "ispf" },
+		{ "KernelCache", "krnl" },
+		{ "LeapHaptics", "lphp" },
+		{ "Liquid", "liqd" },
+		{ "LLB", "illb" },
+		{ "LoadableTrustCache", "ltrs" },
+		{ "LowPowerWallet0", "lpw0" },
+		{ "LowPowerWallet1", "lpw1" },
+		{ "LowPowerWallet2", "lpw2" },
+		{ "MacEFI", "mefi" },
+		{ "Multitouch", "mtfw" },
+		{ "NeedService", "nsrv" },
+		{ "OS", "OS\0\0" },
+		{ "OSRamdisk", "osrd" },
+		{ "PersonalizedDMG", "pdmg" },
+		{ "PEHammer", "hmmr" },
+		{ "PERTOS", "pert" },
+		{ "PHLEET", "phlt" },
+		{ "PMP", "pmpf" },
+		{ "RBM", "rmbt" },
+		{ "RecoveryMode", "recm" },
+		{ "RestoreDCP", "rdcp" },
+		{ "RestoreDeviceTree", "rdtr" },
+		{ "RestoreKernelCache", "rkrn" },
+		{ "RestoreLogo", "rlgo" },
+		{ "RestoreRamDisk", "rdsk" },
+		{ "RestoreSEP", "rsep" },
+		{ "RestoreTrustCache", "rtsc" },
+		{ "rfta", "rfta" },
+		{ "rfts", "rfts" },
+		{ "RTP", "rtpf" },
+		{ "SCE", "scef" },
+		{ "SEP", "sepi" },
+		{ "SIO", "siof" },
+		{ "StaticTrustCache", "trst" },
+		{ "SystemLocker", "lckr" },
+		{ "WCHFirmwareUpdater", "wchf" },
+		{ NULL, NULL }
+	};
+	int i = 0;
+
+	while (component_tags[i].comp) {
+		if (!strcmp(component_tags[i].comp, compname)) {
+			return component_tags[i].tag;
+		}
+		i++;
+	}
+
+	return NULL;
+}
+
 int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img4_data, unsigned int *img4_size)
 {
 	unsigned char* magic_header = NULL;
@@ -227,3 +445,241 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo
 
 	return 0;
 }
+
+#ifndef __bswap_32
+#define __bswap_32(x) ((((x) & 0xFF000000) >> 24) \
+                    | (((x) & 0x00FF0000) >> 8) \
+                    | (((x) & 0x0000FF00) << 8) \
+                    | (((x) & 0x000000FF) << 24))
+#endif
+
+void _manifest_write_key_value(unsigned char **p, unsigned int *length, const char *tag, int type, void *value, int size)
+{
+	uint32_t utag = __bswap_32(*(uint32_t*)tag);
+	asn1_write_priv_element(p, length, utag);
+
+	unsigned char *start = *p;
+	unsigned char *outer_start = *p + 5;
+	unsigned char *inner_start = *p + 5 + 6;
+	unsigned int inner_length = 0;
+	asn1_write_element(&inner_start, &inner_length, ASN1_IA5_STRING, (void*)tag, -1);
+	asn1_write_element(&inner_start, &inner_length, type, value, size);
+
+	unsigned int outer_length = 0;
+	unsigned int this_length = 0;
+	if (!value && size > 0) {
+		asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_length + size, &outer_start, &outer_length);
+		asn1_write_size(outer_length + inner_length + size, &start, &this_length);
+	} else {
+		asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_length, &outer_start, &outer_length);
+		asn1_write_size(outer_length + inner_length, &start, &this_length);
+	}
+
+	memmove(start, outer_start - outer_length, outer_length);
+	outer_start = start + outer_length;
+	*length += this_length;
+	*length += outer_length;
+
+	memmove(outer_start, inner_start - inner_length, inner_length);
+	*length += inner_length;
+
+	*p += this_length + outer_length + inner_length;
+}
+
+void _manifest_write_component(unsigned char **p, unsigned int *length, const char *tag, plist_t comp)
+{
+	uint32_t utag = __bswap_32(*(uint32_t*)tag);
+	asn1_write_priv_element(p, length, utag);
+
+	unsigned char *start = *p;
+	unsigned char *outer_start = *p + 5;
+	unsigned char *inner_start = *p + 5 + 6;
+	unsigned int inner_length = 0;
+	asn1_write_element(&inner_start, &inner_length, ASN1_IA5_STRING, (void*)tag, -1);
+
+	unsigned char tmp_[256] = { 0, };
+	unsigned int tmp_len = 0;
+	unsigned char *tmp = &tmp_[0];
+
+	plist_t node = NULL;
+	uint8_t boolval = 0;
+
+	node = plist_dict_get_item(comp, "Digest");
+	if (node) {
+		char *digest = NULL;
+		uint64_t digest_len = 0;
+		plist_get_data_val(node, &digest, &digest_len);
+		if (digest_len > 0) {
+			_manifest_write_key_value(&tmp, &tmp_len, "DGST", ASN1_OCTET_STRING, digest, digest_len);
+		}
+		free(digest);
+	}
+
+	node = plist_dict_get_item(comp, "Trusted");
+	if (node) {
+		boolval = 0;
+		plist_get_bool_val(node, &boolval);
+		unsigned int int_bool_val = boolval;
+		_manifest_write_key_value(&tmp, &tmp_len, "EKEY", ASN1_BOOLEAN, &int_bool_val, -1);
+	}
+
+	node = plist_dict_get_item(comp, "EPRO");
+	if (node) {
+		boolval = 0;
+		plist_get_bool_val(node, &boolval);
+		unsigned int int_bool_val = boolval;
+		_manifest_write_key_value(&tmp, &tmp_len, "EPRO", ASN1_BOOLEAN, &int_bool_val, -1);
+	}
+
+	node = plist_dict_get_item(comp, "ESEC");
+	if (node) {
+		boolval = 0;
+		plist_get_bool_val(node, &boolval);
+		unsigned int int_bool_val = boolval;
+		_manifest_write_key_value(&tmp, &tmp_len, "ESEC", ASN1_BOOLEAN, &int_bool_val, -1);
+	}
+
+	asn1_write_element_header(ASN1_SET | ASN1_CONSTRUCTED, tmp_len, &inner_start, &inner_length);
+	memcpy(inner_start, tmp_, tmp_len);
+	inner_start += tmp_len;
+	inner_length += tmp_len;
+
+	unsigned int outer_length = 0;
+	asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_length, &outer_start, &outer_length);
+
+	unsigned int this_length = 0;
+	asn1_write_size(outer_length + inner_length, &start, &this_length);
+
+	memmove(start, outer_start - outer_length, outer_length);
+
+	outer_start = start + outer_length;
+	*length += this_length;
+	*length += outer_length;
+
+	memmove(outer_start, inner_start - inner_length, inner_length);
+
+	*length += inner_length;
+
+	*p += this_length + outer_length + inner_length;
+}
+
+int img4_create_local_manifest(plist_t request, plist_t* manifest)
+{
+	if (!request || !manifest) {
+		return -1;
+	}
+
+	unsigned char *buf = calloc(1, 65536);
+	unsigned char *p = buf;
+	unsigned int length = 0;
+	uint64_t uintval = 0;
+	unsigned int boolval = 0;
+	char *strval = NULL;
+	plist_t node = NULL;
+
+	unsigned char tmp_[1024];
+	unsigned char *tmp = &tmp_[0];
+	unsigned int tmp_len = 0;
+
+	/* write manifest properties */
+	uintval = _plist_dict_get_uint(request, "ApBoardID");
+	_manifest_write_key_value(&tmp, &tmp_len, "BORD", ASN1_INTEGER, &uintval, -1);
+
+	uintval = 0;
+	_manifest_write_key_value(&tmp, &tmp_len, "CEPO", ASN1_INTEGER, &uintval, -1);
+
+	uintval = _plist_dict_get_uint(request, "ApChipID");
+	_manifest_write_key_value(&tmp, &tmp_len, "CHIP", ASN1_INTEGER, &uintval, -1);
+
+	boolval = _plist_dict_get_bool(request, "ApProductionMode");
+	_manifest_write_key_value(&tmp, &tmp_len, "CPRO", ASN1_BOOLEAN, &boolval, -1);
+
+	boolval = 0;
+	_manifest_write_key_value(&tmp, &tmp_len, "CSEC", ASN1_BOOLEAN, &boolval, -1);
+
+	uintval = _plist_dict_get_uint(request, "ApSecurityDomain");
+	_manifest_write_key_value(&tmp, &tmp_len, "SDOM", ASN1_INTEGER, &uintval, -1);
+
+	/* create manifest properties set */
+	_manifest_write_key_value(&p, &length, "MANP", ASN1_SET | ASN1_CONSTRUCTED, tmp_, tmp_len);
+
+	/* now write the components */
+	plist_dict_iter iter = NULL;
+	plist_dict_new_iter(request, &iter);
+	char *key = NULL;
+	plist_t val = NULL;
+	do {
+		plist_dict_next_item(request, iter, &key, &val);
+		if (val && plist_get_node_type(val) == PLIST_DICT) {
+			const char *comp = _img4_get_component_tag(key);
+			if (!comp) {
+				error("ERROR: %s: Unhandled component '%s' - can't create manifest\n", __func__, key);
+				free(iter);
+				free(buf);
+				return -1;
+			}
+			debug("DEBUG: found component %s (%s)\n", comp, key);
+			_manifest_write_component(&p, &length, comp, val);
+		}
+		free(key);
+	} while (val);
+	free(iter);
+
+	/* write manifest body header */
+	unsigned char manb_[32];
+	unsigned char *manb = &manb_[0];
+	unsigned int manb_len = 0;
+	_manifest_write_key_value(&manb, &manb_len, "MANB", ASN1_SET | ASN1_CONSTRUCTED, NULL, length);
+
+	/* write inner set */
+	unsigned char inner_set_[8];
+	unsigned char *inner_set = &inner_set_[0];
+	unsigned int inner_set_len = 0;
+	asn1_write_element_header(ASN1_SET | ASN1_CONSTRUCTED, length + manb_len, &inner_set, &inner_set_len);
+
+	/* write header values */
+	unsigned char hdrdata_[16];
+	unsigned char *hdrdata = &hdrdata_[0];
+	unsigned int hdrdata_len = 0;
+	asn1_write_element(&hdrdata, &hdrdata_len, ASN1_IA5_STRING, (void*)"IM4M", -1);
+	uint64_t intval = 0;
+	asn1_write_element(&hdrdata, &hdrdata_len, ASN1_INTEGER, &intval, -1);
+
+	/* write outer sequence now that we know the entire size */
+	unsigned char seq_[8];
+	unsigned char *seq = &seq_[0];
+	unsigned int seq_len = 0;
+	asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_set_len + length + manb_len + hdrdata_len, &seq, &seq_len);
+
+	unsigned int header_len = seq_len + hdrdata_len + inner_set_len + manb_len;
+
+	/* now put everything together */
+	memmove(buf + header_len, buf, length);
+
+	unsigned char *hdr = buf;
+	unsigned int hdr_len = 0;
+
+	memcpy(hdr, seq_, seq_len);
+	hdr += seq_len;
+	hdr_len += seq_len;
+
+	memcpy(hdr, hdrdata_, hdrdata_len);
+	hdr += hdrdata_len;
+	hdr_len += hdrdata_len;
+
+	memcpy(hdr, inner_set_, inner_set_len);
+	hdr += inner_set_len;
+	hdr_len += inner_set_len;
+
+	memcpy(hdr, manb_, manb_len);
+	hdr += manb_len;
+	hdr_len += manb_len;
+
+	length += hdr_len;
+
+	*manifest = plist_new_data((char*)buf, length);
+
+	free(buf);
+
+	return 0;
+}
diff --git a/src/img4.h b/src/img4.h
index b83f1ca..ffbd57d 100644
--- a/src/img4.h
+++ b/src/img4.h
@@ -1,8 +1,8 @@
 /*
  * img4.h
- * Functions for handling the new IMG4 format
+ * Functions for handling the IMG4 format
  *
- * Copyright (c) 2013 Nikias Bassen. All Rights Reserved.
+ * Copyright (c) 2013-2019 Nikias Bassen. All Rights Reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -27,6 +27,7 @@ extern "C" {
 #endif
 
 int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img4_data, unsigned int *img4_size);
+int img4_create_local_manifest(plist_t request, plist_t* manifest);
 
 #ifdef __cplusplus
 }
-- 
cgit v1.1-32-gdbae