]> git.saurik.com Git - apple/security.git/blob - certificates/ota_cert_tool/ios_ota_cert_tool/ValidateAsset.c
Security-57031.40.6.tar.gz
[apple/security.git] / certificates / ota_cert_tool / ios_ota_cert_tool / ValidateAsset.c
1 //
2 // ValidateAsset.c
3 // ios_ota_cert_tool
4 //
5 // Created by James Murphy on 12/13/12.
6 // Copyright (c) 2012 James Murphy. All rights reserved.
7 //
8
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>
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <dirent.h>
17 #include <string.h>
18 #include <unistd.h>
19
20
21 static const char* kPublicManifestKeyData = "MIIBCgKCAQEA7eev+hip+8Vg1kj/q4qnpN37X8vaKZouAyoXZ6gy+2D2wKxR0KORuV9bCFkcyT+LST/Rhn+64YNSZ7UvkQRlU34vZcF7FWuPfEbLGcCG7e1hlshHVUUah+07Qyu82f6OAg8PBFvYwvZHZMcXlvZJQjdNtbIORQfdlrGpRN1C6xKfbX6IKE9LViGQJmljdRuaK/SxmKyMsLfsTCzh+6yxMpPtY75PuSfrVcDSlGhr108QfP5n2WQ9frtyFgdowlXr/kECWSUrj8qDk1JymVd2ZyF3dlTWdzSO17vDt6caQyjQmTyGrGRGOM6THSq9mB/fv1Q5gxEfIIb8SejTvu4GSwIDAQAB";
22
23 static int ValidateFilesInDirectory(const char* dir_path, int num_files, const char * files[])
24 {
25
26 int result = 0; // Assume all is well
27 DIR* dirp = NULL;
28 struct dirent* dp = NULL;
29
30 dirp = opendir(dir_path);
31 if (NULL == dirp)
32 {
33 return -1;
34 }
35
36 for (int iCnt = 0; iCnt < num_files; iCnt++)
37 {
38 int name_length = (int)strlen(files[iCnt]);
39 int found = 0;
40 while (NULL != (dp = readdir(dirp)))
41 {
42 if (dp->d_namlen == name_length && 0 == strcmp(dp->d_name, files[iCnt]))
43 {
44 found = 1;
45 }
46 }
47 if (0 == found)
48 {
49 (void)closedir(dirp);
50
51 return -1;
52 }
53 rewinddir(dirp);
54 }
55 (void)closedir(dirp);
56 return result;
57 }
58
59 static int ReadFileIntoCFDataRef(const char* file_path, CFDataRef* out_data)
60 {
61 int result = -1; // guilt until proven
62 FILE* infile = NULL;
63 void* buffer = NULL;
64 int numbytes = 0;
65
66 if (NULL == file_path || NULL == out_data)
67 {
68 return result;
69 }
70
71 infile = fopen(file_path, "r");
72 if (NULL == infile)
73 {
74 return result;
75 }
76
77 fseek(infile, 0L, SEEK_END);
78 numbytes = (int)ftell(infile);
79
80 fseek(infile, 0L, SEEK_SET);
81 buffer = calloc(numbytes, sizeof(char));
82 if (NULL == buffer)
83 {
84 fclose(infile);
85 return result;
86 }
87
88 fread(buffer, sizeof(char), numbytes, infile);
89 fclose(infile);
90
91 *out_data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)buffer, numbytes);
92 free(buffer);
93 result = (NULL != *out_data) ? 0 : -1;
94 return result;
95 }
96
97 static int CreateHashForData(CFDataRef cfData, int useSHA1, CFDataRef* out_hash)
98 {
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];
103
104 if (NULL == cfData || NULL == out_hash)
105 {
106 return result;
107 }
108
109 *out_hash = NULL;
110
111 memset(buffer, 0, digest_length);
112
113 if (!CCDigest(algo, CFDataGetBytePtr(cfData), CFDataGetLength(cfData), buffer))
114 {
115 *out_hash = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)buffer, digest_length);
116 }
117 else
118 {
119 return result;
120 }
121
122 result = (NULL == *out_hash) ? -1 : 0;
123 return result;
124 }
125
126 static int Base64Data(CFDataRef cfData, int for_encoding, CFDataRef* encoded_data)
127 {
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;
134
135 if (NULL == cfData || NULL == encoded_data)
136 {
137 return result;
138 }
139 memset(buffer, 0, 1024);
140 *encoded_data = NULL;
141
142 status = CNEncode(encoding, direction, CFDataGetBytePtr(cfData), CFDataGetLength(cfData), buffer, &encoded_data_length);
143 if (kCCSuccess == status)
144 {
145 *encoded_data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)buffer, encoded_data_length);
146 result = (NULL == *encoded_data) ? -1 : 0;
147 }
148 return result;
149 }
150
151 static int CreatePropertyListFromData(CFDataRef prop_data, CFTypeID output_type, CFTypeRef* plistRef)
152 {
153 int result = -1; // Guilt until proven
154 CFPropertyListRef aPlistRef = NULL;
155 CFPropertyListFormat list_format = kCFPropertyListXMLFormat_v1_0;
156 CFErrorRef error = NULL;
157
158 if (NULL == prop_data || NULL == plistRef)
159 {
160 return result;
161 }
162
163 *plistRef = NULL;
164
165 aPlistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, prop_data, 0, &list_format, &error);
166 if (NULL != error || NULL == aPlistRef)
167 {
168 if (NULL != error)
169 {
170 CFRelease(error);
171 }
172 return result;
173 }
174
175 if (CFGetTypeID(aPlistRef) != output_type)
176 {
177 CFRelease(aPlistRef);
178 return result;
179 }
180
181 *plistRef = aPlistRef;
182 result = (NULL == *plistRef) ? -1 : 0;
183 return result;
184 }
185
186
187 static int TearOffSignatureAndHashManifest(CFDictionaryRef manifestDict, CFDataRef* signature, CFDataRef* manifest_data)
188 {
189 int result = -1;
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;
195
196 if (NULL == manifestDict || NULL == signature || NULL == manifest_data)
197 {
198 return result;
199 }
200 *signature = NULL;
201 *manifest_data = NULL;
202
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)
206 {
207 CFRelease(new_manifest_dict);
208 return result;
209 }
210
211 sig_data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, sig_data_str, kCFStringEncodingUTF8, 0);
212 if (NULL == sig_data)
213 {
214 CFRelease(sig_data_str);
215 CFRelease(new_manifest_dict);
216 return result;
217 }
218
219 if (Base64Data(sig_data, 0, &decoded_sig_data))
220 {
221 CFRelease(sig_data);
222 CFRelease(new_manifest_dict);
223 return result;
224 }
225
226 *signature = decoded_sig_data;
227
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)
232 {
233 return result;
234 }
235
236 (void)CreateHashForData(prop_list, 1, manifest_data);
237
238 result = (NULL == *manifest_data) ? -1 : 0;
239 return result;
240 }
241
242 static int GetPublicManifestKey(CCRSACryptorRef* key_ref)
243 {
244 int result = -1;
245
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;
250
251 if (NULL == key_ref)
252 {
253 return result;
254 }
255 *key_ref = NULL;
256
257 encoded_key_data_str = CFStringCreateWithCString(kCFAllocatorDefault, kPublicManifestKeyData, kCFStringEncodingUTF8);
258 if (NULL == encoded_key_data_str)
259 {
260 return result;
261 }
262
263 encoded_key_data_str_data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, encoded_key_data_str, kCFStringEncodingUTF8, 0);
264 if (NULL == encoded_key_data_str_data)
265 {
266 CFRelease(encoded_key_data_str);
267 return result;
268 }
269 CFRelease(encoded_key_data_str);
270
271 if (Base64Data(encoded_key_data_str_data, 0, &decoded_key_data))
272 {
273 CFRelease(encoded_key_data_str_data);
274 return result;
275 }
276 CFRelease(encoded_key_data_str_data);
277
278 ccStatus = CCRSACryptorImport(CFDataGetBytePtr(decoded_key_data), CFDataGetLength(decoded_key_data), key_ref);
279 CFRelease(decoded_key_data);
280
281 if (kCCSuccess != ccStatus)
282 {
283 *key_ref = NULL;
284 }
285 else
286 {
287 result = 0;
288 }
289 return result;
290 }
291
292
293 static int ValidateSignature(CFDataRef signature, CFDataRef data)
294 {
295 int result = -1;
296 CCRSACryptorRef key_ref = NULL;
297 CCCryptorStatus ccStatus = kCCSuccess;
298
299
300 if (NULL == signature || NULL == data)
301 {
302 return result;
303 }
304
305 // Get the key
306 if (GetPublicManifestKey(&key_ref))
307 {
308 return result;
309 }
310
311 const void *hash_data_ptr = CFDataGetBytePtr(data);
312 size_t hash_data_len = CFDataGetLength(data);
313
314 const void* sig_data_pre = CFDataGetBytePtr(signature);
315 size_t sig_dat_len = CFDataGetLength(signature);
316
317 ccStatus = CCRSACryptorVerify(
318 key_ref,
319 ccPKCS1Padding,
320 hash_data_ptr,
321 hash_data_len,
322 kCCDigestSHA1,
323 0,
324 sig_data_pre,
325 sig_dat_len);
326
327
328 CCRSACryptorRelease(key_ref);
329
330 result = (kCCSuccess == ccStatus) ? 0 : -1;
331 return result;
332 }
333
334 int ValidateAsset(const char* asset_dir_path, unsigned long current_version)
335 {
336 const char* files[] =
337 {
338 "certs.plist",
339 "distrusted.plist",
340 "EVRoots.plist",
341 "Manifest.plist",
342 "revoked.plist",
343 "roots.plist"
344 };
345 int num_files = (sizeof(files) / sizeof(const char*));
346 int iCnt = 0;
347 const char* file_name = NULL;
348 char wd_buf[1024];
349
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;
362 int iResult = -1;
363
364 // parameter check
365 if (NULL == asset_dir_path)
366 {
367 return iResult;
368 }
369
370 if (ValidateFilesInDirectory(asset_dir_path, num_files, files))
371 {
372 return iResult;
373 }
374
375 if (chdir(asset_dir_path))
376 {
377 return iResult;
378 }
379
380 if (ReadFileIntoCFDataRef("Manifest.plist", &file_data))
381 {
382 (void)chdir(current_working_directory_path);
383 return iResult;
384 }
385
386 if (CreatePropertyListFromData(file_data, CFDictionaryGetTypeID(), (CFTypeRef *)&manifest_dict))
387 {
388 CFRelease(file_data);
389 (void)chdir(current_working_directory_path);
390 return iResult;
391 }
392 CFRelease(file_data);
393
394 // Validate the hash for the files in the manifest
395 for (iCnt = 0; iCnt < num_files; iCnt++)
396 {
397 file_name = files[iCnt];
398 // bypass the manifest file for now
399 if (!strcmp("Manifest.plist", file_name))
400 {
401 continue;
402 }
403
404 if (ReadFileIntoCFDataRef(file_name, &file_data))
405 {
406 CFRelease(manifest_dict);
407 (void)chdir(current_working_directory_path);
408 return iResult;
409 }
410
411 if (CreateHashForData(file_data, 0, &hash_data))
412 {
413 CFRelease(file_data);
414 CFRelease(manifest_dict);
415 (void)chdir(current_working_directory_path);
416 return iResult;
417 }
418 CFRelease(file_data);
419
420
421 if (Base64Data(hash_data, 1, &encoded_hash_data))
422 {
423 CFRelease(hash_data);
424 CFRelease(manifest_dict);
425 (void)chdir(current_working_directory_path);
426 return iResult;
427 }
428 CFRelease(hash_data);
429
430 encoded_hash_str = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, encoded_hash_data, kCFStringEncodingUTF8);
431 if (NULL == encoded_hash_str)
432 {
433 CFRelease(encoded_hash_data);
434 CFRelease(manifest_dict);
435 (void)chdir(current_working_directory_path);
436 return iResult;
437 }
438 CFRelease(encoded_hash_data);
439
440 key_name = CFStringCreateWithCString(kCFAllocatorDefault, file_name, kCFStringEncodingUTF8);
441 if (NULL == key_name)
442 {
443 CFRelease(encoded_hash_str);
444 CFRelease(manifest_dict);
445 (void)chdir(current_working_directory_path);
446 return iResult;
447 }
448
449 manifest_hash_data_str = (CFStringRef)CFDictionaryGetValue(manifest_dict, key_name);
450 if (NULL == manifest_hash_data_str)
451 {
452 CFRelease(key_name);
453 CFRelease(encoded_hash_str);
454 CFRelease(manifest_dict);
455 (void)chdir(current_working_directory_path);
456 return iResult;
457 }
458 CFRelease(key_name);
459
460 if (!CFEqual(encoded_hash_str, manifest_hash_data_str))
461 {
462 CFRelease(encoded_hash_str);
463 CFRelease(manifest_dict);
464 (void)chdir(current_working_directory_path);
465 return iResult;
466 }
467 CFRelease(encoded_hash_str);
468 }
469
470 // Get the version
471 manifest_version = (CFNumberRef)CFDictionaryGetValue(manifest_dict, CFSTR("Version"));
472 if (NULL == manifest_version)
473 {
474 CFRelease(manifest_dict);
475 (void)chdir(current_working_directory_path);
476 return iResult;
477 }
478
479 if (!CFNumberGetValue(manifest_version, kCFNumberLongType, &manifest_verson_number))
480 {
481 CFRelease(manifest_version);
482 CFRelease(manifest_dict);
483 (void)chdir(current_working_directory_path);
484 return iResult;
485 }
486 CFRelease(manifest_version);
487 if (manifest_verson_number < current_version)
488 {
489 CFRelease(manifest_dict);
490 (void)chdir(current_working_directory_path);
491 return iResult;
492 }
493
494 // Deal with the signature
495 if (TearOffSignatureAndHashManifest(manifest_dict, &signature_data, &manifest_data))
496 {
497 CFRelease(manifest_dict);
498 (void)chdir(current_working_directory_path);
499 return iResult;
500 }
501
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);
507 return iResult;
508 }