#include #include #include #include #include #include #include "IOKit.h" #include "IOAESAccelerator.h" #include "AppleEffaceableStorage.h" #include "AppleKeyStore.h" #include "util.h" #include "bsdcrypto/rijndael.h" #include "bsdcrypto/key_wrap.h" CFDictionaryRef AppleKeyStore_loadKeyBag(const char* folder, const char* filename) { char keybagPath[100]; struct BAG1Locker bag1_locker={0}; //unsigned char buffer_bag1[52] = {0}; snprintf(keybagPath, 99, "%s/%s.kb", folder, filename); CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)keybagPath, strlen(keybagPath), 0); if (url == NULL) return NULL; CFReadStreamRef stream = CFReadStreamCreateWithFile (kCFAllocatorDefault, url); if (stream == NULL) return NULL; if (CFReadStreamOpen(stream) != TRUE) return NULL; CFPropertyListRef plist = CFPropertyListCreateWithStream (kCFAllocatorDefault, stream, 0, kCFPropertyListImmutable, NULL, NULL ); if (plist == NULL) return NULL; CFDataRef data = CFDictionaryGetValue(plist, CFSTR("_MKBPAYLOAD")); if (data == NULL) return NULL; uint8_t* mkbpayload = valloc(CFDataGetLength(data)); CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), mkbpayload); int length = CFDataGetLength(data); if (length < 16) { free(mkbpayload); return NULL; } if (AppleEffaceableStorage__getLocker(LOCKER_BAG1, (uint8_t*) &bag1_locker, sizeof(struct BAG1Locker))) { free(mkbpayload); return NULL; } if (bag1_locker.magic != 'BAG1') fprintf(stderr, "AppleKeyStore_loadKeyBag: bad BAG1 magic\n"); size_t decryptedSize = 0; CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, bag1_locker.key, kCCKeySizeAES256, bag1_locker.iv, mkbpayload, length, mkbpayload, length, &decryptedSize); if (cryptStatus != kCCSuccess) { fprintf(stderr, "AppleKeyStore_loadKeyBag CCCrypt kCCDecrypt with BAG1 key failed, return code=%x\n", cryptStatus); free(mkbpayload); return NULL; } CFDataRef data2 = CFDataCreate(kCFAllocatorDefault, mkbpayload, decryptedSize ); if (data2 == NULL) { free(mkbpayload); return NULL; } CFErrorRef e=NULL; CFPropertyListRef plist2 = CFPropertyListCreateWithData(kCFAllocatorDefault, data2, kCFPropertyListImmutable, NULL, &e); if (plist2 == NULL) { fprintf(stderr, "AppleKeyStore_loadKeyBag failed to create plist, AES fail ? decryptedSize=%zx\n", decryptedSize); CFShow(e); } free(mkbpayload); CFRelease(data2); return plist2; } int AppleKeyStoreKeyBagInit() { uint64_t out = 0; uint32_t one = 1; return IOKit_call("AppleKeyStore", kAppleKeyStoreInitUserClient, NULL, 0, NULL, 0, &out, &one, NULL, NULL); } int AppleKeyStoreKeyBagCreateWithData(CFDataRef data, uint64_t* keybagId) { uint32_t outCnt = 1; aes_key_wrap_ctx ctx; uint8_t hmckkey[32] = {0}; int retcode = IOKit_call("AppleKeyStore", kAppleKeyStoreKeyBagCreateWithData, NULL, 0, CFDataGetBytePtr(data), CFDataGetLength(data), keybagId, &outCnt, NULL, NULL ); if (retcode != 0xE00002C9) return retcode; //HAX to load new iOS 7 keybags on previous iOS kernels uint32_t* kbdata = (uint32_t*) CFDataGetBytePtr(data); if ((kbdata[2] == 'SREV') && (kbdata[4] == 0x04000000)) { printf("Patching iOS 7 keybag VERS 4 signature for older kernels\n"); aes_key_wrap_set_key(&ctx, IOAES_key835(), 16); assert(kbdata[0x38/4] == 'KCMH'); assert(!aes_key_unwrap(&ctx, (const uint8_t*) &kbdata[0x38/4 + 2], hmckkey, 4)); assert(kbdata[CFSwapInt32BigToHost(kbdata[1])/4 + 2] == 'NGIS'); CCHmac(kCCHmacAlgSHA1, (const void *) &kbdata[2], CFSwapInt32BigToHost(kbdata[1]), hmckkey, 32, &kbdata[CFSwapInt32BigToHost(kbdata[1])/4 + 2 + 2]); } outCnt = 1; return IOKit_call("AppleKeyStore", kAppleKeyStoreKeyBagCreateWithData, NULL, 0, CFDataGetBytePtr(data), CFDataGetLength(data), keybagId, &outCnt, NULL, NULL ); } int AppleKeyStoreKeyBagSetSystem(uint64_t keybagId) { return IOKit_call("AppleKeyStore", kAppleKeyStoreKeyBagSetSystem, &keybagId, 1, NULL, 0, NULL, NULL, NULL, NULL); } int AppleKeyStoreUnlockDevice(io_connect_t conn, CFDataRef passcode) { return IOConnectCallMethod(conn, kAppleKeyStoreUnlockDevice, NULL, 0, CFDataGetBytePtr(passcode), CFDataGetLength(passcode), NULL, NULL, NULL, NULL); } KeyBag* AppleKeyStore_parseBinaryKeyBag(CFDataRef kb) { const uint8_t* ptr = CFDataGetBytePtr(kb); unsigned int len = CFDataGetLength(kb); struct KeyBagBlobItem* p = (struct KeyBagBlobItem*) ptr; const uint8_t* end; if (p->tag != 'ATAD') { printf("Keybag does not start with DATA\n"); return NULL; } if (8 + CFSwapInt32BigToHost(p->len) > len) { return NULL; } KeyBag* keybag = malloc(sizeof(KeyBag)); if (keybag == NULL) return NULL; memset(keybag, 0, sizeof(KeyBag)); end = ptr + 8 + CFSwapInt32BigToHost(p->len); p = (struct KeyBagBlobItem*) p->data.bytes; int kbuuid=0; int i = -1; while ((uint8_t*)p < end) { //printf("%x\n", p->tag); len = CFSwapInt32BigToHost(p->len); if (p->tag == 'SREV') { keybag->version = CFSwapInt32BigToHost(p->data.intvalue); } else if (p->tag == 'TLAS') { memcpy(keybag->salt, p->data.bytes, 20); } else if (p->tag == 'RETI') { keybag->iter = CFSwapInt32BigToHost(p->data.intvalue); } else if (p->tag == 'DIUU') { if (!kbuuid) { memcpy(keybag->uuid, p->data.bytes, 16); kbuuid = 1; } else { i++; if (i >= MAX_CLASS_KEYS) break; memcpy(keybag->keys[i].uuid, p->data.bytes, 16); } } else if (p->tag == 'SALC') { keybag->keys[i].clas = CFSwapInt32BigToHost(p->data.intvalue); } else if (p->tag == 'PARW' && kbuuid) { keybag->keys[i].wrap = CFSwapInt32BigToHost(p->data.intvalue); } else if (p->tag == 'YKPW') { memcpy(keybag->keys[i].wpky, p->data.bytes, (len > 40) ? 40 : len); } p = (struct KeyBagBlobItem*) &p->data.bytes[len]; } keybag->numKeys = i + 1; return keybag; } void AppleKeyStore_printKeyBag(KeyBag* kb) { int i; printf("Keybag version : %d\n", kb->version); printf("Keybag keys : %d\n", kb->numKeys); printf("Class\tWrap\tKey\n"); for (i=0; i < kb->numKeys; i++) { printf("%d\t%d\t", kb->keys[i].clas, kb->keys[i].wrap); printBytesToHex(kb->keys[i].wpky, kb->keys[i].wrap & 2 ? 40 : 32); printf("\n"); } printf("\n"); } CFMutableDictionaryRef AppleKeyStore_getClassKeys(KeyBag* kb) { int i; CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, kb->numKeys, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef key; for (i=0; i < kb->numKeys; i++) { if(kb->keys[i].wrap == 0) { key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kb->keys[i].clas); addHexaString(dict, key, kb->keys[i].wpky, 32); CFRelease(key); } } return dict; }