Permalink
| /* | |
| * img4 tool | |
| * xerub 2015 | |
| */ | |
| #include <assert.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #ifdef USE_CORECRYPTO | |
| #include <corecrypto/ccrsa.h> | |
| #include <corecrypto/ccsha1.h> | |
| #else | |
| #include <openssl/bn.h> | |
| #include <openssl/err.h> | |
| #include <openssl/evp.h> | |
| #include <openssl/rsa.h> | |
| #include <openssl/sha.h> | |
| #endif | |
| #include <libDER/DER_Decode.h> | |
| #include <libDER/asn1Types.h> | |
| #include <libDER/oids.h> | |
| #include "validate_ca.h" | |
| #define E000000000000000 (ASN1_CONSTRUCTED | ASN1_PRIVATE) | |
| #define IS_EQUAL(a, b) ((a).length == (b).length && !memcmp((a).data, (b).data, (a).length)) | |
| #define FOURCC(tag) (unsigned char)((tag) >> 24), (unsigned char)((tag) >> 16), (unsigned char)((tag) >> 8), (unsigned char)(tag) | |
| #define RESERVE_DIGEST_SPACE 20 | |
| #define panic(fn, args...) do { fprintf(stderr, fn args); exit(1); } while (0) | |
| #ifdef iOS10 | |
| #include "lzfse.h" | |
| #endif | |
| typedef enum { | |
| DictMANP, | |
| DictOBJP | |
| } DictType; | |
| typedef struct { | |
| DERItem item; | |
| DERTag tag; | |
| } DERMonster; | |
| typedef struct { | |
| DERItem magic; // "IM4P" | |
| DERItem type; // "illb" | |
| DERItem version; // "iBoot-2261.3.33" | |
| DERItem imageData; | |
| DERItem keybag; | |
| #ifdef iOS10 | |
| DERItem compression; | |
| #endif | |
| DERByte full_digest[RESERVE_DIGEST_SPACE]; | |
| } TheImg4Payload; | |
| typedef struct { | |
| DERItem magic; // "IM4M" | |
| DERItem version; // 0 | |
| DERItem theset; // MANB + MANP | |
| DERItem sig_blob; // RSA | |
| DERItem chain_blob; // cert chain | |
| DERItem img4_blob; | |
| DERByte full_digest[RESERVE_DIGEST_SPACE]; | |
| DERByte theset_digest[RESERVE_DIGEST_SPACE]; | |
| } TheImg4Manifest; | |
| typedef struct { | |
| DERItem magic; // "IM4R" | |
| DERItem nonce; | |
| } TheImg4RestoreInfo; | |
| typedef struct { | |
| bool payloadHashed; | |
| bool manifestHashed; | |
| DERItem payloadRaw; | |
| DERItem manifestRaw; | |
| DERItem manb; | |
| DERItem manp; | |
| DERItem objp; | |
| TheImg4Payload payload; | |
| TheImg4Manifest manifest; | |
| TheImg4RestoreInfo restoreInfo; | |
| } TheImg4; | |
| const DERItemSpec DERImg4ItemSpecs[4] = { | |
| { 0 * sizeof(DERItem), ASN1_IA5_STRING, 0 }, // "IMG4" | |
| { 1 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, DER_DEC_SAVE_DER }, // SEQUENCE(payload) | |
| { 2 * sizeof(DERItem), ASN1_CONSTRUCTED|ASN1_CONTEXT_SPECIFIC | 0, DER_DEC_OPTIONAL }, // CONS(SEQUENCE(manifest)) | |
| { 3 * sizeof(DERItem), ASN1_CONSTRUCTED|ASN1_CONTEXT_SPECIFIC | 1, DER_DEC_OPTIONAL } // CONS(SEQUENCE(restoreInfo)) | |
| }; | |
| #ifdef iOS10 | |
| const DERItemSpec DERImg4PayloadItemSpecs[6] = { | |
| { 0 * sizeof(DERItem), ASN1_IA5_STRING, 0 }, // "IM4P" | |
| { 1 * sizeof(DERItem), ASN1_IA5_STRING, 0 }, // "illb" | |
| { 2 * sizeof(DERItem), ASN1_IA5_STRING, 0 }, // "iBoot-2261.3.33" | |
| { 3 * sizeof(DERItem), ASN1_OCTET_STRING, 0 }, // binary data | |
| { 4 * sizeof(DERItem), ASN1_OCTET_STRING, DER_DEC_OPTIONAL }, // keybag | |
| { 5 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, DER_DEC_OPTIONAL } // iOS10 compression info | |
| }; | |
| #else | |
| const DERItemSpec DERImg4PayloadItemSpecs[5] = { | |
| { 0 * sizeof(DERItem), ASN1_IA5_STRING, 0 }, // "IM4P" | |
| { 1 * sizeof(DERItem), ASN1_IA5_STRING, 0 }, // "illb" | |
| { 2 * sizeof(DERItem), ASN1_IA5_STRING, 0 }, // "iBoot-2261.3.33" | |
| { 3 * sizeof(DERItem), ASN1_OCTET_STRING, 0 }, // binary data | |
| { 4 * sizeof(DERItem), ASN1_OCTET_STRING, DER_DEC_OPTIONAL } // keybag | |
| }; | |
| #endif | |
| const DERItemSpec DERImg4ManifestItemSpecs[5] = { | |
| { 0 * sizeof(DERItem), ASN1_IA5_STRING, 0 }, // "IM4M" | |
| { 1 * sizeof(DERItem), ASN1_INTEGER, 0 }, // 0 | |
| { 2 * sizeof(DERItem), ASN1_CONSTR_SET, DER_DEC_SAVE_DER }, // SET(things) | |
| { 3 * sizeof(DERItem), ASN1_OCTET_STRING, 0 }, // RSA | |
| { 4 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, 0 } // chain | |
| }; | |
| const DERItemSpec DERImg4RestoreInfoItemSpecs[2] = { | |
| { 0 * sizeof(DERItem), ASN1_IA5_STRING, 0 }, // "IM4R" | |
| { 1 * sizeof(DERItem), ASN1_CONSTR_SET, 0 } // SET(nonce) | |
| }; | |
| const DERItemSpec DERSignedCertCrlItemSpecs[3] = { | |
| { 0 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, DER_DEC_SAVE_DER }, | |
| { 1 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, 0 }, | |
| { 2 * sizeof(DERItem), ASN1_BIT_STRING, 0 } | |
| }; | |
| const DERItemSpec DERTBSCertItemSpecs[10] = { | |
| { 0 * sizeof(DERItem), ASN1_CONSTRUCTED|ASN1_CONTEXT_SPECIFIC | 0, DER_DEC_OPTIONAL }, | |
| { 1 * sizeof(DERItem), ASN1_INTEGER, 0 }, | |
| { 2 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, 0 }, | |
| { 3 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, 0 }, | |
| { 4 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, 0 }, | |
| { 5 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, 0 }, | |
| { 6 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, 0 }, | |
| { 7 * sizeof(DERItem), ASN1_CONTEXT_SPECIFIC | 1, DER_DEC_OPTIONAL }, | |
| { 8 * sizeof(DERItem), ASN1_CONTEXT_SPECIFIC | 2, DER_DEC_OPTIONAL }, | |
| { 9 * sizeof(DERItem), ASN1_CONSTRUCTED|ASN1_CONTEXT_SPECIFIC | 3, DER_DEC_OPTIONAL } | |
| }; | |
| const DERItemSpec DERAttributeTypeAndValueItemSpecs[2] = { | |
| { 0 * sizeof(DERItem), ASN1_OBJECT_ID, 0 }, | |
| { 1 * sizeof(DERItem), 0, DER_DEC_ASN_ANY | DER_DEC_SAVE_DER } | |
| }; | |
| const DERItemSpec DERExtensionItemSpecs[3] = { | |
| { 0 * sizeof(DERItem), ASN1_OBJECT_ID, 0 }, | |
| { 1 * sizeof(DERItem), ASN1_BOOLEAN, DER_DEC_OPTIONAL }, | |
| { 2 * sizeof(DERItem), ASN1_OCTET_STRING, 0 } | |
| }; | |
| const DERItemSpec DERAlgorithmIdItemSpecs[2] = { | |
| { 0 * sizeof(DERItem), ASN1_OBJECT_ID, 0 }, | |
| { 1 * sizeof(DERItem), 0, DER_DEC_OPTIONAL | DER_DEC_ASN_ANY | DER_DEC_SAVE_DER } | |
| }; | |
| const DERItemSpec DERSubjPubKeyInfoItemSpecs[2] = { | |
| { 0 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, 0 }, | |
| { 1 * sizeof(DERItem), ASN1_BIT_STRING, 0 } | |
| }; | |
| const DERItemSpec DERRSAPubKeyPKCS1ItemSpecs[2] = { | |
| { 0 * sizeof(DERItem), ASN1_INTEGER, 0x100 }, | |
| { 1 * sizeof(DERItem), ASN1_INTEGER, 0x100 } | |
| }; | |
| const DERByte _oidAppleImg4ManifestCertSpec[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x63, 0x64, 6, 1, 0xF }; | |
| const DERItem oidAppleImg4ManifestCertSpec = { (DERByte *)_oidAppleImg4ManifestCertSpec, sizeof(_oidAppleImg4ManifestCertSpec) }; | |
| const DERItem AppleSecureBootCA = { (DERByte *)"\x13)Apple Secure Boot Certification Authority", 0x2B }; | |
| /*****************************************************************************/ | |
| int | |
| DERImg4DecodeFindInSequence(unsigned char *a1, unsigned char *a2, DERTag tag, DERItem *a5) | |
| { | |
| DERDecodedInfo currDecoded; | |
| DERSequence derSeq; | |
| derSeq.nextItem = a1; | |
| derSeq.end = a2; | |
| do { | |
| int rv = DERDecodeSeqNext(&derSeq, &currDecoded); | |
| if (rv) { | |
| return rv; | |
| } | |
| } while (currDecoded.tag != tag); | |
| *a5 = currDecoded.content; | |
| return 0; | |
| } | |
| int | |
| DERImg4DecodeContentFindItemWithTag(const DERItem *a1, DERTag tag, DERItem *a4) | |
| { | |
| int rv; | |
| DERSequence derSeq; | |
| rv = DERDecodeSeqContentInit(a1, &derSeq); | |
| if (rv) { | |
| return rv; | |
| } | |
| return DERImg4DecodeFindInSequence(derSeq.nextItem, derSeq.end, tag, a4); | |
| } | |
| int | |
| DERImg4DecodeTagCompare(const DERItem *a1, uint32_t nameTag) | |
| { | |
| uint32_t var_14; | |
| if (a1->length < 4) { | |
| return -1; | |
| } | |
| if (a1->length > 4) { | |
| return 1; | |
| } | |
| if (DERParseInteger(a1, &var_14)) { | |
| return -2; | |
| } | |
| if (var_14 < nameTag) { | |
| return -1; | |
| } | |
| if (var_14 > nameTag) { | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| int | |
| DERImg4Decode(const DERItem *a1, DERItem *a2) | |
| { | |
| int rv; | |
| DERDecodedInfo var_38; | |
| if (a1 == NULL || a2 == NULL) { | |
| return DR_ParamErr; | |
| } | |
| rv = DERDecodeItem(a1, &var_38); | |
| if (rv) { | |
| return rv; | |
| } | |
| if (var_38.tag != ASN1_CONSTR_SEQUENCE) { | |
| return DR_UnexpectedTag; | |
| } | |
| if (a1->data + a1->length != var_38.content.data + var_38.content.length) { | |
| return DR_BufOverflow; | |
| } | |
| rv = DERParseSequenceContent(&var_38.content, 4, DERImg4ItemSpecs, a2, 0); | |
| if (rv) { | |
| return rv; | |
| } | |
| if (DERImg4DecodeTagCompare(a2, 'IMG4')) { | |
| return DR_UnexpectedTag; | |
| } | |
| return 0; | |
| } | |
| int | |
| DERImg4DecodePayload(const DERItem *a1, TheImg4Payload *a2) | |
| { | |
| int rv; | |
| if (a1 == NULL || a2 == NULL) { | |
| return DR_ParamErr; | |
| } | |
| #ifdef iOS10 | |
| rv = DERParseSequence(a1, 6, DERImg4PayloadItemSpecs, a2, 0); | |
| #else | |
| rv = DERParseSequence(a1, 5, DERImg4PayloadItemSpecs, a2, 0); | |
| #endif | |
| if (rv) { | |
| return rv; | |
| } | |
| if (DERImg4DecodeTagCompare(&a2->magic, 'IM4P')) { | |
| return DR_UnexpectedTag; | |
| } | |
| return 0; | |
| } | |
| int | |
| DERImg4DecodeManifest(const DERItem *a1, TheImg4Manifest *a2) | |
| { | |
| int rv; | |
| uint32_t var_14; | |
| if (a1 == NULL || a2 == NULL) { | |
| return DR_ParamErr; | |
| } | |
| if (a1->data == NULL || a1->length == 0) { | |
| return 0; | |
| } | |
| rv = DERParseSequence(a1, 5, DERImg4ManifestItemSpecs, a2, 0); | |
| if (rv) { | |
| return rv; | |
| } | |
| if (DERImg4DecodeTagCompare(&a2->magic, 'IM4M')) { | |
| return DR_UnexpectedTag; | |
| } | |
| rv = DERParseInteger(&a2->version, &var_14); | |
| if (rv) { | |
| return rv; | |
| } | |
| if (var_14) { | |
| return DR_UnexpectedTag; | |
| } | |
| return 0; | |
| } | |
| int | |
| DERImg4DecodeRestoreInfo(const DERItem *a1, TheImg4RestoreInfo *a2) | |
| { | |
| int rv; | |
| if (a1 == NULL) { | |
| return 0; | |
| } | |
| if (a2 == NULL) { | |
| return DR_ParamErr; | |
| } | |
| if (a1->data == NULL || a1->length == 0) { | |
| return 0; | |
| } | |
| rv = DERParseSequence(a1, 2, DERImg4RestoreInfoItemSpecs, a2, 0); | |
| if (rv) { | |
| return rv; | |
| } | |
| if (DERImg4DecodeTagCompare(&a2->magic, 'IM4R')) { | |
| return DR_UnexpectedTag; | |
| } | |
| return 0; | |
| } | |
| int | |
| DERImg4DecodeFindProperty(const DERItem *a1, DERTag etag, DERTag atag, DERMonster *dest) | |
| { | |
| int rv; | |
| DERItemSpec var_70[2]; | |
| uint32_t var_3C; | |
| DERItem var_38; | |
| rv = DERImg4DecodeContentFindItemWithTag(a1, etag, &var_38); | |
| if (rv) { | |
| return rv; | |
| } | |
| var_70[0].offset = 0; | |
| var_70[0].tag = ASN1_IA5_STRING; | |
| var_70[0].options = 0; | |
| var_70[1].offset = sizeof(DERMonster); | |
| var_70[1].tag = atag; | |
| var_70[1].options = 0; | |
| rv = DERParseSequence(&var_38, 2, var_70, dest, 0); | |
| if (rv) { | |
| return rv; | |
| } | |
| rv = DERParseInteger(&dest[0].item, &var_3C); | |
| if (rv) { | |
| return rv; | |
| } | |
| if ((E000000000000000 | var_3C) != etag) { | |
| return DR_UnexpectedTag; | |
| } | |
| dest[0].tag = etag | E000000000000000; | |
| dest[1].tag = atag; | |
| return 0; | |
| } | |
| int | |
| Img4DecodeGetPayload(TheImg4 *img4, DERItem *a2) | |
| { | |
| if (img4 == NULL || a2 == NULL) { | |
| return DR_ParamErr; | |
| } | |
| if (img4->payload.imageData.data == NULL || img4->payload.imageData.length == 0) { | |
| return DR_EndOfSequence; | |
| } | |
| *a2 = img4->payload.imageData; | |
| return 0; | |
| } | |
| int | |
| Img4DecodeGetPayloadType(TheImg4 *img4, unsigned int *a2) | |
| { | |
| if (img4 == NULL || a2 == NULL) { | |
| return DR_ParamErr; | |
| } | |
| if (img4->payload.imageData.data == NULL || img4->payload.imageData.length == 0) { | |
| return DR_EndOfSequence; | |
| } | |
| return DERParseInteger(&img4->payload.type, a2); | |
| } | |
| int | |
| Img4DecodeGetPayloadKeybag(TheImg4 *img4, DERItem *a2) | |
| { | |
| if (img4 == NULL || a2 == NULL) { | |
| return DR_ParamErr; | |
| } | |
| if (img4->payload.imageData.data == NULL || img4->payload.imageData.length == 0) { | |
| return DR_EndOfSequence; | |
| } | |
| *a2 = img4->payload.keybag; | |
| return 0; | |
| } | |
| int | |
| Img4DecodeManifestExists(TheImg4 *img4, bool *exists) | |
| { | |
| if (img4 == NULL || exists == NULL) { | |
| return DR_ParamErr; | |
| } | |
| *exists = (img4->manifestRaw.data != NULL); | |
| return 0; | |
| } | |
| int | |
| Img4DecodeGetRestoreInfoNonce(TheImg4 *img4, DERTag etag, DERTag atag, DERMonster *dest) | |
| { | |
| if (img4 == NULL || dest == NULL) { | |
| return DR_ParamErr; | |
| } | |
| if (img4->restoreInfo.nonce.data == NULL || img4->restoreInfo.nonce.length == 0) { | |
| return 0; | |
| } | |
| return DERImg4DecodeFindProperty(&img4->restoreInfo.nonce, etag, atag, dest); | |
| } | |
| int | |
| Img4DecodeGetRestoreInfoData(TheImg4 *img4, DERTag tag, DERByte **a4, DERSize *a5) | |
| { | |
| int rv; | |
| DERMonster var_40[2]; | |
| if (img4 == NULL || a4 == NULL || a5 == NULL) { | |
| return DR_ParamErr; | |
| } | |
| rv = Img4DecodeGetRestoreInfoNonce(img4, E000000000000000 | tag, ASN1_OCTET_STRING, var_40); | |
| if (rv) { | |
| return rv; | |
| } | |
| *a4 = var_40[1].item.data; | |
| *a5 = var_40[1].item.length; | |
| return 0; | |
| } | |
| int | |
| Img4DecodeInit(DERByte *data, DERSize length, TheImg4 *img4) | |
| { | |
| int rv; | |
| DERItem var_70[4]; | |
| DERItem var_30; | |
| if (data == NULL || img4 == NULL) { | |
| return DR_ParamErr; | |
| } | |
| var_30.data = data; | |
| var_30.length = length; | |
| memset(var_70, 0, sizeof(var_70)); | |
| memset(img4, 0, sizeof(TheImg4)); | |
| rv = DERImg4Decode(&var_30, var_70); | |
| if (rv) { | |
| return rv; | |
| } | |
| rv = DERImg4DecodePayload(&var_70[1], &img4->payload); | |
| if (rv) { | |
| return rv; | |
| } | |
| rv = DERImg4DecodeManifest(&var_70[2], &img4->manifest); | |
| if (rv) { | |
| return rv; | |
| } | |
| rv = DERImg4DecodeRestoreInfo(&var_70[3], &img4->restoreInfo); | |
| if (rv) { | |
| return rv; | |
| } | |
| img4->payloadRaw = var_70[1]; | |
| img4->manifestRaw = var_70[2]; | |
| return 0; | |
| } | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <sys/stat.h> | |
| #include <unistd.h> | |
| #ifdef USE_CORECRYPTO | |
| #include <corecrypto/ccaes.h> | |
| #else | |
| #include <openssl/aes.h> | |
| #endif | |
| #define DWORD_BE(data, offset) __builtin_bswap32(*(uint32_t *)((char *)(data) + (offset))) | |
| #include "lzss.c" | |
| #define OUTSET(ptr) do { if (outdup) { free(output); } output = ptr; outdup = 1; } while (0) | |
| static unsigned char * | |
| read_file(const char *filename, off_t off, size_t *size) | |
| { | |
| int fd; | |
| size_t rv, sz; | |
| struct stat st; | |
| unsigned char *buf; | |
| fd = open(filename, O_RDONLY); | |
| if (fd < 0) { | |
| return NULL; | |
| } | |
| rv = fstat(fd, &st); | |
| if (rv) { | |
| close(fd); | |
| return NULL; | |
| } | |
| if (off > st.st_size) { | |
| off = st.st_size; | |
| } | |
| sz = st.st_size - off; | |
| buf = malloc(sz); | |
| if (buf == NULL) { | |
| close(fd); | |
| return NULL; | |
| } | |
| rv = read(fd, buf, sz); | |
| close(fd); | |
| if (rv != sz) { | |
| free(buf); | |
| return NULL; | |
| } | |
| if (size != NULL) { | |
| *size = sz; | |
| } | |
| return buf; | |
| } | |
| static ssize_t | |
| write_file(const char *filename, void *buf, size_t size) | |
| { | |
| ssize_t rv; | |
| int fd = creat(filename, 0644); | |
| if (fd < 0) { | |
| return -1; | |
| } | |
| rv = write(fd, buf, size); | |
| close(fd); | |
| return rv; | |
| } | |
| static int | |
| str2hex(int buflen, unsigned char *buf, const char *str) | |
| { | |
| unsigned char *ptr = buf; | |
| int seq = -1; | |
| while (buflen > 0) { | |
| int nibble = *str++; | |
| if (nibble >= '0' && nibble <= '9') { | |
| nibble -= '0'; | |
| } else { | |
| nibble |= 0x20; | |
| if (nibble < 'a' || nibble > 'f') { | |
| break; | |
| } | |
| nibble -= 'a' - 10; | |
| } | |
| if (seq >= 0) { | |
| *buf++ = (seq << 4) | nibble; | |
| buflen--; | |
| seq = -1; | |
| } else { | |
| seq = nibble; | |
| } | |
| } | |
| return buf - ptr; | |
| } | |
| static TheImg4 * | |
| parse(unsigned char *data, unsigned length) | |
| { | |
| int rv; | |
| TheImg4 *img4; | |
| img4 = malloc(sizeof(TheImg4)); | |
| if (!img4) { | |
| return NULL; | |
| } | |
| memset(img4, 0, sizeof(TheImg4)); | |
| rv = Img4DecodeInit(data, length, img4); | |
| if (rv) { | |
| DERItem item; | |
| item.data = data; | |
| item.length = length; | |
| rv = DERImg4DecodePayload(&item, &img4->payload); | |
| } | |
| if (rv) { | |
| free(img4); | |
| return NULL; | |
| } | |
| return img4; | |
| } | |
| int | |
| main(int argc, char **argv) | |
| { | |
| int rv; | |
| const char *what; | |
| const char *filename; | |
| const char *outname; | |
| TheImg4 *img4; | |
| unsigned type; | |
| unsigned written; | |
| unsigned char ivkey[16 + 32]; | |
| unsigned char *iv = NULL, *key = NULL; | |
| unsigned char *output = NULL; | |
| unsigned outlen = 0; | |
| int outdup = 0; | |
| DERItem item; | |
| unsigned char *data; | |
| size_t size; | |
| if (argc < 4) { | |
| fprintf(stderr, "usage: %s {-image|-extra|-keybag|-ticket} input output [ivkey]\n", argv[0]); | |
| return 1; | |
| } | |
| what = argv[1]; | |
| filename = argv[2]; | |
| outname = argv[3]; | |
| if (argc > 4) { | |
| rv = str2hex(sizeof(ivkey), ivkey, argv[4]); | |
| if (rv == sizeof(ivkey)) { | |
| iv = ivkey; | |
| key = ivkey + 16; | |
| } | |
| } | |
| data = read_file(filename, 0, &size); | |
| if (data == NULL) { | |
| fprintf(stderr, "[e] cannot read '%s'\n", filename); | |
| return -1; | |
| } | |
| img4 = parse(data, size); | |
| if (!img4) { | |
| fprintf(stderr, "[e] cannot parse '%s'\n", filename); | |
| free(data); | |
| return -1; | |
| } | |
| rv = Img4DecodeGetPayloadType(img4, &type); | |
| if (rv) { | |
| fprintf(stderr, "[e] cannot identify '%s'\n", filename); | |
| goto err; | |
| } | |
| printf("%c%c%c%c\n", FOURCC(type)); | |
| if (!strncmp(what, "-i", 2) || !strncmp(what, "-e", 2)) { | |
| int decompress; | |
| rv = Img4DecodeGetPayload(img4, &item); | |
| if (rv) { | |
| fprintf(stderr, "[e] cannot extract payload from '%s'\n", filename); | |
| goto err; | |
| } | |
| output = item.data; | |
| outlen = item.length; | |
| if (iv && key) { | |
| if (outlen & 15) { | |
| unsigned usize = (outlen + 15) & ~15; | |
| unsigned char *tmp = calloc(1, usize); | |
| if (!tmp) { | |
| fprintf(stderr, "[e] out of memory %u\n", usize); | |
| goto err; | |
| } | |
| memcpy(tmp, output, outlen); | |
| OUTSET(tmp); | |
| } | |
| rv = Img4DecodeGetPayloadKeybag(img4, &item); | |
| if (rv || item.length == 0) { | |
| fprintf(stderr, "[w] image '%s' has no keybag\n", filename); | |
| } | |
| #ifdef USE_CORECRYPTO | |
| cccbc_one_shot(ccaes_cbc_decrypt_mode(), 32, key, iv, (outlen + 15) / 16, output, output); | |
| #else | |
| AES_KEY decryptKey; | |
| AES_set_decrypt_key(key, 256, &decryptKey); | |
| AES_cbc_encrypt(output, output, (outlen + 15) & ~15, &decryptKey, iv, AES_DECRYPT); | |
| #endif | |
| } | |
| #ifdef iOS10 | |
| if (img4->payload.compression.data && img4->payload.compression.length) { | |
| DERItem tmp[2]; | |
| uint32_t deco = 0; | |
| uint64_t usize = 0; | |
| /* XXX ugly hack: reuse DERRSAPubKeyPKCS1ItemSpecs */ | |
| if (DERParseSequenceContent(&img4->payload.compression, 2, DERRSAPubKeyPKCS1ItemSpecs, tmp, 0) || | |
| DERParseInteger(&tmp[0], &deco) || DERParseInteger64(&tmp[1], &usize)) { | |
| fprintf(stderr, "[e] cannot get decompression info\n"); | |
| goto err; | |
| } | |
| if (deco == 1 && what[1] == 'i') { | |
| size_t asize = lzfse_decode_scratch_size(); | |
| unsigned char *dec, *aux = malloc(asize); | |
| if (!aux) { | |
| fprintf(stderr, "[e] out of memory %zu\n", asize); | |
| goto err; | |
| } | |
| dec = malloc(usize + 1); | |
| if (!dec) { | |
| fprintf(stderr, "[e] out of memory %llu\n", usize + 1); | |
| free(aux); | |
| goto err; | |
| } | |
| outlen = lzfse_decode_buffer(dec, usize + 1, output, outlen, aux); | |
| free(aux); | |
| if (outlen != usize) { | |
| fprintf(stderr, "[e] decompression error\n"); | |
| free(dec); | |
| goto err; | |
| } | |
| OUTSET(dec); | |
| } | |
| } | |
| #endif | |
| decompress = (DWORD_BE(output, 0) == 'comp' && DWORD_BE(output, 4) == 'lzss'); | |
| if (decompress && what[1] == 'i') { | |
| uint32_t csize = DWORD_BE(output, 16); | |
| uint32_t usize = DWORD_BE(output, 12); | |
| uint32_t adler = DWORD_BE(output, 8); | |
| unsigned char *dec = malloc(usize); | |
| if (outlen > 0x180 + csize) { | |
| fprintf(stderr, "[i] extra 0x%x bytes after compressed chunk\n", outlen - 0x180 - csize); | |
| } | |
| if (!dec) { | |
| fprintf(stderr, "[e] out of memory %u\n", usize); | |
| goto err; | |
| } | |
| outlen = decompress_lzss(dec, output + 0x180, csize); | |
| if (adler != lzadler32(dec, outlen)) { | |
| fprintf(stderr, "[w] adler32 mismatch\n"); | |
| } | |
| OUTSET(dec); | |
| } else if (decompress) { | |
| uint32_t csize = DWORD_BE(output, 16); | |
| uint32_t usize = outlen - 0x180 - csize; | |
| if (outlen > 0x180 + csize) { | |
| unsigned char *dec = malloc(usize); | |
| if (!dec) { | |
| fprintf(stderr, "[e] out of memory %u\n", usize); | |
| goto err; | |
| } | |
| memcpy(dec, output + 0x180 + csize, usize); | |
| outlen = usize; | |
| OUTSET(dec); | |
| } else { | |
| OUTSET(NULL); | |
| } | |
| } else if (what[1] == 'e') { | |
| OUTSET(NULL); | |
| } | |
| if (!output) { | |
| fprintf(stderr, "[e] nothing to do\n"); | |
| goto err; | |
| } | |
| } | |
| if (!strncmp(what, "-k", 2)) { | |
| rv = Img4DecodeGetPayloadKeybag(img4, &item); | |
| if (rv == 0 && item.length) { | |
| output = item.data; | |
| outlen = item.length; | |
| } else { | |
| fprintf(stderr, "[e] image '%s' has no keybag\n", filename); | |
| goto err; | |
| } | |
| } | |
| if (!strncmp(what, "-t", 2)) { | |
| bool exists = false; | |
| rv = Img4DecodeManifestExists(img4, &exists); | |
| if (rv == 0 && exists) { | |
| output = img4->manifestRaw.data; | |
| outlen = img4->manifestRaw.length; | |
| } else { | |
| fprintf(stderr, "[e] image '%s' has no ticket\n", filename); | |
| goto err; | |
| } | |
| } | |
| written = write_file(outname, output, outlen); | |
| if (written != outlen) { | |
| fprintf(stderr, "[e] cannot write '%s'\n", outname); | |
| goto err; | |
| } | |
| rv = 0; | |
| out: | |
| if (outdup) { | |
| free(output); | |
| } | |
| free(img4); | |
| free(data); | |
| return rv; | |
| err: | |
| rv = -1; | |
| goto out; | |
| } |