5 // Created by James Murphy on 12/13/12.
6 // Copyright (c) 2012 James Murphy. All rights reserved.
9 #include <CoreFoundation/CoreFoundation.h>
10 #include <CommonCrypto/CommonDigest.h>
11 #include <CommonCrypto/CommonDigestSPI.h>
12 #include <CommonCrypto/CommonRSACryptor.h>
13 #include <CommonNumerics/CommonBaseXX.h>
15 #include <sys/types.h>
21 static const char* kPublicManifestKeyData
= "MIIBCgKCAQEA7eev+hip+8Vg1kj/q4qnpN37X8vaKZouAyoXZ6gy+2D2wKxR0KORuV9bCFkcyT+LST/Rhn+64YNSZ7UvkQRlU34vZcF7FWuPfEbLGcCG7e1hlshHVUUah+07Qyu82f6OAg8PBFvYwvZHZMcXlvZJQjdNtbIORQfdlrGpRN1C6xKfbX6IKE9LViGQJmljdRuaK/SxmKyMsLfsTCzh+6yxMpPtY75PuSfrVcDSlGhr108QfP5n2WQ9frtyFgdowlXr/kECWSUrj8qDk1JymVd2ZyF3dlTWdzSO17vDt6caQyjQmTyGrGRGOM6THSq9mB/fv1Q5gxEfIIb8SejTvu4GSwIDAQAB";
23 static int ValidateFilesInDirectory(const char* dir_path
, int num_files
, const char * files
[])
26 int result
= 0; // Assume all is well
28 struct dirent
* dp
= NULL
;
30 dirp
= opendir(dir_path
);
36 for (int iCnt
= 0; iCnt
< num_files
; iCnt
++)
38 int name_length
= (int)strlen(files
[iCnt
]);
40 while (NULL
!= (dp
= readdir(dirp
)))
42 if (dp
->d_namlen
== name_length
&& 0 == strcmp(dp
->d_name
, files
[iCnt
]))
59 static int ReadFileIntoCFDataRef(const char* file_path
, CFDataRef
* out_data
)
61 int result
= -1; // guilt until proven
66 if (NULL
== file_path
|| NULL
== out_data
)
71 infile
= fopen(file_path
, "r");
77 fseek(infile
, 0L, SEEK_END
);
78 numbytes
= (int)ftell(infile
);
80 fseek(infile
, 0L, SEEK_SET
);
81 buffer
= calloc(numbytes
, sizeof(char));
88 fread(buffer
, sizeof(char), numbytes
, infile
);
91 *out_data
= CFDataCreate(kCFAllocatorDefault
, (const UInt8
*)buffer
, numbytes
);
93 result
= (NULL
!= *out_data
) ? 0 : -1;
97 static int CreateHashForData(CFDataRef cfData
, int useSHA1
, CFDataRef
* out_hash
)
99 int result
= -1; // Guilty until proven
100 CCDigestAlgorithm algo
= (useSHA1
) ? kCCDigestSHA1
:kCCDigestSHA256
;
101 size_t digest_length
= (useSHA1
) ? CC_SHA1_DIGEST_LENGTH
: CC_SHA256_DIGEST_LENGTH
;
102 UInt8 buffer
[digest_length
];
104 if (NULL
== cfData
|| NULL
== out_hash
)
111 memset(buffer
, 0, digest_length
);
113 if (!CCDigest(algo
, CFDataGetBytePtr(cfData
), CFDataGetLength(cfData
), buffer
))
115 *out_hash
= CFDataCreate(kCFAllocatorDefault
, (const UInt8
*)buffer
, digest_length
);
122 result
= (NULL
== *out_hash
) ? -1 : 0;
126 static int Base64Data(CFDataRef cfData
, int for_encoding
, CFDataRef
* encoded_data
)
128 int result
= -1; // Guilty until proven
129 CNEncodings encoding
= kCNEncodingBase64
;
130 CNStatus status
= kCCSuccess
;
131 CNEncodingDirection direction
= (for_encoding
) ? kCNEncode
: kCNDecode
;
132 unsigned char buffer
[1024];
133 size_t encoded_data_length
= 1024;
135 if (NULL
== cfData
|| NULL
== encoded_data
)
139 memset(buffer
, 0, 1024);
140 *encoded_data
= NULL
;
142 status
= CNEncode(encoding
, direction
, CFDataGetBytePtr(cfData
), CFDataGetLength(cfData
), buffer
, &encoded_data_length
);
143 if (kCCSuccess
== status
)
145 *encoded_data
= CFDataCreate(kCFAllocatorDefault
, (const UInt8
*)buffer
, encoded_data_length
);
146 result
= (NULL
== *encoded_data
) ? -1 : 0;
151 static int CreatePropertyListFromData(CFDataRef prop_data
, CFTypeID output_type
, CFTypeRef
* plistRef
)
153 int result
= -1; // Guilt until proven
154 CFPropertyListRef aPlistRef
= NULL
;
155 CFPropertyListFormat list_format
= kCFPropertyListXMLFormat_v1_0
;
156 CFErrorRef error
= NULL
;
158 if (NULL
== prop_data
|| NULL
== plistRef
)
165 aPlistRef
= CFPropertyListCreateWithData(kCFAllocatorDefault
, prop_data
, 0, &list_format
, &error
);
166 if (NULL
!= error
|| NULL
== aPlistRef
)
175 if (CFGetTypeID(aPlistRef
) != output_type
)
177 CFRelease(aPlistRef
);
181 *plistRef
= aPlistRef
;
182 result
= (NULL
== *plistRef
) ? -1 : 0;
187 static int TearOffSignatureAndHashManifest(CFDictionaryRef manifestDict
, CFDataRef
* signature
, CFDataRef
* manifest_data
)
190 CFMutableDictionaryRef new_manifest_dict
= NULL
;
191 CFStringRef sig_data_str
= NULL
;
192 CFDataRef sig_data
= NULL
;
193 CFDataRef prop_list
= NULL
;
194 CFDataRef decoded_sig_data
= NULL
;
196 if (NULL
== manifestDict
|| NULL
== signature
|| NULL
== manifest_data
)
201 *manifest_data
= NULL
;
203 new_manifest_dict
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, CFDictionaryGetCount(manifestDict
), manifestDict
);
204 sig_data_str
= (CFStringRef
)CFDictionaryGetValue(new_manifest_dict
, CFSTR("Signature"));
205 if (NULL
== sig_data_str
)
207 CFRelease(new_manifest_dict
);
211 sig_data
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, sig_data_str
, kCFStringEncodingUTF8
, 0);
212 if (NULL
== sig_data
)
214 CFRelease(sig_data_str
);
215 CFRelease(new_manifest_dict
);
219 if (Base64Data(sig_data
, 0, &decoded_sig_data
))
222 CFRelease(new_manifest_dict
);
226 *signature
= decoded_sig_data
;
228 CFDictionaryRemoveValue(new_manifest_dict
, CFSTR("Signature"));
229 prop_list
= CFPropertyListCreateXMLData (kCFAllocatorDefault
, new_manifest_dict
);
230 CFRelease(new_manifest_dict
);
231 if (NULL
== prop_list
)
236 (void)CreateHashForData(prop_list
, 1, manifest_data
);
238 result
= (NULL
== *manifest_data
) ? -1 : 0;
242 static int GetPublicManifestKey(CCRSACryptorRef
* key_ref
)
246 CFStringRef encoded_key_data_str
= NULL
;
247 CFDataRef encoded_key_data_str_data
= NULL
;
248 CFDataRef decoded_key_data
= NULL
;
249 CCCryptorStatus ccStatus
= kCCSuccess
;
257 encoded_key_data_str
= CFStringCreateWithCString(kCFAllocatorDefault
, kPublicManifestKeyData
, kCFStringEncodingUTF8
);
258 if (NULL
== encoded_key_data_str
)
263 encoded_key_data_str_data
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, encoded_key_data_str
, kCFStringEncodingUTF8
, 0);
264 if (NULL
== encoded_key_data_str_data
)
266 CFRelease(encoded_key_data_str
);
269 CFRelease(encoded_key_data_str
);
271 if (Base64Data(encoded_key_data_str_data
, 0, &decoded_key_data
))
273 CFRelease(encoded_key_data_str_data
);
276 CFRelease(encoded_key_data_str_data
);
278 ccStatus
= CCRSACryptorImport(CFDataGetBytePtr(decoded_key_data
), CFDataGetLength(decoded_key_data
), key_ref
);
279 CFRelease(decoded_key_data
);
281 if (kCCSuccess
!= ccStatus
)
293 static int ValidateSignature(CFDataRef signature
, CFDataRef data
)
296 CCRSACryptorRef key_ref
= NULL
;
297 CCCryptorStatus ccStatus
= kCCSuccess
;
300 if (NULL
== signature
|| NULL
== data
)
306 if (GetPublicManifestKey(&key_ref
))
311 const void *hash_data_ptr
= CFDataGetBytePtr(data
);
312 size_t hash_data_len
= CFDataGetLength(data
);
314 const void* sig_data_pre
= CFDataGetBytePtr(signature
);
315 size_t sig_dat_len
= CFDataGetLength(signature
);
317 ccStatus
= CCRSACryptorVerify(
328 CCRSACryptorRelease(key_ref
);
330 result
= (kCCSuccess
== ccStatus
) ? 0 : -1;
334 int ValidateAsset(const char* asset_dir_path
, unsigned long current_version
)
336 const char* files
[] =
345 int num_files
= (sizeof(files
) / sizeof(const char*));
347 const char* file_name
= NULL
;
350 const char* current_working_directory_path
= getcwd(wd_buf
, 1024);
351 CFDataRef file_data
= NULL
;
352 CFDataRef hash_data
= NULL
;
353 CFDataRef encoded_hash_data
= NULL
;
354 CFStringRef manifest_hash_data_str
= NULL
;
355 CFDataRef signature_data
= NULL
;
356 CFStringRef key_name
= NULL
;
357 CFDictionaryRef manifest_dict
= NULL
;
358 CFNumberRef manifest_version
= NULL
;
359 CFStringRef encoded_hash_str
= NULL
;
360 CFDataRef manifest_data
= NULL
;
361 unsigned long manifest_verson_number
;
365 if (NULL
== asset_dir_path
)
370 if (ValidateFilesInDirectory(asset_dir_path
, num_files
, files
))
375 if (chdir(asset_dir_path
))
380 if (ReadFileIntoCFDataRef("Manifest.plist", &file_data
))
382 (void)chdir(current_working_directory_path
);
386 if (CreatePropertyListFromData(file_data
, CFDictionaryGetTypeID(), (CFTypeRef
*)&manifest_dict
))
388 CFRelease(file_data
);
389 (void)chdir(current_working_directory_path
);
392 CFRelease(file_data
);
394 // Validate the hash for the files in the manifest
395 for (iCnt
= 0; iCnt
< num_files
; iCnt
++)
397 file_name
= files
[iCnt
];
398 // bypass the manifest file for now
399 if (!strcmp("Manifest.plist", file_name
))
404 if (ReadFileIntoCFDataRef(file_name
, &file_data
))
406 CFRelease(manifest_dict
);
407 (void)chdir(current_working_directory_path
);
411 if (CreateHashForData(file_data
, 0, &hash_data
))
413 CFRelease(file_data
);
414 CFRelease(manifest_dict
);
415 (void)chdir(current_working_directory_path
);
418 CFRelease(file_data
);
421 if (Base64Data(hash_data
, 1, &encoded_hash_data
))
423 CFRelease(hash_data
);
424 CFRelease(manifest_dict
);
425 (void)chdir(current_working_directory_path
);
428 CFRelease(hash_data
);
430 encoded_hash_str
= CFStringCreateFromExternalRepresentation(kCFAllocatorDefault
, encoded_hash_data
, kCFStringEncodingUTF8
);
431 if (NULL
== encoded_hash_str
)
433 CFRelease(encoded_hash_data
);
434 CFRelease(manifest_dict
);
435 (void)chdir(current_working_directory_path
);
438 CFRelease(encoded_hash_data
);
440 key_name
= CFStringCreateWithCString(kCFAllocatorDefault
, file_name
, kCFStringEncodingUTF8
);
441 if (NULL
== key_name
)
443 CFRelease(encoded_hash_str
);
444 CFRelease(manifest_dict
);
445 (void)chdir(current_working_directory_path
);
449 manifest_hash_data_str
= (CFStringRef
)CFDictionaryGetValue(manifest_dict
, key_name
);
450 if (NULL
== manifest_hash_data_str
)
453 CFRelease(encoded_hash_str
);
454 CFRelease(manifest_dict
);
455 (void)chdir(current_working_directory_path
);
460 if (!CFEqual(encoded_hash_str
, manifest_hash_data_str
))
462 CFRelease(encoded_hash_str
);
463 CFRelease(manifest_dict
);
464 (void)chdir(current_working_directory_path
);
467 CFRelease(encoded_hash_str
);
471 manifest_version
= (CFNumberRef
)CFDictionaryGetValue(manifest_dict
, CFSTR("Version"));
472 if (NULL
== manifest_version
)
474 CFRelease(manifest_dict
);
475 (void)chdir(current_working_directory_path
);
479 if (!CFNumberGetValue(manifest_version
, kCFNumberLongType
, &manifest_verson_number
))
481 CFRelease(manifest_version
);
482 CFRelease(manifest_dict
);
483 (void)chdir(current_working_directory_path
);
486 CFRelease(manifest_version
);
487 if (manifest_verson_number
< current_version
)
489 CFRelease(manifest_dict
);
490 (void)chdir(current_working_directory_path
);
494 // Deal with the signature
495 if (TearOffSignatureAndHashManifest(manifest_dict
, &signature_data
, &manifest_data
))
497 CFRelease(manifest_dict
);
498 (void)chdir(current_working_directory_path
);
502 iResult
= ValidateSignature(signature_data
, manifest_data
);
503 CFRelease(signature_data
);
504 CFRelease(manifest_data
);
505 CFRelease(manifest_dict
);
506 (void)chdir(current_working_directory_path
);