5 // Created by James Murphy on 12/11/12.
6 // Copyright (c) 2012 James Murphy. All rights reserved.
9 #import "PSIOSCertToolApp.h"
11 #import "PSUtilities.h"
12 #import "ValidateAsset.h"
16 @interface PSIOSCertToolApp (PrivateMethods)
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;
25 @implementation PSIOSCertToolApp
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;
38 - (id)init:(int)argc withArguments:(const char**)argv
40 if ((self = [super init]))
42 _app_name = [[NSString alloc] initWithUTF8String:argv[0]];
43 _root_directory = nil;
44 _revoked_directory = nil;
45 _distrusted_directory = nil;
46 _certs_directory = nil;
48 _info_plist_path = nil;
49 _top_level_directory = nil;
50 _output_directory = nil;
56 _plist_name_array = [NSArray arrayWithObjects:@"roots.plist", @"revoked.plist", @"distrusted.plist", @"certs.plist", nil];
58 for (int iCnt = 1; iCnt < argc; iCnt++)
60 const char* arg = argv[iCnt];
61 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
66 else if (!strcmp(arg, "-r") || !strcmp(arg, "--roots_dir"))
68 if ((iCnt + 1) == argc)
74 _root_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
78 else if (!strcmp(arg, "-k") || !strcmp(arg, "--revoked_dir"))
80 if ((iCnt + 1) == argc)
86 _revoked_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
89 else if (!strcmp(arg, "-d") || !strcmp(arg, "--distrusted_dir"))
91 if ((iCnt + 1) == argc)
97 _distrusted_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
100 else if (!strcmp(arg, "-c") || !strcmp(arg, "--certs_dir"))
102 if ((iCnt + 1) == argc)
108 _certs_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
111 else if (!strcmp(arg, "-e") || !strcmp(arg, "--ev_plist_path"))
113 if ((iCnt + 1) == argc)
119 _ev_plist_path = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
123 else if (!strcmp(arg, "-i") || !strcmp(arg, "--info_plist_path"))
125 if ((iCnt + 1) == argc)
131 _info_plist_path = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
134 else if (!strcmp(arg, "-t") || !strcmp(arg, "--top_level_directory"))
136 if ((iCnt + 1) == argc)
142 _top_level_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
145 else if (!strcmp(arg, "-o") || !strcmp(arg, "--output_directory"))
147 if ((iCnt + 1) == argc)
153 _output_directory = [[NSString stringWithUTF8String:argv[iCnt + 1]] stringByExpandingTildeInPath];
158 if (nil == _root_directory)
160 _root_directory = [self checkPath:@"roots" basePath:_top_level_directory isDirectory:YES];
161 if (nil == _root_directory)
168 if (nil == _revoked_directory)
170 _revoked_directory = [self checkPath:@"revoked" basePath:_top_level_directory isDirectory:YES];
171 if (nil == _revoked_directory)
178 if (nil == _distrusted_directory)
180 _distrusted_directory = [self checkPath:@"distrusted" basePath:_top_level_directory isDirectory:YES];
181 if (nil == _distrusted_directory)
188 if (nil == _certs_directory)
190 _certs_directory = [self checkPath:@"certs" basePath:_top_level_directory isDirectory:YES];
191 if (nil == _certs_directory)
198 if (nil == _ev_plist_path)
200 _ev_plist_path = [self checkPath:@"EVRoots/EVRoots.plist" basePath:_top_level_directory isDirectory:NO];
201 if (nil == _ev_plist_path)
207 if (nil == _info_plist_path)
209 _info_plist_path = [self checkPath:@"assetData/Info.plist" basePath:_top_level_directory isDirectory:NO];
210 if (nil == _info_plist_path)
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");
235 - (NSString*)checkPath:(NSString*)name basePath:(NSString *)basePath isDirectory:(BOOL)isDirectory
237 NSString* result = nil;
243 NSFileManager* fileManager = [NSFileManager defaultManager];
246 if ([name hasPrefix:@"/"] || [name hasPrefix:@"~"])
248 name = [name hasPrefix:@"~"] ? [name stringByExpandingTildeInPath] : name;
249 // This is a full path
250 if (![fileManager fileExistsAtPath:name isDirectory:&isDir] || isDir != isDirectory)
252 NSLog(@"%@ is invalid", name);
259 NSString* full_path = nil;
262 NSLog(@"%@ is not a full path but basePath is nil", name);
266 full_path = [basePath stringByAppendingPathComponent:name];
267 if (![fileManager fileExistsAtPath:full_path isDirectory:&isDir] || isDir != isDirectory)
269 NSLog(@"%@ is invalid", full_path);
278 - (BOOL)processCertificates
282 NSString* path = self.root_directory;
283 PSCerts* root_certs = [[PSCerts alloc] initWithCertFilePath:path forBadCerts:NO];
284 _roots = root_certs.certs;
286 path = self.revoked_directory;
287 PSCerts* revoked_certs = [[PSCerts alloc] initWithCertFilePath:path forBadCerts:YES];
288 _revoked = revoked_certs.certs;
290 path = self.distrusted_directory;
291 PSCerts* distrusted_certs = [[PSCerts alloc] initWithCertFilePath:path forBadCerts:YES];
292 _distrusted = distrusted_certs.certs;
294 path = self.certs_directory;
295 PSCerts* certs_certs = [[PSCerts alloc] initWithCertFilePath:path forBadCerts:NO];
296 _certs = certs_certs.certs;
298 result = (nil != _roots && nil != _revoked && nil != _distrusted && nil != _certs);
302 - (BOOL)outputPlistsToDirectory
306 NSString* path = self.output_directory;
308 NSFileManager* fileManager = [NSFileManager defaultManager];
310 NSError* error = nil;
312 if (![fileManager fileExistsAtPath:path isDirectory:&isDir])
314 // The directory does not exist so make it.
315 if (![fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error])
322 // Now make all of the plists files
323 if (nil == _roots || nil == _revoked || nil == _distrusted || nil == _certs)
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];
332 for (NSString* file_name in dict_enum)
334 NSString* full_path = [path stringByAppendingPathComponent:file_name];
335 NSArray* data = [plist_input objectForKey:file_name];
337 NSMutableDictionary* rootObj = [NSMutableDictionary dictionaryWithCapacity:1];
338 NSString* key_str = [file_name stringByDeletingPathExtension];
339 [rootObj setObject:data forKey:key_str];
341 if (![self outputPlistToPath:full_path withData:rootObj])
343 NSLog(@"Failed to write out plist for %@#", file_name);
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])
351 NSLog(@"Failed to copy the EVRoots.plist file");
354 // And the Info plist
355 full_path = [path stringByAppendingPathComponent:@"Info.plist"];
356 if (![fileManager copyItemAtPath:_info_plist_path toPath:full_path error:&error])
358 NSLog(@"Failed to copy the Info.plist file");
362 return [self buildManifest:path];
365 - (BOOL)outputPlistToPath:(NSString *)path withData:(id)data
368 NSError* error = nil;
371 NSData* prop_data = [NSPropertyListSerialization dataWithPropertyList:data format:NSPropertyListXMLFormat_v1_0 options:0 error:&error];
372 if (nil != prop_data)
374 result = [prop_data writeToFile:path atomically:NO];
379 - (NSNumber*)getNextVersionNumber
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];
391 - (BOOL)buildManifest:(NSString *)path
394 NSMutableDictionary* manifest_dict = [NSMutableDictionary dictionary];
396 NSNumber* versionNumber = [self getNextVersionNumber];
397 [manifest_dict setObject:versionNumber forKey:@"Version"];
399 for (NSString* plist_file_path in _plist_name_array)
401 NSString* full_path = [self checkPath:plist_file_path basePath:path isDirectory:NO];
402 if (nil == full_path)
404 NSLog(@"Could not find the %@ file", plist_file_path);
408 CFDataRef dataRef = [PSUtilities readFile:full_path];
411 NSLog(@"Could not read the file %@", plist_file_path);
417 NSString* plist_file_data = [PSUtilities digestAndEncode:dataRef useSHA1:NO];
419 if (nil == plist_file_data)
421 NSLog(@"Could not hash the file %@", plist_file_path);
425 [manifest_dict setObject:plist_file_data forKey:plist_file_path];
428 // Now add the EVRoots plist
429 NSString* evRoots_path = [self checkPath:@"EVRoots.plist" basePath:path isDirectory:NO];
430 if (nil == evRoots_path)
432 NSLog(@"Could not find the EVRoots.plist file");
436 CFDataRef dataRef = [PSUtilities readFile:evRoots_path];
439 NSLog(@"Could not read the file %@", evRoots_path);
443 NSString* ev_plist_file_data = [PSUtilities digestAndEncode:dataRef useSHA1:NO];
445 if (nil == ev_plist_file_data)
447 NSLog(@"Could not hash the file %@", ev_plist_file_data);
451 [manifest_dict setObject:ev_plist_file_data forKey:@"EVRoots.plist"];
454 NSString* full_path = [path stringByAppendingPathComponent:@"Manifest.plist"];
455 if (![self outputPlistToPath:full_path withData:manifest_dict])
457 NSLog(@"Unable to write out the Manifest plist");
461 CFDataRef manifest_file_data = [PSUtilities readFile:full_path];
462 if (NULL == manifest_file_data)
464 NSLog(@"Unable to read in the Manifest plist");
468 SecKeyRef signing_key = [PSUtilities getPrivateKeyWithName:@"Manifest Private Key"];
470 if (NULL == signing_key)
472 NSLog(@"Unable to get the signing key");
476 NSString* signature = [PSUtilities signAndEncode:manifest_file_data usingKey:signing_key useSHA1:YES];
477 if (nil == signature)
479 NSLog(@"Unable to sign the manifest data");
483 [manifest_dict setObject:signature forKey:@"Signature"];
485 if (![self outputPlistToPath:full_path withData:manifest_dict])
487 NSLog(@"Unable to write out the Manifest plist");
497 NSString* path = self.output_directory;
499 result = (ValidateAsset([path UTF8String], 99)) ? NO : YES;