]> git.saurik.com Git - apple/security.git/blob - certificates/ota_cert_tool/ios_ota_cert_tool/PSIOSCertToolApp.m
Security-57031.30.12.tar.gz
[apple/security.git] / certificates / ota_cert_tool / ios_ota_cert_tool / PSIOSCertToolApp.m
1 //
2 // PSIOSCertToolApp.m
3 // ios_ota_cert_tool
4 //
5 // Created by James Murphy on 12/11/12.
6 // Copyright (c) 2012 James Murphy. All rights reserved.
7 //
8
9 #import "PSIOSCertToolApp.h"
10 #import "PSCerts.h"
11 #import "PSUtilities.h"
12 #import "ValidateAsset.h"
13 #import <stdio.h>
14
15
16 @interface PSIOSCertToolApp (PrivateMethods)
17
18 - (void)usage;
19 - (NSString*)checkPath:(NSString*)name basePath:(NSString *)basePath isDirectory:(BOOL)isDirectory;
20 - (BOOL)outputPlistToPath:(NSString *)path withData:(id)data;
21 - (BOOL)buildManifest:(NSString *)path;
22 - (NSNumber*)getNextVersionNumber;
23 @end
24
25 @implementation PSIOSCertToolApp
26
27 @synthesize app_name = _app_name;
28 @synthesize root_directory = _root_directory;
29 @synthesize revoked_directory = _revoked_directory;
30 @synthesize distrusted_directory = _distrusted_directory;
31 @synthesize certs_directory = _certs_directory;
32 @synthesize ev_plist_path = _ev_plist_path;
33 @synthesize info_plist_path = _info_plist_path;
34 @synthesize top_level_directory = _top_level_directory;
35 @synthesize output_directory = _output_directory;
36
37
38 - (id)init:(int)argc withArguments:(const char**)argv
39 {
40 if ((self = [super init]))
41 {
42 _app_name = [[NSString alloc] initWithUTF8String:argv[0]];
43 _root_directory = nil;
44 _revoked_directory = nil;
45 _distrusted_directory = nil;
46 _certs_directory = nil;
47 _ev_plist_path = nil;
48 _info_plist_path = nil;
49 _top_level_directory = nil;
50 _output_directory = nil;
51
52 _roots = nil;
53 _revoked = nil;
54 _distrusted = nil;
55 _certs = nil;
56 _plist_name_array = [NSArray arrayWithObjects:@"roots.plist", @"revoked.plist", @"distrusted.plist", @"certs.plist", nil];
57
58 for (int iCnt = 1; iCnt < argc; iCnt++)
59 {
60 const char* arg = argv[iCnt];
61 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
62 {
63 [self usage];
64 return nil;
65 }
66 else if (!strcmp(arg, "-r") || !strcmp(arg, "--roots_dir"))
67 {
68 if ((iCnt + 1) == argc)
69 {
70 [self usage];
71 return nil;
72 }
73
74 _root_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
75 iCnt++;
76
77 }
78 else if (!strcmp(arg, "-k") || !strcmp(arg, "--revoked_dir"))
79 {
80 if ((iCnt + 1) == argc)
81 {
82 [self usage];
83 return nil;
84 }
85
86 _revoked_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
87 iCnt++;
88 }
89 else if (!strcmp(arg, "-d") || !strcmp(arg, "--distrusted_dir"))
90 {
91 if ((iCnt + 1) == argc)
92 {
93 [self usage];
94 return nil;
95 }
96
97 _distrusted_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
98 iCnt++;
99 }
100 else if (!strcmp(arg, "-c") || !strcmp(arg, "--certs_dir"))
101 {
102 if ((iCnt + 1) == argc)
103 {
104 [self usage];
105 return nil;
106 }
107
108 _certs_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
109 iCnt++;
110 }
111 else if (!strcmp(arg, "-e") || !strcmp(arg, "--ev_plist_path"))
112 {
113 if ((iCnt + 1) == argc)
114 {
115 [self usage];
116 return nil;
117 }
118
119 _ev_plist_path = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
120 iCnt++;
121 }
122
123 else if (!strcmp(arg, "-i") || !strcmp(arg, "--info_plist_path"))
124 {
125 if ((iCnt + 1) == argc)
126 {
127 [self usage];
128 return nil;
129 }
130
131 _info_plist_path = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
132 iCnt++;
133 }
134 else if (!strcmp(arg, "-t") || !strcmp(arg, "--top_level_directory"))
135 {
136 if ((iCnt + 1) == argc)
137 {
138 [self usage];
139 return nil;
140 }
141
142 _top_level_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
143 iCnt++;
144 }
145 else if (!strcmp(arg, "-o") || !strcmp(arg, "--output_directory"))
146 {
147 if ((iCnt + 1) == argc)
148 {
149 [self usage];
150 return nil;
151 }
152
153 _output_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
154 iCnt++;
155 }
156 }
157
158 if (nil == _root_directory)
159 {
160 _root_directory = [self checkPath:@"roots" basePath:_top_level_directory isDirectory:YES];
161 if (nil == _root_directory)
162 {
163 [self usage];
164 return nil;
165 }
166 }
167
168 if (nil == _revoked_directory)
169 {
170 _revoked_directory = [self checkPath:@"revoked" basePath:_top_level_directory isDirectory:YES];
171 if (nil == _revoked_directory)
172 {
173 [self usage];
174 return nil;
175 }
176 }
177
178 if (nil == _distrusted_directory)
179 {
180 _distrusted_directory = [self checkPath:@"distrusted" basePath:_top_level_directory isDirectory:YES];
181 if (nil == _distrusted_directory)
182 {
183 [self usage];
184 return nil;
185 }
186 }
187
188 if (nil == _certs_directory)
189 {
190 _certs_directory = [self checkPath:@"certs" basePath:_top_level_directory isDirectory:YES];
191 if (nil == _certs_directory)
192 {
193 [self usage];
194 return nil;
195 }
196 }
197
198 if (nil == _ev_plist_path)
199 {
200 _ev_plist_path = [self checkPath:@"EVRoots/EVRoots.plist" basePath:_top_level_directory isDirectory:NO];
201 if (nil == _ev_plist_path)
202 {
203 [self usage];
204 return nil;
205 }
206 }
207 if (nil == _info_plist_path)
208 {
209 _info_plist_path = [self checkPath:@"assetData/Info.plist" basePath:_top_level_directory isDirectory:NO];
210 if (nil == _info_plist_path)
211 {
212 [self usage];
213 return nil;
214 }
215 }
216 }
217 return self;
218 }
219
220 - (void)usage
221 {
222 printf("%s usage:\n", [self.app_name UTF8String]);
223 printf(" [-h, --help] \tPrint out this help message\n");
224 printf(" [-r, --roots_dir] \tThe full path to the directory with the certificate roots\n");
225 printf(" [-k, --revoked_dir] \tThe full path to the directory with the revoked certificates\n");
226 printf(" [-d, --distrusted_dir] \tThe full path to the directory with the distrusted certificates\n");
227 printf(" [-c, --certs_dir] \tThe full path to the directory with the cert certificates\n");
228 printf(" [-e, --ev_plist_path] \tThe full path to the EVRoots.plist file\n");
229 printf(" [-i, --info_plist_path]) \tThe full path to the Infor.plist file\n");
230 printf(" [-t, --top_level_directory] \tThe full path to the top level security_certificates directory\n");
231 printf(" [-o, --output_directory] \tThe full path to the directory to write out the results\n");
232 printf("\n");
233 }
234
235 - (NSString*)checkPath:(NSString*)name basePath:(NSString *)basePath isDirectory:(BOOL)isDirectory
236 {
237 NSString* result = nil;
238 if (nil == name)
239 {
240 return result;
241 }
242
243 NSFileManager* fileManager = [NSFileManager defaultManager];
244 BOOL isDir = NO;
245
246 if ([name hasPrefix:@"/"] || [name hasPrefix:@"~"])
247 {
248 name = [name hasPrefix:@"~"] ? [name stringByExpandingTildeInPath] : name;
249 // This is a full path
250 if (![fileManager fileExistsAtPath:name isDirectory:&isDir] || isDir != isDirectory)
251 {
252 NSLog(@"%@ is invalid", name);
253 return result;
254 }
255 result = name;
256 }
257 else
258 {
259 NSString* full_path = nil;
260 if (nil == basePath)
261 {
262 NSLog(@"%@ is not a full path but basePath is nil", name);
263 return result;
264 }
265
266 full_path = [basePath stringByAppendingPathComponent:name];
267 if (![fileManager fileExistsAtPath:full_path isDirectory:&isDir] || isDir != isDirectory)
268 {
269 NSLog(@"%@ is invalid", full_path);
270 return result;
271 }
272 result = full_path;
273 }
274 return result;
275 }
276
277
278 - (BOOL)processCertificates
279 {
280 BOOL result = NO;
281
282 NSString* path = self.root_directory;
283 PSCerts* root_certs = [[PSCerts alloc] initWithCertFilePath:path forBadCerts:NO];
284 _roots = root_certs.certs;
285
286 path = self.revoked_directory;
287 PSCerts* revoked_certs = [[PSCerts alloc] initWithCertFilePath:path forBadCerts:YES];
288 _revoked = revoked_certs.certs;
289
290 path = self.distrusted_directory;
291 PSCerts* distrusted_certs = [[PSCerts alloc] initWithCertFilePath:path forBadCerts:YES];
292 _distrusted = distrusted_certs.certs;
293
294 path = self.certs_directory;
295 PSCerts* certs_certs = [[PSCerts alloc] initWithCertFilePath:path forBadCerts:NO];
296 _certs = certs_certs.certs;
297
298 result = (nil != _roots && nil != _revoked && nil != _distrusted && nil != _certs);
299 return result;
300 }
301
302 - (BOOL)outputPlistsToDirectory
303 {
304 BOOL result = NO;
305
306 NSString* path = self.output_directory;
307
308 NSFileManager* fileManager = [NSFileManager defaultManager];
309 BOOL isDir = NO;
310 NSError* error = nil;
311
312 if (![fileManager fileExistsAtPath:path isDirectory:&isDir])
313 {
314 // The directory does not exist so make it.
315 if (![fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error])
316 {
317 return result;
318 }
319
320 }
321
322 // Now make all of the plists files
323 if (nil == _roots || nil == _revoked || nil == _distrusted || nil == _certs)
324 {
325 return result;
326 }
327
328 NSArray* plist_data = [NSArray arrayWithObjects:_roots, _revoked, _distrusted, _certs, nil];
329 NSDictionary* plist_input = [NSDictionary dictionaryWithObjects:plist_data forKeys:_plist_name_array];
330 NSEnumerator* dict_enum = [plist_input keyEnumerator];
331
332 for (NSString* file_name in dict_enum)
333 {
334 NSString* full_path = [path stringByAppendingPathComponent:file_name];
335 NSArray* data = [plist_input objectForKey:file_name];
336
337 NSMutableDictionary* rootObj = [NSMutableDictionary dictionaryWithCapacity:1];
338 NSString* key_str = [file_name stringByDeletingPathExtension];
339 [rootObj setObject:data forKey:key_str];
340
341 if (![self outputPlistToPath:full_path withData:rootObj])
342 {
343 NSLog(@"Failed to write out plist for %@#", file_name);
344 }
345 }
346
347 // Now output the EVRoots plist
348 NSString* full_path = [path stringByAppendingPathComponent:@"EVRoots.plist"];
349 if (![fileManager copyItemAtPath:_ev_plist_path toPath:full_path error:&error])
350 {
351 NSLog(@"Failed to copy the EVRoots.plist file");
352 }
353
354 // And the Info plist
355 full_path = [path stringByAppendingPathComponent:@"Info.plist"];
356 if (![fileManager copyItemAtPath:_info_plist_path toPath:full_path error:&error])
357 {
358 NSLog(@"Failed to copy the Info.plist file");
359 }
360
361
362 return [self buildManifest:path];
363 }
364
365 - (BOOL)outputPlistToPath:(NSString *)path withData:(id)data
366 {
367 BOOL result = NO;
368 NSError* error = nil;
369
370
371 NSData* prop_data = [NSPropertyListSerialization dataWithPropertyList:data format:NSPropertyListXMLFormat_v1_0 options:0 error:&error];
372 if (nil != prop_data)
373 {
374 result = [prop_data writeToFile:path atomically:NO];
375 }
376 return result;
377 }
378
379 - (NSNumber*)getNextVersionNumber
380 {
381 // This needs to read from a file location and then update the
382 // version number. For now just hard code and I'll fix this later
383 NSNumber* result = nil;
384 NSUInteger version = 100;
385 result = [NSNumber numberWithUnsignedInteger:version];
386
387 return result;
388 }
389
390
391 - (BOOL)buildManifest:(NSString *)path
392 {
393 BOOL result = NO;
394 NSMutableDictionary* manifest_dict = [NSMutableDictionary dictionary];
395
396 NSNumber* versionNumber = [self getNextVersionNumber];
397 [manifest_dict setObject:versionNumber forKey:@"Version"];
398
399 for (NSString* plist_file_path in _plist_name_array)
400 {
401 NSString* full_path = [self checkPath:plist_file_path basePath:path isDirectory:NO];
402 if (nil == full_path)
403 {
404 NSLog(@"Could not find the %@ file", plist_file_path);
405 return result;
406 }
407
408 CFDataRef dataRef = [PSUtilities readFile:full_path];
409 if (NULL == dataRef)
410 {
411 NSLog(@"Could not read the file %@", plist_file_path);
412 return result;
413 }
414
415
416
417 NSString* plist_file_data = [PSUtilities digestAndEncode:dataRef useSHA1:NO];
418 CFRelease(dataRef);
419 if (nil == plist_file_data)
420 {
421 NSLog(@"Could not hash the file %@", plist_file_path);
422 return result;
423 }
424
425 [manifest_dict setObject:plist_file_data forKey:plist_file_path];
426 }
427
428 // Now add the EVRoots plist
429 NSString* evRoots_path = [self checkPath:@"EVRoots.plist" basePath:path isDirectory:NO];
430 if (nil == evRoots_path)
431 {
432 NSLog(@"Could not find the EVRoots.plist file");
433 return result;
434 }
435
436 CFDataRef dataRef = [PSUtilities readFile:evRoots_path];
437 if (NULL == dataRef)
438 {
439 NSLog(@"Could not read the file %@", evRoots_path);
440 return result;
441 }
442
443 NSString* ev_plist_file_data = [PSUtilities digestAndEncode:dataRef useSHA1:NO];
444 CFRelease(dataRef);
445 if (nil == ev_plist_file_data)
446 {
447 NSLog(@"Could not hash the file %@", ev_plist_file_data);
448 return result;
449 }
450
451 [manifest_dict setObject:ev_plist_file_data forKey:@"EVRoots.plist"];
452
453
454 NSString* full_path = [path stringByAppendingPathComponent:@"Manifest.plist"];
455 if (![self outputPlistToPath:full_path withData:manifest_dict])
456 {
457 NSLog(@"Unable to write out the Manifest plist");
458 return result;
459 }
460
461 CFDataRef manifest_file_data = [PSUtilities readFile:full_path];
462 if (NULL == manifest_file_data)
463 {
464 NSLog(@"Unable to read in the Manifest plist");
465 return result;
466 }
467
468 SecKeyRef signing_key = [PSUtilities getPrivateKeyWithName:@"Manifest Private Key"];
469
470 if (NULL == signing_key)
471 {
472 NSLog(@"Unable to get the signing key");
473 return result;
474 }
475
476 NSString* signature = [PSUtilities signAndEncode:manifest_file_data usingKey:signing_key useSHA1:YES];
477 if (nil == signature)
478 {
479 NSLog(@"Unable to sign the manifest data");
480 return result;
481 }
482
483 [manifest_dict setObject:signature forKey:@"Signature"];
484
485 if (![self outputPlistToPath:full_path withData:manifest_dict])
486 {
487 NSLog(@"Unable to write out the Manifest plist");
488 return result;
489 }
490 return YES;
491 }
492
493 - (BOOL)validate
494 {
495 BOOL result = NO;
496
497 NSString* path = self.output_directory;
498
499 result = (ValidateAsset([path UTF8String], 99)) ? NO : YES;
500
501 return result;
502 }
503 @end