CFDictionaryRef APCreateDictionaryForLicenseData(CFDataRef data) { if (!rsaKey->n || !rsaKey->e) return NULL; // Make the property list from the data CFStringRef errorString = NULL; CFPropertyListRef propertyList; propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, data, kCFPropertyListMutableContainers, &errorString); if (errorString || CFDictionaryGetTypeID() != CFGetTypeID(propertyList) || !CFPropertyListIsValid(propertyList, kCFPropertyListXMLFormat_v1_0)) { if (propertyList) CFRelease(propertyList); return NULL; } // Load the signature CFMutableDictionaryRef licenseDictionary = (CFMutableDictionaryRef)propertyList; if (!CFDictionaryContainsKey(licenseDictionary, CFSTR("Signature"))) { CFRelease(licenseDictionary); return NULL; } CFDataRef sigData = CFDictionaryGetValue(licenseDictionary, CFSTR("Signature")); CFIndex sigDataLength = CFDataGetLength(sigData); UInt8 sigBytes[sigDataLength]; CFDataGetBytes(sigData, CFRangeMake(0, sigDataLength), sigBytes); CFDictionaryRemoveValue(licenseDictionary, CFSTR("Signature")); // Decrypt the signature int checkDigestMaxSize = RSA_size(rsaKey)-11; unsigned char checkDigest[checkDigestMaxSize]; if (RSA_public_decrypt((int) sigDataLength, sigBytes, checkDigest, rsaKey, RSA_PKCS1_PADDING) != SHA_DIGEST_LENGTH) { CFRelease(licenseDictionary); return NULL; } // Get the license hash CFMutableStringRef hashCheck = CFStringCreateMutable(kCFAllocatorDefault,0); int hashIndex; for (hashIndex = 0; hashIndex < SHA_DIGEST_LENGTH; hashIndex++) CFStringAppendFormat(hashCheck, nil, CFSTR("%02x"), checkDigest[hashIndex]); APSetHash(hashCheck); CFRelease(hashCheck); if (blacklist && (CFArrayContainsValue(blacklist, CFRangeMake(0, CFArrayGetCount(blacklist)), hash) == true)) return NULL; // Get the number of elements CFIndex count = CFDictionaryGetCount(licenseDictionary); // Load the keys and build up the key array CFMutableArrayRef keyArray = CFArrayCreateMutable(kCFAllocatorDefault, count, NULL); CFStringRef keys[count]; CFDictionaryGetKeysAndValues(licenseDictionary, (const void**)&keys, NULL); int i; for (i = 0; i < count; i++) CFArrayAppendValue(keyArray, keys[i]); // Sort the array int context = kCFCompareCaseInsensitive; CFArraySortValues(keyArray, CFRangeMake(0, count), (CFComparatorFunction)CFStringCompare, &context); // Setup up the hash context SHA_CTX ctx; SHA1_Init(&ctx); // Convert into UTF8 strings for (i = 0; i < count; i++) { char *valueBytes; CFIndex valueLengthAsUTF8; CFStringRef key = CFArrayGetValueAtIndex(keyArray, i); CFStringRef value = CFDictionaryGetValue(licenseDictionary, key); // Account for the null terminator valueLengthAsUTF8 = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), kCFStringEncodingUTF8) + 1; valueBytes = (char *)malloc(valueLengthAsUTF8); CFStringGetCString(value, valueBytes, valueLengthAsUTF8, kCFStringEncodingUTF8); SHA1_Update(&ctx, valueBytes, strlen(valueBytes)); free(valueBytes); } unsigned char digest[SHA_DIGEST_LENGTH]; SHA1_Final(digest, &ctx); if (keyArray != NULL) CFRelease(keyArray); // Check if the signature is a match for (i = 0; i < SHA_DIGEST_LENGTH; i++) { if (checkDigest[i] ^ digest[i]) { CFRelease(licenseDictionary); return NULL; } } // If it's a match, we return the dictionary; otherwise, we never reach this return licenseDictionary; }
CFDictionaryRef APCreateDictionaryForLicenseData(CFDataRef data) { __block CFPropertyListRef propertyList = NULL; __block CFDataRef hashData = NULL; __block CFErrorRef error = NULL; __block SecTransformRef verifyFunction = NULL; __block CFBooleanRef valid = NULL; void(^cleanup)(void) = ^(void) { if (propertyList != NULL) { CFRelease(propertyList); propertyList = NULL; } if (hashData != NULL) { CFRelease(hashData); hashData = NULL; } if (error != NULL) { CFShow(error); CFRelease(error); error = NULL; } if (verifyFunction != NULL) { CFRelease(verifyFunction); verifyFunction = NULL; } if (valid != NULL) { CFRelease(valid); valid = NULL; } }; if (!publicKeyRef) { CFShow(CFSTR("Public key is invalid")); return NULL; } // Make the property list from the data CFStringRef errorString = NULL; propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, data, kCFPropertyListMutableContainers, &errorString); if (errorString || CFDictionaryGetTypeID() != CFGetTypeID(propertyList) || !CFPropertyListIsValid(propertyList, kCFPropertyListXMLFormat_v1_0)) { if (propertyList) CFRelease(propertyList); return NULL; } CFMutableDictionaryRef licenseDictionary = (CFMutableDictionaryRef)propertyList; CFDataRef signature = CFDictionaryGetValue(licenseDictionary, CFSTR("Signature")); if (!signature) { CFShow(CFSTR("No signature")); cleanup(); return NULL; } hashData = APCreateHashFromDictionary(licenseDictionary); CFStringRef hashCheck = APCopyHexStringFromData(hashData); APSetHash(hashCheck); CFRelease(hashCheck); // Check the hash against license blacklist if (blacklist && CFArrayContainsValue(blacklist, CFRangeMake(0, CFArrayGetCount(blacklist)), hash)) { cleanup(); return NULL; } // Verify the signed hash using the public key, passing the raw hash data as the input verifyFunction = SecVerifyTransformCreate(publicKeyRef, signature, &error); if (error) { cleanup(); return NULL; } SecTransformSetAttribute(verifyFunction, kSecTransformInputAttributeName, hashData, &error); if (error) { cleanup(); return NULL; } SecTransformSetAttribute(verifyFunction, kSecInputIsAttributeName, kSecInputIsRaw, &error); if (error) { cleanup(); return NULL; } valid = SecTransformExecute(verifyFunction, &error); if (error) { cleanup(); return NULL; } if (valid != kCFBooleanTrue) { cleanup(); return NULL; } CFDictionaryRef resultDict = CFDictionaryCreateCopy(kCFAllocatorDefault, licenseDictionary); cleanup(); return resultDict; }