// Things below are for utilities to drive and/or test the system
- (NSString*)getSysdiagnoseDumpWithError:(NSError**)error;
-- (NSData*)getLoggingJSONWithError:(NSError**)error;
+- (NSData*)getLoggingJSON:(bool)pretty error:(NSError**)error;
- (BOOL)forceUploadWithError:(NSError**)error;
// --------------------------------
#import "CKKSViewManager.h"
#import "debugging.h"
#import <objc/runtime.h>
+#import <os/variant_private.h>
+#import <CoreFoundation/CFPriv.h>
NSString* const SFAnalyticsLoggerTableSuccessCount = @"success_count";
NSString* const SFAnalyticsLoggerColumnEventType = @"event_type";
NSString* const SFAnalyticsLoggerSplunkEventTime = @"eventTime";
NSString* const SFAnalyticsLoggerSplunkPostTime = @"postTime";
NSString* const SFAnalyticsLoggerSplunkEventType = @"eventType";
+NSString* const SFAnalyticsLoggerSplunkEventBuild = @"build";
+NSString* const SFAnalyticsLoggerSplunkEventProduct = @"product";
+
NSString* const SFAnalyticsLoggerMetricsBase = @"metricsBase";
NSString* const SFAnalyticsLoggerEventClassKey = @"eventClass";
+
NSString* const SFAnalyticsUserDefaultsSuite = @"com.apple.security.analytics";
static NSString* const SFAnalyticsLoggerTableSchema = @"CREATE TABLE IF NOT EXISTS hard_failures (\n"
#define SFANALYTICS_SPLUNK_DEV 0
#define SFANALYTICS_MAX_EVENTS_TO_REPORT 999
+#define SECONDS_PER_DAY (60 * 60 * 24)
+
#if SFANALYTICS_SPLUNK_DEV
-#define SECONDS_BETWEEN_UPLOADS 10
+#define SECONDS_BETWEEN_UPLOADS_CUSTOMER 10
+#define SECONDS_BETWEEN_UPLOADS_INTERNAL 10
#else
-// three days = 60 seconds times 60 minutes * 72 hours
-#define SECONDS_BETWEEN_UPLOADS (60 * 60 * 72)
+#define SECONDS_BETWEEN_UPLOADS_CUSTOMER (3 * SECONDS_PER_DAY)
+#define SECONDS_BETWEEN_UPLOADS_INTERNAL (SECONDS_PER_DAY)
#endif
-#define SECONDS_PER_DAY (60 * 60 * 24)
-
typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
SFAnalyticsEventClassSuccess,
SFAnalyticsEventClassHardFailure,
- (NSInteger)softFailureCountForEventType:(NSString*)eventType;
- (void)addEventDict:(NSDictionary*)eventDict toTable:(NSString*)table;
- (void)clearAllData;
+- (BOOL)tryToOpenDatabase;
- (NSDictionary*)summaryCounts;
if (self = [super init]) {
_database = [SFAnalyticsLoggerSQLiteStore storeWithPath:self.class.databasePath schema:SFAnalyticsLoggerTableSchema];
_queue = dispatch_queue_create("com.apple.security.analytics", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
- _secondsBetweenUploads = SECONDS_BETWEEN_UPLOADS;
+
+ if (os_variant_has_internal_diagnostics("Security")) {
+ _secondsBetweenUploads = SECONDS_BETWEEN_UPLOADS_INTERNAL;
+ } else {
+ _secondsBetweenUploads = SECONDS_BETWEEN_UPLOADS_CUSTOMER;
+ }
NSDictionary* systemDefaultValues = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle bundleWithPath:@"/System/Library/Frameworks/Security.framework"] pathForResource:@"SFAnalyticsLogging" ofType:@"plist"]];
_splunkTopicName = systemDefaultValues[@"splunk_topic"];
- (void)logHardFailureForEventNamed:(NSString*)eventName withAttributes:(NSDictionary*)attributes
{
- [self logEventNamed:eventName class:SFAnalyticsEventClassSoftFailure attributes:attributes];
+ [self logEventNamed:eventName class:SFAnalyticsEventClassHardFailure attributes:attributes];
}
- (void)logSoftFailureForEventNamed:(NSString*)eventName withAttributes:(NSDictionary*)attributes
{
- [self logEventNamed:eventName class:SFAnalyticsEventClassHardFailure attributes:attributes];
+ [self logEventNamed:eventName class:SFAnalyticsEventClassSoftFailure attributes:attributes];
}
- (void)noteEventNamed:(NSString*)eventName
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
__block NSError* error = nil;
- NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
- NSURLSession* storeBagSession = [NSURLSession sessionWithConfiguration:defaultConfiguration
+ NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
+
+ configuration.HTTPAdditionalHeaders = @{ @"User-Agent" : [NSString stringWithFormat:@"securityd/%s", SECURITY_BUILD_VERSION]};
+
+ NSURLSession* storeBagSession = [NSURLSession sessionWithConfiguration:configuration
delegate:self
delegateQueue:nil];
- (BOOL)forceUploadWithError:(NSError**)error
{
__block BOOL result = NO;
- NSData* json = [self getLoggingJSONWithError:error];
+ NSData* json = [self getLoggingJSON:false error: error];
dispatch_sync(_queue, ^{
if (json && [self _onQueuePostJSON:json error:error]) {
secinfo("ckks", "uploading sync health data: %@", json);
* Create the NSURLSession
* We use the ephemeral session config because we don't need cookies or cache
*/
- NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
- NSURLSession* postSession = [NSURLSession sessionWithConfiguration:defaultConfiguration
+ NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
+
+ configuration.HTTPAdditionalHeaders = @{ @"User-Agent" : [NSString stringWithFormat:@"securityd/%s", SECURITY_BUILD_VERSION]};
+
+ NSURLSession* postSession = [NSURLSession sessionWithConfiguration:configuration
delegate:self
delegateQueue:nil];
- (NSString*)getSysdiagnoseDumpWithError:(NSError**)error
{
NSMutableString* sysdiagnose = [[NSMutableString alloc] init];
-
NSDictionary* extraValues = self.extraValuesToUploadToServer;
[extraValues enumerateKeysAndObjectsUsingBlock:^(NSString* key, id object, BOOL* stop) {
[sysdiagnose appendFormat:@"Key: %@, Value: %@\n", key, object];
return sysdiagnose;
}
-- (NSData*)getLoggingJSONWithError:(NSError**)error
++ (void)addOSVersion:(NSMutableDictionary *)event
+{
+ static dispatch_once_t onceToken;
+ static NSString *build = NULL;
+ static NSString *product = NULL;
+ dispatch_once(&onceToken, ^{
+ NSDictionary *version = CFBridgingRelease(_CFCopySystemVersionDictionary());
+ if (version == NULL)
+ return;
+ build = version[(__bridge NSString *)_kCFSystemVersionBuildVersionKey];
+ product = version[(__bridge NSString *)_kCFSystemVersionProductNameKey];
+ });
+ if (build)
+ event[SFAnalyticsLoggerSplunkEventBuild] = build;
+ if (product)
+ event[SFAnalyticsLoggerSplunkEventProduct] = product;
+}
+
+- (NSData*)getLoggingJSON:(bool)pretty error:(NSError**)error
{
__block NSData* json = nil;
NSDictionary* extraValues = self.extraValuesToUploadToServer;
dispatch_sync(_queue, ^{
+ if (![self->_database tryToOpenDatabase]) {
+ // we should not even be here because uploadDate was nil. But since we are, let's get out of here.
+ // Returning nil here will abort the upload (but again, the uploadDate should've done that already)
+ secerror("can't get logging JSON because database is not openable");
+ if (error) {
+ *error = [NSError errorWithDomain:@"SFAnalyticsLogger" code:-1 userInfo:@{NSLocalizedDescriptionKey : @"could not open db to read and process metrics (device in class D?)"}];
+ }
+ return;
+ }
+
NSArray* failureRecords = self->_database.failureRecords;
NSDictionary* successCounts = self->_database.summaryCounts;
healthSummaryEvent[SFAnalyticsLoggerColumnSuccessCount] = @(totalSuccessCount);
healthSummaryEvent[SFAnalyticsLoggerColumnHardFailureCount] = @(totalHardFailureCount);
healthSummaryEvent[SFAnalyticsLoggerColumnSoftFailureCount] = @(totalSoftFailureCount);
+ [SFAnalyticsLogger addOSVersion:healthSummaryEvent];
NSMutableArray* splunkRecords = failureRecords.mutableCopy;
[splunkRecords addObject:healthSummaryEvent];
- NSDictionary* jsonDict = @{SFAnalyticsLoggerSplunkPostTime : @([now timeIntervalSince1970] * 1000), @"events" : splunkRecords};
+ NSDictionary* jsonDict = @{
+ SFAnalyticsLoggerSplunkPostTime : @([now timeIntervalSince1970] * 1000),
+ @"events" : splunkRecords
+ };
- json = [NSJSONSerialization dataWithJSONObject:jsonDict options:NSJSONWritingPrettyPrinted error:error];
+ json = [NSJSONSerialization dataWithJSONObject:jsonDict
+ options:(pretty ? NSJSONWritingPrettyPrinted : 0)
+ error:error];
});
return json;
loggingStores[standardizedPath] = store;
}
- [store open];
+ NSError* error = nil;
+ if (![store openWithError:&error]) {
+ secerror("SFAnalyticsLogger: could not open db at init, will try again later. Error: %@", error);
+ }
+
}
return store;
[self close];
}
+- (BOOL)tryToOpenDatabase
+{
+ if (!self.isOpen) {
+ secwarning("SFAnalyticsLogger: db is closed, attempting to open");
+ NSError* error = nil;
+ if (![self openWithError:&error]) {
+ secerror("SFAnalyticsLogger: failed to open db with error %@", error);
+ return NO;
+ }
+ }
+ return YES;
+}
+
- (NSInteger)successCountForEventType:(NSString*)eventType
{
- return [[[[self select:@[SFAnalyticsLoggerColumnSuccessCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnSuccessCount] integerValue];
+ if ([self tryToOpenDatabase]) {
+ return [[[[self select:@[SFAnalyticsLoggerColumnSuccessCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnSuccessCount] integerValue];
+ }
+ return 0;
}
- (void)incrementSuccessCountForEventType:(NSString*)eventType
{
- NSInteger successCount = [self successCountForEventType:eventType];
- NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType];
- NSInteger softFailureCount = [self softFailureCountForEventType:eventType];
- [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount + 1), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount)}];
+ if ([self tryToOpenDatabase]) {
+ NSInteger successCount = [self successCountForEventType:eventType];
+ NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType];
+ NSInteger softFailureCount = [self softFailureCountForEventType:eventType];
+ [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount + 1), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount)}];
+ }
}
- (NSInteger)hardFailureCountForEventType:(NSString*)eventType
{
- return [[[[self select:@[SFAnalyticsLoggerColumnHardFailureCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnHardFailureCount] integerValue];
+ if ([self tryToOpenDatabase]) {
+ return [[[[self select:@[SFAnalyticsLoggerColumnHardFailureCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnHardFailureCount] integerValue];
+ }
+ return 0;
}
- (NSInteger)softFailureCountForEventType:(NSString*)eventType
{
- return [[[[self select:@[SFAnalyticsLoggerColumnSoftFailureCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnSoftFailureCount] integerValue];
+ if ([self tryToOpenDatabase]) {
+ return [[[[self select:@[SFAnalyticsLoggerColumnSoftFailureCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnSoftFailureCount] integerValue];
+ }
+ return 0;
}
- (void)incrementHardFailureCountForEventType:(NSString*)eventType
{
- NSInteger successCount = [self successCountForEventType:eventType];
- NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType];
- NSInteger softFailureCount = [self softFailureCountForEventType:eventType];
- [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount + 1), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount)}];
+ if ([self tryToOpenDatabase]) {
+ NSInteger successCount = [self successCountForEventType:eventType];
+ NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType];
+ NSInteger softFailureCount = [self softFailureCountForEventType:eventType];
+ [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount + 1), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount)}];
+ }
}
- (void)incrementSoftFailureCountForEventType:(NSString*)eventType
{
- NSInteger successCount = [self successCountForEventType:eventType];
- NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType];
- NSInteger softFailureCount = [self softFailureCountForEventType:eventType];
- [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount + 1)}];
+ if ([self tryToOpenDatabase]) {
+ NSInteger successCount = [self successCountForEventType:eventType];
+ NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType];
+ NSInteger softFailureCount = [self softFailureCountForEventType:eventType];
+ [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount + 1)}];
+ }
}
- (NSDictionary*)summaryCounts
{
- NSMutableDictionary* successCountsDict = [NSMutableDictionary dictionary];
- NSArray* rows = [self selectAllFrom:SFAnalyticsLoggerTableSuccessCount where:nil bindings:nil];
- for (NSDictionary* rowDict in rows) {
- NSString* eventName = rowDict[SFAnalyticsLoggerColumnEventType];
- if (!eventName) {
- secinfo("SFAnalytics", "ignoring entry in success counts table without an event name");
- continue;
- }
+ if ([self tryToOpenDatabase]) {
+ NSMutableDictionary* successCountsDict = [NSMutableDictionary dictionary];
+ NSArray* rows = [self selectAllFrom:SFAnalyticsLoggerTableSuccessCount where:nil bindings:nil];
+ for (NSDictionary* rowDict in rows) {
+ NSString* eventName = rowDict[SFAnalyticsLoggerColumnEventType];
+ if (!eventName) {
+ secinfo("SFAnalytics", "ignoring entry in success counts table without an event name");
+ continue;
+ }
- successCountsDict[eventName] = @{SFAnalyticsLoggerTableSuccessCount : rowDict[SFAnalyticsLoggerColumnSuccessCount], SFAnalyticsLoggerColumnHardFailureCount : rowDict[SFAnalyticsLoggerColumnHardFailureCount], SFAnalyticsLoggerColumnSoftFailureCount : rowDict[SFAnalyticsLoggerColumnSoftFailureCount]};
+ successCountsDict[eventName] = @{SFAnalyticsLoggerTableSuccessCount : rowDict[SFAnalyticsLoggerColumnSuccessCount], SFAnalyticsLoggerColumnHardFailureCount : rowDict[SFAnalyticsLoggerColumnHardFailureCount], SFAnalyticsLoggerColumnSoftFailureCount : rowDict[SFAnalyticsLoggerColumnSoftFailureCount]};
+ }
+ return successCountsDict;
}
-
- return successCountsDict;
+ return [NSDictionary new];
}
- (NSArray*)failureRecords
{
- NSArray* recordBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableHardFailures];
- if (recordBlobs.count < SFANALYTICS_MAX_EVENTS_TO_REPORT) {
- NSArray* softFailureBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableSoftFailures];
- if (softFailureBlobs.count > 0) {
- NSInteger numSoftFailuresToReport = SFANALYTICS_MAX_EVENTS_TO_REPORT - recordBlobs.count;
- recordBlobs = [recordBlobs arrayByAddingObjectsFromArray:[softFailureBlobs subarrayWithRange:NSMakeRange(softFailureBlobs.count - numSoftFailuresToReport, numSoftFailuresToReport)]];
+ if ([self tryToOpenDatabase]) {
+ NSArray* recordBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableHardFailures];
+ if (recordBlobs.count < SFANALYTICS_MAX_EVENTS_TO_REPORT) {
+ NSArray* softFailureBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableSoftFailures];
+ if (softFailureBlobs.count > 0) {
+ NSUInteger numSoftFailuresToReport = SFANALYTICS_MAX_EVENTS_TO_REPORT - recordBlobs.count;
+ if (numSoftFailuresToReport > softFailureBlobs.count)
+ numSoftFailuresToReport = softFailureBlobs.count;
+
+ recordBlobs = [recordBlobs arrayByAddingObjectsFromArray:[softFailureBlobs subarrayWithRange:NSMakeRange(softFailureBlobs.count - numSoftFailuresToReport, numSoftFailuresToReport)]];
+ }
}
- }
- NSMutableArray* failureRecords = [[NSMutableArray alloc] init];
- for (NSDictionary* row in recordBlobs) {
- NSDictionary* deserializedRecord = [NSPropertyListSerialization propertyListWithData:row[SFAnalyticsLoggerColumnData] options:0 format:nil error:nil];
- [failureRecords addObject:deserializedRecord];
+ NSMutableArray* failureRecords = [[NSMutableArray alloc] init];
+ for (NSDictionary* row in recordBlobs) {
+ NSMutableDictionary* deserializedRecord = [NSPropertyListSerialization propertyListWithData:row[SFAnalyticsLoggerColumnData] options:NSPropertyListMutableContainers format:nil error:nil];
+ [SFAnalyticsLogger addOSVersion:deserializedRecord];
+ [failureRecords addObject:deserializedRecord];
+ }
+ return failureRecords;
}
-
- return failureRecords;
+ return [NSArray new];
}
- (NSArray*)allEvents
{
- NSArray* recordBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableAllEvents];
- NSMutableArray* records = [[NSMutableArray alloc] init];
- for (NSDictionary* row in recordBlobs) {
- NSDictionary* deserializedRecord = [NSPropertyListSerialization propertyListWithData:row[SFAnalyticsLoggerColumnData] options:0 format:nil error:nil];
- [records addObject:deserializedRecord];
+ if ([self tryToOpenDatabase]) {
+ NSArray* recordBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableAllEvents];
+ NSMutableArray* records = [[NSMutableArray alloc] init];
+ for (NSDictionary* row in recordBlobs) {
+ NSDictionary* deserializedRecord = [NSPropertyListSerialization propertyListWithData:row[SFAnalyticsLoggerColumnData] options:0 format:nil error:nil];
+ [records addObject:deserializedRecord];
+ }
+ return records;
}
- return records;
+ return [NSArray new];
}
- (void)addEventDict:(NSDictionary*)eventDict toTable:(NSString*)table
{
- NSError* error = nil;
- NSData* serializedRecord = [NSPropertyListSerialization dataWithPropertyList:eventDict format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error];
- if(!error && serializedRecord) {
- [self insertOrReplaceInto:table values:@{SFAnalyticsLoggerColumnDate : [NSDate date], SFAnalyticsLoggerColumnData : serializedRecord}];
- }
- if(error && !serializedRecord) {
- secerror("Couldn't serialize failure record: %@", error);
+ if ([self tryToOpenDatabase]) {
+ NSError* error = nil;
+ NSData* serializedRecord = [NSPropertyListSerialization dataWithPropertyList:eventDict format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error];
+ if(!error && serializedRecord) {
+ [self insertOrReplaceInto:table values:@{SFAnalyticsLoggerColumnDate : [NSDate date], SFAnalyticsLoggerColumnData : serializedRecord}];
+ }
+ if(error && !serializedRecord) {
+ secerror("Couldn't serialize failure record: %@", error);
+ }
}
}
+// the other returning methods give default values in case of closed db,
+// but this needs to be nil so the comparison to 'now' fails and we don't upload
- (NSDate*)uploadDate
{
- return [self datePropertyForKey:SFAnalyticsLoggerUploadDate];
+ if ([self tryToOpenDatabase]) {
+ return [self datePropertyForKey:SFAnalyticsLoggerUploadDate];
+ }
+ return nil;
}
- (void)setUploadDate:(NSDate*)uploadDate
{
- [self setDateProperty:uploadDate forKey:SFAnalyticsLoggerUploadDate];
+ if ([self tryToOpenDatabase]) {
+ [self setDateProperty:uploadDate forKey:SFAnalyticsLoggerUploadDate];
+ }
}
- (void)clearAllData
{
- [self deleteFrom:SFAnalyticsLoggerTableSuccessCount where:@"event_type like ?" bindings:@[@"%"]];
- [self deleteFrom:SFAnalyticsLoggerTableHardFailures where:@"id >= 0" bindings:nil];
- [self deleteFrom:SFAnalyticsLoggerTableSoftFailures where:@"id >= 0" bindings:nil];
+ if ([self tryToOpenDatabase]) {
+ [self deleteFrom:SFAnalyticsLoggerTableSuccessCount where:@"event_type like ?" bindings:@[@"%"]];
+ [self deleteFrom:SFAnalyticsLoggerTableHardFailures where:@"id >= 0" bindings:nil];
+ [self deleteFrom:SFAnalyticsLoggerTableSoftFailures where:@"id >= 0" bindings:nil];
+ [self deleteFrom:SFAnalyticsLoggerTableAllEvents where:@"id >= 0" bindings:nil];
+ }
}
@end
- (void)analyze;
- (void)vacuum;
-// Raise an exception. Including any database error in the description and removing the databse if it's corrupt.
-- (void)raise:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
-
// The rowID assigned to the last record inserted into the database.
- (SFSQLiteRowID)lastInsertRowID;
- (int)changes;
// Execute one-or-more queries. Use prepared statements for anything performance critical.
-- (void)executeSQL:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
-- (void)executeSQL:(NSString *)format arguments:(va_list)args NS_FORMAT_FUNCTION(1, 0);
+- (BOOL)executeSQL:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
+- (BOOL)executeSQL:(NSString *)format arguments:(va_list)args NS_FORMAT_FUNCTION(1, 0);
// Prepared statement pool accessors. Statements must be reset after they're used.
- (SFSQLiteStatement *)statementForSQL:(NSString *)SQL;
#import "SFSQLiteStatement.h"
#include <sqlite3.h>
#include <CommonCrypto/CommonDigest.h>
-
+#import "debugging.h"
#define kSFSQLiteBusyTimeout (5*60*1000)
@" value text\n"
@");\n";
-@interface SFSQLiteError : NSObject
-+ (void)raise:(NSString *)reason code:(int)code extended:(int)extended;
-@end
-
NSArray *SFSQLiteJournalSuffixes() {
return @[@"-journal", @"-wal", @"-shm"];
}
// You don't argue with the Ben: rdar://12685305
- [self executeSQL:@"pragma journal_mode = WAL"];
- [self executeSQL:@"pragma synchronous = %@", [self _synchronousModeString]];
- [self executeSQL:@"pragma auto_vacuum = FULL"];
+ if (![self executeSQL:@"pragma journal_mode = WAL"]) {
+ goto done;
+ }
+ if (![self executeSQL:@"pragma synchronous = %@", [self _synchronousModeString]]) {
+ goto done;
+ }
+ if (![self executeSQL:@"pragma auto_vacuum = FULL"]) {
+ goto done;
+ }
// rdar://problem/32168789
// [self executeSQL:@"pragma foreign_keys = 1"];
success = YES;
done:
+ if (!success) {
+ sqlite3_close_v2(_db);
+ _db = nil;
+ }
+
if (!success && error) {
+ if (!localError) {
+ localError = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithFormat:@"Error opening db at %@, ", _path]}];
+ }
*error = localError;
}
return success;
- (void)open {
NSError *error;
if (![self openWithError:&error]) {
- [self raise:@"Error opening db at %@: %@", self.path, error];
+ secerror("sfsqlite: Error opening db at %@: %@", self.path, error);
+ return;
}
}
[self removeAllStatements];
if (sqlite3_close(_db)) {
- [self raise:@"Error closing database"];
+ secerror("sfsqlite: Error closing database");
+ return;
}
_db = NULL;
}
[self executeSQL:@"vacuum"];
}
-- (void)raise:(NSString *)format, ... {
- va_list args;
- va_start(args, format);
-
- NSString *reason = [[NSString alloc] initWithFormat:format arguments:args];
-
- int code = 0;
- int extendedCode = 0;
- if (_db) {
- code = sqlite3_errcode(_db) & 0xFF;
- extendedCode = sqlite3_extended_errcode(_db);
- const char *errmsg = sqlite3_errmsg(_db);
-
- NSDictionary *dbAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:NULL];
- NSDictionary *fsAttrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:self.path error:NULL];
- reason = [reason stringByAppendingFormat:@" - errcode:%04x, msg:\"%s\", size: %@, path:%@, fs:%@/%@", extendedCode, errmsg, dbAttrs[NSFileSize], _path, fsAttrs[NSFileSystemFreeSize], fsAttrs[NSFileSystemSize]];
-
- if (!_corrupt && (code == SQLITE_CORRUPT || code == SQLITE_NOTADB)) {
- _corrupt = YES;
-
- @try {
- [self close];
- } @catch (NSException *x) {
- NSLog(@"Warn: Error closing corrupt db: %@", x);
- }
-
- [self remove];
- }
- }
-
- va_end(args);
-
- [SFSQLiteError raise:reason code:code extended:extendedCode];
-}
-
- (SFSQLiteRowID)lastInsertRowID {
if (!_db) {
- [self raise:@"Database is closed"];
+ secerror("sfsqlite: Database is closed");
+ return -1;
}
return sqlite3_last_insert_rowid(_db);
- (int)changes
{
if (!_db) {
- [self raise:@"Database is closed"];
+ secerror("sfsqlite: Database is closed");
+ return -1;
}
return sqlite3_changes(_db);
}
-- (void)executeSQL:(NSString *)format, ... {
+- (BOOL)executeSQL:(NSString *)format, ... {
va_list args;
va_start(args, format);
- [self executeSQL:format arguments:args];
+ BOOL result = [self executeSQL:format arguments:args];
va_end(args);
+ return result;
}
-- (void)executeSQL:(NSString *)format arguments:(va_list)args {
+- (BOOL)executeSQL:(NSString *)format arguments:(va_list)args {
NS_VALID_UNTIL_END_OF_SCOPE NSString *SQL = [[NSString alloc] initWithFormat:format arguments:args];
if (!_db) {
- [self raise:@"Database is closed"];
+ secerror("sfsqlite: Database is closed");
+ return NO;
}
int execRet = sqlite3_exec(_db, [SQL UTF8String], NULL, NULL, NULL);
if (execRet != SQLITE_OK) {
- [self raise:@"Error executing SQL: \"%@\" (%d)", SQL, execRet];
+ secerror("sfsqlite: Error executing SQL: \"%@\" (%d)", SQL, execRet);
+ return NO;
}
+
+ return YES;
}
- (SFSQLiteStatement *)statementForSQL:(NSString *)SQL {
if (!_db) {
- [self raise:@"Database is closed"];
+ secerror("sfsqlite: Database is closed");
+ return nil;
}
SFSQLiteStatement *statement = _statementsBySQL[SQL];
sqlite3_stmt *handle = NULL;
NS_VALID_UNTIL_END_OF_SCOPE NSString *arcSafeSQL = SQL;
if (sqlite3_prepare_v2(_db, [arcSafeSQL UTF8String], -1, &handle, NULL)) {
- [self raise:@"Error preparing statement: %@", SQL];
+ secerror("Error preparing statement: %@", SQL);
+ return nil;
}
statement = [[SFSQLiteStatement alloc] initWithSQLite:self SQL:SQL handle:handle];
- (NSString *)_tableNameForClass:(Class)objectClass {
NSString *className = [objectClass SFSQLiteClassName];
if (![className hasPrefix:_objectClassPrefix]) {
- [NSException raise:NSInvalidArgumentException format:@"Object class \"%@\" does not have prefix \"%@\"", className, _objectClassPrefix];
+ secerror("sfsqlite: %@", [NSString stringWithFormat:@"Object class \"%@\" does not have prefix \"%@\"", className, _objectClassPrefix]);
+ return nil;
}
return [className substringFromIndex:_objectClassPrefix.length];
}
}
@end
-
-
-#define SFSQLiteErrorRaiseMethod(SQLiteError) + (void)SQLiteError:(NSString *)reason { [NSException raise:NSGenericException format:@"%@", reason]; }
-#define SFSQLiteErrorCase(SQLiteError) case SQLITE_ ## SQLiteError: [self SQLiteError:reason]; break
-
-@implementation SFSQLiteError
-
-// SQLite error codes
-SFSQLiteErrorRaiseMethod(ERROR)
-SFSQLiteErrorRaiseMethod(INTERNAL)
-SFSQLiteErrorRaiseMethod(PERM)
-SFSQLiteErrorRaiseMethod(ABORT)
-SFSQLiteErrorRaiseMethod(BUSY)
-SFSQLiteErrorRaiseMethod(LOCKED)
-SFSQLiteErrorRaiseMethod(NOMEM)
-SFSQLiteErrorRaiseMethod(READONLY)
-SFSQLiteErrorRaiseMethod(INTERRUPT)
-SFSQLiteErrorRaiseMethod(IOERR)
-SFSQLiteErrorRaiseMethod(CORRUPT)
-SFSQLiteErrorRaiseMethod(NOTFOUND)
-SFSQLiteErrorRaiseMethod(FULL)
-SFSQLiteErrorRaiseMethod(CANTOPEN)
-SFSQLiteErrorRaiseMethod(PROTOCOL)
-SFSQLiteErrorRaiseMethod(SCHEMA)
-SFSQLiteErrorRaiseMethod(TOOBIG)
-SFSQLiteErrorRaiseMethod(CONSTRAINT)
-SFSQLiteErrorRaiseMethod(MISMATCH)
-SFSQLiteErrorRaiseMethod(MISUSE)
-SFSQLiteErrorRaiseMethod(RANGE)
-SFSQLiteErrorRaiseMethod(NOTADB)
-
-// SQLite extended error codes
-SFSQLiteErrorRaiseMethod(IOERR_READ)
-SFSQLiteErrorRaiseMethod(IOERR_SHORT_READ)
-SFSQLiteErrorRaiseMethod(IOERR_WRITE)
-SFSQLiteErrorRaiseMethod(IOERR_FSYNC)
-SFSQLiteErrorRaiseMethod(IOERR_DIR_FSYNC)
-SFSQLiteErrorRaiseMethod(IOERR_TRUNCATE)
-SFSQLiteErrorRaiseMethod(IOERR_FSTAT)
-SFSQLiteErrorRaiseMethod(IOERR_UNLOCK)
-SFSQLiteErrorRaiseMethod(IOERR_RDLOCK)
-SFSQLiteErrorRaiseMethod(IOERR_DELETE)
-SFSQLiteErrorRaiseMethod(IOERR_BLOCKED)
-SFSQLiteErrorRaiseMethod(IOERR_NOMEM)
-SFSQLiteErrorRaiseMethod(IOERR_ACCESS)
-SFSQLiteErrorRaiseMethod(IOERR_CHECKRESERVEDLOCK)
-SFSQLiteErrorRaiseMethod(IOERR_LOCK)
-SFSQLiteErrorRaiseMethod(IOERR_CLOSE)
-SFSQLiteErrorRaiseMethod(IOERR_DIR_CLOSE)
-SFSQLiteErrorRaiseMethod(IOERR_SHMOPEN)
-SFSQLiteErrorRaiseMethod(IOERR_SHMSIZE)
-SFSQLiteErrorRaiseMethod(IOERR_SHMLOCK)
-SFSQLiteErrorRaiseMethod(IOERR_SHMMAP)
-SFSQLiteErrorRaiseMethod(IOERR_SEEK)
-SFSQLiteErrorRaiseMethod(IOERR_DELETE_NOENT)
-SFSQLiteErrorRaiseMethod(IOERR_MMAP)
-SFSQLiteErrorRaiseMethod(IOERR_GETTEMPPATH)
-SFSQLiteErrorRaiseMethod(IOERR_CONVPATH)
-SFSQLiteErrorRaiseMethod(LOCKED_SHAREDCACHE)
-SFSQLiteErrorRaiseMethod(BUSY_RECOVERY)
-SFSQLiteErrorRaiseMethod(BUSY_SNAPSHOT)
-SFSQLiteErrorRaiseMethod(CANTOPEN_NOTEMPDIR)
-SFSQLiteErrorRaiseMethod(CANTOPEN_ISDIR)
-SFSQLiteErrorRaiseMethod(CANTOPEN_FULLPATH)
-SFSQLiteErrorRaiseMethod(CANTOPEN_CONVPATH)
-SFSQLiteErrorRaiseMethod(CORRUPT_VTAB)
-SFSQLiteErrorRaiseMethod(READONLY_RECOVERY)
-SFSQLiteErrorRaiseMethod(READONLY_CANTLOCK)
-SFSQLiteErrorRaiseMethod(READONLY_ROLLBACK)
-SFSQLiteErrorRaiseMethod(READONLY_DBMOVED)
-SFSQLiteErrorRaiseMethod(ABORT_ROLLBACK)
-SFSQLiteErrorRaiseMethod(CONSTRAINT_CHECK)
-SFSQLiteErrorRaiseMethod(CONSTRAINT_COMMITHOOK)
-SFSQLiteErrorRaiseMethod(CONSTRAINT_FOREIGNKEY)
-SFSQLiteErrorRaiseMethod(CONSTRAINT_FUNCTION)
-SFSQLiteErrorRaiseMethod(CONSTRAINT_NOTNULL)
-SFSQLiteErrorRaiseMethod(CONSTRAINT_PRIMARYKEY)
-SFSQLiteErrorRaiseMethod(CONSTRAINT_TRIGGER)
-SFSQLiteErrorRaiseMethod(CONSTRAINT_UNIQUE)
-SFSQLiteErrorRaiseMethod(CONSTRAINT_VTAB)
-SFSQLiteErrorRaiseMethod(CONSTRAINT_ROWID)
-SFSQLiteErrorRaiseMethod(NOTICE_RECOVER_WAL)
-SFSQLiteErrorRaiseMethod(NOTICE_RECOVER_ROLLBACK)
-
-+ (void)raise:(NSString *)reason code:(int)code extended:(int)extended {
- switch(extended) {
- SFSQLiteErrorCase(IOERR_READ);
- SFSQLiteErrorCase(IOERR_SHORT_READ);
- SFSQLiteErrorCase(IOERR_WRITE);
- SFSQLiteErrorCase(IOERR_FSYNC);
- SFSQLiteErrorCase(IOERR_DIR_FSYNC);
- SFSQLiteErrorCase(IOERR_TRUNCATE);
- SFSQLiteErrorCase(IOERR_FSTAT);
- SFSQLiteErrorCase(IOERR_UNLOCK);
- SFSQLiteErrorCase(IOERR_RDLOCK);
- SFSQLiteErrorCase(IOERR_DELETE);
- SFSQLiteErrorCase(IOERR_BLOCKED);
- SFSQLiteErrorCase(IOERR_NOMEM);
- SFSQLiteErrorCase(IOERR_ACCESS);
- SFSQLiteErrorCase(IOERR_CHECKRESERVEDLOCK);
- SFSQLiteErrorCase(IOERR_LOCK);
- SFSQLiteErrorCase(IOERR_CLOSE);
- SFSQLiteErrorCase(IOERR_DIR_CLOSE);
- SFSQLiteErrorCase(IOERR_SHMOPEN);
- SFSQLiteErrorCase(IOERR_SHMSIZE);
- SFSQLiteErrorCase(IOERR_SHMLOCK);
- SFSQLiteErrorCase(IOERR_SHMMAP);
- SFSQLiteErrorCase(IOERR_SEEK);
- SFSQLiteErrorCase(IOERR_DELETE_NOENT);
- SFSQLiteErrorCase(IOERR_MMAP);
- SFSQLiteErrorCase(IOERR_GETTEMPPATH);
- SFSQLiteErrorCase(IOERR_CONVPATH);
- SFSQLiteErrorCase(LOCKED_SHAREDCACHE);
- SFSQLiteErrorCase(BUSY_RECOVERY);
- SFSQLiteErrorCase(BUSY_SNAPSHOT);
- SFSQLiteErrorCase(CANTOPEN_NOTEMPDIR);
- SFSQLiteErrorCase(CANTOPEN_ISDIR);
- SFSQLiteErrorCase(CANTOPEN_FULLPATH);
- SFSQLiteErrorCase(CANTOPEN_CONVPATH);
- SFSQLiteErrorCase(CORRUPT_VTAB);
- SFSQLiteErrorCase(READONLY_RECOVERY);
- SFSQLiteErrorCase(READONLY_CANTLOCK);
- SFSQLiteErrorCase(READONLY_ROLLBACK);
- SFSQLiteErrorCase(READONLY_DBMOVED);
- SFSQLiteErrorCase(ABORT_ROLLBACK);
- SFSQLiteErrorCase(CONSTRAINT_CHECK);
- SFSQLiteErrorCase(CONSTRAINT_COMMITHOOK);
- SFSQLiteErrorCase(CONSTRAINT_FOREIGNKEY);
- SFSQLiteErrorCase(CONSTRAINT_FUNCTION);
- SFSQLiteErrorCase(CONSTRAINT_NOTNULL);
- SFSQLiteErrorCase(CONSTRAINT_PRIMARYKEY);
- SFSQLiteErrorCase(CONSTRAINT_TRIGGER);
- SFSQLiteErrorCase(CONSTRAINT_UNIQUE);
- SFSQLiteErrorCase(CONSTRAINT_VTAB);
- SFSQLiteErrorCase(CONSTRAINT_ROWID);
- SFSQLiteErrorCase(NOTICE_RECOVER_WAL);
- SFSQLiteErrorCase(NOTICE_RECOVER_ROLLBACK);
- default: break;
- }
- switch(code) {
- SFSQLiteErrorCase(ERROR);
- SFSQLiteErrorCase(INTERNAL);
- SFSQLiteErrorCase(PERM);
- SFSQLiteErrorCase(ABORT);
- SFSQLiteErrorCase(BUSY);
- SFSQLiteErrorCase(LOCKED);
- SFSQLiteErrorCase(NOMEM);
- SFSQLiteErrorCase(READONLY);
- SFSQLiteErrorCase(INTERRUPT);
- SFSQLiteErrorCase(IOERR);
- SFSQLiteErrorCase(CORRUPT);
- SFSQLiteErrorCase(NOTFOUND);
- SFSQLiteErrorCase(FULL);
- SFSQLiteErrorCase(CANTOPEN);
- SFSQLiteErrorCase(PROTOCOL);
- SFSQLiteErrorCase(SCHEMA);
- SFSQLiteErrorCase(TOOBIG);
- SFSQLiteErrorCase(CONSTRAINT);
- SFSQLiteErrorCase(MISMATCH);
- SFSQLiteErrorCase(MISUSE);
- SFSQLiteErrorCase(RANGE);
- SFSQLiteErrorCase(NOTADB);
- default: break;
- }
- [NSException raise:NSGenericException format:@"%@", reason];
-}
-
-@end
#import "SFSQLite.h"
#import "SFSQLiteStatement.h"
#import "SFObjCType.h"
+#import "debugging.h"
@interface SFSQLiteStatement ()
@property (nonatomic, strong) NSMutableArray *temporaryBoundObjects;
}
- (void)finalizeStatement {
- SFSQLite *strongSQLite = _SQLite;
-
if (!_reset) {
- [strongSQLite raise: @"Statement not reset after last use: \"%@\"", _SQL];
+ secerror("sfsqlite: Statement not reset after last use: \"%@\"", _SQL);
+ return;
}
if (sqlite3_finalize(_handle)) {
- [strongSQLite raise:@"Error finalizing prepared statement: \"%@\"", _SQL];
+ secerror("sfsqlite: Error finalizing prepared statement: \"%@\"", _SQL);
+ return;
}
}
return NO;
} else {
[self resetAfterStepError];
- [_SQLite raise:@"Failed to step (%d): \"%@\"", rc, _SQL];
+ secerror("sfsqlite: Failed to step (%d): \"%@\"", rc, _SQL);
return NO;
}
}
- (void)reset {
- SFSQLite *strongSQLite = _SQLite;
-
if (!_reset) {
if (sqlite3_reset(_handle)) {
- [strongSQLite raise:@"Error resetting prepared statement: \"%@\"", _SQL];
+ secerror("sfsqlite: Error resetting prepared statement: \"%@\"", _SQL);
+ return;
}
if (sqlite3_clear_bindings(_handle)) {
- [strongSQLite raise:@"Error clearing prepared statement bindings: \"%@\"", _SQL];
+ secerror("sfsqlite: Error clearing prepared statement bindings: \"%@\"", _SQL);
+ return;
}
[_temporaryBoundObjects removeAllObjects];
_reset = YES;
}
- (void)bindInt:(SInt32)value atIndex:(NSUInteger)index {
- NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL);
+ if (!_reset) {
+ secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
+ return;
+ }
if (sqlite3_bind_int(_handle, (int)index+1, value)) {
- [_SQLite raise:@"Error binding int at %ld: \"%@\"", (unsigned long)index, _SQL];
+ secerror("sfsqlite: Error binding int at %ld: \"%@\"", (unsigned long)index, _SQL);
+ return;
}
}
- (void)bindInt64:(SInt64)value atIndex:(NSUInteger)index {
- NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL);
+ if (!_reset) {
+ secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
+ return;
+ }
if (sqlite3_bind_int64(_handle, (int)index+1, value)) {
- [_SQLite raise:@"Error binding int64 at %ld: \"%@\"", (unsigned long)index, _SQL];
+ secerror("sfsqlite: Error binding int64 at %ld: \"%@\"", (unsigned long)index, _SQL);
+ return;
}
}
- (void)bindDouble:(double)value atIndex:(NSUInteger)index {
- NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL);
+ if (!_reset) {
+ secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
+ return;
+ }
if (sqlite3_bind_double(_handle, (int)index+1, value)) {
- [_SQLite raise:@"Error binding double at %ld: \"%@\"", (unsigned long)index, _SQL];
+ secerror("sfsqlite: Error binding double at %ld: \"%@\"", (unsigned long)index, _SQL);
+ return;
}
}
- (void)bindBlob:(NSData *)value atIndex:(NSUInteger)index {
- NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL);
+ if (!_reset) {
+ secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
+ return;
+ }
if (value) {
NS_VALID_UNTIL_END_OF_SCOPE NSData *arcSafeValue = value;
if (sqlite3_bind_blob(_handle, (int)index+1, [arcSafeValue bytes], (int)[arcSafeValue length], NULL)) {
- [_SQLite raise:@"Error binding blob at %ld: \"%@\"", (unsigned long)index, _SQL];
+ secerror("sfsqlite: Error binding blob at %ld: \"%@\"", (unsigned long)index, _SQL);
+ return;
}
} else {
[self bindNullAtIndex:index];
}
- (void)bindText:(NSString *)value atIndex:(NSUInteger)index {
- NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL);
-
+ if (!_reset) {
+ secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
+ return;
+ }
+
if (value) {
NS_VALID_UNTIL_END_OF_SCOPE NSString *arcSafeValue = value;
if (sqlite3_bind_text(_handle, (int)index+1, [arcSafeValue UTF8String], -1, NULL)) {
- [_SQLite raise:@"Error binding text at %ld: \"%@\"", (unsigned long)index, _SQL];
+ secerror("sfsqlite: Error binding text at %ld: \"%@\"", (unsigned long)index, _SQL);
+ return;
}
} else {
[self bindNullAtIndex:index];
- (void)bindNullAtIndex:(NSUInteger)index {
int rc = sqlite3_bind_null(_handle, (int)index+1);
if ((rc & 0x00FF) != SQLITE_OK) {
- [_SQLite raise:@"sqlite3_bind_null error"];
+ secerror("sfsqlite: sqlite3_bind_null error");
+ return;
}
}
} else if ([value isKindOfClass:[NSURL class]]) {
[self bindText:[self retainedTemporaryBoundObject:[value absoluteString]] atIndex:index];
} else {
- [NSException raise:NSInvalidArgumentException format:@"Can't bind object of type %@", [value class]];
+ secerror("sfsqlite: Can't bind object of type %@", [value class]);
+ return;
}
}
return nil;
default:
- [NSException raise:NSGenericException format:@"Unexpected column type: %d", type];
+ secerror("sfsqlite: Unexpected column type: %d", type);
return nil;
}
}
#include "utilities/debugging.h"
#include "utilities/SecAKSWrappers.h"
#include "utilities/SecCFWrappers.h"
+#include <utilities/SecXPCError.h>
#import "CoreCDP/CDPFollowUpController.h"
#import "CoreCDP/CDPFollowUpContext.h"
}
SOSCCStatus circleStatus = SOSCCThisDeviceIsInCircle(&error);
- if(_isAccountICDP && (circleStatus == kSOSCCError || circleStatus == kSOSCCCircleAbsent || circleStatus == kSOSCCNotInCircle) && _hasPostedFollowupAndStillInError == false){
+ if (circleStatus == kSOSCCError && error && CFEqual(sSecXPCErrorDomain, CFErrorGetDomain(error))) {
+ secnotice("cjr", "unable to determine circle status due to xpc failure: %@", error);
+ return;
+ }
+ else if (_isAccountICDP && (circleStatus == kSOSCCError || circleStatus == kSOSCCCircleAbsent || circleStatus == kSOSCCNotInCircle) && _hasPostedFollowupAndStillInError == false) {
NSError *localError = nil;
CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
CFErrorRef error = NULL;
CFErrorRef departError = NULL;
SOSCCStatus circleStatus = SOSCCThisDeviceIsInCircle(&error);
+ enum DepartureReason departureReason = SOSCCGetLastDepartureReason(&departError);
+
+ // Error due to XPC failure does not provide information about the circle.
+ if (circleStatus == kSOSCCError && error && (CFEqual(sSecXPCErrorDomain, CFErrorGetDomain(error)))) {
+ secnotice("cjr", "XPC error while checking circle status: \"%@\", not processing events", error);
+ return true;
+ } else if (departureReason == kSOSDepartureReasonError && departError && (CFEqual(sSecXPCErrorDomain, CFErrorGetDomain(departError)))) {
+ secnotice("cjr", "XPC error while checking last departure reason: \"%@\", not processing events", departError);
+ return true;
+ }
+
NSDate *nowish = [NSDate date];
PersistentState *state = [PersistentState loadFromStorage];
- enum DepartureReason departureReason = SOSCCGetLastDepartureReason(&departError);
secnotice("cjr", "CircleStatus %d -> %d{%d} (s=%p)", state.lastCircleStatus, circleStatus, departureReason, state);
// Pending application reminder
__unused static SOSFullPeerInfoRef SOSNSFullPeerInfoCreate(NSDictionary* gestalt,
- NSData* backupKey, SecKeyRef signingKey, SecKeyRef octagonSigningKey,
- NSError**error)
+ NSData* backupKey, SecKeyRef signingKey,
+ SecKeyRef octagonSigningKey,
+ SecKeyRef octagonEncryptionKey,
+ NSError**error)
{
CFErrorRef errorRef = NULL;
- SOSFullPeerInfoRef result = SOSFullPeerInfoCreate(NULL, (__bridge CFDictionaryRef) gestalt, (__bridge CFDataRef) backupKey, signingKey, octagonSigningKey, &errorRef);
+ SOSFullPeerInfoRef result = SOSFullPeerInfoCreate(NULL, (__bridge CFDictionaryRef) gestalt, (__bridge CFDataRef) backupKey, signingKey, octagonSigningKey, octagonEncryptionKey, &errorRef);
if (errorRef && error) {
*error = (__bridge_transfer NSError*) errorRef;
}
-__unused static SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(NSString* name, SecKeyRef* outSigningKey, SecKeyRef* outOctagonSigningKey, NSError** error)
-{
- if (outSigningKey == NULL || outOctagonSigningKey == NULL)
- return NULL;
-
- *outSigningKey = GenerateFullECKey(256, error);
- if (*outSigningKey == NULL)
- return NULL;
-
- *outOctagonSigningKey = GenerateFullECKey(384, error);
- if (*outOctagonSigningKey == NULL) {
- return NULL;
- }
-
- return SOSNSFullPeerInfoCreate(@{(__bridge NSString*)kPIUserDefinedDeviceNameKey:name}, nil, *outSigningKey, *outOctagonSigningKey, error);
-}
-
-
@interface KCJoiningRequestTestDelegate : NSObject <KCJoiningRequestSecretDelegate, KCJoiningRequestCircleDelegate>
@property (readwrite) NSString* sharedSecret;
SecKeyRef signingKey = GenerateFullECKey(256, NULL);
SecKeyRef octagonSigningKey = GenerateFullECKey(384, NULL);
+ SecKeyRef octagonEncryptionKey = GenerateFullECKey(384, NULL);
- self.peerInfo = SOSPeerInfoCreate(NULL, (__bridge CFDictionaryRef) @{(__bridge NSString*)kPIUserDefinedDeviceNameKey:@"Fakey"}, NULL, signingKey, octagonSigningKey, NULL);
+ self.peerInfo = SOSPeerInfoCreate(NULL, (__bridge CFDictionaryRef) @{(__bridge NSString*)kPIUserDefinedDeviceNameKey:@"Fakey"}, NULL, signingKey, octagonSigningKey, octagonEncryptionKey, NULL);
if (self.peerInfo == NULL)
return nil;
@property (assign) SecKeyRef accountPublicKey;
@property (assign) SecKeyRef deviceKey;
@property (assign) SecKeyRef octagonSigningKey;
+@property (assign) SecKeyRef octagonEncryptionKey;
@property (assign) SOSCircleRef circle;
@property (assign) SOSFullPeerInfoRef fullPeerInfo;
@property (assign) bool application;
}
CFReleaseNull(publicKey);
+ if(SecKeyGeneratePair((__bridge CFDictionaryRef)octagonParameters, &publicKey, &_octagonEncryptionKey) != 0) {
+ NSLog(@"failed to create octagon signing key");
+ return nil;
+ }
+ CFReleaseNull(publicKey);
+
+
_circle = (SOSCircleRef)CFRetain(circle);
CFErrorRef error = NULL;
@"ComputerName" : @"name",
};
- _fullPeerInfo = SOSFullPeerInfoCreate(NULL, gestalt, NULL, _deviceKey, _octagonSigningKey, &error);
+ _fullPeerInfo = SOSFullPeerInfoCreate(NULL, gestalt, NULL, _deviceKey, _octagonSigningKey, _octagonEncryptionKey, &error);
CFReleaseNull(error);
if (randomAccountKey) {
SecItemDelete((__bridge CFTypeRef)@{ (__bridge id)kSecValueRef : (__bridge id)_octagonSigningKey });
CFReleaseNull(_octagonSigningKey);
}
+ if (_octagonEncryptionKey) {
+ SecItemDelete((__bridge CFTypeRef)@{ (__bridge id)kSecValueRef : (__bridge id)_octagonEncryptionKey });
+ CFReleaseNull(_octagonEncryptionKey);
+ }
CFReleaseNull(_circle);
CFReleaseNull(_fullPeerInfo);
}
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "size" : "1024x1024",
+ "scale" : "1x"
}
],
"info" : {
#import <os/activity.h>
#include <utilities/SecAKSWrappers.h>
+#include <utilities/SecADWrapper.h>
#include <utilities/SecCFRelease.h>
#include <AssertMacros.h>
- (void) sendMessageToKVS: (NSDictionary<NSString*, NSDictionary*>*) encapsulatedKeychainMessage
{
+ SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.kvsreroute"), 1);
[encapsulatedKeychainMessage enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if ([key isKindOfClass: [NSString class]] && [obj isKindOfClass:[NSData class]]) {
[self sendToKVS:key message:obj];
if([encapsulatedKeychainMessage isKindOfClass:[NSDictionary class]]){
secnotice("IDS Transport", "Encapsulated message: %@", encapsulatedKeychainMessage);
[self sendMessageToKVS:encapsulatedKeychainMessage];
+
}
}
auth_items_copy(auth_items_t items, auth_items_t src)
{
auth_items_iterate(src, ^bool(const char *key) {
+ if (!key) {
+ return true;
+ }
CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
#define CFReleaseNull(CF) { CFTypeRef _cf = (CF); \
if (_cf) { (CF) = NULL; CFRelease(_cf); } }
#define CFRetainSafe(CF) { CFTypeRef _cf = (CF); if (_cf) CFRetain(_cf); }
-
+#define CFAssignRetained(VAR,CF) ({ \
+__typeof__(VAR) *const _pvar = &(VAR); \
+__typeof__(CF) _cf = (CF); \
+(*_pvar) = *_pvar ? (CFRelease(*_pvar), _cf) : _cf; \
+})
+
#define xpc_release_safe(obj) if (obj) { xpc_release(obj); obj = NULL; }
#define free_safe(obj) if (obj) { free(obj); obj = NULL; }
#include <Security/AuthorizationTags.h>
#include <Security/AuthorizationTagsPriv.h>
+#include <Security/AuthorizationPriv.h>
#include <Security/AuthorizationPlugin.h>
#include <LocalAuthentication/LAPublicDefines.h>
#include <LocalAuthentication/LAPrivateDefines.h>
#include <sandbox.h>
#include <coreauthd_spi.h>
+#include <ctkloginhelper.h>
+
AUTHD_DEFINE_LOG
CFReleaseNull(engine->credentials);
CFReleaseNull(engine->effectiveCredentials);
CFReleaseNull(engine->authenticateRule);
+ CFReleaseNull(engine->la_context);
}
AUTH_TYPE_INSTANCE(engine,
static bool
-_extract_password_from_la(engine_t engine, CFTypeRef la_context)
+_extract_password_from_la(engine_t engine)
{
bool retval = false;
+
+ if (!engine->la_context) {
+ return retval;
+ }
+
// try to retrieve secret
- CFDataRef passdata = LACopyCredential(la_context, kLACredentialTypeExtractablePasscode, NULL);
+ CFDataRef passdata = LACopyCredential(engine->la_context, kLACredentialTypeExtractablePasscode, NULL);
if (passdata) {
if (CFDataGetBytePtr(passdata)) {
auth_items_set_data(engine->context, kAuthorizationEnvironmentPassword, CFDataGetBytePtr(passdata), CFDataGetLength(passdata));
- } else {
- char nulChar = 0;
- auth_items_set_data(engine->context, kAuthorizationEnvironmentPassword, &nulChar, 1);
}
CFRelease(passdata);
}
CFDictionaryRef la_result = NULL;
CFIndex count = CFArrayGetCount(mechanisms);
- for (CFIndex i = 0; i < count; i++) {
+ bool sheet_evaluation = false;
+ if (engine->la_context) {
+ int tmp = kLAOptionNotInteractive;
+ CFNumberRef key = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tmp);
+ tmp = 1;
+ CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tmp);
+ if (key && value) {
+ CFMutableDictionaryRef options = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFDictionarySetValue(options, key, value);
+ la_result = LACopyResultOfPolicyEvaluation(engine->la_context, kLAPolicyDeviceOwnerAuthentication, options, NULL);
+ CFReleaseSafe(options);
+ }
+ CFReleaseSafe(key);
+ CFReleaseSafe(value);
+ }
+
+ for (CFIndex i = 0; i < count; i++) {
mechanism_t mech = (mechanism_t)CFArrayGetValueAtIndex(mechanisms, i);
if (mechanism_get_type(mech)) {
os_log_debug(AUTHD_LOG, "engine: running builtin mechanism %{public}s (%li of %li)", mechanism_get_string(mech), i+1, count);
result = _evaluate_builtin_mechanism(engine, mech);
} else {
- bool sheet_variant_used = false;
+ bool shoud_run_agent = true; // evaluate comes from sheet -> we may not want to run standard SecurityAgent or authhost
if (engine->la_context) {
-
- if (!la_result) {
- int tmp = kLAOptionNotInteractive;
- CFNumberRef key = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tmp);
- tmp = 1;
- CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tmp);
- if (key && value) {
- CFMutableDictionaryRef options = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- CFDictionarySetValue(options, key, value);
- la_result = LACopyResultOfPolicyEvaluation(engine->la_context, kLAPolicyDeviceOwnerAuthentication, options, NULL);
- CFReleaseSafe(options);
- }
- CFReleaseSafe(key);
- CFReleaseSafe(value);
- }
-
// sheet variant in progress
if (strcmp(mechanism_get_string(mech), "builtin:authenticate") == 0) {
- // instead of running SecurityAgent, get uid from the authorization
- os_log(AUTHD_LOG, "engine: running builtin sheet authenticate");
- if (!la_result) {
- result = kAuthorizationResultDeny; // no la_result => was evaluate did not pass
+ // find out if sheet just provided credentials or did real authentication
+ // if password is provided or PAM service name exists, it means authd has to evaluate credentials
+ // otherwise we need to check la_result
+ if (auth_items_exist(engine->context, AGENT_CONTEXT_AP_PAM_SERVICE_NAME) || auth_items_exist(engine->context, kAuthorizationEnvironmentPassword)) {
+ // do not try to get credentials as it has been already passed by sheet
+ os_log(AUTHD_LOG, "engine: ingoring builtin sheet authenticate");
+ } else {
+ // sheet itself did the authenticate the user
+ os_log(AUTHD_LOG, "engine: running builtin sheet authenticate");
+ sheet_evaluation = true;
+ if (!la_result || TKGetSmartcardSetting(kTKEnforceSmartcard) != 0) {
+ result = kAuthorizationResultDeny; // no la_result => evaluate did not pass for sheet method. Enforced smartcard => no way to use sheet based evaluation
+ }
}
- sheet_variant_used = true;
+ shoud_run_agent = false; // SecurityAgent should not be run for builtin:authenticate
} else if (strcmp(mechanism_get_string(mech), "builtin:authenticate,privileged") == 0) {
- os_log(AUTHD_LOG, "engine: running builtin sheet privileged authenticate");
- if (!la_result) {
- result = kAuthorizationResultDeny; // no la_result => was evaluate did not pass
- } else {
- if (!_extract_password_from_la(engine, engine->la_context)) {
- os_log_debug(AUTHD_LOG, "engine: cannot extract cred");
+ if (sheet_evaluation) {
+ os_log(AUTHD_LOG, "engine: running builtin sheet privileged authenticate");
+ shoud_run_agent = false;
+ if (!la_result || TKGetSmartcardSetting(kTKEnforceSmartcard) != 0) { // should not get here under normal circumstances but we need to handle this case as well
+ result = kAuthorizationResultDeny; // no la_result => evaluate did not pass. Enforced smartcard => no way to use sheet based evaluation
}
+ } else {
+ // should_run_agent has to be set to true because we want authorizationhost to verify the credentials
+ os_log(AUTHD_LOG, "engine: running sheet privileged authenticate");
}
- sheet_variant_used = true;
}
}
- if (!sheet_variant_used) {
+ if (shoud_run_agent) {
agent_t agent = _get_agent(engine, mech, true, i == 0);
require_action(agent != NULL, done, result = kAuthorizationResultUndefined; os_log_error(AUTHD_LOG, "engine: error creating mechanism agent"));
require_action(CFArrayGetCount(mechanisms) > 0, done, os_log_debug(AUTHD_LOG, "engine: error no mechanisms found"));
int64_t ruleTries = rule_get_tries(rule);
+
+ if (engine->la_context) {
+ ruleTries = 1;
+ os_log_debug(AUTHD_LOG, "Sheet authentication in progress, one try is enough");
+ }
+
for (engine->tries = 0; engine->tries < ruleTries; engine->tries++) {
auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason));
} else if (status == errAuthorizationDenied) {
os_log_error(AUTHD_LOG, "engine: evaluate denied");
engine->reason = invalidPassphrase;
- if (engine->la_context) {
- // for sheet authorizations do not retry with sheet as there is no new sheet UI
- CFReleaseNull(engine->la_context);
- auth_items_remove(engine->context, AGENT_CONTEXT_UID);
- }
}
}
engine->reason = tooManyTries;
auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason));
auth_items_set_int(engine->hints, AGENT_HINT_TRIES, engine->tries);
- // TODO: determine why evaluate_mechanism is run once again and possibly remove this call
- _evaluate_mechanisms(engine, mechanisms);
ccaudit_log(ccaudit, engine->currentRightName, NULL, 1113);
}
#endif
// Check if a credential was passed into the environment and we were asked to extend the rights
- if (engine->flags & kAuthorizationFlagExtendRights) {
+ if (engine->flags & kAuthorizationFlagExtendRights && !(engine->flags & kAuthorizationFlagSheet)) {
const char * user = auth_items_get_string(environment, kAuthorizationEnvironmentUsername);
const char * pass = auth_items_get_string(environment, kAuthorizationEnvironmentPassword);
const bool password_was_used = auth_items_get_string(environment, AGENT_CONTEXT_AP_PAM_SERVICE_NAME) == nil; // AGENT_CONTEXT_AP_PAM_SERVICE_NAME in the context means alternative PAM was used
engine->flags = kAuthorizationFlagExtendRights;
engine->preauthorizing = true;
- CFTypeRef la_context = engine_copy_context(engine, credentials);
- if (la_context) {
- _extract_password_from_la(engine, la_context);
- CFRelease(la_context);
- }
+ CFAssignRetained(engine->la_context, engine_copy_context(engine, credentials));
+ _extract_password_from_la(engine);
const char *user = auth_items_get_string(credentials, kAuthorizationEnvironmentUsername);
require(user, done);
if (auth_rights_get_count(rights) > 0) {
ccaudit_log(ccaudit, "begin evaluation", NULL, 0);
}
-
+
+ if (!auth_token_apple_signed(engine->auth)) {
+#ifdef NDEBUG
+ flags &= ~kAuthorizationFlagIgnorePasswordOnly;
+ flags &= ~kAuthorizationFlagSheet;
+#else
+ os_log_debug(AUTHD_LOG, "engine: in release mode, extra flags would be ommited as creator is not signed by Apple");
+#endif
+ }
+
engine->flags = flags;
if (environment) {
_parse_environment(engine, environment);
auth_items_copy(engine->hints, environment);
- engine_acquire_sheet_data(engine);
}
+ if (engine->flags & kAuthorizationFlagSheet) {
+ CFTypeRef extract_password_entitlement = auth_token_copy_entitlement_value(engine->auth, "com.apple.authorization.extract-password");
+ if (extract_password_entitlement && (CFGetTypeID(extract_password_entitlement) == CFBooleanGetTypeID()) && extract_password_entitlement == kCFBooleanTrue) {
+ save_password = true;
+ os_log_debug(AUTHD_LOG, "engine: authorization allowed to extract password");
+ } else {
+ os_log_debug(AUTHD_LOG, "engine: authorization NOT allowed to extract password");
+ }
+ CFReleaseSafe(extract_password_entitlement);
+
+ // TODO: Remove when all clients have adopted entitlement
+ if (!enforced_entitlement()) {
+ save_password = true;
+ }
+ const char *user = auth_items_get_string(environment, kAuthorizationEnvironmentUsername);
+ require(user, done);
+
+ auth_items_set_string(engine->context, kAuthorizationEnvironmentUsername, user);
+ struct passwd *pwd = getpwnam(user);
+ require(pwd, done);
+ auth_items_set_int(engine->context, AGENT_CONTEXT_UID, pwd->pw_uid);
+
+ // move sheet-specific items from hints to context
+ const char *service = auth_items_get_string(engine->hints, AGENT_CONTEXT_AP_PAM_SERVICE_NAME);
+ if (service) {
+ if (auth_items_exist(engine->hints, AGENT_CONTEXT_AP_USER_NAME)) {
+ auth_items_set_string(engine->context, AGENT_CONTEXT_AP_USER_NAME, auth_items_get_string(engine->hints, AGENT_CONTEXT_AP_USER_NAME));
+ auth_items_remove(engine->hints, AGENT_CONTEXT_AP_USER_NAME);
+ } else {
+ auth_items_set_string(engine->context, AGENT_CONTEXT_AP_USER_NAME, user);
+ }
+
+ auth_items_set_string(engine->context, AGENT_CONTEXT_AP_PAM_SERVICE_NAME, service);
+ auth_items_remove(engine->hints, AGENT_CONTEXT_AP_PAM_SERVICE_NAME);
+ }
+
+ if (auth_items_exist(environment, AGENT_CONTEXT_AP_TOKEN)) {
+ size_t datalen = 0;
+ const void *data = auth_items_get_data(engine->hints, AGENT_CONTEXT_AP_TOKEN, &datalen);
+ if (data) {
+ auth_items_set_data(engine->context, AGENT_CONTEXT_AP_TOKEN, data, datalen);
+ }
+ auth_items_remove(engine->hints, AGENT_CONTEXT_AP_TOKEN);
+ }
+
+ engine_acquire_sheet_data(engine);
+ _extract_password_from_la(engine);
+ engine->preauthorizing = true;
+ }
+
auth_items_t decrypted_items = auth_items_create();
require_action(decrypted_items != NULL, done, os_log_error(AUTHD_LOG, "engine: enable to create items"));
auth_items_content_copy(decrypted_items, auth_token_get_context(engine->auth));
engine->dismissed = false;
auth_rights_clear(engine->grantedRights);
+ if (!(engine->flags & kAuthorizationFlagIgnorePasswordOnly))
{
// first check if any of rights uses rule with password-only set to true
// if so, set appropriate hint so SecurityAgent won't use alternate authentication methods like smartcard etc.
return true;
});
authdb_connection_release(&dbconn); // release db handle
+ } else {
+ os_log_info(AUTHD_LOG, "engine: password-only ignored");
}
if (password_only) {
}
os_log_debug(AUTHD_LOG, "engine: authorize result: %d", (int)status);
-
+
+ if (engine->flags & kAuthorizationFlagSheet) {
+ engine->preauthorizing = false;
+ }
+
if ((engine->flags & kAuthorizationFlagExtendRights) && !(engine->flags & kAuthorizationFlagDestroyRights)) {
_cf_set_iterate(engine->credentials, ^bool(CFTypeRef value) {
credential_t cred = (credential_t)value;
bool engine_acquire_sheet_data(engine_t engine)
{
- uid_t uid = auth_items_get_int(engine->hints, AGENT_CONTEXT_UID);
+ uid_t uid = auth_items_get_int(engine->context, AGENT_CONTEXT_UID);
if (!uid)
return false;
CFReleaseSafe(engine->la_context);
engine->la_context = engine_copy_context(engine, engine->hints);
if (engine->la_context) {
- // copy UID to the context of the authorization
os_log_debug(AUTHD_LOG, "engine: Sheet user UID %d", uid);
- auth_items_set_int(engine->context, AGENT_CONTEXT_UID, uid);
- struct passwd *pwd = getpwuid(uid);
- if (pwd) {
- auth_items_set_string(engine->context, kAuthorizationEnvironmentUsername, pwd->pw_name);
- }
return true;
} else {
// this is not real failure as no LA context in authorization context is very valid scenario
+
SDKROOT = macosx.internal
ARCHS[sdk=macosx*] = $(ARCHS_STANDARD_32_64_BIT)
CODE_SIGN_IDENTITY = -;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
-CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion)
-VERSIONING_SYSTEM = apple-generic;
DEAD_CODE_STRIPPING = YES
#include "OSX/config/base.xcconfig"
+#include "xcconfig/Version.xcconfig"
GCC_PRECOMPILE_PREFIX_HEADER = YES
SUPPORTED_PLATFORMS = macOS
// Don't use the inherited cflags; they set SEC_IOS_ON_OSX
-GCC_PREPROCESSOR_DEFINITIONS =
+GCC_PREPROCESSOR_DEFINITIONS = SECURITY_BUILD_VERSION=\"$(SECURITY_BUILD_VERSION)\"
GCC_TREAT_WARNINGS_AS_ERRORS = YES
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO
"com.apple.ctkbind.admin" = "__APPNAME__ is trying to pair the current user with the SmartCard identity.";
-"com.apple.builtin.sc-kc-new-passphrase" = "The system will now create a keychain to store your secrets. Your smart card will automatically unlock it. Please choose a password that can unlock it separately. You may use your account password or pick another one. For security reasons, do not use your smart card PIN or similar text.";
+"com.apple.builtin.sc-kc-new-passphrase" = "The system will now create a keychain to store your secrets. Your SmartCard will automatically unlock it. Please choose a password that can unlock it separately. You may use your account password or pick another one. For security reasons, do not use your SmartCard PIN or similar text.";
"com.apple.security.sudo" = "__APPNAME__ is trying to execute a command as administrator.";
@enum Private (for now) AuthorizationFlags
*/
enum {
- kAuthorizationFlagLeastPrivileged = (1 << 5)
+ kAuthorizationFlagLeastPrivileged = (1 << 5),
+ kAuthorizationFlagSheet = (1 << 6),
+ kAuthorizationFlagIgnorePasswordOnly = (1 << 7),
};
/*!
}
// Did we implicitly trust the signer?
- mTrustedSigningCertChain = (trustResult == kSecTrustResultUnspecified);
+ mTrustedSigningCertChain = (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed);
return false; // XXX: Not checking for expired certs
#endif
/*
- * Copyright (c) 2002-2014 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2002-2017 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
- *
+ *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
- *
+ *
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
- *
+ *
* @APPLE_LICENSE_HEADER_END@
*/
#include <Security/SecCertificatePriv.h>
#include "SecCertificateOIDs.h"
#include "CertificateValues.h"
-#include "SecCertificateP.h"
-#include "SecCertificatePrivP.h"
#include <CoreFoundation/CFNumber.h>
-#include "SecCertificateP.h"
-/* FIXME including SecCertificateInternalP.h here produces errors; investigate */
-extern "C" CFDataRef SecCertificateCopyIssuerSequenceP(SecCertificateRefP certificate);
-extern "C" CFDataRef SecCertificateCopySubjectSequenceP(SecCertificateRefP certificate);
-extern "C" CFDictionaryRef SecCertificateCopyAttributeDictionaryP(SecCertificateRefP certificate);
-
-extern "C" void appendPropertyP(CFMutableArrayRef properties, CFStringRef propertyType, CFStringRef label, CFTypeRef value);
+// SecCertificateInternal.h cannot be included in this file, due to its
+// use of types which are not resolved in our macOS-only library.
+//
+extern "C" CFArrayRef SecCertificateCopyLegacyProperties(SecCertificateRef certificate);
+extern "C" void appendProperty(CFMutableArrayRef properties, CFStringRef propertyType,
+ CFStringRef label, CFStringRef localizedLabel, CFTypeRef value);
extern const CFStringRef __nonnull kSecPropertyKeyType;
extern const CFStringRef __nonnull kSecPropertyKeyLabel;
} FieldValueFilterContext;
CertificateValues::CertificateValues(SecCertificateRef certificateRef) : mCertificateRef(certificateRef),
- mCertificateData(NULL)
+ mCertificateData(NULL),
+ mCertificateProperties(NULL)
{
if (mCertificateRef)
CFRetain(mCertificateRef);
CertificateValues::~CertificateValues() throw()
{
+ if (mCertificateProperties)
+ CFRelease(mCertificateProperties);
if (mCertificateData)
CFRelease(mCertificateData);
if (mCertificateRef)
CFRelease(mCertificateRef);
}
+CFArrayRef CertificateValues::copyPropertyValues(CFErrorRef *error)
+{
+ if (!mCertificateProperties) {
+ mCertificateProperties = SecCertificateCopyLegacyProperties(mCertificateRef);
+ }
+ if (mCertificateProperties) {
+ CFRetain(mCertificateProperties);
+ }
+ else if (error) {
+ *error = CFErrorCreate(NULL,
+ kCFErrorDomainOSStatus, errSecInvalidCertificateRef, NULL);
+ }
+ return mCertificateProperties;
+}
+
CFDictionaryRef CertificateValues::copyFieldValues(CFArrayRef keys, CFErrorRef *error)
{
if (keys)
}
}
- SecCertificateRefP certificateP = SecCertificateCreateWithDataP(kCFAllocatorDefault, mCertificateData);
- if (!certificateP)
+ SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, mCertificateData);
+ if (!certificate)
{
if (error)
*error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateGroup, NULL);
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
// Return an array of CFStringRefs representing the common names in the certificates subject if any
- CFArrayRef commonNames=SecCertificateCopyCommonNamesP(certificateP);
+ CFArrayRef commonNames=SecCertificateCopyCommonNames(certificate);
if (commonNames)
{
CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- appendPropertyP(additionalValues, kSecPropertyTypeArray, CFSTR("CN"), commonNames);
+ appendProperty(additionalValues, kSecPropertyTypeArray, CFSTR("CN"), NULL, commonNames);
CFDictionaryAddValue(fieldValues, kSecOIDCommonName, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0));
CFRelease(commonNames);
CFRelease(additionalValues);
}
// These can exist in the subject alt name or in the subject
- CFArrayRef dnsNames=SecCertificateCopyDNSNamesP(certificateP);
+ CFArrayRef dnsNames=SecCertificateCopyDNSNames(certificate);
if (dnsNames)
{
CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- appendPropertyP(additionalValues, kSecPropertyTypeArray, CFSTR("DNS"), dnsNames);
+ appendProperty(additionalValues, kSecPropertyTypeArray, CFSTR("DNS"), NULL, dnsNames);
CFDictionaryAddValue(fieldValues, CFSTR("DNSNAMES"), (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0));
CFRelease(dnsNames);
CFRelease(additionalValues);
}
- CFArrayRef ipAddresses=SecCertificateCopyIPAddressesP(certificateP);
+ CFArrayRef ipAddresses=SecCertificateCopyIPAddresses(certificate);
if (ipAddresses)
{
CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- appendPropertyP(additionalValues, kSecPropertyTypeArray, CFSTR("IP"), dnsNames);
+ appendProperty(additionalValues, kSecPropertyTypeArray, CFSTR("IP"), NULL, dnsNames);
CFDictionaryAddValue(fieldValues, CFSTR("IPADDRESSES"), (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0));
CFRelease(ipAddresses);
CFRelease(additionalValues);
}
// These can exist in the subject alt name or in the subject
- CFArrayRef emailAddrs=SecCertificateCopyRFC822NamesP(certificateP);
+ CFArrayRef emailAddrs=SecCertificateCopyRFC822Names(certificate);
if (emailAddrs)
{
CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- appendPropertyP(additionalValues, kSecPropertyTypeArray, CFSTR("DNS"), dnsNames);
+ appendProperty(additionalValues, kSecPropertyTypeArray, CFSTR("DNS"), NULL, dnsNames);
CFDictionaryAddValue(fieldValues, kSecOIDEmailAddress, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0));
CFRelease(emailAddrs);
CFRelease(additionalValues);
}
- CFAbsoluteTime notBefore = SecCertificateNotValidBeforeP(certificateP);
+ CFAbsoluteTime notBefore = SecCertificateNotValidBefore(certificate);
CFNumberRef notBeforeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, ¬Before);
if (notBeforeRef)
{
CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- appendPropertyP(additionalValues, kSecPropertyTypeNumber, CFSTR("Not Valid Before"), notBeforeRef);
+ appendProperty(additionalValues, kSecPropertyTypeNumber, CFSTR("Not Valid Before"), NULL, notBeforeRef);
CFDictionaryAddValue(fieldValues, kSecOIDX509V1ValidityNotBefore, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0));
CFRelease(notBeforeRef);
CFRelease(additionalValues);
}
- CFAbsoluteTime notAfter = SecCertificateNotValidAfterP(certificateP);
+ CFAbsoluteTime notAfter = SecCertificateNotValidAfter(certificate);
CFNumberRef notAfterRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, ¬After);
if (notAfterRef)
{
CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- appendPropertyP(additionalValues, kSecPropertyTypeNumber, CFSTR("Not Valid After"), notAfterRef);
+ appendProperty(additionalValues, kSecPropertyTypeNumber, CFSTR("Not Valid After"), NULL, notAfterRef);
CFDictionaryAddValue(fieldValues, kSecOIDX509V1ValidityNotAfter, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0));
CFRelease(notAfterRef);
CFRelease(additionalValues);
}
- SecKeyUsage keyUsage=SecCertificateGetKeyUsageP(certificateP);
+ SecKeyUsage keyUsage=SecCertificateGetKeyUsage(certificate);
CFNumberRef ku = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &keyUsage);
if (ku)
{
CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- appendPropertyP(additionalValues, kSecPropertyTypeNumber, CFSTR("Key Usage"), ku);
+ appendProperty(additionalValues, kSecPropertyTypeNumber, CFSTR("Key Usage"), NULL, ku);
CFDictionaryAddValue(fieldValues, kSecOIDKeyUsage, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0));
CFRelease(ku);
CFRelease(additionalValues);
}
- CFArrayRef ekus = SecCertificateCopyExtendedKeyUsageP(certificateP);
+ CFArrayRef ekus = SecCertificateCopyExtendedKeyUsage(certificate);
if (ekus)
{
CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- appendPropertyP(additionalValues, kSecPropertyTypeArray, CFSTR("Extended Key Usage"), ekus);
+ appendProperty(additionalValues, kSecPropertyTypeArray, CFSTR("Extended Key Usage"), NULL, ekus);
CFDictionaryAddValue(fieldValues, kSecOIDExtendedKeyUsage, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0));
CFRelease(ekus);
CFRelease(additionalValues);
}
// Add all values from properties dictionary
- CFArrayRef properties = SecCertificateCopyPropertiesP(certificateP);
+ CFArrayRef properties = copyPropertyValues(NULL);
if (properties)
{
CFRange range = CFRangeMake(0, CFArrayGetCount((CFArrayRef)properties));
CFAbsoluteTime verifyTime = CFAbsoluteTimeGetCurrent();
CFMutableArrayRef summaryProperties =
- SecCertificateCopySummaryPropertiesP(certificateP, verifyTime);
+ SecCertificateCopySummaryProperties(certificate, verifyTime);
if (summaryProperties)
{
CFRange range = CFRangeMake(0, CFArrayGetCount((CFArrayRef)summaryProperties));
CFRelease(summaryProperties);
}
- if (certificateP)
- CFRelease(certificateP);
+ if (certificate)
+ CFRelease(certificate);
if (keys==NULL)
return (CFDictionaryRef)fieldValues;
CFDataRef CertificateValues::copySerialNumber(CFErrorRef *error)
{
CFDataRef result = NULL;
- SecCertificateRefP certificateP = getSecCertificateRefP(error);
+ SecCertificateRef certificate = copySecCertificateRef(error);
- if (certificateP)
+ if (certificate)
{
- result = SecCertificateCopySerialNumberP(certificateP);
- CFRelease(certificateP);
+ result = SecCertificateCopySerialNumberData(certificate, error);
+ CFRelease(certificate);
}
return result;
}
CFDataRef CertificateValues::copyNormalizedIssuerContent(CFErrorRef *error)
{
CFDataRef result = NULL;
- SecCertificateRefP certificateP = getSecCertificateRefP(error);
- if (certificateP)
+ SecCertificateRef certificate = copySecCertificateRef(error);
+ if (certificate)
{
- result = SecCertificateCopyNormalizedIssuerSequenceP(certificateP);
- CFRelease(certificateP);
+ // this matches the behavior on OS X prior to 10.12, where
+ // normalized content was actually returned as a sequence.
+
+ result = SecCertificateCopyNormalizedIssuerSequence(certificate);
+ CFRelease(certificate);
}
return result;
}
CFDataRef CertificateValues::copyNormalizedSubjectContent(CFErrorRef *error)
{
CFDataRef result = NULL;
- SecCertificateRefP certificateP = getSecCertificateRefP(error);
- if (certificateP)
+ SecCertificateRef certificate = copySecCertificateRef(error);
+ if (certificate)
{
- result = SecCertificateCopyNormalizedSubjectSequenceP(certificateP);
- CFRelease(certificateP);
+ // this matches the behavior on OS X prior to 10.12, where
+ // normalized content was actually returned as a sequence.
+
+ result = SecCertificateCopyNormalizedSubjectSequence(certificate);
+ CFRelease(certificate);
}
return result;
}
CFDataRef CertificateValues::copyIssuerSequence(CFErrorRef *error)
{
CFDataRef result = NULL;
- SecCertificateRefP certificateP = getSecCertificateRefP(error);
- if (certificateP)
+ SecCertificateRef certificate = copySecCertificateRef(error);
+ if (certificate)
{
- result = SecCertificateCopyIssuerSequenceP(certificateP);
- CFRelease(certificateP);
+ result = SecCertificateCopyIssuerSequence(certificate);
+ CFRelease(certificate);
}
return result;
}
CFDataRef CertificateValues::copySubjectSequence(CFErrorRef *error)
{
CFDataRef result = NULL;
- SecCertificateRefP certificateP = getSecCertificateRefP(error);
- if (certificateP)
+ SecCertificateRef certificate = copySecCertificateRef(error);
+ if (certificate)
+ {
+ result = SecCertificateCopySubjectSequence(certificate);
+ CFRelease(certificate);
+ }
+ return result;
+}
+
+CFStringRef CertificateValues::copyIssuerSummary(CFErrorRef *error)
+{
+ CFStringRef result = NULL;
+ SecCertificateRef certificate = copySecCertificateRef(error);
+ if (certificate)
{
- result = SecCertificateCopySubjectSequenceP(certificateP);
- CFRelease(certificateP);
+ result = SecCertificateCopyIssuerSummary(certificate);
+ CFRelease(certificate);
+ }
+ return result;
+}
+
+CFStringRef CertificateValues::copySubjectSummary(CFErrorRef *error)
+{
+ CFStringRef result = NULL;
+ SecCertificateRef certificate = copySecCertificateRef(error);
+ if (certificate)
+ {
+ result = SecCertificateCopySubjectSummary(certificate);
+ CFRelease(certificate);
}
return result;
}
CFDictionaryRef CertificateValues::copyAttributeDictionary(CFErrorRef *error)
{
- CFDictionaryRef result = NULL;
- SecCertificateRefP certificateP = getSecCertificateRefP(error);
- if (certificateP)
- {
- result = SecCertificateCopyAttributeDictionaryP(certificateP);
- CFRelease(certificateP);
- }
- return result;
+ CFDictionaryRef result = NULL;
+ SecCertificateRef certificate = copySecCertificateRef(error);
+ if (certificate)
+ {
+ result = SecCertificateCopyAttributeDictionary(certificate);
+ CFRelease(certificate);
+ }
+ return result;
}
bool CertificateValues::isValid(CFAbsoluteTime verifyTime, CFErrorRef *error)
{
bool result = NULL;
- SecCertificateRefP certificateP = getSecCertificateRefP(error);
- if (certificateP)
+ SecCertificateRef certificate = copySecCertificateRef(error);
+ if (certificate)
{
- result = SecCertificateIsValidP(certificateP, verifyTime);
- CFRelease(certificateP);
+ result = SecCertificateIsValid(certificate, verifyTime);
+ CFRelease(certificate);
}
return result;
}
CFAbsoluteTime CertificateValues::notValidBefore(CFErrorRef *error)
{
CFAbsoluteTime result = 0;
- SecCertificateRefP certificateP = getSecCertificateRefP(error);
- if (certificateP)
+ SecCertificateRef certificate = copySecCertificateRef(error);
+ if (certificate)
{
- result = SecCertificateNotValidBeforeP(certificateP);
- CFRelease(certificateP);
+ result = SecCertificateNotValidBefore(certificate);
+ CFRelease(certificate);
}
return result;
}
CFAbsoluteTime CertificateValues::notValidAfter(CFErrorRef *error)
{
CFAbsoluteTime result = 0;
- SecCertificateRefP certificateP = getSecCertificateRefP(error);
- if (certificateP)
+ SecCertificateRef certificate = copySecCertificateRef(error);
+ if (certificate)
{
- result = SecCertificateNotValidAfterP(certificateP);
- CFRelease(certificateP);
+ result = SecCertificateNotValidAfter(certificate);
+ CFRelease(certificate);
}
return result;
}
-SecCertificateRefP CertificateValues::getSecCertificateRefP(CFErrorRef *error)
+SecCertificateRef CertificateValues::copySecCertificateRef(CFErrorRef *error)
{
// SecCertificateCopyData returns an object created with CFDataCreate, so we
// own it and must release it
}
mCertificateData = SecCertificateCopyData(mCertificateRef); // OK to call, no big lock
- if (!mCertificateData && error)
+ if (!mCertificateData)
{
- *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateRef, NULL);
+ if (error)
+ {
+ *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateRef, NULL);
+ }
return NULL;
}
- SecCertificateRefP certificateP = SecCertificateCreateWithDataP(kCFAllocatorDefault, mCertificateData);
- if (!certificateP && error)
+ SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, mCertificateData);
+ if (!certificate)
{
- *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateGroup, NULL);
+ if (error)
+ {
+ *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateGroup, NULL);
+ }
return NULL;
}
- return certificateP;
+ return certificate;
}
#pragma mark ---------- OID Constants ----------
/*
- * Copyright (c) 2002-2014 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2002-2017 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
- *
+ *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
- *
+ *
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
- *
+ *
* @APPLE_LICENSE_HEADER_END@
*/
#ifndef _SECURITY_CERTIFICATEVALUES_H_
#define _SECURITY_CERTIFICATEVALUES_H_
-#include <security_keychain/Certificate.h>
-#include "SecBaseP.h"
-//#include <security_utilities/seccfobject.h>
+#include <Security/SecBase.h>
+
namespace Security
{
namespace KeychainCore
{
-class CertificateValues// : public SecCFObject
+class CertificateValues
{
NOCOPY(CertificateValues)
public:
CertificateValues(SecCertificateRef certificateRef);
- virtual ~CertificateValues() throw();
+ virtual ~CertificateValues() throw();
static CFStringRef remapLabelToKey(CFStringRef label);
+ CFArrayRef copyPropertyValues(CFErrorRef *error);
CFDictionaryRef copyFieldValues(CFArrayRef keys, CFErrorRef *error);
CFDataRef copySerialNumber(CFErrorRef *error);
CFDataRef copyNormalizedIssuerContent(CFErrorRef *error);
CFDataRef copyNormalizedSubjectContent(CFErrorRef *error);
CFDataRef copyIssuerSequence(CFErrorRef *error);
CFDataRef copySubjectSequence(CFErrorRef *error);
- CFDictionaryRef copyAttributeDictionary(CFErrorRef *error);
+ CFStringRef copyIssuerSummary(CFErrorRef *error);
+ CFStringRef copySubjectSummary(CFErrorRef *error);
+ CFDictionaryRef copyAttributeDictionary(CFErrorRef *error);
bool isValid(CFAbsoluteTime verifyTime, CFErrorRef *error);
CFAbsoluteTime notValidBefore(CFErrorRef *error);
CFAbsoluteTime notValidAfter(CFErrorRef *error);
private:
- SecCertificateRefP getSecCertificateRefP(CFErrorRef *error);
+ SecCertificateRef copySecCertificateRef(CFErrorRef *error);
SecCertificateRef mCertificateRef;
CFDataRef mCertificateData;
+ CFArrayRef mCertificateProperties;
static CFDictionaryRef mOIDRemap;
};
+++ /dev/null
-/* /////////////////////////////////////////////////////////////////////////////
- * File: b64.c
- *
- * Purpose: Implementation file for the b64 library
- *
- * Created: 18th October 2004
- * Updated: 2nd August 2006
- *
- * Home: http://synesis.com.au/software/
- *
- * Copyright (c) 2004-2006, Matthew Wilson and Synesis Software
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
- * any contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * ////////////////////////////////////////////////////////////////////////// */
-
-
-/** \file b64.c Implementation file for the b64 library
- */
-
-#include "SecBase64P.h"
-
-#include <assert.h>
-#include <string.h>
-
-/* /////////////////////////////////////////////////////////////////////////////
- * Constants and definitions
- */
-
-#ifndef B64_DOCUMENTATION_SKIP_SECTION
-# define NUM_PLAIN_DATA_BYTES (3)
-# define NUM_ENCODED_DATA_BYTES (4)
-#endif /* !B64_DOCUMENTATION_SKIP_SECTION */
-
-/* /////////////////////////////////////////////////////////////////////////////
- * Warnings
- */
-
-#if defined(_MSC_VER) && \
- _MSC_VER < 1000
-# pragma warning(disable : 4705)
-#endif /* _MSC_VER < 1000 */
-
-/* /////////////////////////////////////////////////////////////////////////////
- * Data
- */
-
-static const char b64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-static const signed char b64_indexes[] =
-{
- /* 0 - 31 / 0x00 - 0x1f */
- -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
- /* 32 - 63 / 0x20 - 0x3f */
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, 62, -1, -1, -1, 63 /* ... , '+', ... '/' */
- , 52, 53, 54, 55, 56, 57, 58, 59 /* '0' - '7' */
- , 60, 61, -1, -1, -1, -1, -1, -1 /* '8', '9', ... */
- /* 64 - 95 / 0x40 - 0x5f */
- , -1, 0, 1, 2, 3, 4, 5, 6 /* ..., 'A' - 'G' */
- , 7, 8, 9, 10, 11, 12, 13, 14 /* 'H' - 'O' */
- , 15, 16, 17, 18, 19, 20, 21, 22 /* 'P' - 'W' */
- , 23, 24, 25, -1, -1, -1, -1, -1 /* 'X', 'Y', 'Z', ... */
- /* 96 - 127 / 0x60 - 0x7f */
- , -1, 26, 27, 28, 29, 30, 31, 32 /* ..., 'a' - 'g' */
- , 33, 34, 35, 36, 37, 38, 39, 40 /* 'h' - 'o' */
- , 41, 42, 43, 44, 45, 46, 47, 48 /* 'p' - 'w' */
- , 49, 50, 51, -1, -1, -1, -1, -1 /* 'x', 'y', 'z', ... */
-
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
-
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
-
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
-
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
- , -1, -1, -1, -1, -1, -1, -1, -1
-};
-
-/* /////////////////////////////////////////////////////////////////////////////
- * Helper functions
- */
-
-/** This function reads in 3 bytes at a time, and translates them into 4
- * characters.
- */
-static size_t SecBase64Encode_( unsigned char const *src
- , size_t srcSize
- , char *const dest
- , size_t destLen
- , unsigned lineLen
- , SecBase64Result *rc)
-{
- size_t total = ((srcSize + (NUM_PLAIN_DATA_BYTES - 1)) / NUM_PLAIN_DATA_BYTES) * NUM_ENCODED_DATA_BYTES;
-
- assert(NULL != rc);
- *rc = kSecB64_R_OK;
-
- if(lineLen > 0)
- {
- size_t numLines = (total + (lineLen - 1)) / lineLen;
-
- total += 2 * (numLines - 1);
- }
-
- if(NULL == dest)
- {
- return total;
- }
- else if(destLen < total)
- {
- *rc = kSecB64_R_INSUFFICIENT_BUFFER;
-
- return 0;
- }
- else
- {
- char *p = dest;
- char *end = dest + destLen;
- size_t len = 0;
-
- for(; NUM_PLAIN_DATA_BYTES <= srcSize; srcSize -= NUM_PLAIN_DATA_BYTES)
- {
- char characters[NUM_ENCODED_DATA_BYTES];
-
- /*
- *
- * | 0 | 1 | 2 |
- *
- * | | | |
- * | | | | | | |
- * | | | | | | | | | | | | |
- * | | | | | | | | | | | | | | | | | | | | | | | | |
- *
- * | 0 | 1 | 2 | 3 |
- *
- */
-
- /* characters[0] is the 6 left-most bits of src[0] */
- characters[0] = (char)((src[0] & 0xfc) >> 2);
- /* characters[0] is the right-most 2 bits of src[0] and the left-most 4 bits of src[1] */
- characters[1] = (char)(((src[0] & 0x03) << 4) + ((src[1] & 0xf0) >> 4));
- /* characters[0] is the right-most 4 bits of src[1] and the 2 left-most bits of src[2] */
- characters[2] = (char)(((src[1] & 0x0f) << 2) + ((src[2] & 0xc0) >> 6));
- /* characters[3] is the right-most 6 bits of src[2] */
- characters[3] = (char)(src[2] & 0x3f);
-
-#ifndef __WATCOMC__
- assert(characters[0] >= 0 && characters[0] < 64);
- assert(characters[1] >= 0 && characters[1] < 64);
- assert(characters[2] >= 0 && characters[2] < 64);
- assert(characters[3] >= 0 && characters[3] < 64);
-#endif /* __WATCOMC__ */
-
- src += NUM_PLAIN_DATA_BYTES;
- *p++ = b64_chars[(unsigned char)characters[0]];
- assert(NULL != strchr(b64_chars, *(p-1)));
- ++len;
- assert(len != lineLen);
-
- *p++ = b64_chars[(unsigned char)characters[1]];
- assert(NULL != strchr(b64_chars, *(p-1)));
- ++len;
- assert(len != lineLen);
-
- *p++ = b64_chars[(unsigned char)characters[2]];
- assert(NULL != strchr(b64_chars, *(p-1)));
- ++len;
- assert(len != lineLen);
-
- *p++ = b64_chars[(unsigned char)characters[3]];
- assert(NULL != strchr(b64_chars, *(p-1)));
-
- if( ++len == lineLen &&
- p != end)
- {
- *p++ = '\r';
- *p++ = '\n';
- len = 0;
- }
- }
-
- if(0 != srcSize)
- {
- /* Deal with the overspill, by boosting it up to three bytes (using 0s)
- * and then appending '=' for any missing characters.
- *
- * This is done into a temporary buffer, so we can call ourselves and
- * have the output continue to be written direct to the destination.
- */
-
- unsigned char dummy[NUM_PLAIN_DATA_BYTES];
- size_t i;
-
- for(i = 0; i < srcSize; ++i)
- {
- dummy[i] = *src++;
- }
-
- for(; i < NUM_PLAIN_DATA_BYTES; ++i)
- {
- dummy[i] = '\0';
- }
-
- SecBase64Encode_(&dummy[0], NUM_PLAIN_DATA_BYTES, p, NUM_ENCODED_DATA_BYTES * (1 + 2), 0, rc);
-
- for(p += 1 + srcSize; srcSize++ < NUM_PLAIN_DATA_BYTES; )
- {
- *p++ = '=';
- }
- }
-
- return total;
- }
-}
-
-/** This function reads in a character string in 4-character chunks, and writes
- * out the converted form in 3-byte chunks to the destination.
- */
-static size_t SecBase64Decode_( char const *src
- , size_t srcLen
- , unsigned char *dest
- , size_t destSize
- , unsigned flags
- , char const **badChar
- , SecBase64Result *rc)
-{
- const size_t wholeChunks = (srcLen / NUM_ENCODED_DATA_BYTES);
- const size_t remainderBytes = (srcLen % NUM_ENCODED_DATA_BYTES);
- size_t maxTotal = (wholeChunks + (0 != remainderBytes)) * NUM_PLAIN_DATA_BYTES;
- unsigned char *dest_ = dest;
-
- ((void)remainderBytes);
-
- assert(NULL != badChar);
- assert(NULL != rc);
-
- *badChar = NULL;
- *rc = kSecB64_R_OK;
-
- if(NULL == dest)
- {
- return maxTotal;
- }
- else if(destSize < maxTotal)
- {
- *rc = kSecB64_R_INSUFFICIENT_BUFFER;
-
- return 0;
- }
- else
- {
- /* Now we iterate through the src, collecting together four characters
- * at a time from the Base-64 alphabet, until the end-point is reached.
- *
- *
- */
-
- char const *begin = src;
- char const *const end = begin + srcLen;
- size_t currIndex = 0;
- size_t numPads = 0;
- signed char indexes[NUM_ENCODED_DATA_BYTES]; /* 4 */
-
- for(; begin != end; ++begin)
- {
- const char ch = *begin;
-
- if('=' == ch)
- {
- assert(currIndex < NUM_ENCODED_DATA_BYTES);
-
- indexes[currIndex++] = '\0';
-
- ++numPads;
- }
- else
- {
- signed char ix = b64_indexes[(unsigned char)ch];
-
- if(-1 == ix)
- {
- switch(ch)
- {
- case ' ':
- case '\t':
- case '\b':
- case '\v':
- if(kSecB64_F_STOP_ON_UNEXPECTED_WS & flags)
- {
- *rc = kSecB64_R_DATA_ERROR;
- *badChar = begin;
- return 0;
- }
- else
- {
- /* Fall through */
- }
- case '\r':
- case '\n':
- continue;
- default:
- if(kSecB64_F_STOP_ON_UNKNOWN_CHAR & flags)
- {
- *rc = kSecB64_R_DATA_ERROR;
- *badChar = begin;
- return 0;
- }
- else
- {
- continue;
- }
- }
- }
- else
- {
- numPads = 0;
-
- assert(currIndex < NUM_ENCODED_DATA_BYTES);
-
- indexes[currIndex++] = ix;
- }
- }
-
- if(NUM_ENCODED_DATA_BYTES == currIndex)
- {
- unsigned char bytes[NUM_PLAIN_DATA_BYTES]; /* 3 */
-
- bytes[0] = (unsigned char)((indexes[0] << 2) + ((indexes[1] & 0x30) >> 4));
-
- currIndex = 0;
-
- *dest++ = bytes[0];
- if(2 != numPads)
- {
- bytes[1] = (unsigned char)(((indexes[1] & 0xf) << 4) + ((indexes[2] & 0x3c) >> 2));
-
- *dest++ = bytes[1];
-
- if(1 != numPads)
- {
- bytes[2] = (unsigned char)(((indexes[2] & 0x3) << 6) + indexes[3]);
-
- *dest++ = bytes[2];
- }
- }
- if(0 != numPads)
- {
- break;
- }
- }
- }
-
- return (size_t)(dest - dest_);
- }
-}
-
-/* /////////////////////////////////////////////////////////////////////////////
- * API functions
- */
-
-size_t SecBase64Encode(void const *src, size_t srcSize, char *dest, size_t destLen)
-{
- /* Use Null Object (Variable) here for rc, so do not need to check
- * elsewhere.
- */
- SecBase64Result rc_;
-
- return SecBase64Encode_((unsigned char const*)src, srcSize, dest, destLen, 0, &rc_);
-}
-
-size_t SecBase64Encode2( void const *src
- , size_t srcSize
- , char *dest
- , size_t destLen
- , unsigned flags
- , int lineLen /* = -1 */
- , SecBase64Result *rc /* = NULL */)
-{
- /* Use Null Object (Variable) here for rc, so do not need to check
- * elsewhere
- */
- SecBase64Result rc_;
- if(NULL == rc)
- {
- rc = &rc_;
- }
-
- switch(kSecB64_F_LINE_LEN_MASK & flags)
- {
- case kSecB64_F_LINE_LEN_USE_PARAM:
- if(lineLen >= 0)
- {
- break;
- }
- /* Fall through to 64 */
- case kSecB64_F_LINE_LEN_64:
- lineLen = 64;
- break;
- case kSecB64_F_LINE_LEN_76:
- lineLen = 76;
- break;
- default:
- assert(!"Bad line length flag specified to SecBase64Encode2()");
- case kSecB64_F_LINE_LEN_INFINITE:
- lineLen = 0;
- break;
- }
-
- assert(0 == (lineLen % 4));
-
- return SecBase64Encode_((unsigned char const*)src, srcSize, dest, destLen, (unsigned)lineLen, rc);
-}
-
-size_t SecBase64Decode(char const *src, size_t srcLen, void *dest, size_t destSize)
-{
- /* Use Null Object (Variable) here for rc and badChar, so do not need to
- * check elsewhere.
- */
- char const *badChar_;
- SecBase64Result rc_;
-
- return SecBase64Decode_(src, srcLen, (unsigned char*)dest, destSize, kSecB64_F_STOP_ON_NOTHING, &badChar_, &rc_);
-}
-
-size_t SecBase64Decode2( char const *src
- , size_t srcLen
- , void *dest
- , size_t destSize
- , unsigned flags
- , char const **badChar /* = NULL */
- , SecBase64Result *rc /* = NULL */)
-{
- char const *badChar_;
- SecBase64Result rc_;
-
- /* Use Null Object (Variable) here for rc and badChar, so do not need to
- * check elsewhere.
- */
- if(NULL == badChar)
- {
- badChar = &badChar_;
- }
- if(NULL == rc)
- {
- rc = &rc_;
- }
-
- return SecBase64Decode_(src, srcLen, (unsigned char*)dest, destSize, flags, badChar, rc);
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
#include <sys/param.h>
#include <syslog.h>
#include "CertificateValues.h"
-#include "SecCertificateP.h"
-#include "SecCertificatePrivP.h"
#include "AppleBaselineEscrowCertificates.h"
*/
return SecCertificateIsValid(certificate, verifyTime);
}
+
+/* OS X only */
+CFDataRef SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator,
+ CFDataRef der_certificate)
+{
+ CFDataRef result = NULL;
+ SecCertificateRef iosCertRef = SecCertificateCreateWithData(allocator, der_certificate);
+ if (NULL == iosCertRef)
+ {
+ return result;
+ }
+
+ result = SecCertificateCopyPublicKeySHA1Digest(iosCertRef);
+ CFRelease(iosCertRef);
+ return result;
+}
+
+++ /dev/null
-/*
- * Copyright (c) 2007-2011,2013-2015 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*
- SecCertificateInternal.h
-*/
-
-#ifndef _SECURITY_SECCERTIFICATEINTERNAL_H_
-#define _SECURITY_SECCERTIFICATEINTERNAL_H_
-
-//#include <Security/SecCertificatePrivP.h>
-#include "SecCertificatePrivP.h"
-#include "certextensionsP.h"
-#include <libDER/DER_Keys.h>
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-CFDataRef SecCertificateGetAuthorityKeyIDP(SecCertificateRefP certificate);
-CFDataRef SecCertificateGetSubjectKeyIDP(SecCertificateRefP certificate);
-
-/* Return an array of CFURLRefs each of which is an crl distribution point for
- this certificate. */
-CFArrayRef SecCertificateGetCRLDistributionPointsP(SecCertificateRefP certificate);
-
-/* Return an array of CFURLRefs each of which is an ocspResponder for this
- certificate. */
-CFArrayRef SecCertificateGetOCSPRespondersP(SecCertificateRefP certificate);
-
-/* Return an array of CFURLRefs each of which is an caIssuer for this
- certificate. */
-CFArrayRef SecCertificateGetCAIssuersP(SecCertificateRefP certificate);
-
-/* Dump certificate for debugging. */
-void SecCertificateShowP(SecCertificateRefP certificate);
-
-/* Return the DER encoded issuer sequence for the receiving certificates issuer. */
-CFDataRef SecCertificateCopyIssuerSequenceP(SecCertificateRefP certificate);
-
-/* Return the DER encoded subject sequence for the receiving certificates subject. */
-CFDataRef SecCertificateCopySubjectSequenceP(SecCertificateRefP certificate);
-
-/* Return the content of a DER encoded X.501 name (without the tag and length
- fields) for the receiving certificates issuer. */
-CFDataRef SecCertificateGetNormalizedIssuerContentP(SecCertificateRefP certificate);
-
-/* Return the content of a DER encoded X.501 name (without the tag and length
- fields) for the receiving certificates subject. */
-CFDataRef SecCertificateGetNormalizedSubjectContentP(SecCertificateRefP certificate);
-
-CFDataRef SecDERItemCopySequenceP(DERItem *content);
-
-/* Return true iff the certificate has a subject. */
-bool SecCertificateHasSubjectP(SecCertificateRefP certificate);
-
-/* Return true iff the certificate has a critical subject alt name. */
-bool SecCertificateHasCriticalSubjectAltNameP(SecCertificateRefP certificate);
-
-/* Return true if certificate contains one or more critical extensions we
- are unable to parse. */
-bool SecCertificateHasUnknownCriticalExtensionP(SecCertificateRefP certificate);
-
-/* Return true iff certificate is valid as of verifyTime. */
-bool SecCertificateIsValidP(SecCertificateRefP certificate,
- CFAbsoluteTime verifyTime);
-
-/* Return an attribute dictionary used to store this item in a keychain. */
-CFDictionaryRef SecCertificateCopyAttributeDictionaryP(
- SecCertificateRefP certificate);
-
-/* Return a certificate from the attribute dictionary that was used to store
- this item in a keychain. */
-SecCertificateRefP SecCertificateCreateFromAttributeDictionaryP(
- CFDictionaryRef refAttributes);
-
-/* Return a SecKeyRef for the public key embedded in the cert. */
-SecKeyRefP SecCertificateCopyPublicKeyP(SecCertificateRefP certificate);
-
-/* Return the SecCEBasicConstraints extension for this certificate if it
- has one. */
-const SecCEBasicConstraints *
-SecCertificateGetBasicConstraintsP(SecCertificateRefP certificate);
-
-/* Return the SecCEPolicyConstraints extension for this certificate if it
- has one. */
-const SecCEPolicyConstraints *
-SecCertificateGetPolicyConstraintsP(SecCertificateRefP certificate);
-
-/* Return a dictionary from CFDataRef to CFArrayRef of CFDataRef
- representing the policyMapping extension of this certificate. */
-CFDictionaryRef
-SecCertificateGetPolicyMappingsP(SecCertificateRefP certificate);
-
-/* Return the SecCECertificatePolicies extension for this certificate if it
- has one. */
-const SecCECertificatePolicies *
-SecCertificateGetCertificatePoliciesP(SecCertificateRefP certificate);
-
-/* Returns UINT32_MAX if InhibitAnyPolicy extension is not present or invalid,
- returns the value of the SkipCerts field of the InhibitAnyPolicy extension
- otherwise. */
-uint32_t
-SecCertificateGetInhibitAnyPolicySkipCertsP(SecCertificateRefP certificate);
-
-/* Return the public key algorithm and parameters for certificate. */
-const DERAlgorithmId *SecCertificateGetPublicKeyAlgorithmP(
- SecCertificateRefP certificate);
-
-/* Return the raw public key data for certificate. */
-const DERItem *SecCertificateGetPublicKeyDataP(SecCertificateRefP certificate);
-
-#pragma mark -
-#pragma mark Certificate Operations
-
-OSStatus SecCertificateIsSignedByP(SecCertificateRefP certificate,
- SecKeyRefP issuerKey);
-
-#pragma mark -
-#pragma mark Certificate Creation
-
-#ifdef OPTIONAL_METHODS
-/* Return a certificate for the PEM representation of this certificate.
- Return NULL the passed in der_certificate is not a valid DER encoded X.509
- certificate, and return a CFError by reference. It is the
- responsibility of the caller to release the CFError. */
-SecCertificateRefP SecCertificateCreateWithPEMP(CFAllocatorRef allocator,
- CFStringRef pem_certificate);
-
-/* Return a CFStringRef containing the the pem representation of this
- certificate. */
-CFStringRef SecCertificateGetPEMP(SecCertificateRefP der_certificate);
-
-#endif /* OPTIONAL_METHODS */
-
-#if 0
-/* Complete the certificate chain of this certificate, setting the parent
- certificate for each certificate along they way. Return 0 if the
- system is able to find all the certificates to complete the certificate
- chain either in the passed in other_certificates array or in the user or
- the systems keychain(s).
- If the certificate's issuer chain can not be completed, this function
- will return an error status code.
- NOTE: This function does not verify whether the certificate is trusted it's
- main use is just to ensure that anyone using this certificate upstream will
- have access to a complete (or as complete as possible in the case of
- something going wrong) certificate chain. */
-OSStatus SecCertificateCompleteChainP(SecCertificateRefP certificate,
- CFArrayRef other_certificates);
-#endif
-
-#if 0
-
-/*!
- @function SecCertificateGetVersionNumberP
- @abstract Retrieves the version of a given certificate as a CFNumberRef.
- @param certificate A reference to the certificate from which to obtain the certificate version.
- @result A CFNumberRef representing the certificate version. The following values are currently known to be returned, but more may be added in the future:
- 1: X509v1
- 2: X509v2
- 3: X509v3
-*/
-CFNumberRef SecCertificateGetVersionNumberP(SecCertificateRefP certificate);
-
-/*!
- @function SecCertificateGetSerialDERP
- @abstract Retrieves the serial number of a given certificate in DER encoding.
- @param certificate A reference to the certificate from which to obtain the serial number.
- @result A CFDataRef containing the DER encoded serial number of the certificate, minus the tag and length fields.
-*/
-CFDataRef SecCertificateGetSerialDERP(SecCertificateRefP certificate);
-
-
-/*!
- @function SecCertificateGetSerialStringP
- @abstract Retrieves the serial number of a given certificate in human readable form.
- @param certificate A reference to the certificate from which to obtain the serial number.
- @result A CFStringRef containing the human readable serial number of the certificate in decimal form.
-*/
-CFStringRef SecCertificateGetSerialStringP(SecCertificateRefP certificate);
-
-
-
-CFDataRef SecCertificateGetPublicKeyDERP(SecCertificateRefP certificate);
-CFDataRef SecCertificateGetPublicKeySHA1FingerPrintP(SecCertificateRefP certificate);
-CFDataRef SecCertificateGetPublicKeyMD5FingerPrintP(SecCertificateRefP certificate);
-CFDataRef SecCertificateGetSignatureAlgorithmDERP(SecCertificateRefP certificate);
-CFDataRef SecCertificateGetSignatureAlgorithmNameP(SecCertificateRefP certificate);
-CFStringRef SecCertificateGetSignatureAlgorithmOIDP(SecCertificateRefP certificate);
-CFDataRef SecCertificateGetSignatureDERP(SecCertificateRefP certificate);
-CFDataRef SecCertificateGetSignatureAlgorithmParametersDERP(SecCertificateRefP certificate);
-
-/* plist top level array is orderd list of key/value pairs */
-CFArrayRef SecCertificateGetSignatureAlgorithmParametersArrayP(SecCertificateRefP certificate);
-
-#if 0
-/* This cert is signed by its parent? */
-bool SecCertificateIsSignatureValidP(SecCertificateRefP certificate);
-
-/* This cert is signed by its parent and so on until no parent certificate can be found? */
-bool SecCertificateIsIssuerChainValidP(SecCertificateRefP certificate, CFArrayRef additionalCertificatesToSearch);
-
-/* This cert is signed by its parent and so on until no parent certificate can be found? */
-bool SecCertificateIsSignatureChainValidP(SecCertificateRefP certificate);
-
-/* This cert is signed by its parent and so on until a certiicate in anchors can be found. */
-bool SecCertificateIssuerChainHasAnchorInP(SecCertificateRefP certificate, CFArrayRef anchors);
-
-/* This cert is signed by its parent and so on until a certiicate in anchors can be found. */
-bool SecCertificateSignatureChainHasAnchorInP(SecCertificateRefP certificate, CFArrayRef anchors);
-
-bool SecCertificateIsSelfSignedP(SecCertificateRefP certificate);
-#endif
-
-
-/* The entire certificate in DER encoding including the outer tag and length fields. */
-CFDataRef SecCertificateGetDERP(SecCertificateRefP certificate);
-
-/* Returns the status code of the last failed call for this certificate on this thread. */
-OSStatus SecCertificateGetStatusP(SecCertificateRefP certificate);
-
-CFDataRef SecCertificateGetIssuerDERP(SecCertificateRefP certificate);
-CFDataRef SecCertificateGetNormalizedIssuerDERP(SecCertificateRefP certificate);
-
-/* Return the issuer as an X509 name encoded in an array. Each element in this array is an array. Each inner array has en even number of elements. Each pair of elements in the inner array represents a key and a value. The key is a string and the value is also a string. Elements in the outer array should be considered ordered while pairs in the inner array should not. */
-CFArrayRef SecCertificateGetIssuerArrayP(SecCertificateRefP certificate);
-
-
-CFDataRef SecCertificateGetSubjectDERP(SecCertificateRefP certificate);
-CFDataRef SecCertificateGetNormalizedSubjectDERP(SecCertificateRefP certificate);
-/* See SecCertificateGetIssuerArray for a description of the returned array. */
-CFArrayRef SecCertificateGetSubjectArrayP(SecCertificateRefP certificate);
-
-CFDateRef SecCertificateGetNotValidBeforeDateP(SecCertificateRefP certificate);
-CFDateRef SecCertificateGetNotValidDateP(SecCertificateRefP certificate);
-
-
-#if 0
-
-CFIndex SecCertificateGetExtensionCountP(SecCertificateRefP certificate, index);
-CFDataRef SecCertificateGetExtensionAtIndexDERP(SecCertificateRefP certificate, CFIndex index);
-bool SecCertificateIsExtensionAtIndexCriticalP(SecCertificateRefP certificate, CFIndex index);
-
-/* array see email example. */
-CFArrayRef SecCertificateGetExtensionAtIndexParamsArrayP(SecCertificateRefP certificate, CFIndex index);
-
-CFStringRef SecCertificateGetExtensionAtIndexNameP(SecCertificateRefP certificate, CFIndex index);
-CFStringRef SecCertificateGetExtensionAtIndexOIDP(SecCertificateRefP certificate, CFIndex index);
-
-#else
-
-/* Return an array with all of this certificates SecCertificateExtensionRefs. */
-CFArrayRef SecCertificateGetExtensionsP(SecCertificateRefP certificate);
-
-/* Return the SecCertificateExtensionRef for the extension with the given oid. Return NULL if it does not exist or if an error occours call SecCertificateGetStatus() to see if an error occured or not. */
-SecCertificateExtensionRef SecCertificateGetExtensionWithOIDP(SecCertificateRefP certificate, CFDataRef oid);
-
-CFDataRef SecCertificateExtensionGetDERP(SecCertificateExtensionRef extension, CFDataRef oid);
-CFStringRef SecCertificateExtensionNameP(SecCertificateExtensionRef extension);
-CFDataRef SecCertificateExtensionGetOIDDERP(SecCertificateExtensionRef extension, CFDataRef oid);
-CFStringRef SecCertificateExtensionGetOIDStringP(SecCertificateExtensionRef extension, CFDataRef oid);
-bool SecCertificateExtensionIsCriticalP(SecCertificateExtensionRef extension);
-CFArrayRef SecCertificateExtensionGetContentDERP(SecCertificateExtensionRef extension);
-
-/* Return the content of extension as an array. The array has en even number of elements. Each pair of elements in the array represents a key and a value. The key is a string and the value is either a string, or dictionary or an array of key value pairs like the outer array. */
-CFArrayRef SecCertificateExtensionGetContentArrayP(SecCertificateExtensionRef extension);
-
-#endif /* 0 */
-
-#endif /* 0 */
-
-
-void appendPropertyP(CFMutableArrayRef properties,
- CFStringRef propertyType, CFStringRef label, CFTypeRef value);
-
-#if 0
-/* Utility functions. */
-CFStringRef SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator,
- const DERItem *oid);
-CFDataRef createNormalizedX501Name(CFAllocatorRef allocator,
- const DERItem *x501name);
-
-/* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
- an absoluteTime if the date was valid and properly decoded. Return
- NULL_TIME otherwise. */
-CFAbsoluteTime SecAbsoluteTimeFromDateContent(DERTag tag, const uint8_t *bytes,
- size_t length);
-#endif
-
-#if defined(__cplusplus)
-}
-#endif
-
-#endif /* !_SECURITY_SECCERTIFICATEINTERNAL_H_ */
+++ /dev/null
-/*
- * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*
- * SecCertificate.c - CoreFoundation based certificate object
- */
-
-
-#include "SecCertificateInternalP.h"
-
-#include <CommonCrypto/CommonDigest.h>
-#include <CoreFoundation/CFRuntime.h>
-#include <CoreFoundation/CFString.h>
-#include <CoreFoundation/CFBundle.h>
-#include <CoreFoundation/CFDictionary.h>
-#include <CoreFoundation/CFNumber.h>
-#include <CoreFoundation/CFTimeZone.h>
-#include <pthread.h>
-#include <string.h>
-#include <AssertMacros.h>
-#include <libDER/libDER.h>
-#include <libDER/DER_CertCrl.h>
-#include <libDER/DER_Encode.h>
-#include <libDER/DER_Keys.h>
-#include <libDER/asn1Types.h>
-#include <libDER/oidsPriv.h>
-
-#include "SecBasePriv.h"
-
-#include "SecRSAKeyP.h"
-#include "SecFrameworkP.h"
-#include "SecItem.h"
-#include "SecItemPriv.h"
-#include <stdbool.h>
-#include <stdlib.h>
-#include <libkern/OSByteOrder.h>
-#include <ctype.h>
-#include "SecInternal.h"
-#include "SecBase64P.h"
-
-#include <security_utilities/debugging.h>
-
-typedef struct SecCertificateExtension {
- DERItem extnID;
- bool critical;
- DERItem extnValue;
-} SecCertificateExtension;
-
-#if 0
-typedef struct KnownExtension {
- bool critical;
- DERItem extnValue;
-} KnownExtension;
-
-enum {
- kSecSelfSignedUnknown = 0,
- kSecSelfSignedFalse,
- kSecSelfSignedTrue,
-};
-#endif
-
-struct __SecCertificate {
- CFRuntimeBase _base;
-
- DERItem _der; /* Entire certificate in DER form. */
- DERItem _tbs; /* To Be Signed cert DER bytes. */
- DERAlgorithmId _sigAlg; /* Top level signature algorithm. */
- DERItem _signature; /* The content of the sig bit string. */
-
- UInt8 _version;
- DERItem _serialNum; /* Integer. */
- DERAlgorithmId _tbsSigAlg; /* sig alg MUST be same as _sigAlg. */
- DERItem _issuer; /* Sequence of RDN. */
- CFAbsoluteTime _notBefore;
- CFAbsoluteTime _notAfter;
- DERItem _subject; /* Sequence of RDN. */
- DERAlgorithmId _algId; /* oid and params of _pubKeyDER. */
- DERItem _pubKeyDER; /* contents of bit string */
- DERItem _issuerUniqueID; /* bit string, optional */
- DERItem _subjectUniqueID; /* bit string, optional */
-
-#if 0
- /* Known extensions if the certificate contains them,
- extnValue.length will be > 0. */
- KnownExtension _authorityKeyID;
-
- /* This extension is used to uniquely identify a certificate from among
- several that have the same subject name. If the extension is not
- present, its value is calculated by performing a SHA-1 hash of the
- certificate's DER encoded subjectPublicKeyInfo, as recommended by
- PKIX. */
- KnownExtension _subjectKeyID;
- KnownExtension _keyUsage;
- KnownExtension _extendedKeyUsage;
- KnownExtension _basicConstraints;
- KnownExtension _netscapeCertType;
- KnownExtension _subjectAltName;
- KnownExtension _qualCertStatements;
-
-#endif
- bool _foundUnknownCriticalExtension;
-
- /* Well known certificate extensions. */
- SecCEBasicConstraints _basicConstraints;
- SecCEPolicyConstraints _policyConstraints;
- CFDictionaryRef _policyMappings;
- SecCECertificatePolicies _certificatePolicies;
-
- /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
- value of the SkipCerts field of the InhibitAnyPolicy extension
- otherwise. */
- uint32_t _inhibitAnyPolicySkipCerts;
-
- /* If KeyUsage extension is not present this is 0, otherwise it's
- the value of the extension. */
- SecKeyUsage _keyUsage;
-
- /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
- Length = 0 if not present. */
- DERItem _subjectKeyIdentifier;
-
- /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
- Length = 0 if not present. */
- DERItem _authorityKeyIdentifier;
- /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
- _authorityKeyIdentifierSerialNumber have non zero length if present.
- Both are either present or absent together. */
- DERItem _authorityKeyIdentifierIssuer;
- DERItem _authorityKeyIdentifierSerialNumber;
-
- /* Subject alt name extension, if present. Not malloced, it's just a
- pointer to an element in the _extensions array. */
- const SecCertificateExtension *_subjectAltName;
-
- /* Parsed extension values. */
-
- /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
- CFMutableArrayRef _crlDistributionPoints;
-
- /* Array of CFURLRefs containing the URI values of accessLocations of each
- id-ad-ocsp AccessDescription in the Authority Information Access
- extension. */
- CFMutableArrayRef _ocspResponders;
-
- /* Array of CFURLRefs containing the URI values of accessLocations of each
- id-ad-caIssuers AccessDescription in the Authority Information Access
- extension. */
- CFMutableArrayRef _caIssuers;
-
- /* All other (non known) extensions. The _extensions array is malloced. */
- CFIndex _extensionCount;
- SecCertificateExtension *_extensions;
-
- /* Optional cached fields. */
- SecKeyRef _pubKey;
- CFDataRef _der_data;
- CFArrayRef _properties;
- CFDataRef _serialNumber;
- CFDataRef _normalizedIssuer;
- CFDataRef _normalizedSubject;
- CFDataRef _authorityKeyID;
- CFDataRef _subjectKeyID;
-
- CFDataRef _sha1Digest;
- uint8_t _isSelfSigned;
-
-};
-
-/* Public Constants for property list keys. */
-const CFStringRef kSecPropertyKeyType = CFSTR("type");
-const CFStringRef kSecPropertyKeyLabel = CFSTR("label");
-const CFStringRef kSecPropertyKeyLocalizedLabel = CFSTR("localized label");
-const CFStringRef kSecPropertyKeyValue = CFSTR("value");
-
-/* Public Constants for property list values. */
-const CFStringRef kSecPropertyTypeWarning = CFSTR("warning");
-const CFStringRef kSecPropertyTypeError = CFSTR("error");
-const CFStringRef kSecPropertyTypeSuccess = CFSTR("success");
-const CFStringRef kSecPropertyTypeTitle = CFSTR("title");
-const CFStringRef kSecPropertyTypeSection = CFSTR("section");
-const CFStringRef kSecPropertyTypeData = CFSTR("data");
-const CFStringRef kSecPropertyTypeString = CFSTR("string");
-const CFStringRef kSecPropertyTypeURL = CFSTR("url");
-const CFStringRef kSecPropertyTypeDate = CFSTR("date");
-
-/* Extension parsing routine. */
-typedef void (*SecCertificateExtensionParser)(SecCertificateRefP certificate,
- const SecCertificateExtension *extn);
-
-/* CFRuntime regsitration data. */
-static pthread_once_t kSecCertificateRegisterClass = PTHREAD_ONCE_INIT;
-static CFTypeID kSecCertificateTypeID = _kCFRuntimeNotATypeID;
-
-/* Mapping from extension OIDs (as a DERItem *) to
- SecCertificateExtensionParser extension parsing routines. */
-static CFDictionaryRef gExtensionParsers;
-
-/* Forward declartions of static functions. */
-static CFStringRef SecCertificateCopyDescription(CFTypeRef cf);
-static void SecCertificateDestroy(CFTypeRef cf);
-static bool derDateGetAbsoluteTime(const DERItem *dateChoice,
- CFAbsoluteTime *absTime);
-
-/* Static functions. */
-static CFStringRef SecCertificateCopyDescription(CFTypeRef cf) {
- SecCertificateRefP certificate = (SecCertificateRefP)cf;
- CFStringRef ret = NULL;
- CFStringRef subjectSummary = SecCertificateCopySubjectSummaryP(certificate);
- CFStringRef issuerSummary = SecCertificateCopyIssuerSummaryP(certificate);
-
- ret = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
- CFSTR("<cert(%p) s: %@ i: %@>"), certificate,
- subjectSummary,
- issuerSummary);
-
- CFReleaseNull(subjectSummary);
- CFReleaseNull(issuerSummary);
- return ret;
-}
-
-static void SecCertificateDestroy(CFTypeRef cf) {
- SecCertificateRefP certificate = (SecCertificateRefP)cf;
- if (certificate->_certificatePolicies.policies) {
- free(certificate->_certificatePolicies.policies);
- certificate->_certificatePolicies.policies = NULL;
- }
- CFReleaseNull(certificate->_policyMappings);
- CFReleaseNull(certificate->_crlDistributionPoints);
- CFReleaseNull(certificate->_ocspResponders);
- CFReleaseNull(certificate->_caIssuers);
- if (certificate->_extensions) {
- free(certificate->_extensions);
- certificate->_extensions = NULL;
- }
- CFReleaseNull(certificate->_pubKey);
- CFReleaseNull(certificate->_der_data);
- CFReleaseNull(certificate->_properties);
- CFReleaseNull(certificate->_serialNumber);
- CFReleaseNull(certificate->_normalizedIssuer);
- CFReleaseNull(certificate->_normalizedSubject);
- CFReleaseNull(certificate->_authorityKeyID);
- CFReleaseNull(certificate->_subjectKeyID);
- CFReleaseNull(certificate->_sha1Digest);
-}
-
-static Boolean SecCertificateEqual(CFTypeRef cf1, CFTypeRef cf2) {
- SecCertificateRefP cert1 = (SecCertificateRefP)cf1;
- SecCertificateRefP cert2 = (SecCertificateRefP)cf2;
- if (cert1 == cert2)
- return true;
- if (!cert2 || cert1->_der.length != cert2->_der.length)
- return false;
- return !memcmp(cert1->_der.data, cert2->_der.data, cert1->_der.length);
-}
-
-/* Hash of the certificate is der length + signature length + last 4 bytes
- of signature. */
-static CFHashCode SecCertificateHash(CFTypeRef cf) {
- SecCertificateRefP certificate = (SecCertificateRefP)cf;
- DERSize der_length = certificate->_der.length;
- DERSize sig_length = certificate->_signature.length;
- DERSize ix = (sig_length > 4) ? sig_length - 4 : 0;
- CFHashCode hashCode = 0;
- for (; ix < sig_length; ++ix)
- hashCode = (hashCode << 8) + certificate->_signature.data[ix];
-
- return (hashCode + der_length + sig_length);
-}
-
-#if 1
-
-/************************************************************************/
-/************************* General Name Parsing *************************/
-/************************************************************************/
-
-typedef OSStatus (*parseGeneralNameCallback)(void *context,
- SecCEGeneralNameType type, const DERItem *value);
-
-
-/*
- GeneralName ::= CHOICE {
- otherName [0] OtherName,
- rfc822Name [1] IA5String,
- dNSName [2] IA5String,
- x400Address [3] ORAddress,
- directoryName [4] Name,
- ediPartyName [5] EDIPartyName,
- uniformResourceIdentifier [6] IA5String,
- iPAddress [7] OCTET STRING,
- registeredID [8] OBJECT IDENTIFIER}
-
- OtherName ::= SEQUENCE {
- type-id OBJECT IDENTIFIER,
- value [0] EXPLICIT ANY DEFINED BY type-id }
-
- EDIPartyName ::= SEQUENCE {
- nameAssigner [0] DirectoryString OPTIONAL,
- partyName [1] DirectoryString }
- */
-static OSStatus parseGeneralNameContentProperty(DERTag tag,
- const DERItem *generalNameContent,
- void *context, parseGeneralNameCallback callback) {
- switch (tag) {
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0:
- return callback(context, GNT_OtherName, generalNameContent);
- case ASN1_CONTEXT_SPECIFIC | 1:
- return callback(context, GNT_RFC822Name, generalNameContent);
- case ASN1_CONTEXT_SPECIFIC | 2:
- return callback(context, GNT_DNSName, generalNameContent);
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3:
- return callback(context, GNT_X400Address, generalNameContent);
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 4:
- return callback(context, GNT_DirectoryName, generalNameContent);
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 5:
- return callback(context, GNT_EdiPartyName, generalNameContent);
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6:
- {
- /* Technically I don't think this is valid, but there are certs out
- in the wild that use a constructed IA5String. In particular the
- VeriSign Time Stamping Authority CA.cer does this. */
- DERDecodedInfo uriContent;
- require_noerr(DERDecodeItem(generalNameContent, &uriContent), badDER);
- require(uriContent.tag == ASN1_IA5_STRING, badDER);
- return callback(context, GNT_URI, &uriContent.content);
- }
- case ASN1_CONTEXT_SPECIFIC | 6:
- return callback(context, GNT_URI, generalNameContent);
- case ASN1_CONTEXT_SPECIFIC | 7:
- return callback(context, GNT_IPAddress, generalNameContent);
- case ASN1_CONTEXT_SPECIFIC | 8:
- return callback(context, GNT_RegisteredID, generalNameContent);
- default:
- goto badDER;
- }
-badDER:
- return errSecInvalidCertificate;
-}
-
-static OSStatus parseGeneralNamesContent(const DERItem *generalNamesContent,
- void *context, parseGeneralNameCallback callback) {
- DERSequence gnSeq;
- DERReturn drtn = DERDecodeSeqContentInit(generalNamesContent, &gnSeq);
- require_noerr_quiet(drtn, badDER);
- DERDecodedInfo generalNameContent;
- while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) ==
- DR_Success) {
- OSStatus status = parseGeneralNameContentProperty(
- generalNameContent.tag, &generalNameContent.content, context,
- callback);
- if (status)
- return status;
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return errSecSuccess;
-
-badDER:
- return errSecInvalidCertificate;
-}
-
-static OSStatus parseGeneralNames(const DERItem *generalNames, void *context,
- parseGeneralNameCallback callback) {
- DERDecodedInfo generalNamesContent;
- DERReturn drtn = DERDecodeItem(generalNames, &generalNamesContent);
- require_noerr_quiet(drtn, badDER);
- require_quiet(generalNamesContent.tag == ASN1_CONSTR_SEQUENCE, badDER);
- return parseGeneralNamesContent(&generalNamesContent.content, context,
- callback);
-badDER:
- return errSecInvalidCertificate;
-}
-
-#else
-
-/*
- GeneralName ::= CHOICE {
- otherName [0] OtherName,
- rfc822Name [1] IA5String,
- dNSName [2] IA5String,
- x400Address [3] ORAddress,
- directoryName [4] Name,
- ediPartyName [5] EDIPartyName,
- uniformResourceIdentifier [6] IA5String,
- iPAddress [7] OCTET STRING,
- registeredID [8] OBJECT IDENTIFIER}
-
- EDIPartyName ::= SEQUENCE {
- nameAssigner [0] DirectoryString OPTIONAL,
- partyName [1] DirectoryString }
- */
-static OSStatus parseGeneralNameContentProperty(DERTag tag,
- const DERItem *generalNameContent, SecCEGeneralName *generalName) {
- switch (tag) {
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0:
- generalName->nameType = GNT_OtherName;
- generalName->berEncoded = true;
- generalName->name = *generalNameContent;
- break;
- case ASN1_CONTEXT_SPECIFIC | 1:
- /* IA5String. */
- generalName->nameType = GNT_RFC822Name;
- generalName->berEncoded = false;
- generalName->name = *generalNameContent;
- break;
- case ASN1_CONTEXT_SPECIFIC | 2:
- /* IA5String. */
- generalName->nameType = GNT_DNSName;
- generalName->berEncoded = false;
- generalName->name = *generalNameContent;
- break;
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3:
- generalName->nameType = GNT_X400Address;
- generalName->berEncoded = true;
- generalName->name = *generalNameContent;
- break;
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 4:
- generalName->nameType = GNT_DirectoryName;
- generalName->berEncoded = true;
- generalName->name = *generalNameContent;
- break;
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 5:
- generalName->nameType = GNT_EdiPartyName;
- generalName->berEncoded = true;
- generalName->name = *generalNameContent;
- break;
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6:
- {
- /* Technically I don't think this is valid, but there are certs out
- in the wild that use a constructed IA5String. In particular the
- VeriSign Time Stamping Authority CA.cer does this. */
- DERDecodedInfo decoded;
- require_noerr(DERDecodeItem(generalNameContent, &decoded), badDER);
- require(decoded.tag == ASN1_IA5_STRING, badDER);
- generalName->nameType = GNT_URI;
- generalName->berEncoded = false;
- generalName->name = decoded.content;
- break;
- }
- case ASN1_CONTEXT_SPECIFIC | 6:
- generalName->nameType = GNT_URI;
- generalName->berEncoded = false;
- generalName->name = *generalNameContent;
- break;
- case ASN1_CONTEXT_SPECIFIC | 7:
- /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
- 8 octects, addr/mask for ipv6 it's 32. */
- generalName->nameType = GNT_IPAddress;
- generalName->berEncoded = false;
- generalName->name = *generalNameContent;
- break;
- case ASN1_CONTEXT_SPECIFIC | 8:
- /* name is the content of an OID. */
- generalName->nameType = GNT_RegisteredID;
- generalName->berEncoded = false;
- generalName->name = *generalNameContent;
- break;
- default:
- goto badDER;
- break;
- }
- return errSecSuccess;
-badDER:
- return errSecInvalidCertificate;
-}
-
-/*
- GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
- */
-static OSStatus parseGeneralNamesContent(const DERItem *generalNamesContent,
- CFIndex *count, SecCEGeneralName **name) {
- SecCEGeneralName *generalNames = NULL;
- DERSequence gnSeq;
- DERReturn drtn = DERDecodeSeqContentInit(generalNamesContent, &gnSeq);
- require_noerr_quiet(drtn, badDER);
- DERDecodedInfo generalNameContent;
- CFIndex generalNamesCount = 0;
- while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) ==
- DR_Success) {
- ++generalNamesCount;
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
-
- require(generalNames = calloc(generalNamesCount, sizeof(SecCEGeneralName)),
- badDER);
- DERDecodeSeqContentInit(generalNamesContent, &gnSeq);
- CFIndex ix = 0;
- while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) ==
- DR_Success) {
- if (!parseGeneralNameContentProperty(generalNameContent.tag,
- &generalNameContent.content, &generalNames[ix])) {
- goto badDER;
- }
- ++ix;
- }
- *count = generalNamesCount;
- *name = generalNames;
- return errSecSuccess;
-
-badDER:
- if (generalNames)
- free(generalNames);
- return errSecInvalidCertificate;
-}
-
-static OSStatus parseGeneralNames(const DERItem *generalNames,
- CFIndex *count, SecCEGeneralName **name) {
- DERDecodedInfo generalNamesContent;
- DERReturn drtn = DERDecodeItem(generalNames, &generalNamesContent);
- require_noerr_quiet(drtn, badDER);
- require_quiet(generalNamesContent.tag == ASN1_CONSTR_SEQUENCE,
- badDER);
- parseGeneralNamesContent(&generalNamesContent.content, count, name);
- return errSecSuccess;
-badDER:
- return errSecInvalidCertificate;
-}
-#endif
-
-/************************************************************************/
-/************************** X.509 Name Parsing **************************/
-/************************************************************************/
-
-typedef OSStatus (*parseX501NameCallback)(void *context, const DERItem *type,
- const DERItem *value, CFIndex rdnIX);
-
-static OSStatus parseRDNContent(const DERItem *rdnSetContent, void *context,
- parseX501NameCallback callback) {
- DERSequence rdn;
- DERReturn drtn = DERDecodeSeqContentInit(rdnSetContent, &rdn);
- require_noerr_quiet(drtn, badDER);
- DERDecodedInfo atvContent;
- CFIndex rdnIX = 0;
- while ((drtn = DERDecodeSeqNext(&rdn, &atvContent)) == DR_Success) {
- require_quiet(atvContent.tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERAttributeTypeAndValue atv;
- drtn = DERParseSequenceContent(&atvContent.content,
- DERNumAttributeTypeAndValueItemSpecs,
- DERAttributeTypeAndValueItemSpecs,
- &atv, sizeof(atv));
- require_noerr_quiet(drtn, badDER);
- require_quiet(atv.type.length != 0, badDER);
- OSStatus status = callback(context, &atv.type, &atv.value, rdnIX++);
- if (status)
- return status;
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
-
- return errSecSuccess;
-badDER:
- return errSecInvalidCertificate;
-}
-
-static OSStatus parseX501NameContent(const DERItem *x501NameContent, void *context,
- parseX501NameCallback callback) {
- DERSequence derSeq;
- DERReturn drtn = DERDecodeSeqContentInit(x501NameContent, &derSeq);
- require_noerr_quiet(drtn, badDER);
- DERDecodedInfo currDecoded;
- while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) {
- require_quiet(currDecoded.tag == ASN1_CONSTR_SET, badDER);
- OSStatus status = parseRDNContent(&currDecoded.content, context,
- callback);
- if (status)
- return status;
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
-
- return errSecSuccess;
-
-badDER:
- return errSecInvalidCertificate;
-}
-
-static OSStatus parseX501Name(const DERItem *x501Name, void *context,
- parseX501NameCallback callback) {
- DERDecodedInfo x501NameContent;
- if (DERDecodeItem(x501Name, &x501NameContent) ||
- x501NameContent.tag != ASN1_CONSTR_SEQUENCE) {
- return errSecInvalidCertificate;
- } else {
- return parseX501NameContent(&x501NameContent.content, context,
- callback);
- }
-}
-
-/************************************************************************/
-/********************** Extension Parsing Routines **********************/
-/************************************************************************/
-
-static void SecCEPSubjectKeyIdentifier(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- DERDecodedInfo keyIdentifier;
- DERReturn drtn = DERDecodeItem(&extn->extnValue, &keyIdentifier);
- require_noerr_quiet(drtn, badDER);
- require_quiet(keyIdentifier.tag == ASN1_OCTET_STRING, badDER);
- certificate->_subjectKeyIdentifier = keyIdentifier.content;
-
- return;
-badDER:
- secinfo("cert", "Invalid SubjectKeyIdentifier Extension");
-}
-
-static void SecCEPKeyUsage(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- SecKeyUsage keyUsage = extn->critical ? kSecKeyUsageCritical : 0;
- DERDecodedInfo bitStringContent;
- DERReturn drtn = DERDecodeItem(&extn->extnValue, &bitStringContent);
- require_noerr_quiet(drtn, badDER);
- require_quiet(bitStringContent.tag == ASN1_BIT_STRING, badDER);
- DERSize len = bitStringContent.content.length - 1;
- require_quiet(len == 1 || len == 2, badDER);
- DERByte numUnusedBits = bitStringContent.content.data[0];
- require_quiet(numUnusedBits < 8, badDER);
- /* Flip the bits in the bit string so the first bit in the lsb. */
- uint_fast16_t bits = 8 * len - numUnusedBits;
- uint_fast16_t value = bitStringContent.content.data[1];
- uint_fast16_t mask;
- if (len > 1) {
- value = (value << 8) + bitStringContent.content.data[2];
- mask = 0x8000;
- } else {
- mask = 0x80;
- }
- uint_fast16_t ix;
- for (ix = 0; ix < bits; ++ix) {
- if (value & mask) {
- keyUsage |= 1 << ix;
- }
- mask >>= 1;
- }
- certificate->_keyUsage = keyUsage;
- return;
-badDER:
- certificate->_keyUsage = kSecKeyUsageUnspecified;
-}
-
-static void SecCEPPrivateKeyUsagePeriod(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-static void SecCEPSubjectAltName(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- certificate->_subjectAltName = extn;
-}
-
-static void SecCEPIssuerAltName(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-static void SecCEPBasicConstraints(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- DERBasicConstraints basicConstraints;
- require_noerr_quiet(DERParseSequence(&extn->extnValue,
- DERNumBasicConstraintsItemSpecs, DERBasicConstraintsItemSpecs,
- &basicConstraints, sizeof(basicConstraints)), badDER);
- require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints.cA, false,
- &certificate->_basicConstraints.isCA), badDER);
- if (basicConstraints.pathLenConstraint.length != 0) {
- require_noerr_quiet(DERParseInteger(
- &basicConstraints.pathLenConstraint,
- &certificate->_basicConstraints.pathLenConstraint), badDER);
- certificate->_basicConstraints.pathLenConstraintPresent = true;
- }
- certificate->_basicConstraints.present = true;
- certificate->_basicConstraints.critical = extn->critical;
- return;
-badDER:
- certificate->_basicConstraints.present = false;
- secinfo("cert", "Invalid BasicConstraints Extension");
-}
-
-static void SecCEPCrlDistributionPoints(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-/*
- certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
-
- PolicyInformation ::= SEQUENCE {
- policyIdentifier CertPolicyId,
- policyQualifiers SEQUENCE SIZE (1..MAX) OF
- PolicyQualifierInfo OPTIONAL }
-
- CertPolicyId ::= OBJECT IDENTIFIER
-
- PolicyQualifierInfo ::= SEQUENCE {
- policyQualifierId PolicyQualifierId,
- qualifier ANY DEFINED BY policyQualifierId }
-*/
-/* maximum number of policies of 8192 seems more than adequate */
-#define MAX_CERTIFICATE_POLICIES 8192
-static void SecCEPCertificatePolicies(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- DERTag tag;
- DERSequence piSeq;
- SecCEPolicyInformation *policies = NULL;
- DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &piSeq);
- require_noerr_quiet(drtn, badDER);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERDecodedInfo piContent;
- DERSize policy_count = 0;
- while ((policy_count < MAX_CERTIFICATE_POLICIES) &&
- (drtn = DERDecodeSeqNext(&piSeq, &piContent)) == DR_Success) {
- require_quiet(piContent.tag == ASN1_CONSTR_SEQUENCE, badDER);
- policy_count++;
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- DERSize malloc_policies = policy_count > 0 ? policy_count : 1;
- require_quiet(policies = (SecCEPolicyInformation *)malloc(sizeof(SecCEPolicyInformation)
- * malloc_policies), badDER);
- drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &piSeq);
- require_noerr_quiet(drtn, badDER);
- DERSize policy_ix = 0;
- while ((policy_ix < (policy_count > 0 ? policy_count : 1)) &&
- (drtn = DERDecodeSeqNext(&piSeq, &piContent)) == DR_Success) {
- DERPolicyInformation pi;
- drtn = DERParseSequenceContent(&piContent.content,
- DERNumPolicyInformationItemSpecs,
- DERPolicyInformationItemSpecs,
- &pi, sizeof(pi));
- require_noerr_quiet(drtn, badDER);
- policies[policy_ix].policyIdentifier = pi.policyIdentifier;
- policies[policy_ix++].policyQualifiers = pi.policyQualifiers;
- }
- certificate->_certificatePolicies.present = true;
- certificate->_certificatePolicies.critical = extn->critical;
- certificate->_certificatePolicies.numPolicies = (uint32_t)policy_count;
- certificate->_certificatePolicies.policies = policies;
- return;
-badDER:
- if (policies)
- free(policies);
- certificate->_certificatePolicies.present = false;
- secinfo("cert", "Invalid CertificatePolicies Extension");
-}
-
-/*
- id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
-
- PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
- issuerDomainPolicy CertPolicyId,
- subjectDomainPolicy CertPolicyId }
-*/
-#if 0
-static void SecCEPPolicyMappings(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- DERTag tag;
- DERSequence pmSeq;
- SecCEPolicyMapping *mappings = NULL;
- DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &pmSeq);
- require_noerr_quiet(drtn, badDER);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERDecodedInfo pmContent;
- DERSize mapping_count = 0;
- while ((drtn = DERDecodeSeqNext(&pmSeq, &pmContent)) == DR_Success) {
- require_quiet(pmContent.tag == ASN1_CONSTR_SEQUENCE, badDER);
- mapping_count++;
- }
- mappings = (SecCEPolicyMapping *)malloc(sizeof(SecCEPolicyMapping)
- * mapping_count);
- drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &pmSeq);
- require_noerr_quiet(drtn, badDER);
- DERSize mapping_ix = 0;
- while ((drtn = DERDecodeSeqNext(&pmSeq, &pmContent)) == DR_Success) {
- DERPolicyMapping pm;
- drtn = DERParseSequenceContent(&pmContent.content,
- DERNumPolicyMappingItemSpecs,
- DERPolicyMappingItemSpecs,
- &pm, sizeof(pm));
- require_noerr_quiet(drtn, badDER);
- mappings[mapping_ix].issuerDomainPolicy = pm.issuerDomainPolicy;
- mappings[mapping_ix++].subjectDomainPolicy = pm.subjectDomainPolicy;
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- certificate->_policyMappings.present = true;
- certificate->_policyMappings.critical = extn->critical;
- certificate->_policyMappings.numMappings = mapping_count;
- certificate->_policyMappings.mappings = mappings;
- return;
-badDER:
- if (mappings)
- free(mappings);
- CFReleaseSafe(mappings);
- certificate->_policyMappings.present = false;
- secinfo("cert", "Invalid CertificatePolicies Extension");
-}
-#else
-static void SecCEPPolicyMappings(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- DERTag tag;
- DERSequence pmSeq;
- CFMutableDictionaryRef mappings = NULL;
- CFDataRef idp = NULL, sdp = NULL;
- DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &pmSeq);
- require_noerr_quiet(drtn, badDER);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERDecodedInfo pmContent;
- require_quiet(mappings = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks),
- badDER);;
- while ((drtn = DERDecodeSeqNext(&pmSeq, &pmContent)) == DR_Success) {
- require_quiet(pmContent.tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERPolicyMapping pm;
- drtn = DERParseSequenceContent(&pmContent.content,
- DERNumPolicyMappingItemSpecs,
- DERPolicyMappingItemSpecs,
- &pm, sizeof(pm));
- require_noerr_quiet(drtn, badDER);
-
- require_quiet(idp = CFDataCreate(kCFAllocatorDefault,
- pm.issuerDomainPolicy.data, pm.issuerDomainPolicy.length), badDER);
- require_quiet(sdp = CFDataCreate(kCFAllocatorDefault,
- pm.subjectDomainPolicy.data, pm.subjectDomainPolicy.length), badDER);
- CFMutableArrayRef sdps =
- (CFMutableArrayRef)CFDictionaryGetValue(mappings, idp);
- if (sdps) {
- CFArrayAppendValue(sdps, sdp);
- } else {
- require_quiet(sdps = CFArrayCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeArrayCallBacks), badDER);
- CFDictionarySetValue(mappings, idp, sdps);
- CFRelease(sdps);
- }
- CFReleaseNull(idp);
- CFReleaseNull(sdp);
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- certificate->_policyMappings = mappings;
- return;
-badDER:
- CFReleaseNull(idp);
- CFReleaseNull(sdp);
- CFReleaseSafe(mappings);
- certificate->_policyMappings = NULL;
- secinfo("cert", "Invalid CertificatePolicies Extension");
-}
-#endif
-
-/*
-AuthorityKeyIdentifier ::= SEQUENCE {
- keyIdentifier [0] KeyIdentifier OPTIONAL,
- authorityCertIssuer [1] GeneralNames OPTIONAL,
- authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
- -- authorityCertIssuer and authorityCertSerialNumber MUST both
- -- be present or both be absent
-
-KeyIdentifier ::= OCTET STRING
-*/
-static void SecCEPAuthorityKeyIdentifier(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- DERAuthorityKeyIdentifier akid;
- DERReturn drtn;
- drtn = DERParseSequence(&extn->extnValue,
- DERNumAuthorityKeyIdentifierItemSpecs,
- DERAuthorityKeyIdentifierItemSpecs,
- &akid, sizeof(akid));
- require_noerr_quiet(drtn, badDER);
- if (akid.keyIdentifier.length) {
- certificate->_authorityKeyIdentifier = akid.keyIdentifier;
- }
- if (akid.authorityCertIssuer.length ||
- akid.authorityCertSerialNumber.length) {
- require_quiet(akid.authorityCertIssuer.length &&
- akid.authorityCertSerialNumber.length, badDER);
- /* Perhaps put in a subsection called Authority Certificate Issuer. */
- certificate->_authorityKeyIdentifierIssuer = akid.authorityCertIssuer;
- certificate->_authorityKeyIdentifierSerialNumber = akid.authorityCertSerialNumber;
- }
-
- return;
-badDER:
- secinfo("cert", "Invalid AuthorityKeyIdentifier Extension");
-}
-
-static void SecCEPPolicyConstraints(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- DERPolicyConstraints pc;
- DERReturn drtn;
- drtn = DERParseSequence(&extn->extnValue,
- DERNumPolicyConstraintsItemSpecs,
- DERPolicyConstraintsItemSpecs,
- &pc, sizeof(pc));
- require_noerr_quiet(drtn, badDER);
- if (pc.requireExplicitPolicy.length) {
- require_noerr_quiet(DERParseInteger(
- &pc.requireExplicitPolicy,
- &certificate->_policyConstraints.requireExplicitPolicy), badDER);
- certificate->_policyConstraints.requireExplicitPolicyPresent = true;
- }
- if (pc.inhibitPolicyMapping.length) {
- require_noerr_quiet(DERParseInteger(
- &pc.inhibitPolicyMapping,
- &certificate->_policyConstraints.inhibitPolicyMapping), badDER);
- certificate->_policyConstraints.inhibitPolicyMappingPresent = true;
- }
-
- certificate->_policyConstraints.present = true;
- certificate->_policyConstraints.critical = extn->critical;
-
- return;
-badDER:
- certificate->_policyConstraints.present = false;
- secinfo("cert", "Invalid PolicyConstraints Extension");
-}
-
-static void SecCEPExtendedKeyUsage(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-/*
- InhibitAnyPolicy ::= SkipCerts
-
- SkipCerts ::= INTEGER (0..MAX)
-*/
-static void SecCEPInhibitAnyPolicy(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- require_noerr_quiet(DERParseInteger(
- &extn->extnValue,
- &certificate->_inhibitAnyPolicySkipCerts), badDER);
- return;
-badDER:
- certificate->_inhibitAnyPolicySkipCerts = UINT32_MAX;
- secinfo("cert", "Invalid InhibitAnyPolicy Extension");
-}
-
-/*
- id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
-
- AuthorityInfoAccessSyntax ::=
- SEQUENCE SIZE (1..MAX) OF AccessDescription
-
- AccessDescription ::= SEQUENCE {
- accessMethod OBJECT IDENTIFIER,
- accessLocation GeneralName }
-
- id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
-
- id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
-
- id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
- */
-static void SecCEPAuthorityInfoAccess(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
- DERTag tag;
- DERSequence adSeq;
- DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &adSeq);
- require_noerr_quiet(drtn, badDER);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERDecodedInfo adContent;
- while ((drtn = DERDecodeSeqNext(&adSeq, &adContent)) == DR_Success) {
- require_quiet(adContent.tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERAccessDescription ad;
- drtn = DERParseSequenceContent(&adContent.content,
- DERNumAccessDescriptionItemSpecs,
- DERAccessDescriptionItemSpecs,
- &ad, sizeof(ad));
- require_noerr_quiet(drtn, badDER);
- CFMutableArrayRef *urls;
- if (DEROidCompare(&ad.accessMethod, &oidAdOCSP))
- urls = &certificate->_ocspResponders;
- else if (DEROidCompare(&ad.accessMethod, &oidAdCAIssuer))
- urls = &certificate->_caIssuers;
- else
- continue;
-
- DERDecodedInfo generalNameContent;
- drtn = DERDecodeItem(&ad.accessLocation, &generalNameContent);
- require_noerr_quiet(drtn, badDER);
- switch (generalNameContent.tag) {
-#if 0
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6:
- /* Technically I don't think this is valid, but there are certs out
- in the wild that use a constructed IA5String. In particular the
- VeriSign Time Stamping Authority CA.cer does this. */
-#endif
- case ASN1_CONTEXT_SPECIFIC | 6:
- {
- CFURLRef url = CFURLCreateWithBytes(kCFAllocatorDefault,
- generalNameContent.content.data, generalNameContent.content.length,
- kCFStringEncodingASCII, NULL);
- if (url) {
- if (!*urls)
- *urls = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- CFArrayAppendValue(*urls, url);
- CFRelease(url);
- }
- break;
- }
- default:
- secinfo("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
- generalNameContent.tag, (int)generalNameContent.content.length, generalNameContent.content.data);
- goto badDER;
- }
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return;
-badDER:
- secinfo("cert", "failed to parse Authority Information Access extension");
-}
-
-static void SecCEPSubjectInfoAccess(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-static void SecCEPNetscapeCertType(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-static void SecCEPEntrustVersInfo(SecCertificateRefP certificate,
- const SecCertificateExtension *extn) {
- secinfo("cert", "critical: %s", extn->critical ? "yes" : "no");
-}
-
-/* Dictionary key callback for comparing to DERItems. */
-static Boolean SecDERItemEqual(const void *value1, const void *value2) {
- return DEROidCompare((const DERItem *)value1, (const DERItem *)value2);
-}
-
-/* Dictionary key callback calculating the hash of a DERItem. */
-static CFHashCode SecDERItemHash(const void *value) {
- const DERItem *derItem = (const DERItem *)value;
- CFHashCode hash = derItem->length;
- DERSize ix = derItem->length > 8 ? derItem->length - 8 : 0;
- for (; ix < derItem->length; ++ix) {
- hash = (hash << 9) + (hash >> 23) + derItem->data[ix];
- }
-
- return hash;
-}
-
-/* Dictionary key callbacks using the above 2 functions. */
-static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks = {
- 0, /* version */
- NULL, /* retain */
- NULL, /* release */
- NULL, /* copyDescription */
- SecDERItemEqual, /* equal */
- SecDERItemHash /* hash */
-};
-
-static void SecCertificateRegisterClass(void) {
- static const CFRuntimeClass kSecCertificateClass = {
- 0, /* version */
- "SecCertificate", /* class name */
- NULL, /* init */
- NULL, /* copy */
- SecCertificateDestroy, /* dealloc */
- SecCertificateEqual, /* equal */
- SecCertificateHash, /* hash */
- NULL, /* copyFormattingDesc */
- SecCertificateCopyDescription /* copyDebugDesc */
- };
-
- kSecCertificateTypeID = _CFRuntimeRegisterClass(&kSecCertificateClass);
-
- /* Build a dictionary that maps from extension OIDs to callback functions
- which can parse the extension of the type given. */
- static const void *extnOIDs[] = {
- &oidSubjectKeyIdentifier,
- &oidKeyUsage,
- &oidPrivateKeyUsagePeriod,
- &oidSubjectAltName,
- &oidIssuerAltName,
- &oidBasicConstraints,
- &oidCrlDistributionPoints,
- &oidCertificatePolicies,
- &oidPolicyMappings,
- &oidAuthorityKeyIdentifier,
- &oidPolicyConstraints,
- &oidExtendedKeyUsage,
- &oidInhibitAnyPolicy,
- &oidAuthorityInfoAccess,
- &oidSubjectInfoAccess,
- &oidNetscapeCertType,
- &oidEntrustVersInfo
- };
- static const void *extnParsers[] = {
- SecCEPSubjectKeyIdentifier,
- SecCEPKeyUsage,
- SecCEPPrivateKeyUsagePeriod,
- SecCEPSubjectAltName,
- SecCEPIssuerAltName,
- SecCEPBasicConstraints,
- SecCEPCrlDistributionPoints,
- SecCEPCertificatePolicies,
- SecCEPPolicyMappings,
- SecCEPAuthorityKeyIdentifier,
- SecCEPPolicyConstraints,
- SecCEPExtendedKeyUsage,
- SecCEPInhibitAnyPolicy,
- SecCEPAuthorityInfoAccess,
- SecCEPSubjectInfoAccess,
- SecCEPNetscapeCertType,
- SecCEPEntrustVersInfo
- };
- gExtensionParsers = CFDictionaryCreate(kCFAllocatorDefault, extnOIDs,
- extnParsers, sizeof(extnOIDs) / sizeof(*extnOIDs),
- &SecDERItemKeyCallBacks, NULL);
-}
-
-/* Given the contents of an X.501 Name return the contents of a normalized
- X.501 name. */
-static CFDataRef createNormalizedX501Name(CFAllocatorRef allocator,
- const DERItem *x501name) {
- CFMutableDataRef result = CFDataCreateMutable(allocator, x501name->length);
- CFIndex length = x501name->length;
- CFDataSetLength(result, length);
- UInt8 *base = CFDataGetMutableBytePtr(result);
-
- DERSequence rdnSeq;
- DERReturn drtn = DERDecodeSeqContentInit(x501name, &rdnSeq);
-
- require_noerr_quiet(drtn, badDER);
- DERDecodedInfo rdn;
-
- /* Always points to last rdn tag. */
- const DERByte *rdnTag = rdnSeq.nextItem;
- /* Offset relative to base of current rdn set tag. */
- CFIndex rdnTagLocation = 0;
- while ((drtn = DERDecodeSeqNext(&rdnSeq, &rdn)) == DR_Success) {
- require_quiet(rdn.tag == ASN1_CONSTR_SET, badDER);
- /* We don't allow empty RDNs. */
- require_quiet(rdn.content.length != 0, badDER);
- /* Length of the tag and length of the current rdn. */
- CFIndex rdnTLLength = rdn.content.data - rdnTag;
- CFIndex rdnContentLength = rdn.content.length;
- /* Copy the tag and length of the RDN. */
- memcpy(base + rdnTagLocation, rdnTag, rdnTLLength);
-
- DERSequence atvSeq;
- drtn = DERDecodeSeqContentInit(&rdn.content, &atvSeq);
- DERDecodedInfo atv;
- /* Always points to tag of current atv sequence. */
- const DERByte *atvTag = atvSeq.nextItem;
- /* Offset relative to base of current atv sequence tag. */
- CFIndex atvTagLocation = rdnTagLocation + rdnTLLength;
- while ((drtn = DERDecodeSeqNext(&atvSeq, &atv)) == DR_Success) {
- require_quiet(atv.tag == ASN1_CONSTR_SEQUENCE, badDER);
- /* Length of the tag and length of the current atv. */
- CFIndex atvTLLength = atv.content.data - atvTag;
- CFIndex atvContentLength = atv.content.length;
- /* Copy the tag and length of the atv and the atv itself. */
- memcpy(base + atvTagLocation, atvTag,
- atvTLLength + atv.content.length);
-
- /* Now decode the atv sequence. */
- DERAttributeTypeAndValue atvPair;
- drtn = DERParseSequenceContent(&atv.content,
- DERNumAttributeTypeAndValueItemSpecs,
- DERAttributeTypeAndValueItemSpecs,
- &atvPair, sizeof(atvPair));
- require_noerr_quiet(drtn, badDER);
- require_quiet(atvPair.type.length != 0, badDER);
- DERDecodedInfo value;
- drtn = DERDecodeItem(&atvPair.value, &value);
- require_noerr_quiet(drtn, badDER);
-
- /* (c) attribute values in PrintableString are not case sensitive
- (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
-
- (d) attribute values in PrintableString are compared after
- removing leading and trailing white space and converting internal
- substrings of one or more consecutive white space characters to a
- single space. */
- if (value.tag == ASN1_PRINTABLE_STRING) {
- /* Offset relative to base of current value tag. */
- CFIndex valueTagLocation = atvTagLocation + atvPair.value.data - atvTag;
- CFIndex valueTLLength = value.content.data - atvPair.value.data;
- CFIndex valueContentLength = value.content.length;
-
- /* Now copy all the bytes, but convert to upper case while
- doing so and convert multiple whitespace chars into a
- single space. */
- bool lastWasBlank = false;
- CFIndex valueLocation = valueTagLocation + valueTLLength;
- CFIndex valueCurrentLocation = valueLocation;
- CFIndex ix;
- for (ix = 0; ix < valueContentLength; ++ix) {
- UInt8 ch = value.content.data[ix];
- if (isblank(ch)) {
- if (lastWasBlank) {
- continue;
- } else {
- /* Don't insert a space for first character
- we encounter. */
- if (valueCurrentLocation > valueLocation) {
- base[valueCurrentLocation++] = ' ';
- }
- lastWasBlank = true;
- }
- } else {
- lastWasBlank = false;
- if ('a' <= ch && ch <= 'z') {
- base[valueCurrentLocation++] = ch + 'A' - 'a';
- } else {
- base[valueCurrentLocation++] = ch;
- }
- }
- }
- /* Finally if lastWasBlank remove the trailing space. */
- if (lastWasBlank && valueCurrentLocation > valueLocation) {
- valueCurrentLocation--;
- }
- /* Adjust content length to normalized length. */
- valueContentLength = valueCurrentLocation - valueLocation;
-
- /* Number of bytes by which the length should be shorted. */
- CFIndex lengthDiff = value.content.length - valueContentLength;
- if (lengthDiff == 0) {
- /* Easy case no need to adjust lengths. */
- } else {
- /* Hard work we need to go back and fix up length fields
- for:
- 1) The value itself.
- 2) The ATV Sequence containing type/value
- 3) The RDN Set containing one or more atv pairs.
- 4) The result.
- */
-
- /* Step 1 fix up length of value. */
- /* Length of value tag and length minus the tag. */
- DERSize newValueTLLength = valueTLLength - 1;
- drtn = DEREncodeLength(valueContentLength,
- base + valueTagLocation + 1, &newValueTLLength);
- /* Add the length of the tag back in. */
- newValueTLLength++;
- CFIndex valueLLDiff = valueTLLength - newValueTLLength;
- if (valueLLDiff) {
- /* The size of the length field changed, let's slide
- the value back by valueLLDiff bytes. */
- memmove(base + valueTagLocation + newValueTLLength,
- base + valueTagLocation + valueTLLength,
- valueContentLength);
- /* The length diff for the enclosing object. */
- lengthDiff += valueLLDiff;
- }
-
- /* Step 2 fix up length of the enclosing ATV Sequence. */
- atvContentLength -= lengthDiff;
- DERSize newATVTLLength = atvTLLength - 1;
- drtn = DEREncodeLength(atvContentLength,
- base + atvTagLocation + 1, &newATVTLLength);
- /* Add the length of the tag back in. */
- newATVTLLength++;
- CFIndex atvLLDiff = atvTLLength - newATVTLLength;
- if (atvLLDiff) {
- /* The size of the length field changed, let's slide
- the value back by valueLLDiff bytes. */
- memmove(base + atvTagLocation + newATVTLLength,
- base + atvTagLocation + atvTLLength,
- atvContentLength);
- /* The length diff for the enclosing object. */
- lengthDiff += atvLLDiff;
- atvTLLength = newATVTLLength;
- }
-
- /* Step 3 fix up length of enclosing RDN Set. */
- rdnContentLength -= lengthDiff;
- DERSize newRDNTLLength = rdnTLLength - 1;
- drtn = DEREncodeLength(rdnContentLength,
- base + rdnTagLocation + 1, &newRDNTLLength);
- /* Add the length of the tag back in. */
- newRDNTLLength++;
- CFIndex rdnLLDiff = rdnTLLength - newRDNTLLength;
- if (rdnLLDiff) {
- /* The size of the length field changed, let's slide
- the value back by valueLLDiff bytes. */
- memmove(base + rdnTagLocation + newRDNTLLength,
- base + rdnTagLocation + rdnTLLength,
- rdnContentLength);
- /* The length diff for the enclosing object. */
- lengthDiff += rdnLLDiff;
- rdnTLLength = newRDNTLLength;
-
- /* Adjust the locations that might have changed due to
- this slide. */
- atvTagLocation -= rdnLLDiff;
- }
- }
- }
- atvTagLocation += atvTLLength + atvContentLength;
- atvTag = atvSeq.nextItem;
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- rdnTagLocation += rdnTLLength + rdnContentLength;
- rdnTag = rdnSeq.nextItem;
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- /* Truncate the result to the proper length. */
- CFDataSetLength(result, rdnTagLocation);
-
- return result;
-
-badDER:
- CFRelease(result);
- return NULL;
-}
-
-/* AUDIT[securityd]:
- certificate->_der is a caller provided data of any length (might be 0).
-
- Top level certificate decode.
- */
-static bool SecCertificateParse(SecCertificateRefP certificate)
-{
- DERReturn drtn;
-
- check(certificate);
- CFAllocatorRef allocator = CFGetAllocator(certificate);
-
- /* top level decode */
- DERSignedCertCrl signedCert;
- drtn = DERParseSequence(&certificate->_der, DERNumSignedCertCrlItemSpecs,
- DERSignedCertCrlItemSpecs, &signedCert,
- sizeof(signedCert));
- require_noerr_quiet(drtn, badCert);
- /* Store tbs since we need to digest it for verification later on. */
- certificate->_tbs = signedCert.tbs;
-
- /* decode the TBSCert - it was saved in full DER form */
- DERTBSCert tbsCert;
- drtn = DERParseSequence(&signedCert.tbs,
- DERNumTBSCertItemSpecs, DERTBSCertItemSpecs,
- &tbsCert, sizeof(tbsCert));
- require_noerr_quiet(drtn, badCert);
-
- /* sequence we're given: decode the signedCerts Signature Algorithm. */
- /* This MUST be the same as the certificate->_tbsSigAlg with the exception
- of the params field. */
- drtn = DERParseSequenceContent(&signedCert.sigAlg,
- DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs,
- &certificate->_sigAlg, sizeof(certificate->_sigAlg));
- require_noerr_quiet(drtn, badCert);
-
- /* The contents of signedCert.sig is a bit string whose contents
- are the signature itself. */
- DERByte numUnusedBits;
- drtn = DERParseBitString(&signedCert.sig,
- &certificate->_signature, &numUnusedBits);
- require_noerr_quiet(drtn, badCert);
-
- /* Now decode the tbsCert. */
-
- /* First we turn the optional version into an int. */
- if (tbsCert.version.length) {
- DERDecodedInfo decoded;
- drtn = DERDecodeItem(&tbsCert.version, &decoded);
- require_noerr_quiet(drtn, badCert);
- require_quiet(decoded.tag == ASN1_INTEGER, badCert);
- require_quiet(decoded.content.length == 1, badCert);
- certificate->_version = decoded.content.data[0];
- require_quiet(certificate->_version > 0, badCert);
- require_quiet(certificate->_version < 3, badCert);
- } else {
- certificate->_version = 0;
- }
-
- /* The serial number is in the tbsCert.serialNum - it was saved in
- INTEGER form without the tag and length. */
- certificate->_serialNum = tbsCert.serialNum;
- certificate->_serialNumber = CFDataCreate(allocator,
- tbsCert.serialNum.data, tbsCert.serialNum.length);
-
- /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
- drtn = DERParseSequenceContent(&tbsCert.tbsSigAlg,
- DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs,
- &certificate->_tbsSigAlg, sizeof(certificate->_tbsSigAlg));
- require_noerr_quiet(drtn, badCert);
-
- /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
- and length fields. */
- certificate->_issuer = tbsCert.issuer;
- certificate->_normalizedIssuer = createNormalizedX501Name(allocator,
- &tbsCert.issuer);
-
- /* sequence we're given: decode the tbsCerts Validity sequence. */
- DERValidity validity;
- drtn = DERParseSequenceContent(&tbsCert.validity,
- DERNumValidityItemSpecs, DERValidityItemSpecs,
- &validity, sizeof(validity));
- require_noerr_quiet(drtn, badCert);
- require_quiet(derDateGetAbsoluteTime(&validity.notBefore,
- &certificate->_notBefore), badCert);
- require_quiet(derDateGetAbsoluteTime(&validity.notAfter,
- &certificate->_notAfter), badCert);
-
- /* The subject is in the tbsCert.subject - it's a sequence without the tag
- and length fields. */
- certificate->_subject = tbsCert.subject;
- certificate->_normalizedSubject = createNormalizedX501Name(allocator,
- &tbsCert.subject);
-
- /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */
- DERSubjPubKeyInfo pubKeyInfo;
- drtn = DERParseSequence(&tbsCert.subjectPubKey,
- DERNumSubjPubKeyInfoItemSpecs, DERSubjPubKeyInfoItemSpecs,
- &pubKeyInfo, sizeof(pubKeyInfo));
- require_noerr_quiet(drtn, badCert);
-
- /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
- drtn = DERParseSequenceContent(&pubKeyInfo.algId,
- DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs,
- &certificate->_algId, sizeof(certificate->_algId));
- require_noerr_quiet(drtn, badCert);
-
- /* Now we can figure out the key's algorithm id and params based on
- certificate->_algId.oid. */
-
- /* The contents of pubKeyInfo.pubKey is a bit string whose contents
- are a PKCS1 format RSA key. */
- drtn = DERParseBitString(&pubKeyInfo.pubKey,
- &certificate->_pubKeyDER, &numUnusedBits);
- require_noerr_quiet(drtn, badCert);
-
- /* The contents of tbsCert.issuerID is a bit string. */
- certificate->_issuerUniqueID = tbsCert.issuerID;
-
- /* The contents of tbsCert.subjectID is a bit string. */
- certificate->_subjectUniqueID = tbsCert.subjectID;
-
- /* Extensions. */
- if (tbsCert.extensions.length) {
- CFIndex extensionCount = 0;
- DERSequence derSeq;
- DERTag tag;
- drtn = DERDecodeSeqInit(&tbsCert.extensions, &tag,
- &derSeq);
- require_noerr_quiet(drtn, badCert);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, badCert);
- DERDecodedInfo currDecoded;
- while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) {
-#if 0
-/* ! = MUST recognize ? = SHOULD recognize
-*/
-
- KnownExtension _subjectKeyID; /* ?SubjectKeyIdentifier id-ce 14 */
- KnownExtension _keyUsage; /* !KeyUsage id-ce 15 */
- KnownExtension _subjectAltName; /* !SubjectAltName id-ce 17 */
- KnownExtension _basicConstraints; /* !BasicConstraints id-ce 19 */
- KnownExtension _authorityKeyID; /* ?AuthorityKeyIdentifier id-ce 35 */
- KnownExtension _extKeyUsage; /* !ExtKeyUsage id-ce 37 */
- KnownExtension _netscapeCertType; /* 2.16.840.1.113730.1.1 netscape 1 1 */
- KnownExtension _qualCertStatements; /* QCStatements id-pe 3 */
-
- KnownExtension _issuerAltName; /* IssuerAltName id-ce 18 */
- KnownExtension _nameConstraints; /* !NameConstraints id-ce 30 */
- KnownExtension _cRLDistributionPoints; /* CRLDistributionPoints id-ce 31 */
- KnownExtension _certificatePolicies; /* !CertificatePolicies id-ce 32 */
- KnownExtension _policyMappings; /* ?PolicyMappings id-ce 33 */
- KnownExtension _policyConstraints; /* !PolicyConstraints id-ce 36 */
- KnownExtension _freshestCRL; /* FreshestCRL id-ce 46 */
- KnownExtension _inhibitAnyPolicy; /* !InhibitAnyPolicy id-ce 54 */
-
- KnownExtension _authorityInfoAccess; /* AuthorityInfoAccess id-pe 1 */
- KnownExtension _subjectInfoAccess; /* SubjectInfoAccess id-pe 11 */
-#endif
-
- extensionCount++;
- }
- require_quiet(drtn == DR_EndOfSequence, badCert);
-
- /* Put some upper limit on the number of extentions allowed. */
- require_quiet(extensionCount < 10000, badCert);
- certificate->_extensionCount = extensionCount;
- CFIndex mallocCount = extensionCount > 0 ? extensionCount : 1;
- certificate->_extensions =
- malloc(sizeof(SecCertificateExtension) * mallocCount);
- require_quiet(certificate->_extensions, badCert);
-
- CFIndex ix = 0;
- drtn = DERDecodeSeqInit(&tbsCert.extensions, &tag, &derSeq);
- require_noerr_quiet(drtn, badCert);
- for (ix = 0; ix < extensionCount; ++ix) {
- drtn = DERDecodeSeqNext(&derSeq, &currDecoded);
- require_quiet(drtn == DR_Success ||
- (ix == extensionCount - 1 && drtn == DR_EndOfSequence), badCert);
- require_quiet(currDecoded.tag == ASN1_CONSTR_SEQUENCE, badCert);
- DERExtension extn;
- drtn = DERParseSequenceContent(&currDecoded.content,
- DERNumExtensionItemSpecs, DERExtensionItemSpecs,
- &extn, sizeof(extn));
- require_noerr_quiet(drtn, badCert);
- /* Copy stuff into certificate->extensions[ix]. */
- certificate->_extensions[ix].extnID = extn.extnID;
- require_noerr_quiet(drtn = DERParseBooleanWithDefault(&extn.critical, false,
- &certificate->_extensions[ix].critical), badCert);
- certificate->_extensions[ix].extnValue = extn.extnValue;
-
- SecCertificateExtensionParser parser =
- (SecCertificateExtensionParser)CFDictionaryGetValue(
- gExtensionParsers, &certificate->_extensions[ix].extnID);
- if (parser) {
- /* Invoke the parser. */
- parser(certificate, &certificate->_extensions[ix]);
- } else if (certificate->_extensions[ix].critical) {
- secinfo("cert", "Found unknown critical extension");
- certificate->_foundUnknownCriticalExtension = true;
- } else {
- secinfo("cert", "Found unknown non critical extension");
- }
- }
- }
-
- return true;
-
-badCert:
- return false;
-}
-
-
-/* Public API functions. */
-CFTypeID SecCertificateGetTypeIDP(void) {
- pthread_once(&kSecCertificateRegisterClass, SecCertificateRegisterClass);
- return kSecCertificateTypeID;
-}
-
-SecCertificateRefP SecCertificateCreateWithBytesP(CFAllocatorRef allocator,
- const UInt8 *der_bytes, CFIndex der_length) {
- check(der_bytes);
- check(der_length);
- CFIndex size = sizeof(struct __SecCertificate) + der_length;
- SecCertificateRefP result = (SecCertificateRefP)_CFRuntimeCreateInstance(
- allocator, SecCertificateGetTypeIDP(), size - sizeof(CFRuntimeBase), 0);
- if (result) {
- memset((char*)result + sizeof(result->_base), 0,
- sizeof(*result) - sizeof(result->_base));
- result->_der.data = ((DERByte *)result + sizeof(*result));
- result->_der.length = der_length;
- if(der_bytes) {
- memcpy(result->_der.data, der_bytes, der_length);
- }
- if (!SecCertificateParse(result)) {
- CFRelease(result);
- return NULL;
- }
- }
- return result;
-}
-
-/* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
-SecCertificateRefP SecCertificateCreate(CFAllocatorRef allocator,
- const UInt8 *der_bytes, CFIndex der_length);
-
-SecCertificateRefP SecCertificateCreate(CFAllocatorRef allocator,
- const UInt8 *der_bytes, CFIndex der_length) {
- return SecCertificateCreateWithBytesP(allocator, der_bytes, der_length);
-}
-/* @@@ End of placeholder. */
-
-/* AUDIT[securityd](done):
- der_certificate is a caller provided data of any length (might be 0), only
- its cf type has been checked.
- */
-SecCertificateRefP SecCertificateCreateWithDataP(CFAllocatorRef allocator,
- CFDataRef der_certificate) {
- check(der_certificate);
- CFIndex size = sizeof(struct __SecCertificate);
- SecCertificateRefP result = (SecCertificateRefP)_CFRuntimeCreateInstance(
- allocator, SecCertificateGetTypeIDP(), size - sizeof(CFRuntimeBase), 0);
- if (result) {
- memset((char*)result + sizeof(result->_base), 0, size - sizeof(result->_base));
- result->_der_data = CFDataCreateCopy(allocator, der_certificate);
- result->_der.data = (DERByte *)CFDataGetBytePtr(result->_der_data);
- result->_der.length = CFDataGetLength(result->_der_data);
- if (!SecCertificateParse(result)) {
- CFRelease(result);
- return NULL;
- }
- }
- return result;
-}
-
-CFDataRef SecCertificateCopyDataP(SecCertificateRefP certificate) {
- check(certificate);
- CFDataRef result;
- if (certificate->_der_data) {
- CFRetain(certificate->_der_data);
- result = certificate->_der_data;
- } else {
- result = CFDataCreate(CFGetAllocator(certificate),
- certificate->_der.data, certificate->_der.length);
-#if 0
- /* FIXME: If we wish to cache result we need to lock the certificate.
- Also this create 2 copies of the certificate data which is somewhat
- suboptimal. */
- CFRetain(result);
- certificate->_der_data = result;
-#endif
- }
-
- return result;
-}
-
-CFIndex SecCertificateGetLengthP(SecCertificateRefP certificate) {
- return certificate->_der.length;
-}
-
-const UInt8 *SecCertificateGetBytePtrP(SecCertificateRefP certificate) {
- return certificate->_der.data;
-}
-
-/* From rfc3280 - Appendix B. ASN.1 Notes
-
- Object Identifiers (OIDs) are used throughout this specification to
- identify certificate policies, public key and signature algorithms,
- certificate extensions, etc. There is no maximum size for OIDs.
- This specification mandates support for OIDs which have arc elements
- with values that are less than 2^28, that is, they MUST be between 0
- and 268,435,455, inclusive. This allows each arc element to be
- represented within a single 32 bit word. Implementations MUST also
- support OIDs where the length of the dotted decimal (see [RFC 2252],
- section 4.1) string representation can be up to 100 bytes
- (inclusive). Implementations MUST be able to handle OIDs with up to
- 20 elements (inclusive). CAs SHOULD NOT issue certificates which
- contain OIDs that exceed these requirements. Likewise, CRL issuers
- SHOULD NOT issue CRLs which contain OIDs that exceed these
- requirements.
-*/
-
-/* Oids longer than this are considered invalid. */
-#define MAX_OID_SIZE 32
-
-static CFStringRef SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator,
- const DERItem *oid) {
-
- if (oid->length == 0) {
- return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
- CFSTR("SecCertificate"));
- }
- if (oid->length > MAX_OID_SIZE) {
- return SecFrameworkCopyLocalizedString(CFSTR("Oid too long"),
- CFSTR("SecCertificate"));
- }
-
- CFMutableStringRef result = CFStringCreateMutable(allocator, 0);
-
- // The first two levels are encoded into one byte, since the root level
- // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
- // y may be > 39, so we have to add special-case handling for this.
- uint32_t x = oid->data[0] / 40;
- uint32_t y = oid->data[0] % 40;
- if (x > 2)
- {
- // Handle special case for large y if x = 2
- y += (x - 2) * 40;
- x = 2;
- }
- CFStringAppendFormat(result, NULL, CFSTR("%u.%u"), x, y);
-
- uint32_t value = 0;
- for (x = 1; x < oid->length; ++x)
- {
- value = (value << 7) | (oid->data[x] & 0x7F);
- /* @@@ value may not span more than 4 bytes. */
- /* A max number of 20 values is allowed. */
- if (!(oid->data[x] & 0x80))
- {
- CFStringAppendFormat(result, NULL, CFSTR(".%lu"), (unsigned long)value);
- value = 0;
- }
- }
- return result;
-}
-
-static CFStringRef copyLocalizedOidDescription(CFAllocatorRef allocator,
- const DERItem *oid) {
- if (oid->length == 0) {
- return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
- CFSTR("SecCertificate"));
- }
-
- /* Build the key we use to lookup the localized OID description. */
- CFMutableStringRef oidKey = CFStringCreateMutable(allocator,
- oid->length * 3 + 5);
- CFStringAppendFormat(oidKey, NULL, CFSTR("06 %02lX"), (unsigned long)oid->length);
- DERSize ix;
- for (ix = 0; ix < oid->length; ++ix)
- CFStringAppendFormat(oidKey, NULL, CFSTR(" %02X"), oid->data[ix]);
-
- CFStringRef name = SecFrameworkCopyLocalizedString(oidKey, CFSTR("OID"));
- if (CFEqual(oidKey, name)) {
- CFRelease(name);
- name = SecDERItemCopyOIDDecimalRepresentation(allocator, oid);
- }
- CFRelease(oidKey);
-
- return name;
-}
-
-/* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
- 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
- have a length of exactly 4 or 16 octects. */
-static CFStringRef copyIPAddressContentDescription(CFAllocatorRef allocator,
- const DERItem *ip) {
- /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
- 4 octects addr, or 8 octects, addr/mask for ipv6 it's
- 16 octects addr, or 32 octects addr/mask. */
- CFStringRef value = NULL;
- if (ip->length == 4) {
- value = CFStringCreateWithFormat(allocator, NULL,
- CFSTR("%u.%u.%u.%u"),
- ip->data[0], ip->data[1], ip->data[2], ip->data[3]);
- } else if (ip->length == 16) {
- value = CFStringCreateWithFormat(allocator, NULL,
- CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
- "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
- ip->data[0], ip->data[1], ip->data[2], ip->data[3],
- ip->data[4], ip->data[5], ip->data[6], ip->data[7],
- ip->data[8], ip->data[9], ip->data[10], ip->data[11],
- ip->data[12], ip->data[13], ip->data[14], ip->data[15]);
- }
-
- return value;
-}
-
-#if 0
-static CFStringRef copyFullOidDescription(CFAllocatorRef allocator,
- const DERItem *oid) {
- CFStringRef decimal = SecDERItemCopyOIDDecimalRepresentation(allocator, oid);
- CFStringRef name = copyLocalizedOidDescription(allocator, oid);
- CFStringRef oid_string = CFStringCreateWithFormat(allocator, NULL,
- CFSTR("%@ (%@)"), name, decimal);
- CFRelease(name);
- CFRelease(decimal);
- return oid_string;
-}
-#endif
-
-void appendPropertyP(CFMutableArrayRef properties,
- CFStringRef propertyType, CFStringRef label, CFTypeRef value) {
- CFDictionaryRef property;
- if (label) {
- CFStringRef localizedLabel = SecFrameworkCopyLocalizedString(label,
- CFSTR("SecCertificate"));
- const void *all_keys[4];
- all_keys[0] = kSecPropertyKeyType;
- all_keys[1] = kSecPropertyKeyLabel;
- all_keys[2] = kSecPropertyKeyLocalizedLabel;
- all_keys[3] = kSecPropertyKeyValue;
- const void *property_values[] = {
- propertyType,
- label,
- localizedLabel,
- value,
- };
- property = CFDictionaryCreate(CFGetAllocator(properties),
- all_keys, property_values, value ? 4 : 3,
- &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- CFRelease(localizedLabel);
- } else {
- const void *nolabel_keys[2];
- nolabel_keys[0] = kSecPropertyKeyType;
- nolabel_keys[1] = kSecPropertyKeyValue;
- const void *property_values[] = {
- propertyType,
- value,
- };
- property = CFDictionaryCreate(CFGetAllocator(properties),
- nolabel_keys, property_values, 2,
- &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- }
-
- CFArrayAppendValue(properties, property);
- CFRelease(property);
-}
-
-/* YYMMDDhhmmZ */
-#define UTC_TIME_NOSEC_ZULU_LEN 11
-/* YYMMDDhhmmssZ */
-#define UTC_TIME_ZULU_LEN 13
-/* YYMMDDhhmmssThhmm */
-#define UTC_TIME_LOCALIZED_LEN 17
-/* YYYYMMDDhhmmssZ */
-#define GENERALIZED_TIME_ZULU_LEN 15
-/* YYYYMMDDhhmmssThhmm */
-#define GENERALIZED_TIME_LOCALIZED_LEN 19
-
-/* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
- advance *p by 2. */
-static inline SInt32 parseDecimalPair(const DERByte **p) {
- const DERByte *cp = *p;
- *p += 2;
- return 10 * (cp[0] - '0') + cp[1] - '0';
-}
-
-/* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
- true if the date was valid and properly decoded, also return the result in
- absTime. Return false otherwise. */
-static CFAbsoluteTime SecAbsoluteTimeFromDateContent(DERTag tag, const uint8_t *bytes,
- size_t length) {
- check(bytes);
- if (length == 0)
- return NULL_TIME;
-
- bool isUtcLength = false;
- bool isLocalized = false;
- bool noSeconds = false;
- switch (length) {
- case UTC_TIME_NOSEC_ZULU_LEN: /* YYMMDDhhmmZ */
- isUtcLength = true;
- noSeconds = true;
- break;
- case UTC_TIME_ZULU_LEN: /* YYMMDDhhmmssZ */
- isUtcLength = true;
- break;
- case GENERALIZED_TIME_ZULU_LEN: /* YYYYMMDDhhmmssZ */
- break;
- case UTC_TIME_LOCALIZED_LEN: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
- isUtcLength = true;
- /*DROPTHROUGH*/
- case GENERALIZED_TIME_LOCALIZED_LEN:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
- isLocalized = true;
- break;
- default: /* unknown format */
- return NULL_TIME;
- }
-
- /* Make sure the der tag fits the thing inside it. */
- if (tag == ASN1_UTC_TIME) {
- if (!isUtcLength)
- return NULL_TIME;
- } else if (tag == ASN1_GENERALIZED_TIME) {
- if (isUtcLength)
- return NULL_TIME;
- } else {
- return NULL_TIME;
- }
-
- const DERByte *cp = bytes;
- /* Check that all characters are digits, except if localized the timezone
- indicator or if not localized the 'Z' at the end. */
- DERSize ix;
- for (ix = 0; ix < length; ++ix) {
- if (!(isdigit(cp[ix]))) {
- if ((isLocalized && ix == length - 5 &&
- (cp[ix] == '+' || cp[ix] == '-')) ||
- (!isLocalized && ix == length - 1 && cp[ix] == 'Z')) {
- continue;
- }
- return NULL_TIME;
- }
- }
-
- /* Initialize the fields in a gregorian date struct. */
- CFGregorianDate gdate;
- if (isUtcLength) {
- SInt32 year = parseDecimalPair(&cp);
- if (year < 50) {
- /* 0 <= year < 50 : assume century 21 */
- gdate.year = 2000 + year;
- } else if (year < 70) {
- /* 50 <= year < 70 : illegal per PKIX */
- return false;
- } else {
- /* 70 < year <= 99 : assume century 20 */
- gdate.year = 1900 + year;
- }
- } else {
- gdate.year = 100 * parseDecimalPair(&cp) + parseDecimalPair(&cp);
- }
- gdate.month = parseDecimalPair(&cp);
- gdate.day = parseDecimalPair(&cp);
- gdate.hour = parseDecimalPair(&cp);
- gdate.minute = parseDecimalPair(&cp);
- if (noSeconds) {
- gdate.second = 0;
- } else {
- gdate.second = parseDecimalPair(&cp);
- }
-
- CFTimeInterval timeZoneOffset = 0;
- if (isLocalized) {
- /* ZONE INDICATOR */
- SInt32 multiplier = *cp++ == '+' ? 60 : -60;
- timeZoneOffset = multiplier *
- (parseDecimalPair(&cp) + 60 * parseDecimalPair(&cp));
- } else {
- timeZoneOffset = 0;
- }
-
- secinfo("dateparse",
- "date %.*s year: %04d-%02d-%02d %02d:%02d:%02.f %+05.f",
- (int)length, bytes, (int)gdate.year, gdate.month,
- gdate.day, gdate.hour, gdate.minute, gdate.second,
- timeZoneOffset / 60);
-
- if (!CFGregorianDateIsValid(gdate, kCFGregorianAllUnits))
- return false;
- CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL,
- timeZoneOffset);
- if (!timeZone)
- return NULL_TIME;
- CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(gdate, timeZone);
- CFRelease(timeZone);
- return absTime;
-}
-
-static bool derDateContentGetAbsoluteTime(DERTag tag, const DERItem *date,
- CFAbsoluteTime *pabsTime) {
- CFAbsoluteTime absTime = SecAbsoluteTimeFromDateContent(tag, date->data,
- date->length);
- if (absTime == NULL_TIME)
- return false;
-
- *pabsTime = absTime;
- return true;
-}
-
-/* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
- true if the date was valid and properly decoded, also return the result in
- absTime. Return false otherwise. */
-static bool derDateGetAbsoluteTime(const DERItem *dateChoice,
- CFAbsoluteTime *absTime) {
- check(dateChoice);
- check(absTime);
- if (dateChoice->length == 0)
- return false;
-
- DERDecodedInfo decoded;
- if (DERDecodeItem(dateChoice, &decoded))
- return false;
-
- return derDateContentGetAbsoluteTime(decoded.tag, &decoded.content,
- absTime);
-}
-
-static void appendDataProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *der_data) {
- CFDataRef data = CFDataCreate(CFGetAllocator(properties),
- der_data->data, der_data->length);
- appendPropertyP(properties, kSecPropertyTypeData, label, data);
- CFRelease(data);
-}
-
-static void appendUnparsedProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *der_data) {
- CFStringRef newLabel = CFStringCreateWithFormat(CFGetAllocator(properties),
- NULL, CFSTR("Unparsed %@"), label);
- appendDataProperty(properties, newLabel, der_data);
- CFRelease(newLabel);
-}
-
-static void appendInvalidProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *der_data) {
- CFStringRef newLabel = CFStringCreateWithFormat(CFGetAllocator(properties),
- NULL, CFSTR("Invalid %@"), label);
- appendDataProperty(properties, newLabel, der_data);
- CFRelease(newLabel);
-}
-
-static void appendDateContentProperty(CFMutableArrayRef properties,
- CFStringRef label, DERTag tag, const DERItem *dateContent) {
- CFAbsoluteTime absTime;
- if (!derDateContentGetAbsoluteTime(tag, dateContent, &absTime)) {
- /* Date decode failure insert hex bytes instead. */
- return appendInvalidProperty(properties, label, dateContent);
- }
- CFDateRef date = CFDateCreate(CFGetAllocator(properties), absTime);
- appendPropertyP(properties, kSecPropertyTypeDate, label, date);
- CFRelease(date);
-}
-
-static void appendDateProperty(CFMutableArrayRef properties,
- CFStringRef label, CFAbsoluteTime absTime) {
- CFDateRef date = CFDateCreate(CFGetAllocator(properties), absTime);
- appendPropertyP(properties, kSecPropertyTypeDate, label, date);
- CFRelease(date);
-}
-
-static void appendIPAddressContentProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *ip) {
- CFStringRef value =
- copyIPAddressContentDescription(CFGetAllocator(properties), ip);
- if (value) {
- appendPropertyP(properties, kSecPropertyTypeString, label, value);
- CFRelease(value);
- } else {
- appendUnparsedProperty(properties, label, ip);
- }
-}
-
-static void appendURLContentProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *urlContent) {
- CFURLRef url = CFURLCreateWithBytes(CFGetAllocator(properties),
- urlContent->data, urlContent->length, kCFStringEncodingASCII, NULL);
- if (url) {
- appendPropertyP(properties, kSecPropertyTypeURL, label, url);
- CFRelease(url);
- } else {
- appendInvalidProperty(properties, label, urlContent);
- }
-}
-
-static void appendURLProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *url) {
- DERDecodedInfo decoded;
- DERReturn drtn;
-
- drtn = DERDecodeItem(url, &decoded);
- if (drtn || decoded.tag != ASN1_IA5_STRING) {
- appendInvalidProperty(properties, label, url);
- } else {
- appendURLContentProperty(properties, label, &decoded.content);
- }
-}
-
-static void appendOIDProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *oid) {
- CFStringRef oid_string = copyLocalizedOidDescription(CFGetAllocator(properties),
- oid);
- appendPropertyP(properties, kSecPropertyTypeString, label, oid_string);
- CFRelease(oid_string);
-}
-
-static void appendAlgorithmProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERAlgorithmId *algorithm) {
- CFMutableArrayRef alg_props =
- CFArrayCreateMutable(CFGetAllocator(properties), 0,
- &kCFTypeArrayCallBacks);
- appendOIDProperty(alg_props, CFSTR("Algorithm"), &algorithm->oid);
- if (algorithm->params.length) {
- if (algorithm->params.length == 2 &&
- algorithm->params.data[0] == ASN1_NULL &&
- algorithm->params.data[1] == 0) {
- /* @@@ Localize <NULL> or perhaps skip it? */
- appendPropertyP(alg_props, kSecPropertyTypeString,
- CFSTR("Parameters"), CFSTR("none"));
- } else {
- appendUnparsedProperty(alg_props, CFSTR("Parameters"),
- &algorithm->params);
- }
- }
- appendPropertyP(properties, kSecPropertyTypeSection, label, alg_props);
- CFRelease(alg_props);
-}
-
-static CFStringRef copyHexDescription(CFAllocatorRef allocator,
- const DERItem *blob) {
- CFIndex ix, length = blob->length /* < 24 ? blob->length : 24 */;
- CFMutableStringRef string = CFStringCreateMutable(allocator,
- blob->length * 3 - 1);
- for (ix = 0; ix < length; ++ix)
- if (ix == 0)
- CFStringAppendFormat(string, NULL, CFSTR("%02X"), blob->data[ix]);
- else
- CFStringAppendFormat(string, NULL, CFSTR(" %02X"), blob->data[ix]);
-
- return string;
-}
-
-static CFStringRef copyBlobString(CFAllocatorRef allocator,
- CFStringRef blobType, CFStringRef quanta, const DERItem *blob) {
- CFStringRef blobFormat = SecFrameworkCopyLocalizedString(
- CFSTR("%@; %d %@; data = %@"), CFSTR("SecCertificate")
- /*, "format string for encoded field data (e.g. Sequence; 128 bytes; "
- "data = 00 00 ...)" */);
- CFStringRef hex = copyHexDescription(allocator, blob);
- CFStringRef result = CFStringCreateWithFormat(allocator, NULL,
- blobFormat, blobType, blob->length, quanta, hex);
- CFRelease(hex);
- CFRelease(blobFormat);
-
- return result;
-}
-
-static CFStringRef copyContentString(CFAllocatorRef allocator,
- const DERItem *string, CFStringEncoding encoding,
- bool printableOnly) {
- /* Strip potential bogus trailing zero from printable strings. */
- DERSize length = string->length;
- if (length && string->data[length - 1] == 0) {
- /* Don't mess with the length of UTF16 strings though. */
- if (encoding != kCFStringEncodingUTF16)
- length--;
- }
- /* A zero length string isn't considered printable. */
- if (!length && printableOnly)
- return NULL;
-
- /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
- it treat kCFStringEncodingUTF16 as big endian by default, whereas
- passing false makes it treat it as native endian by default. */
- CFStringRef result = CFStringCreateWithBytes(allocator, string->data,
- length, encoding, encoding == kCFStringEncodingUTF16);
- if (result)
- return result;
-
- return printableOnly ? NULL : copyHexDescription(allocator, string);
-}
-
-/* From rfc3280 - Appendix B. ASN.1 Notes
-
- CAs MUST force the serialNumber to be a non-negative integer, that
- is, the sign bit in the DER encoding of the INTEGER value MUST be
- zero - this can be done by adding a leading (leftmost) `00'H octet if
- necessary. This removes a potential ambiguity in mapping between a
- string of octets and an integer value.
-
- As noted in section 4.1.2.2, serial numbers can be expected to
- contain long integers. Certificate users MUST be able to handle
- serialNumber values up to 20 octets in length. Conformant CAs MUST
- NOT use serialNumber values longer than 20 octets.
-*/
-
-/* Return the given numeric data as a string: decimal up to 64 bits,
- hex otherwise. */
-static CFStringRef copyIntegerContentDescription(CFAllocatorRef allocator,
- const DERItem *integer) {
- uint64_t value = 0;
- CFIndex ix, length = integer->length;
-
- if (length == 0 || length > 8)
- return copyHexDescription(allocator, integer);
-
- for(ix = 0; ix < length; ++ix) {
- value <<= 8;
- value += integer->data[ix];
- }
-
- return CFStringCreateWithFormat(allocator, NULL, CFSTR("%llu"), value);
-}
-
-static CFStringRef copyDERThingContentDescription(CFAllocatorRef allocator,
- DERTag tag, const DERItem *derThing, bool printableOnly) {
- switch(tag) {
- case ASN1_INTEGER:
- case ASN1_BOOLEAN:
- return printableOnly ? NULL : copyIntegerContentDescription(allocator, derThing);
- case ASN1_PRINTABLE_STRING:
- case ASN1_IA5_STRING:
- return copyContentString(allocator, derThing, kCFStringEncodingASCII, printableOnly);
- case ASN1_UTF8_STRING:
- case ASN1_GENERAL_STRING:
- case ASN1_UNIVERSAL_STRING:
- return copyContentString(allocator, derThing, kCFStringEncodingUTF8, printableOnly);
- case ASN1_T61_STRING: // 20, also BER_TAG_TELETEX_STRING
- case ASN1_VIDEOTEX_STRING: // 21
- case ASN1_VISIBLE_STRING: // 26
- return copyContentString(allocator, derThing, kCFStringEncodingISOLatin1, printableOnly);
- case ASN1_BMP_STRING: // 30
- return copyContentString(allocator, derThing, kCFStringEncodingUTF16, printableOnly);
- case ASN1_OCTET_STRING:
- return printableOnly ? NULL : copyBlobString(allocator, CFSTR("Byte string"), CFSTR("bytes"),
- derThing);
- //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
- case ASN1_BIT_STRING:
- return printableOnly ? NULL : copyBlobString(allocator, CFSTR("Bit string"), CFSTR("bits"),
- derThing);
- case (DERByte)ASN1_CONSTR_SEQUENCE:
- return printableOnly ? NULL : copyBlobString(allocator, CFSTR("Sequence"), CFSTR("bytes"),
- derThing);
- case (DERByte)ASN1_CONSTR_SET:
- return printableOnly ? NULL : copyBlobString(allocator, CFSTR("Set"), CFSTR("bytes"),
- derThing);
- case ASN1_OBJECT_ID:
- return printableOnly ? NULL : copyLocalizedOidDescription(allocator, derThing);
- default:
- /* @@@ Localize. */
- /* "format string for undisplayed field data with a given DER tag" */
- return printableOnly ? NULL : CFStringCreateWithFormat(allocator, NULL,
- CFSTR("not displayed (tag = %llu; length %d)"),
- tag, (int)derThing->length);
- }
-}
-
-static CFStringRef copyDERThingDescription(CFAllocatorRef allocator,
- const DERItem *derThing, bool printableOnly) {
- DERDecodedInfo decoded;
- DERReturn drtn;
-
- drtn = DERDecodeItem(derThing, &decoded);
- if (drtn) {
- return printableOnly ? NULL : copyHexDescription(allocator, derThing);
- } else {
- return copyDERThingContentDescription(allocator, decoded.tag,
- &decoded.content, false);
- }
-}
-
-static void appendDERThingProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *derThing) {
- CFStringRef value = copyDERThingDescription(CFGetAllocator(properties),
- derThing, false);
- appendPropertyP(properties, kSecPropertyTypeString, label, value);
- CFReleaseSafe(value);
-}
-
-static OSStatus appendRDNProperty(void *context, const DERItem *rdnType,
- const DERItem *rdnValue, CFIndex rdnIX) {
- CFMutableArrayRef properties = (CFMutableArrayRef)context;
- if (rdnIX > 0) {
- /* If there is more than one value pair we create a subsection for the
- second pair, and append things to the subsection for subsequent
- pairs. */
- CFIndex lastIX = CFArrayGetCount(properties) - 1;
- CFTypeRef lastValue = CFArrayGetValueAtIndex(properties, lastIX);
- if (rdnIX == 1) {
- /* Since this is the second rdn pair for a given rdn, we setup a
- new subsection for this rdn. We remove the first property
- from the properties array and make it the first element in the
- subsection instead. */
- CFMutableArrayRef rdn_props = CFArrayCreateMutable(
- CFGetAllocator(properties), 0, &kCFTypeArrayCallBacks);
- CFArrayAppendValue(rdn_props, lastValue);
- CFArrayRemoveValueAtIndex(properties, lastIX);
- appendPropertyP(properties, kSecPropertyTypeSection, NULL, rdn_props);
- properties = rdn_props;
- } else {
- /* Since this is the third or later rdn pair we have already
- created a subsection in the top level properties array. Instead
- of appending to that directly we append to the array inside the
- subsection. */
- properties = (CFMutableArrayRef)CFDictionaryGetValue(
- (CFDictionaryRef)lastValue, kSecPropertyKeyValue);
- }
- }
-
- /* Finally we append the new rdn value to the property array. */
- CFStringRef label = copyLocalizedOidDescription(CFGetAllocator(properties),
- rdnType);
- if (label) {
- appendDERThingProperty(properties, label, rdnValue);
- CFRelease(label);
- return errSecSuccess;
- } else {
- return errSecInvalidCertificate;
- }
-}
-
-static CFArrayRef createPropertiesForRDNContent(CFAllocatorRef allocator,
- const DERItem *rdnSetContent) {
- CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0,
- &kCFTypeArrayCallBacks);
- OSStatus status = parseRDNContent(rdnSetContent, properties,
- appendRDNProperty);
- if (status) {
- CFArrayRemoveAllValues(properties);
- appendInvalidProperty(properties, CFSTR("RDN"), rdnSetContent);
- }
-
- return properties;
-}
-
-/*
- From rfc3739 - 3.1.2. Subject
-
- When parsing the subject here are some tips for a short name of the cert.
- Choice I: commonName
- Choice II: givenName
- Choice III: pseudonym
-
- The commonName attribute value SHALL, when present, contain a name
- of the subject. This MAY be in the subject's preferred
- presentation format, or a format preferred by the CA, or some
- other format. Pseudonyms, nicknames, and names with spelling
- other than defined by the registered name MAY be used. To
- understand the nature of the name presented in commonName,
- complying applications MAY have to examine present values of the
- givenName and surname attributes, or the pseudonym attribute.
-
-*/
-static CFArrayRef createPropertiesForX501NameContent(CFAllocatorRef allocator,
- const DERItem *x501NameContent) {
- CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0,
- &kCFTypeArrayCallBacks);
- OSStatus status = parseX501NameContent(x501NameContent, properties,
- appendRDNProperty);
- if (status) {
- CFArrayRemoveAllValues(properties);
- appendInvalidProperty(properties, CFSTR("X.501 Name"), x501NameContent);
- }
-
- return properties;
-}
-
-static CFArrayRef createPropertiesForX501Name(CFAllocatorRef allocator,
- const DERItem *x501Name) {
- CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0,
- &kCFTypeArrayCallBacks);
- OSStatus status = parseX501Name(x501Name, properties, appendRDNProperty);
- if (status) {
- CFArrayRemoveAllValues(properties);
- appendInvalidProperty(properties, CFSTR("X.501 Name"), x501Name);
- }
-
- return properties;
-}
-
-static void appendIntegerProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *integer) {
- CFStringRef string = copyIntegerContentDescription(
- CFGetAllocator(properties), integer);
- appendPropertyP(properties, kSecPropertyTypeString, label, string);
- CFRelease(string);
-}
-
-static void appendBoolProperty(CFMutableArrayRef properties,
- CFStringRef label, bool boolean) {
- appendPropertyP(properties, kSecPropertyTypeString,
- label, boolean ? CFSTR("Yes") : CFSTR("No"));
-}
-
-static void appendBooleanProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *boolean, bool defaultValue) {
- bool result;
- DERReturn drtn = DERParseBooleanWithDefault(boolean, defaultValue, &result);
- if (drtn) {
- /* Couldn't parse boolean; dump the raw unparsed data as hex. */
- appendInvalidProperty(properties, label, boolean);
- } else {
- appendBoolProperty(properties, label, result);
- }
-}
-
-static void appendBitStringContentNames(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *bitStringContent,
- const CFStringRef *names, CFIndex namesCount) {
- DERSize len = bitStringContent->length - 1;
- require_quiet(len == 1 || len == 2, badDER);
- DERByte numUnusedBits = bitStringContent->data[0];
- require_quiet(numUnusedBits < 8, badDER);
- uint_fast16_t bits = 8 * len - numUnusedBits;
- require_quiet(bits <= (uint_fast16_t)namesCount, badDER);
- uint_fast16_t value = bitStringContent->data[1];
- uint_fast16_t mask;
- if (len > 1) {
- value = (value << 8) + bitStringContent->data[2];
- mask = 0x8000;
- } else {
- mask = 0x80;
- }
- uint_fast16_t ix;
- bool didOne = false;
- CFMutableStringRef string =
- CFStringCreateMutable(CFGetAllocator(properties), 0);
- for (ix = 0; ix < bits; ++ix) {
- if (value & mask) {
- if (didOne) {
- CFStringAppend(string, CFSTR(", "));
- } else {
- didOne = true;
- }
- CFStringAppend(string, names[ix]);
- }
- mask >>= 1;
- }
- appendPropertyP(properties, kSecPropertyTypeString, label, string);
- CFRelease(string);
- return;
-badDER:
- appendInvalidProperty(properties, label, bitStringContent);
-}
-
-static void appendBitStringNames(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *bitString,
- const CFStringRef *names, CFIndex namesCount) {
- DERDecodedInfo bitStringContent;
- DERReturn drtn = DERDecodeItem(bitString, &bitStringContent);
- require_noerr_quiet(drtn, badDER);
- require_quiet(bitStringContent.tag == ASN1_BIT_STRING, badDER);
- appendBitStringContentNames(properties, label, &bitStringContent.content,
- names, namesCount);
- return;
-badDER:
- appendInvalidProperty(properties, label, bitString);
-}
-
-#if 0
-typedef uint16_t SecKeyUsage;
-
-#define kSecKeyUsageDigitalSignature 0x8000
-#define kSecKeyUsageNonRepudiation 0x4000
-#define kSecKeyUsageKeyEncipherment 0x2000
-#define kSecKeyUsageDataEncipherment 0x1000
-#define kSecKeyUsageKeyAgreement 0x0800
-#define kSecKeyUsageKeyCertSign 0x0400
-#define kSecKeyUsageCRLSign 0x0200
-#define kSecKeyUsageEncipherOnly 0x0100
-#define kSecKeyUsageDecipherOnly 0x0080
-
-/*
- KeyUsage ::= BIT STRING {
- digitalSignature (0),
- nonRepudiation (1),
- keyEncipherment (2),
- dataEncipherment (3),
- keyAgreement (4),
- keyCertSign (5),
- cRLSign (6),
- encipherOnly (7),
- decipherOnly (8) }
- */
-static void appendKeyUsage(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- if ((extnValue->length != 4 && extnValue->length != 5) ||
- extnValue->data[0] != ASN1_BIT_STRING ||
- extnValue->data[1] < 2 || extnValue->data[1] > 3 ||
- extnValue->data[2] > 7) {
- appendInvalidProperty(properties, CFSTR("KeyUsage Extension"),
- extnValue);
- } else {
- CFMutableStringRef string =
- CFStringCreateMutable(CFGetAllocator(properties), 0);
- SecKeyUsage usage = (extnValue->data[3] << 8);
- if (extnValue->length == 5)
- usage += extnValue->data[4];
- secinfo("keyusage", "keyusage: %04X", usage);
- static const CFStringRef usageNames[] = {
- CFSTR("Digital Signature"),
- CFSTR("Non-Repudiation"),
- CFSTR("Key Encipherment"),
- CFSTR("Data Encipherment"),
- CFSTR("Key Agreement"),
- CFSTR("Cert Sign"),
- CFSTR("CRL Sign"),
- CFSTR("Encipher"),
- CFSTR("Decipher"),
- };
- bool didOne = false;
- SecKeyUsage mask = kSecKeyUsageDigitalSignature;
- CFIndex ix, bits = (extnValue->data[1] - 1) * 8 - extnValue->data[2];
- for (ix = 0; ix < bits; ++ix) {
- if (usage & mask) {
- if (didOne) {
- CFStringAppend(string, CFSTR(", "));
- } else {
- didOne = true;
- }
- /* @@@ Localize usageNames[ix]. */
- CFStringAppend(string, usageNames[ix]);
- }
- mask >>= 1;
- }
- appendPropertyP(properties, kSecPropertyTypeString, CFSTR("Usage"),
- string);
- CFRelease(string);
- }
-}
-#else
-static void appendKeyUsage(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- static const CFStringRef usageNames[] = {
- CFSTR("Digital Signature"),
- CFSTR("Non-Repudiation"),
- CFSTR("Key Encipherment"),
- CFSTR("Data Encipherment"),
- CFSTR("Key Agreement"),
- CFSTR("Cert Sign"),
- CFSTR("CRL Sign"),
- CFSTR("Encipher Only"),
- CFSTR("Decipher Only")
- };
- appendBitStringNames(properties, CFSTR("Usage"), extnValue,
- usageNames, sizeof(usageNames) / sizeof(*usageNames));
-}
-#endif
-
-static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- DERPrivateKeyUsagePeriod pkup;
- DERReturn drtn = DERParseSequence(extnValue,
- DERNumPrivateKeyUsagePeriodItemSpecs, DERPrivateKeyUsagePeriodItemSpecs,
- &pkup, sizeof(pkup));
- require_noerr_quiet(drtn, badDER);
- if (pkup.notBefore.length) {
- appendDateContentProperty(properties, CFSTR("Not Valid Before"),
- ASN1_GENERALIZED_TIME, &pkup.notBefore);
- }
- if (pkup.notAfter.length) {
- appendDateContentProperty(properties, CFSTR("Not Valid After"),
- ASN1_GENERALIZED_TIME, &pkup.notAfter);
- }
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Private Key Usage Period"),
- extnValue);
-}
-
-static void appendStringContentProperty(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *stringContent,
- CFStringEncoding encoding) {
- CFStringRef string = CFStringCreateWithBytes(CFGetAllocator(properties),
- stringContent->data, stringContent->length, encoding, FALSE);
- if (string) {
- appendPropertyP(properties, kSecPropertyTypeString, label, string);
- CFRelease(string);
- } else {
- appendInvalidProperty(properties, label, stringContent);
- }
-}
-
-/*
- OtherName ::= SEQUENCE {
- type-id OBJECT IDENTIFIER,
- value [0] EXPLICIT ANY DEFINED BY type-id }
-*/
-static void appendOtherNameContentProperty(CFMutableArrayRef properties,
- const DERItem *otherNameContent) {
- DEROtherName on;
- DERReturn drtn = DERParseSequenceContent(otherNameContent,
- DERNumOtherNameItemSpecs, DEROtherNameItemSpecs,
- &on, sizeof(on));
- require_noerr_quiet(drtn, badDER);
- CFAllocatorRef allocator = CFGetAllocator(properties);
- CFStringRef oid_string = copyLocalizedOidDescription(allocator,
- &on.typeIdentifier);
- CFStringRef value_string = copyDERThingDescription(allocator, &on.value, false);
- if (value_string)
- appendPropertyP(properties, kSecPropertyTypeString, oid_string,
- value_string);
- else
- appendUnparsedProperty(properties, oid_string, &on.value);
-
- CFReleaseNull(value_string);
- CFReleaseNull(oid_string);
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Other Name"), otherNameContent);
-}
-
-/*
- GeneralName ::= CHOICE {
- otherName [0] OtherName,
- rfc822Name [1] IA5String,
- dNSName [2] IA5String,
- x400Address [3] ORAddress,
- directoryName [4] Name,
- ediPartyName [5] EDIPartyName,
- uniformResourceIdentifier [6] IA5String,
- iPAddress [7] OCTET STRING,
- registeredID [8] OBJECT IDENTIFIER}
-
- EDIPartyName ::= SEQUENCE {
- nameAssigner [0] DirectoryString OPTIONAL,
- partyName [1] DirectoryString }
- */
-static bool appendGeneralNameContentProperty(CFMutableArrayRef properties,
- DERTag tag, const DERItem *generalName) {
- switch (tag) {
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0:
- appendOtherNameContentProperty(properties, generalName);
- break;
- case ASN1_CONTEXT_SPECIFIC | 1:
- /* IA5String. */
- appendStringContentProperty(properties, CFSTR("Email Address"),
- generalName, kCFStringEncodingASCII);
- break;
- case ASN1_CONTEXT_SPECIFIC | 2:
- /* IA5String. */
- appendStringContentProperty(properties, CFSTR("DNS Name"), generalName,
- kCFStringEncodingASCII);
- break;
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3:
- appendUnparsedProperty(properties, CFSTR("X.400 Address"),
- generalName);
- break;
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 4:
- {
- CFArrayRef directory_plist =
- createPropertiesForX501Name(CFGetAllocator(properties),
- generalName);
- appendPropertyP(properties, kSecPropertyTypeSection,
- CFSTR("Directory Name"), directory_plist);
- CFRelease(directory_plist);
- break;
- }
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 5:
- appendUnparsedProperty(properties, CFSTR("EDI Party Name"),
- generalName);
- break;
- case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6:
- /* Technically I don't think this is valid, but there are certs out
- in the wild that use a constructed IA5String. In particular the
- VeriSign Time Stamping Authority CA.cer does this. */
- appendURLProperty(properties, CFSTR("URI"), generalName);
- break;
- case ASN1_CONTEXT_SPECIFIC | 6:
- appendURLContentProperty(properties, CFSTR("URI"), generalName);
- break;
- case ASN1_CONTEXT_SPECIFIC | 7:
- appendIPAddressContentProperty(properties, CFSTR("IP Address"),
- generalName);
- break;
- case ASN1_CONTEXT_SPECIFIC | 8:
- appendOIDProperty(properties, CFSTR("Registered ID"), generalName);
- break;
- default:
- goto badDER;
- }
- return true;
-badDER:
- return false;
-}
-
-static void appendGeneralNameProperty(CFMutableArrayRef properties,
- const DERItem *generalName) {
- DERDecodedInfo generalNameContent;
- DERReturn drtn = DERDecodeItem(generalName, &generalNameContent);
- require_noerr_quiet(drtn, badDER);
- if (appendGeneralNameContentProperty(properties, generalNameContent.tag,
- &generalNameContent.content))
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("General Name"), generalName);
-}
-
-
-/*
- GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
- */
-static void appendGeneralNamesContent(CFMutableArrayRef properties,
- const DERItem *generalNamesContent) {
- DERSequence gnSeq;
- DERReturn drtn = DERDecodeSeqContentInit(generalNamesContent, &gnSeq);
- require_noerr_quiet(drtn, badDER);
- DERDecodedInfo generalNameContent;
- while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) ==
- DR_Success) {
- if (!appendGeneralNameContentProperty(properties,
- generalNameContent.tag, &generalNameContent.content)) {
- goto badDER;
- }
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("General Names"),
- generalNamesContent);
-}
-
-static void appendGeneralNames(CFMutableArrayRef properties,
- const DERItem *generalNames) {
- DERDecodedInfo generalNamesContent;
- DERReturn drtn = DERDecodeItem(generalNames, &generalNamesContent);
- require_noerr_quiet(drtn, badDER);
- require_quiet(generalNamesContent.tag == ASN1_CONSTR_SEQUENCE,
- badDER);
- appendGeneralNamesContent(properties, &generalNamesContent.content);
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("General Names"), generalNames);
-}
-
-/*
-BasicConstraints ::= SEQUENCE {
- cA BOOLEAN DEFAULT FALSE,
- pathLenConstraint INTEGER (0..MAX) OPTIONAL }
-*/
-static void appendBasicConstraints(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- DERBasicConstraints basicConstraints;
- DERReturn drtn = DERParseSequence(extnValue,
- DERNumBasicConstraintsItemSpecs, DERBasicConstraintsItemSpecs,
- &basicConstraints, sizeof(basicConstraints));
- require_noerr_quiet(drtn, badDER);
-
- appendBooleanProperty(properties, CFSTR("Certificate Authority"),
- &basicConstraints.cA, false);
-
- if (basicConstraints.pathLenConstraint.length != 0) {
- appendIntegerProperty(properties, CFSTR("Path Length Constraint"),
- &basicConstraints.pathLenConstraint);
- }
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Basic Constraints"), extnValue);
-}
-
-/*
- CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
-
- DistributionPoint ::= SEQUENCE {
- distributionPoint [0] DistributionPointName OPTIONAL,
- reasons [1] ReasonFlags OPTIONAL,
- cRLIssuer [2] GeneralNames OPTIONAL }
-
- DistributionPointName ::= CHOICE {
- fullName [0] GeneralNames,
- nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
-
- ReasonFlags ::= BIT STRING {
- unused (0),
- keyCompromise (1),
- cACompromise (2),
- affiliationChanged (3),
- superseded (4),
- cessationOfOperation (5),
- certificateHold (6),
- privilegeWithdrawn (7),
- aACompromise (8) }
-*/
-static void appendCrlDistributionPoints(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- CFAllocatorRef allocator = CFGetAllocator(properties);
- DERTag tag;
- DERSequence dpSeq;
- DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &dpSeq);
- require_noerr_quiet(drtn, badDER);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERDecodedInfo dpSeqContent;
- while ((drtn = DERDecodeSeqNext(&dpSeq, &dpSeqContent)) == DR_Success) {
- require_quiet(dpSeqContent.tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERDistributionPoint dp;
- drtn = DERParseSequenceContent(&dpSeqContent.content,
- DERNumDistributionPointItemSpecs,
- DERDistributionPointItemSpecs,
- &dp, sizeof(dp));
- require_noerr_quiet(drtn, badDER);
- if (dp.distributionPoint.length) {
- DERDecodedInfo distributionPointName;
- drtn = DERDecodeItem(&dp.distributionPoint, &distributionPointName);
- require_noerr_quiet(drtn, badDER);
- if (distributionPointName.tag ==
- (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0)) {
- /* Full Name */
- appendGeneralNamesContent(properties,
- &distributionPointName.content);
- } else if (distributionPointName.tag ==
- (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 1)) {
- CFArrayRef rdn_props = createPropertiesForRDNContent(allocator,
- &dp.reasons);
- appendPropertyP(properties, kSecPropertyTypeSection,
- CFSTR("Name Relative To CRL Issuer"), rdn_props);
- CFRelease(rdn_props);
- } else {
- goto badDER;
- }
- }
- if (dp.reasons.length) {
- static const CFStringRef reasonNames[] = {
- CFSTR("Unused"),
- CFSTR("Key Compromise"),
- CFSTR("CA Compromise"),
- CFSTR("Affiliation Changed"),
- CFSTR("Superseded"),
- CFSTR("Cessation Of Operation"),
- CFSTR("Certificate Hold"),
- CFSTR("Priviledge Withdrawn"),
- CFSTR("AA Compromise")
- };
- appendBitStringContentNames(properties, CFSTR("Reasons"),
- &dp.reasons,
- reasonNames, sizeof(reasonNames) / sizeof(*reasonNames));
- }
- if (dp.cRLIssuer.length) {
- CFMutableArrayRef crlIssuer = CFArrayCreateMutable(allocator, 0,
- &kCFTypeArrayCallBacks);
- appendPropertyP(properties, kSecPropertyTypeSection,
- CFSTR("CRL Issuer"), crlIssuer);
- CFRelease(crlIssuer);
- appendGeneralNames(crlIssuer, &dp.cRLIssuer);
- }
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Crl Distribution Points"),
- extnValue);
-}
-
-/* Decode a sequence of integers into a comma separated list of ints. */
-static void appendIntegerSequenceContent(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *intSequenceContent) {
- CFMutableStringRef value = NULL;
- CFStringRef intDesc = NULL;
- CFAllocatorRef allocator = CFGetAllocator(properties);
- DERSequence intSeq;
- DERReturn drtn = DERDecodeSeqContentInit(intSequenceContent, &intSeq);
- require_noerr_quiet(drtn, badDER);
- DERDecodedInfo intContent;
-
- while ((drtn = DERDecodeSeqNext(&intSeq, &intContent))
- == DR_Success) {
- require_quiet(intContent.tag == ASN1_INTEGER, badDER);
- intDesc = copyIntegerContentDescription(
- allocator, &intContent.content);
- require_quiet(intDesc, badDER);
- if (value) {
- CFStringAppendFormat(value, NULL, CFSTR(", %@"), intDesc);
- } else {
- value = CFStringCreateMutableCopy(allocator, 0, intDesc);
- require_quiet(value, badDER);
- }
- CFReleaseNull(intDesc);
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- if (value) {
- appendPropertyP(properties, kSecPropertyTypeString,
- CFSTR("Notice Numbers"), value);
- CFRelease(value);
- return;
- }
- /* DROPTHOUGH if !value. */
-badDER:
- CFReleaseNull(value);
- CFReleaseNull(intDesc);
- appendInvalidProperty(properties, label, intSequenceContent);
-}
-
-static void appendCertificatePolicies(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- CFAllocatorRef allocator = CFGetAllocator(properties);
- CFStringRef piLabel = NULL, pqLabel = NULL;
- DERTag tag;
- DERSequence piSeq;
- DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &piSeq);
- require_noerr_quiet(drtn, badDER);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERDecodedInfo piContent;
- int pin = 1;
- while ((drtn = DERDecodeSeqNext(&piSeq, &piContent)) == DR_Success) {
- require_quiet(piContent.tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERPolicyInformation pi;
- drtn = DERParseSequenceContent(&piContent.content,
- DERNumPolicyInformationItemSpecs,
- DERPolicyInformationItemSpecs,
- &pi, sizeof(pi));
- require_noerr_quiet(drtn, badDER);
- require_quiet(piLabel = CFStringCreateWithFormat(allocator, NULL,
- CFSTR("Policy Identifier #%d"), pin++), badDER);
- appendOIDProperty(properties, piLabel, &pi.policyIdentifier);
- CFReleaseNull(piLabel);
- if (pi.policyQualifiers.length == 0)
- continue;
-
- DERSequence pqSeq;
- drtn = DERDecodeSeqContentInit(&pi.policyQualifiers, &pqSeq);
- require_noerr_quiet(drtn, badDER);
- DERDecodedInfo pqContent;
- int pqn = 1;
- while ((drtn = DERDecodeSeqNext(&pqSeq, &pqContent)) == DR_Success) {
- DERPolicyQualifierInfo pqi;
- drtn = DERParseSequenceContent(&pqContent.content,
- DERNumPolicyQualifierInfoItemSpecs,
- DERPolicyQualifierInfoItemSpecs,
- &pqi, sizeof(pqi));
- require_noerr_quiet(drtn, badDER);
- DERDecodedInfo qualifierContent;
- drtn = DERDecodeItem(&pqi.qualifier, &qualifierContent);
- require_noerr_quiet(drtn, badDER);
- require_quiet(pqLabel = CFStringCreateWithFormat(allocator, NULL,
- CFSTR("Policy Qualifier #%d"), pqn++), badDER);
- appendOIDProperty(properties, pqLabel, &pqi.policyQualifierID);
- CFReleaseNull(pqLabel);
- if (DEROidCompare(&oidQtCps, &pqi.policyQualifierID)) {
- require_quiet(qualifierContent.tag == ASN1_IA5_STRING, badDER);
- appendURLContentProperty(properties,
- CFSTR("CPS URI"),
- &qualifierContent.content);
- } else if (DEROidCompare(&oidQtUNotice, &pqi.policyQualifierID)) {
- require_quiet(qualifierContent.tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERUserNotice un;
- drtn = DERParseSequenceContent(&qualifierContent.content,
- DERNumUserNoticeItemSpecs,
- DERUserNoticeItemSpecs,
- &un, sizeof(un));
- require_noerr_quiet(drtn, badDER);
- if (un.noticeRef.length) {
- DERNoticeReference nr;
- drtn = DERParseSequenceContent(&un.noticeRef,
- DERNumNoticeReferenceItemSpecs,
- DERNoticeReferenceItemSpecs,
- &nr, sizeof(nr));
- require_noerr_quiet(drtn, badDER);
- appendDERThingProperty(properties,
- CFSTR("Organization"),
- &nr.organization);
- appendIntegerSequenceContent(properties,
- CFSTR("Notice Numbers"), &nr.noticeNumbers);
- }
- if (un.explicitText.length) {
- appendDERThingProperty(properties, CFSTR("Explicit Text"),
- &un.explicitText);
- }
- } else {
- appendUnparsedProperty(properties, CFSTR("Qualifier"),
- &pqi.qualifier);
- }
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return;
-badDER:
- CFReleaseNull(piLabel);
- CFReleaseNull(pqLabel);
- appendInvalidProperty(properties, CFSTR("Certificate Policies"),
- extnValue);
-}
-
-static void appendSubjectKeyIdentifier(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- DERReturn drtn;
- DERDecodedInfo keyIdentifier;
- drtn = DERDecodeItem(extnValue, &keyIdentifier);
- require_noerr_quiet(drtn, badDER);
- require_quiet(keyIdentifier.tag == ASN1_OCTET_STRING, badDER);
- appendDataProperty(properties, CFSTR("Key Identifier"),
- &keyIdentifier.content);
-
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Invalid Subject Key Identifier"),
- extnValue);
-}
-
-/*
-AuthorityKeyIdentifier ::= SEQUENCE {
- keyIdentifier [0] KeyIdentifier OPTIONAL,
- authorityCertIssuer [1] GeneralNames OPTIONAL,
- authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
- -- authorityCertIssuer and authorityCertSerialNumber MUST both
- -- be present or both be absent
-
-KeyIdentifier ::= OCTET STRING
-*/
-static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- DERAuthorityKeyIdentifier akid;
- DERReturn drtn;
- drtn = DERParseSequence(extnValue,
- DERNumAuthorityKeyIdentifierItemSpecs,
- DERAuthorityKeyIdentifierItemSpecs,
- &akid, sizeof(akid));
- require_noerr_quiet(drtn, badDER);
- if (akid.keyIdentifier.length) {
- appendDataProperty(properties, CFSTR("Key Identifier"),
- &akid.keyIdentifier);
- }
- if (akid.authorityCertIssuer.length ||
- akid.authorityCertSerialNumber.length) {
- require_quiet(akid.authorityCertIssuer.length &&
- akid.authorityCertSerialNumber.length, badDER);
- /* Perhaps put in a subsection called Authority Certificate Issuer. */
- appendGeneralNamesContent(properties,
- &akid.authorityCertIssuer);
- appendIntegerProperty(properties,
- CFSTR("Authority Certificate Serial Number"),
- &akid.authorityCertSerialNumber);
- }
-
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Authority Key Identifier"),
- extnValue);
-}
-
-/*
- PolicyConstraints ::= SEQUENCE {
- requireExplicitPolicy [0] SkipCerts OPTIONAL,
- inhibitPolicyMapping [1] SkipCerts OPTIONAL }
-
- SkipCerts ::= INTEGER (0..MAX)
-*/
-static void appendPolicyConstraints(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- DERPolicyConstraints pc;
- DERReturn drtn;
- drtn = DERParseSequence(extnValue,
- DERNumPolicyConstraintsItemSpecs,
- DERPolicyConstraintsItemSpecs,
- &pc, sizeof(pc));
- require_noerr_quiet(drtn, badDER);
- if (pc.requireExplicitPolicy.length) {
- appendIntegerProperty(properties,
- CFSTR("Require Explicit Policy"), &pc.requireExplicitPolicy);
- }
- if (pc.inhibitPolicyMapping.length) {
- appendIntegerProperty(properties,
- CFSTR("Inhibit Policy Mapping"), &pc.inhibitPolicyMapping);
- }
-
- return;
-
-badDER:
- appendInvalidProperty(properties, CFSTR("Policy Constraints"), extnValue);
-}
-
-/*
-extendedKeyUsage EXTENSION ::= {
- SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
- IDENTIFIED BY id-ce-extKeyUsage }
-
-KeyPurposeId ::= OBJECT IDENTIFIER
-*/
-static void appendExtendedKeyUsage(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- DERTag tag;
- DERSequence derSeq;
- DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &derSeq);
- require_noerr_quiet(drtn, badDER);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERDecodedInfo currDecoded;
- while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) {
- require_quiet(currDecoded.tag == ASN1_OBJECT_ID, badDER);
- appendOIDProperty(properties, CFSTR("Purpose"),
- &currDecoded.content);
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Extended Key Usage"), extnValue);
-}
-
-/*
- id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
-
- AuthorityInfoAccessSyntax ::=
- SEQUENCE SIZE (1..MAX) OF AccessDescription
-
- AccessDescription ::= SEQUENCE {
- accessMethod OBJECT IDENTIFIER,
- accessLocation GeneralName }
-
- id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
-
- id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
-
- id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
-*/
-static void appendInfoAccess(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- DERTag tag;
- DERSequence adSeq;
- DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &adSeq);
- require_noerr_quiet(drtn, badDER);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERDecodedInfo adContent;
- while ((drtn = DERDecodeSeqNext(&adSeq, &adContent)) == DR_Success) {
- require_quiet(adContent.tag == ASN1_CONSTR_SEQUENCE, badDER);
- DERAccessDescription ad;
- drtn = DERParseSequenceContent(&adContent.content,
- DERNumAccessDescriptionItemSpecs,
- DERAccessDescriptionItemSpecs,
- &ad, sizeof(ad));
- require_noerr_quiet(drtn, badDER);
- appendOIDProperty(properties, CFSTR("Access Method"),
- &ad.accessMethod);
- //CFSTR("Access Location");
- appendGeneralNameProperty(properties, &ad.accessLocation);
- }
- require_quiet(drtn == DR_EndOfSequence, badDER);
- return;
-badDER:
- appendInvalidProperty(properties, CFSTR("Authority Information Access"),
- extnValue);
-}
-
-static void appendNetscapeCertType(CFMutableArrayRef properties,
- const DERItem *extnValue) {
- static const CFStringRef certTypes[] = {
- CFSTR("SSL client"),
- CFSTR("SSL server"),
- CFSTR("S/MIME"),
- CFSTR("Object Signing"),
- CFSTR("Reserved"),
- CFSTR("SSL CA"),
- CFSTR("S/MIME CA"),
- CFSTR("Object Signing CA")
- };
- appendBitStringNames(properties, CFSTR("Usage"), extnValue,
- certTypes, sizeof(certTypes) / sizeof(*certTypes));
-}
-
-#if 0
-static void appendEntrustVersInfo(CFMutableArrayRef properties,
- const DERItem *extnValue) {
-}
-
-/*
- * The list of Qualified Cert Statement statementIds we understand, even though
- * we don't actually do anything with them; if these are found in a Qualified
- * Cert Statement that's critical, we can truthfully say "yes we understand this".
- */
-static const CSSM_OID_PTR knownQualifiedCertStatements[] =
-{
- /* id-qcs := { id-pkix 11 } */
- (const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V1, /* id-qcs 1 */
- (const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V2, /* id-qcs 2 */
- (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_COMPLIANCE,
- (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE,
- (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_RETENTION,
- (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_SSCD
-};
-#define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
-*/
-static void appendQCCertStatements(CFMutableArrayRef properties,
- const DERItem *extnValue) {
-}
-
-#endif
-
-static bool appendPrintableDERSequenceP(CFMutableArrayRef properties,
- CFStringRef label, const DERItem *sequence) {
- DERTag tag;
- DERSequence derSeq;
- DERReturn drtn = DERDecodeSeqInit(sequence, &tag, &derSeq);
- require_noerr_quiet(drtn, badSequence);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, badSequence);
- DERDecodedInfo currDecoded;
- bool appendedSomething = false;
- while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) {
- switch (currDecoded.tag)
- {
- case 0: // 0
- case ASN1_SEQUENCE: // 16
- case ASN1_SET: // 17
- // skip constructed object lengths
- break;
-
- case ASN1_UTF8_STRING: // 12
- case ASN1_NUMERIC_STRING: // 18
- case ASN1_PRINTABLE_STRING: // 19
- case ASN1_T61_STRING: // 20, also ASN1_TELETEX_STRING
- case ASN1_VIDEOTEX_STRING: // 21
- case ASN1_IA5_STRING: // 22
- case ASN1_GRAPHIC_STRING: // 25
- case ASN1_VISIBLE_STRING: // 26, also ASN1_ISO646_STRING
- case ASN1_GENERAL_STRING: // 27
- case ASN1_UNIVERSAL_STRING: // 28
- {
- CFStringRef string =
- copyDERThingContentDescription(CFGetAllocator(properties),
- currDecoded.tag, &currDecoded.content, false);
- require_quiet(string, badSequence);
-
- appendPropertyP(properties, kSecPropertyTypeString, label,
- string);
- CFReleaseNull(string);
- appendedSomething = true;
- break;
- }
- default:
- break;
- }
- }
- require_quiet(drtn == DR_EndOfSequence, badSequence);
- return appendedSomething;
-badSequence:
- return false;
-}
-
-static void appendExtension(CFMutableArrayRef parent,
- const SecCertificateExtension *extn) {
- CFAllocatorRef allocator = CFGetAllocator(parent);
- CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0,
- &kCFTypeArrayCallBacks);
- const DERItem
- *extnID = &extn->extnID,
- *extnValue = &extn->extnValue;
-
- appendBoolProperty(properties, CFSTR("Critical"), extn->critical);
-
-#if 1
- bool handled = true;
- /* Extensions that we know how to handle ourselves... */
- if (extnID->length == oidSubjectKeyIdentifier.length &&
- !memcmp(extnID->data, oidSubjectKeyIdentifier.data, extnID->length - 1))
- {
- switch (extnID->data[extnID->length - 1]) {
- case 14: /* SubjectKeyIdentifier id-ce 14 */
- appendSubjectKeyIdentifier(properties, extnValue);
- break;
- case 15: /* KeyUsage id-ce 15 */
- appendKeyUsage(properties, extnValue);
- break;
- case 16: /* PrivateKeyUsagePeriod id-ce 16 */
- appendPrivateKeyUsagePeriod(properties, extnValue);
- break;
- case 17: /* SubjectAltName id-ce 17 */
- case 18: /* IssuerAltName id-ce 18 */
- appendGeneralNames(properties, extnValue);
- break;
- case 19: /* BasicConstraints id-ce 19 */
- appendBasicConstraints(properties, extnValue);
- break;
- case 30: /* NameConstraints id-ce 30 */
- handled = false;
- break;
- case 31: /* CRLDistributionPoints id-ce 31 */
- appendCrlDistributionPoints(properties, extnValue);
- break;
- case 32: /* CertificatePolicies id-ce 32 */
- appendCertificatePolicies(properties, extnValue);
- break;
- case 33: /* PolicyMappings id-ce 33 */
- handled = false;
- break;
- case 35: /* AuthorityKeyIdentifier id-ce 35 */
- appendAuthorityKeyIdentifier(properties, extnValue);
- break;
- case 36: /* PolicyConstraints id-ce 36 */
- appendPolicyConstraints(properties, extnValue);
- break;
- case 37: /* ExtKeyUsage id-ce 37 */
- appendExtendedKeyUsage(properties, extnValue);
- break;
- case 46: /* FreshestCRL id-ce 46 */
- handled = false;
- break;
- case 54: /* InhibitAnyPolicy id-ce 54 */
- handled = false;
- break;
- default:
- handled = false;
- break;
- }
- } else if (extnID->length == oidAuthorityInfoAccess.length &&
- !memcmp(extnID->data, oidAuthorityInfoAccess.data, extnID->length - 1))
- {
- switch (extnID->data[extnID->length - 1]) {
- case 1: /* AuthorityInfoAccess id-pe 1 */
- appendInfoAccess(properties, extnValue);
- break;
- case 3: /* QCStatements id-pe 3 */
- handled = false;
- break;
- case 11: /* SubjectInfoAccess id-pe 11 */
- appendInfoAccess(properties, extnValue);
- break;
- default:
- handled = false;
- break;
- }
- } else if (DEROidCompare(extnID, &oidNetscapeCertType)) {
- /* 2.16.840.1.113730.1.1 netscape 1 1 */
- appendNetscapeCertType(properties, extnValue);
- } else {
- handled = false;
- }
-
- if (!handled) {
- /* Try to parse and display printable string(s). */
- if (appendPrintableDERSequenceP(properties, CFSTR("Data"), extnValue)) {
- /* Nothing to do here appendPrintableDERSequenceP did the work. */
- } else {
- /* Couldn't parse extension; dump the raw unparsed data as hex. */
- appendUnparsedProperty(properties, CFSTR("Data"), extnValue);
- }
- }
-#else
- /* Extensions that we know how to handle ourselves... */
- if (DEROidCompare(extnID, &oidSubjectKeyIdentifier)) {
- appendSubjectKeyIdentifier(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidKeyUsage)) {
- appendKeyUsage(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidPrivateKeyUsagePeriod)) {
- appendPrivateKeyUsagePeriod(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidSubjectAltName)) {
- appendGeneralNames(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidIssuerAltName)) {
- appendGeneralNames(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidBasicConstraints)) {
- appendBasicConstraints(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidCrlDistributionPoints)) {
- appendCrlDistributionPoints(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidCertificatePolicies)) {
- appendCertificatePolicies(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidAuthorityKeyIdentifier)) {
- appendAuthorityKeyIdentifier(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidPolicyConstraints)) {
- appendPolicyConstraints(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidExtendedKeyUsage)) {
- appendExtendedKeyUsage(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidAuthorityInfoAccess)) {
- appendInfoAccess(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidSubjectInfoAccess)) {
- appendInfoAccess(properties, extnValue);
- } else if (DEROidCompare(extnID, &oidNetscapeCertType)) {
- appendNetscapeCertType(properties, extnValue);
-#if 0
- } else if (DEROidCompare(extnID, &oidEntrustVersInfo)) {
- appendEntrustVersInfo(properties, extnValue);
-#endif
- } else
- /* Try to parse and display printable string(s). */
- if (appendPrintableDERSequenceP(properties, CFSTR("Data"), extnValue)) {
- /* Nothing to do here appendPrintableDERSequenceP did the work. */
- } else {
- /* Couldn't parse extension; dump the raw unparsed data as hex. */
- appendUnparsedProperty(properties, CFSTR("Data"), extnValue);
- }
-#endif
- CFStringRef oid_string = copyLocalizedOidDescription(allocator, extnID);
- appendPropertyP(parent, kSecPropertyTypeSection, oid_string, properties);
- CFRelease(oid_string);
- CFRelease(properties);
-}
-
-/* Different types of summary types from least desired to most desired. */
-enum SummaryType {
- kSummaryTypeNone,
- kSummaryTypePrintable,
- kSummaryTypeOrganizationName,
- kSummaryTypeOrganizationalUnitName,
- kSummaryTypeCommonName,
-};
-
-struct Summary {
- enum SummaryType type;
- CFStringRef summary;
- CFStringRef description;
-};
-
-static OSStatus obtainSummaryFromX501Name(void *context,
- const DERItem *type, const DERItem *value, CFIndex rdnIX) {
- struct Summary *summary = (struct Summary *)context;
- enum SummaryType stype = kSummaryTypeNone;
- CFStringRef string = NULL;
- if (DEROidCompare(type, &oidCommonName)) {
- /* We skip Common Names that have generic values. */
- const char tfm[] = "Thawte Freemail Member";
- if ((value->length == sizeof(tfm) + 1) &&
- !memcmp(value->data + 2, tfm, sizeof(tfm) - 1)) {
- return errSecSuccess;
- }
- stype = kSummaryTypeCommonName;
- } else if (DEROidCompare(type, &oidOrganizationalUnitName)) {
- stype = kSummaryTypeOrganizationalUnitName;
- } else if (DEROidCompare(type, &oidOrganizationName)) {
- stype = kSummaryTypeOrganizationName;
- } else if (DEROidCompare(type, &oidDescription)) {
- if (!summary->description) {
- summary->description = string = copyDERThingDescription(kCFAllocatorDefault, value, true);
- CFRetain(string);
- }
- stype = kSummaryTypePrintable;
- } else {
- stype = kSummaryTypePrintable;
- }
-
- /* Use the first field we encounter of the highest priority type. */
- if (summary->type < stype) {
- if (!string) {
- string = copyDERThingDescription(kCFAllocatorDefault, value, true);
- }
-
- if (string) {
- CFReleaseSafe(summary->summary);
- summary->summary = string;
- summary->type = stype;
- }
- } else {
- CFReleaseSafe(string);
- }
-
- return errSecSuccess;
-}
-
-CFStringRef SecCertificateCopySubjectSummaryP(SecCertificateRefP certificate) {
- struct Summary summary = {};
- parseX501NameContent(&certificate->_subject, &summary, obtainSummaryFromX501Name);
- /* If we found a description and a common name we change the summary to
- CommonName (Description). */
- if (summary.description) {
- if (summary.type == kSummaryTypeCommonName) {
- CFStringRef newSummary = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
- CFSTR("%@ (%@)"), summary.summary, summary.description);
- CFRelease(summary.summary);
- summary.summary = newSummary;
- }
- CFRelease(summary.description);
- }
-
- if (!summary.summary) {
- /* If we didn't find a suitable printable string in the subject at all, we try
- the first email address in the certificate instead. */
- CFArrayRef names = SecCertificateCopyRFC822NamesP(certificate);
- if (!names) {
- /* If we didn't find any email addresses in the certificate, we try finding
- a DNS name instead. */
- names = SecCertificateCopyDNSNamesP(certificate);
- }
- if (names) {
- summary.summary = CFArrayGetValueAtIndex(names, 0);
- CFRetain(summary.summary);
- CFRelease(names);
- }
- }
-
- return summary.summary;
-}
-
-CFStringRef SecCertificateCopyIssuerSummaryP(SecCertificateRefP certificate) {
- struct Summary summary = {};
- parseX501NameContent(&certificate->_issuer, &summary, obtainSummaryFromX501Name);
- /* If we found a description and a common name we change the summary to
- CommonName (Description). */
- if (summary.description) {
- if (summary.type == kSummaryTypeCommonName) {
- CFStringRef newSummary = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
- CFSTR("%@ (%@)"), summary.summary, summary.description);
- CFRelease(summary.summary);
- summary.summary = newSummary;
- }
- CFRelease(summary.description);
- }
-
- return summary.summary;
-}
-
-/* Return the earliest date on which all certificates in this chain are still
- valid. */
-static CFAbsoluteTime SecCertificateGetChainsLastValidity(
- SecCertificateRefP certificate) {
- CFAbsoluteTime earliest = certificate->_notAfter;
-#if 0
- while (certificate->_parent) {
- certificate = certificate->_parent;
- if (earliest > certificate->_notAfter)
- earliest = certificate->_notAfter;
- }
-#endif
-
- return earliest;
-}
-
-/* Return the latest date on which all certificates in this chain will be
- valid. */
-static CFAbsoluteTime SecCertificateGetChainsFirstValidity(
- SecCertificateRefP certificate) {
- CFAbsoluteTime latest = certificate->_notBefore;
-#if 0
- while (certificate->_parent) {
- certificate = certificate->_parent;
- if (latest < certificate->_notBefore)
- latest = certificate->_notBefore;
- }
-#endif
-
- return latest;
-}
-
-bool SecCertificateIsValidP(SecCertificateRefP certificate,
- CFAbsoluteTime verifyTime) {
- check(certificate);
- return certificate->_notBefore <= verifyTime &&
- verifyTime <= certificate->_notAfter;
-}
-
-CFIndex SecCertificateVersionP(SecCertificateRefP certificate) {
- return certificate->_version + 1;
-}
-
-CFAbsoluteTime SecCertificateNotValidBeforeP(SecCertificateRefP certificate) {
- return certificate->_notBefore;
-}
-
-CFAbsoluteTime SecCertificateNotValidAfterP(SecCertificateRefP certificate) {
- return certificate->_notAfter;
-}
-
-CFMutableArrayRef SecCertificateCopySummaryPropertiesP(
- SecCertificateRefP certificate, CFAbsoluteTime verifyTime) {
- CFAllocatorRef allocator = CFGetAllocator(certificate);
- CFMutableArrayRef summary = CFArrayCreateMutable(allocator, 0,
- &kCFTypeArrayCallBacks);
-
- /* First we put the subject summary name. */
- CFStringRef ssummary = SecCertificateCopySubjectSummaryP(certificate);
- if (ssummary) {
- appendPropertyP(summary, kSecPropertyTypeTitle,
- NULL, ssummary);
- CFRelease(ssummary);
- }
-#if 0
- CFStringRef isummary = CFSTR("Issuer Summary");
- appendPropertyP(summary, kSecPropertyTypeString,
- CFSTR("Issued By"), isummary);
- CFRelease(isummary);
-#endif
-
- /* Let see if this certificate is currently valid. */
- CFStringRef label;
- CFAbsoluteTime when;
- CFStringRef message;
- CFStringRef ptype;
- if (verifyTime > certificate->_notAfter) {
- label = CFSTR("Expired");
- when = certificate->_notAfter;
- ptype = kSecPropertyTypeError;
- message = CFSTR("This certificate has expired");
- } else if (certificate->_notBefore > verifyTime) {
- label = CFSTR("Valid from");
- when = certificate->_notBefore;
- ptype = kSecPropertyTypeError;
- message = CFSTR("This certificate is not yet valid");
- } else {
- CFAbsoluteTime last = SecCertificateGetChainsLastValidity(certificate);
- CFAbsoluteTime first = SecCertificateGetChainsFirstValidity(certificate);
- if (verifyTime > last) {
- label = CFSTR("Expired");
- when = last;
- ptype = kSecPropertyTypeError;
- message = CFSTR("This certificate has an issuer that has expired");
- } else if (verifyTime < first) {
- label = CFSTR("Valid from");
- when = first;
- ptype = kSecPropertyTypeError;
- message = CFSTR("This certificate has an issuer that is not yet valid");
- } else {
- label = CFSTR("Expires");
- when = certificate->_notAfter;
- ptype = kSecPropertyTypeSuccess;
- message = CFSTR("This certificate is valid");
- }
- }
-
- appendDateProperty(summary, label, when);
- appendPropertyP(summary, ptype, NULL, message);
-
- return summary;
-}
-
-CFArrayRef SecCertificateCopyPropertiesP(SecCertificateRefP certificate) {
- if (!certificate->_properties) {
- CFAllocatorRef allocator = CFGetAllocator(certificate);
- CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0,
- &kCFTypeArrayCallBacks);
- require_quiet(properties, out);
-
- /* First we put the Subject Name in the property list. */
- CFArrayRef subject_plist = createPropertiesForX501NameContent(allocator,
- &certificate->_subject);
- if (subject_plist) {
- appendPropertyP(properties, kSecPropertyTypeSection,
- CFSTR("Subject Name"), subject_plist);
- }
- CFReleaseNull(subject_plist);
-
- /* Next we put the Issuer Name in the property list. */
- CFArrayRef issuer_plist = createPropertiesForX501NameContent(allocator,
- &certificate->_issuer);
- if (issuer_plist) {
- appendPropertyP(properties, kSecPropertyTypeSection,
- CFSTR("Issuer Name"), issuer_plist);
- }
- CFReleaseNull(issuer_plist);
-
- /* Version */
- CFStringRef versionString = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"),
- certificate->_version + 1);
- if (versionString) {
- appendPropertyP(properties, kSecPropertyTypeString,
- CFSTR("Version"), versionString);
- }
- CFReleaseNull(versionString);
-
- /* Serial Number */
- if (certificate->_serialNum.length) {
- appendIntegerProperty(properties, CFSTR("Serial Number"),
- &certificate->_serialNum);
- }
-
- /* Signature algorithm. */
- appendAlgorithmProperty(properties, CFSTR("Signature Algorithm"),
- &certificate->_tbsSigAlg);
-
-
- /* Validity dates. */
- appendDateProperty(properties, CFSTR("Not Valid Before"),
- certificate->_notBefore);
- appendDateProperty(properties, CFSTR("Not Valid After"),
- certificate->_notAfter);
-
- if (certificate->_subjectUniqueID.length) {
- appendDataProperty(properties, CFSTR("Subject Unique ID"),
- &certificate->_subjectUniqueID);
- }
- if (certificate->_issuerUniqueID.length) {
- appendDataProperty(properties, CFSTR("Issuer Unique ID"),
- &certificate->_issuerUniqueID);
- }
-
- /* Public key algorithm. */
- appendAlgorithmProperty(properties, CFSTR("Public Key Algorithm"),
- &certificate->_algId);
-
- /* Consider breaking down an RSA public key into modulus and
- exponent? */
- appendDataProperty(properties, CFSTR("Public Key Data"),
- &certificate->_pubKeyDER);
- /* @@@ Key Size. */
- /* @@@ Key Usage. */
-
- appendDataProperty(properties, CFSTR("Signature"),
- &certificate->_signature);
-
- CFIndex ix;
- for (ix = 0; ix < certificate->_extensionCount; ++ix) {
- appendExtension(properties, &certificate->_extensions[ix]);
- }
-
- /* @@@ Key Fingerprints. */
-
- certificate->_properties = properties;
- }
-
-out:
- CFRetainSafe(certificate->_properties);
- return certificate->_properties;
-}
-
-CFDataRef SecCertificateCopySerialNumberP(
- SecCertificateRefP certificate) {
- if (certificate->_serialNumber) {
- CFRetain(certificate->_serialNumber);
- }
- return certificate->_serialNumber;
-}
-
-/*
- * Accessor for normalized issuer content
- */
-CFDataRef SecCertificateGetNormalizedIssuerContentP(
- SecCertificateRefP certificate) {
- return certificate->_normalizedIssuer;
-}
-
-/*
- * Accessor for normalized subject content
- */
-CFDataRef SecCertificateGetNormalizedSubjectContentP(
- SecCertificateRefP certificate) {
- return certificate->_normalizedSubject;
-}
-
-/*
- * Returns DER-encoded normalized issuer sequence
- * for use with SecItemCopyMatching; caller must release
- */
-CFDataRef SecCertificateCopyNormalizedIssuerSequenceP(
- SecCertificateRefP certificate) {
- if (!certificate || !certificate->_normalizedIssuer) {
- return NULL;
- }
- DERItem tmpdi;
- tmpdi.data = (DERByte *)CFDataGetBytePtr(certificate->_normalizedIssuer);
- tmpdi.length = CFDataGetLength(certificate->_normalizedIssuer);
-
- return SecDERItemCopySequenceP(&tmpdi);
-}
-
-/*
- * Returns DER-encoded normalized subject sequence
- * for use with SecItemCopyMatching; caller must release
- */
-CFDataRef SecCertificateCopyNormalizedSubjectSequenceP(
- SecCertificateRefP certificate) {
- if (!certificate || !certificate->_normalizedSubject) {
- return NULL;
- }
- DERItem tmpdi;
- tmpdi.data = (DERByte *)CFDataGetBytePtr(certificate->_normalizedSubject);
- tmpdi.length = CFDataGetLength(certificate->_normalizedSubject);
-
- return SecDERItemCopySequenceP(&tmpdi);
-}
-
-/* Verify that certificate was signed by issuerKey. */
-OSStatus SecCertificateIsSignedByP(SecCertificateRefP certificate,
- SecKeyRefP issuerKey) {
- /* Setup algId in SecAsn1AlgId format. */
- SecAsn1AlgId algId;
- algId.algorithm.Length = certificate->_tbsSigAlg.oid.length;
- algId.algorithm.Data = certificate->_tbsSigAlg.oid.data;
- algId.parameters.Length = certificate->_tbsSigAlg.params.length;
- algId.parameters.Data = certificate->_tbsSigAlg.params.data;
-
-#if 0
- OSStatus status = SecKeyDigestAndVerify(issuerKey, &algId,
- certificate->_tbs.data, certificate->_tbs.length,
- certificate->_signature.data, certificate->_signature.length);
- if (status) {
- secinfo("verify", "signature verify failed: %d", status);
- return errSecNotSigner;
- }
-#endif
-
- return errSecSuccess;
-}
-
-#if 0
-static OSStatus SecCertificateIsIssuedBy(SecCertificateRefP certificate,
- SecCertificateRefP issuer, bool signatureCheckOnly) {
- if (!signatureCheckOnly) {
- /* It turns out we don't actually need to use normalized subject and
- issuer according to rfc2459. */
-
- /* If present we should check issuerID against the issuer subjectID. */
-
- /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
- then we should look for a SubjectKeyIdentifier in the issuer
- certificate.
- If we have a authorityCertSerialNumber we can use that for chaining.
- If we have a authorityCertIssuer we can use that? (or not) */
-
- /* Verify that this cert was issued by issuer. Do so by chaining
- either issuerID to subjectID or normalized issuer to normalized
- subject. */
- CFDataRef normalizedIssuer =
- SecCertificateGetNormalizedIssuerContentP(certificate);
- CFDataRef normalizedIssuerSubject =
- SecCertificateGetNormalizedSubjectContentP(issuer);
- if (normalizedIssuer && normalizedIssuerSubject &&
- !CFEqual(normalizedIssuer, normalizedIssuerSubject))
- return errSecIssuerMismatch;
- }
-
- /* Next verify that this cert was signed by issuer. */
- SecKeyRef issuerKey = SecCertificateGetPublicKey(issuer);
-
- /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
- /* FIXME: We sould cache this (or at least the digest) until we find
- a suitable issuer. */
- uint8_t signedData[DER_SHA1_DIGEST_INFO_LEN];
- CFIndex signedDataLength;
- CertVerifyReturn crtn;
- if (DEROidCompare(&certificate->_tbsSigAlg.oid, &oidSha1Rsa)) {
- signedDataLength = DER_SHA1_DIGEST_INFO_LEN;
- crtn = sha1DigestInfo(&certificate->_tbs, signedData);
- } else if(DEROidCompare(&certificate->_tbsSigAlg.oid, &oidMd5Rsa)) {
- signedDataLength = DER_MD_DIGEST_INFO_LEN;
- crtn = mdDigestInfo(WD_MD5, &certificate->_tbs, signedData);
- } else if(DEROidCompare(&certificate->_tbsSigAlg.oid, &oidMd2Rsa)) {
- signedDataLength = DER_MD_DIGEST_INFO_LEN;
- crtn = mdDigestInfo(WD_MD2, &certificate->_tbs, signedData);
- } else {
- secinfo("verify", "unsupported algorithm");
- return errSecUnsupportedAlgorithm;
- }
- if (crtn) {
- secinfo("verify", "*DigestInfo returned: %d", crtn);
- /* FIXME: Do proper error code translation. */
- return errSecUnsupportedAlgorithm;
- }
-
- OSStatus status = SecKeyRawVerify(issuerKey, kSecPaddingPKCS1,
- signedData, signedDataLength,
- certificate->_signature.data, certificate->_signature.length);
- if (status) {
- secinfo("verify", "signature verify failed: %d", status);
- return errSecNotSigner;
- }
-
- return errSecSuccess;
-}
-
-static OSStatus _SecCertificateSetParent(SecCertificateRefP certificate,
- SecCertificateRefP issuer, bool signatureCheckOnly) {
- check(issuer);
- if (certificate->_parent) {
- /* Setting a certificates issuer twice is only allowed if the new
- issuer is equal to the current one. */
- return issuer && CFEqual(certificate->_parent, issuer);
- }
-
-#if 0
- OSStatus status = SecCertificateIsIssuedBy(certificate, issuer,
- signatureCheckOnly);
-#else
- OSStatus status = errSecSuccess;
-#endif
- if (!status) {
- if (CFEqual(certificate, issuer)) {
- /* We don't retain ourselves cause that would be bad mojo,
- however we do record that we are properly self signed. */
- certificate->_isSelfSigned = kSecSelfSignedTrue;
- secinfo("cert", "set self as parent");
- return errSecSuccess;
- }
-
- CFRetain(issuer);
- certificate->_parent = issuer;
- certificate->_isSelfSigned = kSecSelfSignedFalse;
- }
-
- return status;
-}
-
-static bool SecCertificateIsSelfSignedP(SecCertificateRefP certificate) {
- if (certificate->_isSelfSigned == kSecSelfSignedUnknown) {
- certificate->_isSelfSigned =
- (SecCertificateIsIssuedBy(certificate, certificate, false) ?
- kSecSelfSignedTrue : kSecSelfSignedFalse);
- }
-
- return certificate->_isSelfSigned == kSecSelfSignedTrue;
-}
-
-/* Return true iff we were able to set our own parent from one of the
- certificates in other_certificates, return false otherwise. If
- signatureCheckOnly is true, we can skip the subject == issuer or
- authorityKeyIdentifier tests. */
-static bool SecCertificateSetParentFrom(SecCertificateRefP certificate,
- CFArrayRef other_certificates, bool signatureCheckOnly) {
- CFIndex count = CFArrayGetCount(other_certificates);
- CFIndex ix;
- for (ix = 0; ix < count; ++ix) {
- SecCertificateRefP candidate = (SecCertificateRefP)
- CFArrayGetValueAtIndex(other_certificates, ix);
- if (_SecCertificateSetParent(certificate, candidate,
- signatureCheckOnly))
- return true;
- }
- return false;
-}
-
-/* Lookup the parent of certificate in the keychain and set it. */
-static bool SecCertificateFindParent(SecCertificateRefP certificate) {
- /* FIXME: Search for things other than just subject of our issuer if we
- have a subjectID or authorityKeyIdentifier. */
- CFDataRef normalizedIssuer =
- SecCertificateGetNormalizedIssuerContentP(certificate);
- const void *keys[] = {
- kSecClass,
- kSecReturnRef,
- kSecMatchLimit,
- kSecAttrSubject
- },
- *values[] = {
- kSecClassCertificate,
- kCFBooleanTrue,
- kSecMatchLimitAll,
- normalizedIssuer
- };
- CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 4,
- &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- CFTypeRef results;
- OSStatus status = SecItemCopyMatching(query, &results);
- CFRelease(query);
- if (status) {
- secinfo("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
- status);
- return false;
- }
- CFArrayRef certs = (CFArrayRef)results;
- /* Since we already know the certificates we are providing as candidates
- have been checked for subject matching, we can ask
- SecCertificateSetParentFrom to skip everything except the signature
- checks. */
- bool result = SecCertificateSetParentFrom(certificate, certs, true);
- CFRelease(certs);
- return result;
-}
-
-OSStatus SecCertificateCompleteChainP(SecCertificateRefP certificate,
- CFArrayRef other_certificates) {
- for (;;) {
- if (certificate->_parent == NULL) {
- if (SecCertificateIsSelfSignedP(certificate))
- return errSecSuccess;
- if (!other_certificates ||
- !SecCertificateSetParentFrom(certificate, other_certificates,\
- false)) {
- if (!SecCertificateFindParent(certificate))
- return errSecIssuerNotFound;
- }
- }
- certificate = certificate->_parent;
- }
-}
-#endif
-
-static OSStatus appendIPAddressesFromGeneralNames(void *context,
- SecCEGeneralNameType gnType, const DERItem *generalName) {
- CFMutableArrayRef ipAddresses = (CFMutableArrayRef)context;
- if (gnType == GNT_IPAddress) {
- CFStringRef string = copyIPAddressContentDescription(
- kCFAllocatorDefault, generalName);
- if (string) {
- CFArrayAppendValue(ipAddresses, string);
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-CFArrayRef SecCertificateCopyIPAddressesP(SecCertificateRefP certificate) {
- /* These can only exist in the subject alt name. */
- if (!certificate->_subjectAltName)
- return NULL;
-
- CFMutableArrayRef ipAddresses = CFArrayCreateMutable(kCFAllocatorDefault,
- 0, &kCFTypeArrayCallBacks);
- OSStatus status = parseGeneralNames(&certificate->_subjectAltName->extnValue,
- ipAddresses, appendIPAddressesFromGeneralNames);
- if (status || CFArrayGetCount(ipAddresses) == 0) {
- CFRelease(ipAddresses);
- ipAddresses = NULL;
- }
- return ipAddresses;
-}
-
-static OSStatus appendDNSNamesFromGeneralNames(void *context, SecCEGeneralNameType gnType,
- const DERItem *generalName) {
- CFMutableArrayRef dnsNames = (CFMutableArrayRef)context;
- if (gnType == GNT_DNSName) {
- CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault,
- generalName->data, generalName->length,
- kCFStringEncodingUTF8, FALSE);
- if (string) {
- CFArrayAppendValue(dnsNames, string);
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-/* Return true if the passed in string matches the
- Preferred name syntax from sections 2.3.1. in RFC 1035.
- With the added check that we disallow empty dns names.
- Also in order to support wildcard DNSNames we allow for the '*'
- character anywhere in a dns component where we currently allow
- a letter.
-
- <domain> ::= <subdomain> | " "
-
- <subdomain> ::= <label> | <subdomain> "." <label>
-
- <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
-
- <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
-
- <let-dig-hyp> ::= <let-dig> | "-"
-
- <let-dig> ::= <letter> | <digit>
-
- <letter> ::= any one of the 52 alphabetic characters A through Z in
- upper case and a through z in lower case
-
- <digit> ::= any one of the ten digits 0 through 9
- */
-static bool isDNSName(CFStringRef string) {
- CFStringInlineBuffer buf;
- CFIndex ix, labelLength = 0, length = CFStringGetLength(string);
- /* From RFC 1035 2.3.4. Size limits:
- labels 63 octets or less
- names 255 octets or less */
- require_quiet(length <= 255, notDNS);
- CFRange range = { 0, length };
- CFStringInitInlineBuffer(string, &buf, range);
- enum {
- kDNSStateInital,
- kDNSStateAfterDot,
- kDNSStateAfterAlpha,
- kDNSStateAfterDigit,
- kDNSStateAfterDash,
- } state = kDNSStateInital;
-
- bool nonAlpha = false;
- for (ix = 0; ix < length; ++ix) {
- UniChar ch = CFStringGetCharacterFromInlineBuffer(&buf, ix);
- labelLength++;
- if (ch == '.') {
- require_quiet(labelLength <= 64 &&
- (state == kDNSStateAfterAlpha || state == kDNSStateAfterDigit),
- notDNS);
- state = kDNSStateAfterDot;
- labelLength = 0;
- nonAlpha = false;
- } else if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') ||
- ch == '*') {
- state = kDNSStateAfterAlpha;
- } else if ('0' <= ch && ch <= '9') {
-#if 0
- /* The requirement for labels to start with a letter was
- dropped so we don't check this anymore. */
- require_quiet(state == kDNSStateAfterAlpha ||
- state == kDNSStateAfterDigit ||
- state == kDNSStateAfterDash, notDNS);
-#endif
- state = kDNSStateAfterDigit;
- nonAlpha = true;
- } else if (ch == '-') {
- require_quiet(state == kDNSStateAfterAlpha ||
- state == kDNSStateAfterDigit ||
- state == kDNSStateAfterDash, notDNS);
- state = kDNSStateAfterDash;
- nonAlpha = true;
- } else {
- goto notDNS;
- }
- }
-
- /* We don't allow a dns name to end in a dot, and we require the
- final name component to only have alphanumeric chars. */
- require_quiet(!nonAlpha && labelLength <= 63 &&
- (state == kDNSStateAfterAlpha || state == kDNSStateAfterDigit),
- notDNS);
-
- return true;
-notDNS:
- return false;
-}
-
-static OSStatus appendDNSNamesFromX501Name(void *context, const DERItem *type,
- const DERItem *value, CFIndex rdnIX) {
- CFMutableArrayRef dnsNames = (CFMutableArrayRef)context;
- if (DEROidCompare(type, &oidCommonName)) {
- CFStringRef string = copyDERThingDescription(kCFAllocatorDefault,
- value, true);
- if (string) {
- if (isDNSName(string)) {
- /* We found a common name that is formatted like a valid
- dns name. */
- CFArrayAppendValue(dnsNames, string);
- }
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-/* Not everything returned by this function is going to be a proper DNS name,
- we also return the certificates common name entries from the subject,
- assuming they look like dns names as specified in RFC 1035. */
-CFArrayRef SecCertificateCopyDNSNamesP(SecCertificateRefP certificate) {
- /* These can exist in the subject alt name or in the subject. */
- CFMutableArrayRef dnsNames = CFArrayCreateMutable(kCFAllocatorDefault,
- 0, &kCFTypeArrayCallBacks);
- OSStatus status = errSecSuccess;
- if (certificate->_subjectAltName) {
- status = parseGeneralNames(&certificate->_subjectAltName->extnValue,
- dnsNames, appendDNSNamesFromGeneralNames);
- }
- /* RFC 2818 section 3.1. Server Identity
- [...]
- If a subjectAltName extension of type dNSName is present, that MUST
- be used as the identity. Otherwise, the (most specific) Common Name
- field in the Subject field of the certificate MUST be used. Although
- the use of the Common Name is existing practice, it is deprecated and
- Certification Authorities are encouraged to use the dNSName instead.
- [...]
-
- This implies that if we found 1 or more DNSNames in the
- subjectAltName, we should not use the Common Name of the subject as
- a DNSName.
- */
- if (!status && CFArrayGetCount(dnsNames) == 0) {
- status = parseX501NameContent(&certificate->_subject, dnsNames,
- appendDNSNamesFromX501Name);
- }
- if (status || CFArrayGetCount(dnsNames) == 0) {
- CFRelease(dnsNames);
- dnsNames = NULL;
- }
- return dnsNames;
-}
-
-static OSStatus appendRFC822NamesFromGeneralNames(void *context,
- SecCEGeneralNameType gnType, const DERItem *generalName) {
- CFMutableArrayRef dnsNames = (CFMutableArrayRef)context;
- if (gnType == GNT_RFC822Name) {
- CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault,
- generalName->data, generalName->length,
- kCFStringEncodingASCII, FALSE);
- if (string) {
- CFArrayAppendValue(dnsNames, string);
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-static OSStatus appendRFC822NamesFromX501Name(void *context, const DERItem *type,
- const DERItem *value, CFIndex rdnIX) {
- CFMutableArrayRef dnsNames = (CFMutableArrayRef)context;
- if (DEROidCompare(type, &oidEmailAddress)) {
- CFStringRef string = copyDERThingDescription(kCFAllocatorDefault,
- value, true);
- if (string) {
- CFArrayAppendValue(dnsNames, string);
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-CFArrayRef SecCertificateCopyRFC822NamesP(SecCertificateRefP certificate) {
- /* These can exist in the subject alt name or in the subject. */
- CFMutableArrayRef rfc822Names = CFArrayCreateMutable(kCFAllocatorDefault,
- 0, &kCFTypeArrayCallBacks);
- OSStatus status = errSecSuccess;
- if (certificate->_subjectAltName) {
- status = parseGeneralNames(&certificate->_subjectAltName->extnValue,
- rfc822Names, appendRFC822NamesFromGeneralNames);
- }
- if (!status) {
- status = parseX501NameContent(&certificate->_subject, rfc822Names,
- appendRFC822NamesFromX501Name);
- }
- if (status || CFArrayGetCount(rfc822Names) == 0) {
- CFRelease(rfc822Names);
- rfc822Names = NULL;
- }
- return rfc822Names;
-}
-
-static OSStatus appendCommonNamesFromX501Name(void *context,
- const DERItem *type, const DERItem *value, CFIndex rdnIX) {
- CFMutableArrayRef commonNames = (CFMutableArrayRef)context;
- if (DEROidCompare(type, &oidCommonName)) {
- CFStringRef string = copyDERThingDescription(kCFAllocatorDefault,
- value, true);
- if (string) {
- CFArrayAppendValue(commonNames, string);
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-CFArrayRef SecCertificateCopyCommonNamesP(SecCertificateRefP certificate) {
- CFMutableArrayRef commonNames = CFArrayCreateMutable(kCFAllocatorDefault,
- 0, &kCFTypeArrayCallBacks);
- OSStatus status;
- status = parseX501NameContent(&certificate->_subject, commonNames,
- appendCommonNamesFromX501Name);
- if (status || CFArrayGetCount(commonNames) == 0) {
- CFRelease(commonNames);
- commonNames = NULL;
- }
- return commonNames;
-}
-
-static OSStatus appendOrganizationFromX501Name(void *context,
- const DERItem *type, const DERItem *value, CFIndex rdnIX) {
- CFMutableArrayRef organization = (CFMutableArrayRef)context;
- if (DEROidCompare(type, &oidOrganizationName)) {
- CFStringRef string = copyDERThingDescription(kCFAllocatorDefault,
- value, true);
- if (string) {
- CFArrayAppendValue(organization, string);
- CFRelease(string);
- } else {
- return errSecInvalidCertificate;
- }
- }
- return errSecSuccess;
-}
-
-CFArrayRef SecCertificateCopyOrganizationP(SecCertificateRefP certificate) {
- CFMutableArrayRef organization = CFArrayCreateMutable(kCFAllocatorDefault,
- 0, &kCFTypeArrayCallBacks);
- OSStatus status;
- status = parseX501NameContent(&certificate->_subject, organization,
- appendOrganizationFromX501Name);
- if (status || CFArrayGetCount(organization) == 0) {
- CFRelease(organization);
- organization = NULL;
- }
- return organization;
-}
-
-const SecCEBasicConstraints *
-SecCertificateGetBasicConstraintsP(SecCertificateRefP certificate) {
- if (certificate->_basicConstraints.present)
- return &certificate->_basicConstraints;
- else
- return NULL;
-}
-
-const SecCEPolicyConstraints *
-SecCertificateGetPolicyConstraintsP(SecCertificateRefP certificate) {
- if (certificate->_policyConstraints.present)
- return &certificate->_policyConstraints;
- else
- return NULL;
-}
-
-CFDictionaryRef
-SecCertificateGetPolicyMappingsP(SecCertificateRefP certificate) {
- return certificate->_policyMappings;
-}
-
-const SecCECertificatePolicies *
-SecCertificateGetCertificatePoliciesP(SecCertificateRefP certificate) {
- if (certificate->_certificatePolicies.present)
- return &certificate->_certificatePolicies;
- else
- return NULL;
-}
-
-uint32_t
-SecCertificateGetInhibitAnyPolicySkipCertsP(SecCertificateRefP certificate) {
- return certificate->_inhibitAnyPolicySkipCerts;
-}
-
-static OSStatus appendNTPrincipalNamesFromGeneralNames(void *context,
- SecCEGeneralNameType gnType, const DERItem *generalName) {
- CFMutableArrayRef ntPrincipalNames = (CFMutableArrayRef)context;
- if (gnType == GNT_OtherName) {
- DEROtherName on;
- DERReturn drtn = DERParseSequenceContent(generalName,
- DERNumOtherNameItemSpecs, DEROtherNameItemSpecs,
- &on, sizeof(on));
- require_noerr_quiet(drtn, badDER);
- if (DEROidCompare(&on.typeIdentifier, &oidMSNTPrincipalName)) {
- CFStringRef string;
- require_quiet(string = copyDERThingDescription(kCFAllocatorDefault,
- &on.value, true), badDER);
- CFArrayAppendValue(ntPrincipalNames, string);
- CFRelease(string);
- }
- }
- return errSecSuccess;
-
-badDER:
- return errSecInvalidCertificate;
-
-}
-
-CFArrayRef SecCertificateCopyNTPrincipalNamesP(SecCertificateRefP certificate) {
- CFMutableArrayRef ntPrincipalNames = CFArrayCreateMutable(kCFAllocatorDefault,
- 0, &kCFTypeArrayCallBacks);
- OSStatus status = errSecSuccess;
- if (certificate->_subjectAltName) {
- status = parseGeneralNames(&certificate->_subjectAltName->extnValue,
- ntPrincipalNames, appendNTPrincipalNamesFromGeneralNames);
- }
- if (status || CFArrayGetCount(ntPrincipalNames) == 0) {
- CFRelease(ntPrincipalNames);
- ntPrincipalNames = NULL;
- }
- return ntPrincipalNames;
-}
-
-static OSStatus appendToRFC2253String(void *context,
- const DERItem *type, const DERItem *value, CFIndex rdnIX) {
- CFMutableStringRef string = (CFMutableStringRef)context;
- /*
- CN commonName
- L localityName
- ST stateOrProvinceName
- O organizationName
- OU organizationalUnitName
- C countryName
- STREET streetAddress
- DC domainComponent
- UID userid
- */
- /* Prepend a + if this is not the first RDN in an RDN set.
- Otherwise prepend a , if this is not the first RDN. */
- if (rdnIX > 0)
- CFStringAppend(string, CFSTR("+"));
- else if (CFStringGetLength(string)) {
- CFStringAppend(string, CFSTR(","));
- }
-
- CFStringRef label, oid = NULL;
- /* @@@ Consider changing this to a dictionary lookup keyed by the
- decimal representation. */
-#if 0 // represent all labels as oids
- if (DEROidCompare(type, &oidCommonName)) {
- label = CFSTR("CN");
- } else if (DEROidCompare(type, &oidLocalityName)) {
- label = CFSTR("L");
- } else if (DEROidCompare(type, &oidStateOrProvinceName)) {
- label = CFSTR("ST");
- } else if (DEROidCompare(type, &oidOrganizationName)) {
- label = CFSTR("O");
- } else if (DEROidCompare(type, &oidOrganizationalUnitName)) {
- label = CFSTR("OU");
- } else if (DEROidCompare(type, &oidCountryName)) {
- label = CFSTR("C");
-#if 0
- } else if (DEROidCompare(type, &oidStreetAddress)) {
- label = CFSTR("STREET");
- } else if (DEROidCompare(type, &oidDomainComponent)) {
- label = CFSTR("DC");
- } else if (DEROidCompare(type, &oidUserID)) {
- label = CFSTR("UID");
-#endif
- } else
-#endif
- {
- label = oid = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, type);
- }
-
- CFStringAppend(string, label);
- CFStringAppend(string, CFSTR("="));
- CFStringRef raw = NULL;
- if (!oid)
- raw = copyDERThingDescription(kCFAllocatorDefault, value, true);
-
- if (raw) {
- /* Append raw to string while escaping:
- a space or "#" character occurring at the beginning of the string
- a space character occurring at the end of the string
- one of the characters ",", "+", """, "\", "<", ">" or ";"
- */
- CFStringInlineBuffer buffer;
- CFIndex ix, length = CFStringGetLength(raw);
- CFRange range = { 0, length };
- CFStringInitInlineBuffer(raw, &buffer, range);
- for (ix = 0; ix < length; ++ix) {
- UniChar ch = CFStringGetCharacterFromInlineBuffer(&buffer, ix);
- if (ch < 0x20) {
- CFStringAppendFormat(string, NULL, CFSTR("\\%02X"), ch);
- } else if (ch == ',' || ch == '+' || ch == '"' || ch == '\\' ||
- ch == '<' || ch == '>' || ch == ';' ||
- (ch == ' ' && (ix == 0 || ix == length - 1)) ||
- (ch == '#' && ix == 0)) {
- UniChar chars[] = { '\\', ch };
- CFStringAppendCharacters(string, chars, 2);
- } else {
- CFStringAppendCharacters(string, &ch, 1);
- }
- }
- CFRelease(raw);
- } else {
- /* Append the value in hex. */
- CFStringAppend(string, CFSTR("#"));
- DERSize ix;
- for (ix = 0; ix < value->length; ++ix)
- CFStringAppendFormat(string, NULL, CFSTR("%02X"), value->data[ix]);
- }
-
- CFReleaseSafe(oid);
-
- return errSecSuccess;
-}
-
-CFStringRef SecCertificateCopySubjectStringP(SecCertificateRefP certificate) {
- CFMutableStringRef string = CFStringCreateMutable(kCFAllocatorDefault, 0);
- OSStatus status = parseX501NameContent(&certificate->_subject, string, appendToRFC2253String);
- if (status || CFStringGetLength(string) == 0) {
- CFRelease(string);
- string = NULL;
- }
- return string;
-}
-
-static OSStatus appendToCompanyNameString(void *context,
- const DERItem *type, const DERItem *value, CFIndex rdnIX) {
- CFMutableStringRef string = (CFMutableStringRef)context;
- if (CFStringGetLength(string) != 0)
- return errSecSuccess;
-
- if (!DEROidCompare(type, &oidOrganizationName))
- return errSecSuccess;
-
- CFStringRef raw;
- raw = copyDERThingDescription(kCFAllocatorDefault, value, true);
- if (!raw)
- return errSecSuccess;
- CFStringAppend(string, raw);
- CFRelease(raw);
-
- return errSecSuccess;
-}
-
-CFStringRef SecCertificateCopyCompanyNameP(SecCertificateRefP certificate) {
- CFMutableStringRef string = CFStringCreateMutable(kCFAllocatorDefault, 0);
- OSStatus status = parseX501NameContent(&certificate->_subject, string,
- appendToCompanyNameString);
- if (status || CFStringGetLength(string) == 0) {
- CFRelease(string);
- string = NULL;
- }
- return string;
-}
-
-CFDataRef SecDERItemCopySequenceP(DERItem *content) {
- DERSize seq_len_length = DERLengthOfLength(content->length);
- size_t sequence_length = 1 + seq_len_length + content->length;
- CFMutableDataRef sequence = CFDataCreateMutable(kCFAllocatorDefault,
- sequence_length);
- CFDataSetLength(sequence, sequence_length);
- uint8_t *sequence_ptr = CFDataGetMutableBytePtr(sequence);
- *sequence_ptr++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE;
- require_noerr_quiet(DEREncodeLength(content->length,
- sequence_ptr, &seq_len_length), out);
- sequence_ptr += seq_len_length;
- memcpy(sequence_ptr, content->data, content->length);
- return sequence;
-out:
- CFReleaseSafe(sequence);
- return NULL;
-}
-
-CFDataRef SecCertificateCopyIssuerSequenceP(
- SecCertificateRefP certificate) {
- return SecDERItemCopySequenceP(&certificate->_issuer);
-}
-
-CFDataRef SecCertificateCopySubjectSequenceP(
- SecCertificateRefP certificate) {
- return SecDERItemCopySequenceP(&certificate->_subject);
-}
-
-const DERAlgorithmId *SecCertificateGetPublicKeyAlgorithmP(
- SecCertificateRefP certificate) {
- return &certificate->_algId;
-}
-
-const DERItem *SecCertificateGetPublicKeyDataP(SecCertificateRefP certificate) {
- return &certificate->_pubKeyDER;
-}
-
-SecKeyRefP SecCertificateCopyPublicKeyP(SecCertificateRefP certificate) {
- SecKeyRefP publicKey = NULL;
-#if 0
- const DERAlgorithmId *algId =
- SecCertificateGetPublicKeyAlgorithmP(certificate);
- const DERItem *keyData = SecCertificateGetPublicKeyData(certificate);
- if (DEROidCompare(&algId->oid, &oidRsa)) {
- publicKey = SecKeyCreateRSAPublicKey(kCFAllocatorDefault,
- keyData->data, keyData->length, kSecKeyEncodingPkcs1);
- } else {
- secinfo("cert", "Unsupported algorithm oid");
- }
-#endif
-
- return publicKey;
-}
-
-CFDataRef SecCertificateGetSHA1DigestP(SecCertificateRefP certificate) {
- if (!certificate->_sha1Digest) {
- certificate->_sha1Digest =
- SecSHA1DigestCreate(CFGetAllocator(certificate),
- certificate->_der.data, certificate->_der.length);
- }
-
- return certificate->_sha1Digest;
-}
-
-CFDataRef SecCertificateCopyIssuerSHA1DigestP(SecCertificateRefP certificate) {
- CFDataRef digest = NULL;
- CFDataRef issuer = SecCertificateCopyIssuerSequenceP(certificate);
- if (issuer) {
- digest = SecSHA1DigestCreate(kCFAllocatorDefault,
- CFDataGetBytePtr(issuer), CFDataGetLength(issuer));
- CFRelease(issuer);
- }
- return digest;
-}
-
-CFDataRef SecCertificateCopyPublicKeySHA1DigestP(SecCertificateRefP certificate) {
- return SecSHA1DigestCreate(CFGetAllocator(certificate),
- certificate->_pubKeyDER.data, certificate->_pubKeyDER.length);
-}
-
-/* note: this function is exported with a non-P-suffix name.
- * since it doesn't accept or return a SecCertificateRefP type, this is OK for now.
- */
-CFDataRef SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator,
- CFDataRef der_certificate)
-{
- CFDataRef result = NULL;
- SecCertificateRefP iosCertRef = SecCertificateCreateWithDataP(allocator, der_certificate);
- if (NULL == iosCertRef)
- {
- return result;
- }
-
- result = SecCertificateCopyPublicKeySHA1DigestP(iosCertRef);
- CFRelease(iosCertRef);
- return result;
-}
-
-CFDataRef SecCertificateGetAuthorityKeyIDP(SecCertificateRefP certificate) {
- if (!certificate->_authorityKeyID &&
- certificate->_authorityKeyIdentifier.length) {
- certificate->_authorityKeyID = CFDataCreate(kCFAllocatorDefault,
- certificate->_authorityKeyIdentifier.data,
- certificate->_authorityKeyIdentifier.length);
- }
-
- return certificate->_authorityKeyID;
-}
-
-CFDataRef SecCertificateGetSubjectKeyIDP(SecCertificateRefP certificate) {
- if (!certificate->_subjectKeyID &&
- certificate->_subjectKeyIdentifier.length) {
- certificate->_subjectKeyID = CFDataCreate(kCFAllocatorDefault,
- certificate->_subjectKeyIdentifier.data,
- certificate->_subjectKeyIdentifier.length);
- }
-
- return certificate->_subjectKeyID;
-}
-
-CFArrayRef SecCertificateGetCRLDistributionPointsP(SecCertificateRefP certificate) {
- return certificate->_crlDistributionPoints;
-}
-
-CFArrayRef SecCertificateGetOCSPRespondersP(SecCertificateRefP certificate) {
- return certificate->_ocspResponders;
-}
-
-CFArrayRef SecCertificateGetCAIssuersP(SecCertificateRefP certificate) {
- return certificate->_caIssuers;
-}
-
-bool SecCertificateHasCriticalSubjectAltNameP(SecCertificateRefP certificate) {
- return certificate->_subjectAltName &&
- certificate->_subjectAltName->critical;
-}
-
-bool SecCertificateHasSubjectP(SecCertificateRefP certificate) {
- /* Since the _subject field is the content of the subject and not the
- whole thing, we can simply check for a 0 length subject here. */
- return certificate->_subject.length != 0;
-}
-
-bool SecCertificateHasUnknownCriticalExtensionP(SecCertificateRefP certificate) {
- return certificate->_foundUnknownCriticalExtension;
-}
-
-/* Private API functions. */
-void SecCertificateShowP(SecCertificateRefP certificate) {
- check(certificate);
- fprintf(stderr, "SecCertificate instance %p:\n", certificate);
- fprintf(stderr, "\n");
-}
-
-CFDictionaryRef SecCertificateCopyAttributeDictionaryP(
- SecCertificateRefP certificate) {
- CFAllocatorRef allocator = CFGetAllocator(certificate);
- CFNumberRef certificateType, certificateEncoding;
- CFStringRef label, alias;
- CFDataRef skid, pubKeyDigest, certData;
- CFDictionaryRef dict = NULL;
-
- DICT_DECLARE(11);
-
- /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
- SInt32 ctv = certificate->_version + 1;
- SInt32 cev = 3; /* CSSM_CERT_ENCODING_DER */
- certificateType = CFNumberCreate(allocator, kCFNumberSInt32Type, &ctv);
- certificateEncoding = CFNumberCreate(allocator, kCFNumberSInt32Type, &cev);
- certData = SecCertificateCopyDataP(certificate);
- skid = SecCertificateGetSubjectKeyIDP(certificate);
- pubKeyDigest = SecSHA1DigestCreate(allocator, certificate->_pubKeyDER.data,
- certificate->_pubKeyDER.length);
-#if 0
- /* We still need to figure out how to deal with multi valued attributes. */
- alias = SecCertificateCopyRFC822NamesP(certificate);
- label = SecCertificateCopySubjectSummary(certificate);
-#else
- alias = NULL;
- label = NULL;
-#endif
-
- DICT_ADDPAIR(kSecClass, kSecClassCertificate);
- DICT_ADDPAIR(kSecAttrCertificateType, certificateType);
- DICT_ADDPAIR(kSecAttrCertificateEncoding, certificateEncoding);
- if (label)
- DICT_ADDPAIR(kSecAttrLabel, label);
- if (alias)
- DICT_ADDPAIR(kSecAttrAlias, alias);
- DICT_ADDPAIR(kSecAttrSubject, certificate->_normalizedSubject);
- DICT_ADDPAIR(kSecAttrIssuer, certificate->_normalizedIssuer);
- DICT_ADDPAIR(kSecAttrSerialNumber, certificate->_serialNumber);
- if (skid)
- DICT_ADDPAIR(kSecAttrSubjectKeyID, skid);
- DICT_ADDPAIR(kSecAttrPublicKeyHash, pubKeyDigest);
- DICT_ADDPAIR(kSecValueData, certData);
- dict = DICT_CREATE(allocator);
-
- CFReleaseSafe(label);
- CFReleaseSafe(pubKeyDigest);
- CFReleaseSafe(certData);
- CFReleaseSafe(certificateEncoding);
- CFReleaseSafe(certificateType);
-
- return dict;
-}
-
-SecCertificateRefP SecCertificateCreateFromAttributeDictionaryP(
- CFDictionaryRef refAttributes) {
- /* @@@ Support having an allocator in refAttributes. */
- CFAllocatorRef allocator = NULL;
- CFDataRef data = CFDictionaryGetValue(refAttributes, kSecValueData);
- return SecCertificateCreateWithDataP(allocator, data);
-}
-
-bool SecCertificateIsSelfSignedCAP(SecCertificateRefP certificate) {
- bool result = false;
- SecKeyRefP publicKey;
- require(publicKey = SecCertificateCopyPublicKeyP(certificate), out);
- CFDataRef normalizedIssuer =
- SecCertificateGetNormalizedIssuerContentP(certificate);
- CFDataRef normalizedSubject =
- SecCertificateGetNormalizedSubjectContentP(certificate);
- require_quiet(normalizedIssuer && normalizedSubject &&
- CFEqual(normalizedIssuer, normalizedSubject), out);
-
- CFDataRef authorityKeyID = SecCertificateGetAuthorityKeyIDP(certificate);
- CFDataRef subjectKeyID = SecCertificateGetSubjectKeyIDP(certificate);
- if (authorityKeyID) {
- require_quiet(subjectKeyID && CFEqual(subjectKeyID, authorityKeyID), out);
- }
-
- if (SecCertificateVersionP(certificate) >= 3) {
- const SecCEBasicConstraints *basicConstraints = SecCertificateGetBasicConstraintsP(certificate);
- require_quiet(basicConstraints && basicConstraints->isCA, out);
- require_noerr_quiet(SecCertificateIsSignedByP(certificate, publicKey), out);
- }
-
- result = true;
-out:
- CFReleaseSafe(publicKey);
- return result;
-}
-
-SecKeyUsage SecCertificateGetKeyUsageP(SecCertificateRefP certificate) {
- return certificate->_keyUsage;
-}
-
-CFArrayRef SecCertificateCopyExtendedKeyUsageP(SecCertificateRefP certificate)
-{
- CFMutableArrayRef extended_key_usage_oids =
- CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- require_quiet(extended_key_usage_oids, out);
- int ix;
- for (ix = 0; ix < certificate->_extensionCount; ++ix) {
- const SecCertificateExtension *extn = &certificate->_extensions[ix];
- if (extn->extnID.length == oidExtendedKeyUsage.length &&
- !memcmp(extn->extnID.data, oidExtendedKeyUsage.data, extn->extnID.length)) {
- DERTag tag;
- DERSequence derSeq;
- DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &derSeq);
- require_noerr_quiet(drtn, out);
- require_quiet(tag == ASN1_CONSTR_SEQUENCE, out);
- DERDecodedInfo currDecoded;
-
- while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) {
- require_quiet(currDecoded.tag == ASN1_OBJECT_ID, out);
- CFDataRef oid = CFDataCreate(kCFAllocatorDefault,
- currDecoded.content.data, currDecoded.content.length);
- require_quiet(oid, out);
- CFArrayAppendValue(extended_key_usage_oids, oid);
- CFReleaseNull(oid);
- }
- require_quiet(drtn == DR_EndOfSequence, out);
- return extended_key_usage_oids;
- }
- }
-out:
- CFReleaseSafe(extended_key_usage_oids);
- return NULL;
-}
-
-SecCertificateRefP SecCertificateCreateWithPEMP(CFAllocatorRef allocator,
- CFDataRef pem_certificate)
-{
- static const char begin_cert[] = "-----BEGIN CERTIFICATE-----\n";
- static const char end_cert[] = "-----END CERTIFICATE-----\n";
- uint8_t *base64_data = NULL;
- SecCertificateRefP cert = NULL;
- const unsigned char *data = CFDataGetBytePtr(pem_certificate);
- //const size_t length = CFDataGetLength(pem_certificate);
- char *begin = strstr((const char *)data, begin_cert);
- char *end = strstr((const char *)data, end_cert);
- if (!begin || !end)
- return NULL;
- begin += sizeof(begin_cert) - 1;
- size_t base64_length = SecBase64Decode(begin, end - begin, NULL, 0);
- if (base64_length) {
- require_quiet(base64_data = calloc(1, base64_length), out);
- require_quiet(base64_length = SecBase64Decode(begin, end - begin, base64_data, base64_length), out);
- cert = SecCertificateCreateWithBytesP(kCFAllocatorDefault, base64_data, base64_length);
- free(base64_data);
- }
-out:
- return cert;
-}
-
-static void convertCertificateToCFData(const void *value, void *context) {
- CFMutableArrayRef result = (CFMutableArrayRef)context;
- SecCertificateRefP certificate = (SecCertificateRefP)value;
- CFDataRef data = SecCertificateCopyDataP(certificate);
- CFArrayAppendValue(result, data);
- CFRelease(data);
-}
-
-/* Return an array of CFDataRefs from an array of SecCertificateRefPs. */
-CFArrayRef SecCertificateArrayCopyDataArrayP(CFArrayRef certificates) {
- CFIndex count = CFArrayGetCount(certificates);
- CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks);
- CFRange all_certs = { 0, count };
- CFArrayApplyFunction(certificates, all_certs, convertCertificateToCFData, result);
- return result;
-}
-
-/* AUDIT[securityd](done):
- value (ok) is an element in a caller provided array.
- */
-static void convertCFDataToCertificate(const void *value, void *context) {
- CFMutableArrayRef result = (CFMutableArrayRef)context;
- CFDataRef data = (CFDataRef)value;
- if (data && CFGetTypeID(data) == CFDataGetTypeID()) {
- SecCertificateRefP certificate = SecCertificateCreateWithDataP(kCFAllocatorDefault, data);
- if (certificate) {
- CFArrayAppendValue(result, certificate);
- CFRelease(certificate);
- }
- }
-}
-
-/* AUDIT[securityd](done):
- certificates (ok) is a caller provided array, only its cf type has
- been checked.
- */
-CFArrayRef SecCertificateDataArrayCopyArrayP(CFArrayRef certificates) {
- CFIndex count = CFArrayGetCount(certificates);
- CFMutableArrayRef result = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
- CFRange all_certs = { 0, count };
- CFArrayApplyFunction(certificates, all_certs, convertCFDataToCertificate, result);
- return result;
-}
+++ /dev/null
-/*
- * Copyright (c) 2006-2009,2011-2015 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*!
- @header SecCertificate
- The functions provided in SecCertificate.h implement and manage a
- particular type of keychain item that represents a X.509 public key
- certificate. You can store a certificate in a keychain, but a
- certificate can also be a transient object.
-
- You can use a certificate as a keychain item in most functions.
-*/
-
-#ifndef _SECURITY_SECCERTIFICATEP_H_
-#define _SECURITY_SECCERTIFICATEP_H_
-
-#include "SecBaseP.h"
-#include <CoreFoundation/CFData.h>
-#include <CoreFoundation/CFDate.h>
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-/*!
- @function SecCertificateGetTypeIDP
- @abstract Returns the type identifier of SecCertificate instances.
- @result The CFTypeID of SecCertificate instances.
-*/
-CFTypeID SecCertificateGetTypeIDP(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_2_0);
-
-/*!
- @function SecCertificateCreateWithDataP
- @abstract Create a certificate given it's DER representation as a CFData.
- @param allocator CFAllocator to allocate the certificate with.
- @param data DER encoded X.509 certificate.
- @result Return NULL if the passed-in data is not a valid DER-encoded
- X.509 certificate, return a SecCertificateRef otherwise.
-*/
-SecCertificateRefP SecCertificateCreateWithDataP(CFAllocatorRef allocator,
- CFDataRef data) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);
-
-/*!
- @function SecCertificateCopyDataP
- @abstract Return the DER representation of an X.509 certificate.
- @param certificate SecCertificate object created with
- SecCertificateCreateWithDataP().
- @result DER encoded X.509 certificate.
-*/
-CFDataRef SecCertificateCopyDataP(SecCertificateRefP certificate)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);
-
-/*!
- @function SecCertificateCopySubjectSummary
- @abstract Return a simple string which hopefully represents a human
- understandable summary.
- @param certificate SecCertificate object created with
- SecCertificateCreateWithDataP().
- @discussion All the data in this string comes from the certificate itself
- and thus it's in whatever language the certificate itself is in.
- @result A CFStringRef which the caller should CFRelease() once it's no
- longer needed.
-*/
-CFStringRef SecCertificateCopySubjectSummaryP(SecCertificateRefP certificate)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);
-
-/*!
- @function SecCertificateIsValidP
- @abstract Returns true if the given certificate is valid
- at the specified verifyTime.
- @param certificate SecCertificate object created with
- SecCertificateCreateWithDataP().
- @result DER encoded X.509 certificate.
-*/
-bool SecCertificateIsValidP(SecCertificateRefP certificate, CFAbsoluteTime verifyTime)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);
-
-/*!
- @function SecCertificateCopyPublicKeySHA1DigestFromCertificateData
- @abstract Returns the SHA1 hash of the public key of a certificate or NULL
- @param allocator CFAllocator to allocate the certificate with.
- @param der_certificate DER encoded X.509 certificate.
- @result SHA1 hash of the public key of a certificate or NULL
-*/
-CFDataRef SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator,
- CFDataRef der_certificate);
-
-
-#if defined(__cplusplus)
-}
-#endif
-
-#endif /* !_SECURITY_SECCERTIFICATEP_H_ */
+++ /dev/null
-/*
- * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*!
- @header SecCertificatePriv
- The functions provided in SecCertificatePriv.h implement and manage a particular
- type of keychain item that represents a certificate. You can store a
- certificate in a keychain, but a certificate can also be a transient
- object.
-
- You can use a certificate as a keychain item in most functions.
- Certificates are able to compute their parent certificates, and much more.
-*/
-
-#ifndef _SECURITY_SECCERTIFICATEPRIVP_H_
-#define _SECURITY_SECCERTIFICATEPRIVP_H_
-
-#include <Security/SecCertificate.h>
-#include "SecCertificateP.h"
-#include <CoreFoundation/CFArray.h>
-#include <CoreFoundation/CFData.h>
-#include <CoreFoundation/CFDate.h>
-#include <CoreFoundation/CFDictionary.h>
-#include <stdbool.h>
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-/* Return a certificate for the DER representation of this certificate.
- Return NULL if the passed-in data is not a valid DER-encoded X.509
- certificate. */
-SecCertificateRefP SecCertificateCreateWithBytesP(CFAllocatorRef allocator,
- const UInt8 *bytes, CFIndex length);
-
-/* Return the length of the DER representation of this certificate. */
-CFIndex SecCertificateGetLengthP(SecCertificateRefP certificate);
-
-/* Return the bytes of the DER representation of this certificate. */
-const UInt8 *SecCertificateGetBytePtrP(SecCertificateRefP certificate);
-
-#pragma mark -
-#pragma mark Certificate Accessors
-
-CFDataRef SecCertificateGetSHA1DigestP(SecCertificateRefP certificate);
-
-CFDataRef SecCertificateCopyIssuerSHA1DigestP(SecCertificateRefP certificate);
-
-CFDataRef SecCertificateCopyPublicKeySHA1DigestP(SecCertificateRefP certificate);
-
-CFStringRef SecCertificateCopyIssuerSummaryP(SecCertificateRefP certificate);
-
-/*!
- @function SecCertificateCopyPropertiesP
- @abstract Return a property array for this trust certificate.
- @param certificate A reference to the certificate to evaluate.
- @result A property array. It is the caller's responsability to CFRelease
- the returned array when it is no longer needed.
- See SecTrustCopySummaryPropertiesAtIndex on how to intepret this array.
- Unlike that function call this function returns a detailed description
- of the certificate in question.
-*/
-CFArrayRef SecCertificateCopyPropertiesP(SecCertificateRefP certificate);
-
-CFMutableArrayRef SecCertificateCopySummaryPropertiesP(
- SecCertificateRefP certificate, CFAbsoluteTime verifyTime);
-
-/* Return the content of a DER-encoded integer (without the tag and length
- fields) for this certificate's serial number. The caller must CFRelease
- the value returned. */
-CFDataRef SecCertificateCopySerialNumberP(SecCertificateRefP certificate);
-
-/* Return an array of CFStringRefs representing the ip addresses in the
- certificate if any. */
-CFArrayRef SecCertificateCopyIPAddressesP(SecCertificateRefP certificate);
-
-/* Return an array of CFStringRefs representing the dns addresses in the
- certificate if any. */
-CFArrayRef SecCertificateCopyDNSNamesP(SecCertificateRefP certificate);
-
-/* Return an array of CFStringRefs representing the email addresses in the
- certificate if any. */
-CFArrayRef SecCertificateCopyRFC822NamesP(SecCertificateRefP certificate);
-
-/* Return an array of CFStringRefs representing the common names in the
- certificates subject if any. */
-CFArrayRef SecCertificateCopyCommonNamesP(SecCertificateRefP certificate);
-
-/* Return an array of CFStringRefs representing the organization in the
- certificate's subject if any. */
-CFArrayRef SecCertificateCopyOrganizationP(SecCertificateRefP certificate);
-
-/* Return an array of CFStringRefs representing the NTPrincipalNames in the
- certificate if any. */
-CFArrayRef SecCertificateCopyNTPrincipalNamesP(SecCertificateRefP certificate);
-
-/* Return a string formatted according to RFC 2253 representing the complete
- subject of certificate. */
-CFStringRef SecCertificateCopySubjectStringP(SecCertificateRefP certificate);
-
-/* Return a string with the company name of an ev leaf certificate. */
-CFStringRef SecCertificateCopyCompanyNameP(SecCertificateRefP certificate);
-
-/* X.509 Certificate Version: 1, 2 or 3. */
-CFIndex SecCertificateVersionP(SecCertificateRefP certificate);
-
-CFAbsoluteTime SecCertificateNotValidBeforeP(SecCertificateRefP certificate);
-CFAbsoluteTime SecCertificateNotValidAfterP(SecCertificateRefP certificate);
-
-/* Return true iff certificate is self signed and has a basic constraints
- extension indicating that it's a certificate authority. */
-bool SecCertificateIsSelfSignedCAP(SecCertificateRefP certificate);
-
-SecKeyUsage SecCertificateGetKeyUsageP(SecCertificateRefP certificate);
-
-/* Returns an array of CFDataRefs for all extended key usage oids or NULL */
-CFArrayRef SecCertificateCopyExtendedKeyUsageP(SecCertificateRefP certificate);
-
-/* Returns a certificate from a pem blob */
-SecCertificateRefP SecCertificateCreateWithPEMP(CFAllocatorRef allocator,
- CFDataRef pem_certificate);
-
-/* Return an array of CFDataRefs from an array of SecCertificateRefPs. */
-CFArrayRef SecCertificateArrayCopyDataArrayP(CFArrayRef certificates);
-
-/* Return an array of SecCertificateRefPs from an array of CFDataRefs. */
-CFArrayRef SecCertificateDataArrayCopyArrayP(CFArrayRef certificates);
-
-CFDataRef SecCertificateGetNormalizedIssuerContentP(SecCertificateRefP certificate);
-CFDataRef SecCertificateGetNormalizedSubjectContentP(SecCertificateRefP certificate);
-
-CFDataRef SecCertificateCopyNormalizedIssuerSequenceP(SecCertificateRefP certificate);
-CFDataRef SecCertificateCopyNormalizedSubjectSequenceP(SecCertificateRefP certificate);
-
-#if defined(__cplusplus)
-}
-#endif
-
-#endif /* !_SECURITY_SECCERTIFICATEPRIVP_H_ */
+++ /dev/null
-/*
- * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*
- * SecFramework.c - generic non API class specific functions
- */
-
-
-#include "SecFrameworkP.h"
-#include <pthread.h>
-#include <CoreFoundation/CFBundle.h>
-#include <CoreFoundation/CFURLAccess.h>
-#if 0
-#include "SecRandomP.h"
-#endif
-#include <CommonCrypto/CommonDigest.h>
-#include <Security/SecAsn1Coder.h>
-#include <Security/oidsalg.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <errno.h>
-#include <dlfcn.h>
-#include <string.h>
-#include <CoreFoundation/CFBundlePriv.h>
-
-#include <utilities/debugging.h>
-
-/* Security framework's own bundle used for localized string lookups. */
-static CFBundleRef kSecFrameworkBundle;
-static pthread_once_t kSecFrameworkBundleLookup = PTHREAD_ONCE_INIT;
-
-static void SecFrameworkBundleLookup(void) {
- // figure out the path to our executable
- Dl_info info;
- dladdr("", &info);
-
- // make a file URL from the returned string
- CFURLRef urlRef = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8*) info.dli_fname, strlen(info.dli_fname), false);
- kSecFrameworkBundle = _CFBundleCreateWithExecutableURLIfLooksLikeBundle(NULL, urlRef);
- CFRelease(urlRef);
-
- if (kSecFrameworkBundle)
- CFRetain(kSecFrameworkBundle);
-}
-
-CFStringRef SecFrameworkCopyLocalizedString(CFStringRef key,
- CFStringRef tableName) {
- pthread_once(&kSecFrameworkBundleLookup, SecFrameworkBundleLookup);
- if (kSecFrameworkBundle) {
- return CFBundleCopyLocalizedString(kSecFrameworkBundle, key, key,
- tableName);
- }
-
- CFRetain(key);
- return key;
-}
-
-CFURLRef SecFrameworkCopyResourceURL(CFStringRef resourceName,
- CFStringRef resourceType, CFStringRef subDirName) {
- CFURLRef url = NULL;
- pthread_once(&kSecFrameworkBundleLookup, SecFrameworkBundleLookup);
- if (kSecFrameworkBundle) {
- url = CFBundleCopyResourceURL(kSecFrameworkBundle, resourceName,
- resourceType, subDirName);
- if (!url) {
- secinfo("SecFramework", "resource: %@.%@ in %@ not found", resourceName,
- resourceType, subDirName);
- }
- }
-
- return url;
-}
-
-
-CFDataRef SecFrameworkCopyResourceContents(CFStringRef resourceName,
- CFStringRef resourceType, CFStringRef subDirName) {
- CFURLRef url = SecFrameworkCopyResourceURL(resourceName, resourceType,
- subDirName);
- CFDataRef data = NULL;
- if (url) {
- SInt32 error;
- if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
- url, &data, NULL, NULL, &error)) {
- secinfo("SecFramework", "read: %d", (int)error);
- }
- CFRelease(url);
- }
-
- return data;
-}
-
-/* Return the SHA1 digest of a chunk of data as newly allocated CFDataRef. */
-CFDataRef SecSHA1DigestCreate(CFAllocatorRef allocator,
- const UInt8 *data, CFIndex length) {
- CFMutableDataRef digest = CFDataCreateMutable(allocator,
- CC_SHA1_DIGEST_LENGTH);
- CFDataSetLength(digest, CC_SHA1_DIGEST_LENGTH);
- CC_SHA1(data, (CC_LONG)length, CFDataGetMutableBytePtr(digest));
- return digest;
-}
-
-#if 0
-CFDataRef SecDigestCreate(CFAllocatorRef allocator,
- const SecAsn1Oid *algorithm, const SecAsn1Item *params,
- const UInt8 *data, CFIndex length) {
- unsigned char *(*digestFcn)(const void *data, CC_LONG len, unsigned char *md);
- CFIndex digestLen;
-
- if (SecAsn1OidCompare(algorithm, &CSSMOID_SHA1)) {
- digestFcn = CC_SHA1;
- digestLen = CC_SHA1_DIGEST_LENGTH;
- } else if (SecAsn1OidCompare(algorithm, &CSSMOID_SHA224)) {
- digestFcn = CC_SHA224;
- digestLen = CC_SHA224_DIGEST_LENGTH;
- } else if (SecAsn1OidCompare(algorithm, &CSSMOID_SHA256)) {
- digestFcn = CC_SHA256;
- digestLen = CC_SHA256_DIGEST_LENGTH;
- } else if (SecAsn1OidCompare(algorithm, &CSSMOID_SHA384)) {
- digestFcn = CC_SHA384;
- digestLen = CC_SHA384_DIGEST_LENGTH;
- } else if (SecAsn1OidCompare(algorithm, &CSSMOID_SHA512)) {
- digestFcn = CC_SHA512;
- digestLen = CC_SHA512_DIGEST_LENGTH;
- } else {
- return NULL;
- }
-
- CFMutableDataRef digest = CFDataCreateMutable(allocator, digestLen);
- CFDataSetLength(digest, digestLen);
- digestFcn(data, length, CFDataGetMutableBytePtr(digest));
- return digest;
-}
-#endif
#include "SecIdentitySearchPriv.h"
#include "SecKeychainPriv.h"
#include "SecCertificatePriv.h"
-#include "SecCertificatePrivP.h"
#include "TrustAdditions.h"
#include "TrustSettingsSchema.h"
#include <Security/SecTrustPriv.h>
#include "SecTrustSettingsPriv.h"
#include "SecTrustStatusCodes.h"
#include "SecCertificatePriv.h"
-#include "SecCertificateP.h"
-#include "SecCertificatePrivP.h"
#include "SecPolicyPriv.h"
#include <security_utilities/cfutilities.h>
#include <security_utilities/cfmunge.h>
+++ /dev/null
-/*
- * Copyright (c) 2009,2012,2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#ifndef APPLEMOBILEPERSONALIZEDTICKET_H
-#define APPLEMOBILEPERSONALIZEDTICKET_H
-
-const unsigned kApECIDTag = 1;
-const unsigned kApChipIDTag = 2;
-const unsigned kApBoardIDTag = 3;
-const unsigned kApProductionModeTag = 4;
-const unsigned kApSecurityDomainTag = 5;
-const unsigned kLLBBuildStringTag = 6;
-const unsigned kiBootDigestTag = 7;
-const unsigned kAppleLogoDigestTag = 8;
-const unsigned kDeviceTreeDigestTag = 9;
-const unsigned kKernelCacheDigestTag = 10;
-const unsigned kDiagsDigestTag = 11;
-const unsigned kBatteryChargingDigestTag = 12;
-const unsigned kBatteryPluginDigestTag = 13;
-const unsigned kBatteryLow0DigestTag = 14;
-const unsigned kBatteryLow1DigestTag = 15;
-const unsigned kRecoveryModeDigestTag = 16;
-const unsigned kNeedServiceDigestTag = 17;
-const unsigned kApNonceTag = 18;
-const unsigned kApPriorTicketIDTag = 19;
-const unsigned kiBSSBuildStringTag = 20;
-const unsigned kHostiBootTag = 21;
-const unsigned kiBECBuildStringTag = 22;
-const unsigned kRestoreLogoDigestTag = 23;
-const unsigned kRestoreDeviceTreeDigestTag = 24;
-const unsigned kRestoreKernelCacheDigestTag = 25;
-const unsigned kRestoreRamDiskDigestTag = 26;
-const unsigned kOSDigestTag = 27;
-const unsigned kApBindingDigestTag = 28;
-const unsigned kApServerNonceTag = 29;
-const unsigned kLLBPartialDigestTag = 30;
-const unsigned kiBootPartialDigestTag = 31;
-const unsigned kAppleLogoPartialDigestTag = 32;
-const unsigned kDeviceTreePartialDigestTag = 33;
-const unsigned kKernelCachePartialDigestTag = 34;
-const unsigned kDiagsPartialDigestTag = 35;
-const unsigned kBatteryChargingPartialDigestTag = 36;
-const unsigned kBatteryPluginPartialDigestTag = 37;
-const unsigned kBatteryLow0PartialDigestTag = 38;
-const unsigned kBatteryLow1PartialDigestTag = 39;
-const unsigned kRecoveryModePartialDigestTag = 40;
-const unsigned kNeedServicePartialDigestTag = 41;
-const unsigned kiBSSPartialDigestTag = 42;
-const unsigned kiBECPartialDigestTag = 43;
-const unsigned kRestoreLogoPartialDigestTag = 44;
-const unsigned kRestoreDeviceTreePartialDigestTag = 45;
-const unsigned kRestoreKernelCachePartialDigestTag = 46;
-const unsigned kRestoreRamDiskPartialDigestTag = 47;
-const unsigned kiBootTrustedTag = 48;
-const unsigned kAppleLogoTrustedTag = 49;
-const unsigned kDeviceTreeTrustedTag = 50;
-const unsigned kKernelCacheTrustedTag = 51;
-const unsigned kDiagsTrustedTag = 52;
-const unsigned kBatteryChargingTrustedTag = 53;
-const unsigned kBatteryPluginTrustedTag = 54;
-const unsigned kBatteryLow0TrustedTag = 55;
-const unsigned kBatteryLow1TrustedTag = 56;
-const unsigned kRecoveryModeTrustedTag = 57;
-const unsigned kNeedServiceTrustedTag = 58;
-const unsigned kRestoreLogoTrustedTag = 59;
-const unsigned kRestoreDeviceTreeTrustedTag = 60;
-const unsigned kRestoreKernelCacheTrustedTag = 61;
-const unsigned kRestoreRamDiskTrustedTag = 62;
-const unsigned kBbSNUMTag = 63;
-const unsigned kBbChipIDTag = 64;
-const unsigned kBbProductionModeTag = 65;
-const unsigned kFlashPSIBuildStringTag = 66;
-const unsigned kModemStackDigestTag = 67;
-const unsigned kBbNonceTag = 68;
-const unsigned kBbPriorTicketIdTag = 69;
-const unsigned kRamPSIBuildStringTag = 70;
-const unsigned kHostFlashPSITag = 71;
-const unsigned kEBLDigestTag = 72;
-const unsigned kStaticEEPDigestTag = 73;
-const unsigned kBbApBindingDigestTag = 74;
-const unsigned kBbServerNonceTag = 75;
-const unsigned kRamPSIPartialDigestTag = 76;
-const unsigned kFlashPSIPartialDigestTag = 77;
-const unsigned kBatteryCharging0DigestTag = 78;
-const unsigned kBatteryCharging1DigestTag = 79;
-const unsigned kBatteryFullDigestTag = 80;
-const unsigned kBatteryCharging0PartialDigestTag = 81;
-const unsigned kBatteryCharging1PartialDigestTag = 82;
-const unsigned kBatteryFullPartialDigestTag = 83;
-const unsigned kBatteryCharging0TrustedTag = 84;
-const unsigned kBatteryCharging1TrustedTag = 85;
-const unsigned kBatteryFullTrustedTag = 86;
-const unsigned kUniqueBuildIDTag = 87;
-const unsigned kBbGoldCertIdTag = 88;
-const unsigned kBbSkeyIdTag = 89;
-const unsigned kBasebandFirmwareFlashPSIVersionTag = 90;
-const unsigned kBasebandFirmwareModemStackDigestTag = 91;
-const unsigned kBasebandFirmwareRamPSIVersionTag = 92;
-const unsigned kBasebandFirmwareEBLDigestTag = 93;
-const unsigned kBasebandFirmwareFlashPSISecPackDigestTag = 94;
-const unsigned kBasebandFirmwareModemStackSecPackDigestTag= 95;
-const unsigned kBasebandFirmwareFlashPSIDigestTag = 96;
-const unsigned kBasebandFirmwareRamPSIPartialDigestTag = 97;
-const unsigned kBasebandFirmwareFlashPSIPartialDigestTag = 98;
-const unsigned kBbJtagEnableTag = 99;
-
-
-#endif /* APPLEMOBILEPERSONALIZEDTICKET_H */
+++ /dev/null
-/*
- * Copyright (c) 2009,2012,2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#include "DER_Ticket.h"
-
-#include <libDER/asn1Types.h>
-#include <libDER/DER_Decode.h>
-#include <libDER/DER_Encode.h>
-#include <libDER/DER_Keys.h>
-
-/* Application Processor Ticket */
-const DERItemSpec DERApTicketItemSpecs[] =
-{
- { DER_OFFSET(DERApTicket, signatureAlgorithm),
- ASN1_CONSTR_SEQUENCE,
- DER_DEC_NO_OPTS | DER_ENC_WRITE_DER },
- { DER_OFFSET(DERApTicket, body),
- ASN1_CONSTR_SET,
- DER_DEC_NO_OPTS | DER_DEC_SAVE_DER | DER_ENC_WRITE_DER },
- { DER_OFFSET(DERApTicket, signature),
- ASN1_OCTET_STRING,
- DER_DEC_NO_OPTS },
- { DER_OFFSET(DERApTicket, certificates),
- ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 1,
- DER_DEC_NO_OPTS | DER_ENC_WRITE_DER }
-};
-const DERSize DERNumApTicketItemSpecs =
- sizeof(DERApTicketItemSpecs) / sizeof(DERItemSpec);
-
-/* Baseband Ticket */
-const DERItemSpec DERBbTicketItemSpecs[] =
-{
- { DER_OFFSET(DERBbTicket, signatureAlgorithm),
- ASN1_CONSTR_SEQUENCE,
- DER_DEC_NO_OPTS | DER_ENC_WRITE_DER },
- { DER_OFFSET(DERBbTicket, body),
- ASN1_CONSTR_SET,
- DER_DEC_NO_OPTS | DER_DEC_SAVE_DER | DER_ENC_WRITE_DER },
- { DER_OFFSET(DERBbTicket, signature),
- ASN1_OCTET_STRING,
- DER_DEC_NO_OPTS },
- { DER_OFFSET(DERBbTicket, gpuk),
- ASN1_CONTEXT_SPECIFIC | 2,
- DER_DEC_NO_OPTS }
-};
-const DERSize DERNumBbTicketItemSpecs =
- sizeof(DERBbTicketItemSpecs) / sizeof(DERItemSpec);
-
-#if 0
-/* We need to verify this value and use it here. */
-const DERByte rsaWithSha1Algorithm[] = {
- 0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x05
-};
-#endif
-
-#ifdef FAST_SET_LOOKUP
-/* Iterates over all the tags in the set to build an index returned in
- derSet. */
-DERReturn DERDecodeSetContentInit(
- const DERItem *content, /* data to decode */
- DERSet *derSet) /* IN/OUT, to use in DERDecodeSetTag */
-{
- DERReturn drtn;
- DERSequence derSeq;
- memset(derSet->byTag, 0, derSet->capacity);
- drtn = DERDecodeSeqContentInit(content, &derSeq);
- if (drtn == DR_Success) {
- DERDecodedInfo element;
- while ((drtn = DERDecodeSeqNext(&derSeq, &element)) == DR_Success) {
- if (element.tag >= derSet->capacity) return DR_UnexpectedTag;
- derSet->byTag[element.tag] = element.content.data;
- }
- if (drtn == DR_EndOfSequence) drtn = DR_Success;
- }
- derSet->end = content->data + content->length;
-
- return drtn;
-}
-
-DERReturn DERDecodeSetTag(
- DERSet *derSet, /* data to decode */
- DERTag tag, /* tag in sequence/set we are looking for. */
- DERItem *content) /* RETURNED */
-{
- DERReturn drtn;
- DERTag tagNumber = tag & ASN1_TAGNUM_MASK;
- if (tagNumber > derSet->capacity)
- return DR_UnexpectedTag;
- DERByte *start = derSet->byTag[tagNumber];
- if (!start) return DR_UnexpectedTag;
- DERItem derItem = { .data = start, .length = derSet->end - start };
- DERDecodedInfo element;
- drtn = DERDecodeItem(&derItem, &element);
- if (drtn) return drtn;
- if (tag != element.tag) return DR_UnexpectedTag;
- *content = element.content;
-
- return drtn;
-}
-#endif /* FAST_SET_LOOKUP */
-
-/* Returns the item with tag from the sequence or set pointed to by der.
- result DR_EndOfSequence if the tag was not found. */
-DERReturn DERSetDecodeItemWithTag(
- const DERItem *der, /* data to decode */
- DERTag tag, /* tag in sequence/set we are looking for. */
- DERItem *content) /* RETURNED */
-{
- DERReturn drtn;
- DERSequence derSeq;
- DERTag topTag;
- drtn = DERDecodeSeqInit(der, &topTag, &derSeq);
- if (drtn == DR_Success) {
- DERDecodedInfo info;
- while ((drtn = DERDecodeSeqNext(&derSeq, &info)) == DR_Success) {
- if (info.tag == tag) {
- *content = info.content;
- return DR_Success;
- }
- }
- }
-
- return drtn;
-}
-
-DERReturn DERDecodeApTicket(
- const DERItem *contents,
- DERApTicket *ticket, /* RETURNED */
- DERSize *numUsedBytes) /* RETURNED */
-{
- DERReturn drtn;
- DERDecodedInfo decodedTicket;
- drtn = DERDecodeItem(contents, &decodedTicket);
- if (drtn != DR_Success) goto badTicket;
- drtn = DERParseSequenceContent(&decodedTicket.content,
- DERNumApTicketItemSpecs, DERApTicketItemSpecs, ticket, 0);
- if (drtn != DR_Success) goto badTicket;
-
- /* Decode the algorithm sequence. */
- DERAlgorithmId algorithm = {};
- drtn = DERParseSequenceContent(&ticket->signatureAlgorithm,
- DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs, &algorithm, 0);
- if (drtn != DR_Success) goto badTicket;
- /* TODO Check algorithm oid and ensure there are no params.
- Alternatively replace the code above with a simple memcmp with
- an already ASN.1 encoded algorithm parms block. */
-
-badTicket:
- *numUsedBytes = decodedTicket.content.length +
- decodedTicket.content.data - contents->data;
-
- return drtn;
-}
-
-DERReturn DERDecodeBbTicket(
- const DERItem *contents,
- DERBbTicket *ticket, /* RETURNED */
- DERSize *numUsedBytes) /* RETURNED */
-{
- DERReturn drtn;
- DERDecodedInfo decodedTicket;
- drtn = DERDecodeItem(contents, &decodedTicket);
- if (drtn != DR_Success) goto badTicket;
- drtn = DERParseSequenceContent(&decodedTicket.content,
- DERNumBbTicketItemSpecs, DERBbTicketItemSpecs, ticket, 0);
- if (drtn != DR_Success) goto badTicket;
-
- /* Decode the algorithm sequence. */
- DERAlgorithmId algorithm = {};
- drtn = DERParseSequenceContent(&ticket->signatureAlgorithm,
- DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs, &algorithm, 0);
- if (drtn != DR_Success) goto badTicket;
- /* TODO Check algorithm oid and ensure there are no params.
- Alternatively replace the code above with a simple memcmp with
- an already ASN.1 encoded algorithm parms block. */
-
-badTicket:
- *numUsedBytes = decodedTicket.content.length +
- decodedTicket.content.data - contents->data;
-
- return drtn;
-}
+++ /dev/null
-/*
- * Copyright (c) 2009,2012,2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#include <libDER/libDER.h>
-
-
-#define FAST_SET_LOOKUP 1
-
-#ifdef FAST_SET_LOOKUP
-/* state representing a fast by tag set accessor, the caller needs to provide
- a set large enough to hold all */
-typedef struct {
- DERTag capacity; /* should be large enough to hold all encountered tags.
- otherwise DR_UnexpectedTag will be returned, note
- that only one tag per tag number can exist. */
- DERByte *end;
- DERByte *byTag[]; /* maxTag element array of pointers to tag + length
- of items in set indexed by tagNumber. */
-} DERSet;
-
-/* Iterates over all the tags in the set to build an index returned in
- derSet. */
-DERReturn DERDecodeSetContentInit(
- const DERItem *der, /* data to decode */
- DERSet *derSet); /* IN/OUT, to use in DERDecodeSetTag */
-
-/* Returns DR_UnexpectedTag if the requested tag is not in derSet, returns
- the content of the decoded item in content otherwise. */
-DERReturn DERDecodeSetTag(
- DERSet *derSeq, /* data to decode */
- DERTag tag, /* tag in sequence/set we are looking for. */
- DERItem *content); /* RETURNED */
-#endif /* FAST_SET_LOOKUP */
-
-
-DERReturn DERSetDecodeItemWithTag(
- const DERItem *der, /* data to decode */
- DERTag tag, /* tag in sequence/set we are looking for. */
- DERItem *content); /* RETURNED */
-
-
-/* Application Processor Ticket */
-typedef struct {
- DERItem signatureAlgorithm; /* AlgorithmId */
- DERItem body; /* SET OF OCTECT STRING, DER_DEC_SAVE_DER */
- DERItem signature; /* OCTET STRING */
- DERItem certificates; /* SEQUENCE of CERTIFICATE */
-} DERApTicket;
-
-/* DERItemSpecs to decode into a DERApTicket */
-extern const DERItemSpec DERApTicketItemSpecs[];
-extern const DERSize DERNumApTicketItemSpecs;
-
-DERReturn DERDecodeApTicket(
- const DERItem *contents,
- DERApTicket *ticket, /* RETURNED */
- DERSize *numUsedBytes); /* RETURNED */
-
-
-/* Baseband Ticket */
-typedef struct {
- DERItem signatureAlgorithm; /* AlgorithmId */
- DERItem body; /* SET OF OCTECT STRING, DER_DEC_SAVE_DER */
- DERItem signature; /* OCTET STRING */
- DERItem gpuk; /* OCTET STRING */
-} DERBbTicket;
-
-/* DERItemSpecs to decode into a DERBbTicket */
-extern const DERItemSpec DERBbTicketItemSpecs[];
-extern const DERSize DERNumBbTicketItemSpecs;
-
-DERReturn DERDecodeBbTicket(
- const DERItem *contents,
- DERBbTicket *ticket, /* RETURNED */
- DERSize *numUsedBytes); /* RETURNED */
+++ /dev/null
-/*
- * Copyright (c) 2005-2007,2010-2011 Apple Inc. All Rights Reserved.
- *
- * parseCert.c - parse a DER-encoded X509 certificate using libDER.
- */
-
-#include <stdlib.h>
-#include <strings.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <libDER/libDER.h>
-#include <libDER/asn1Types.h>
-#include <libDER/DER_CertCrl.h>
-#include <libDER/DER_Keys.h>
-#include <libDERUtils/fileIo.h>
-#include <libDERUtils/libDERUtils.h>
-#include <libDERUtils/printFields.h>
-
-static void usage(char **argv)
-{
- printf("usage: %s certFile [options]\n", argv[0]);
- printf("Options:\n");
- printf(" -v -- verbose \n");
- /* etc. */
- exit(1);
-}
-
-static void printValidity(
- DERItem *validity,
- int verbose)
-{
- DERReturn drtn;
- DERValidity derv;
-
- drtn = DERParseSequenceContent(validity,
- DERNumValidityItemSpecs, DERValidityItemSpecs,
- &derv, sizeof(derv));
- if(drtn) {
- DERPerror("DERParseSequenceContent(validity)", drtn);
- return;
- }
- decodePrintItem("notBefore", IT_Leaf, verbose, &derv.notBefore);
- decodePrintItem("notAfter", IT_Leaf, verbose, &derv.notAfter);
-
-}
-
-int main(int argc, char **argv)
-{
- unsigned char *certData = NULL;
- unsigned certDataLen = 0;
- DERSignedCertCrl signedCert;
- DERTBSCert tbs;
- DERReturn drtn;
- DERItem item;
- int verbose = 0;
- extern char *optarg;
- int arg;
- extern int optind;
-
- if(argc < 2) {
- usage(argv);
- }
- if(readFile(argv[1], &certData, &certDataLen)) {
- printf("***Error reading cert from %s. Aborting.\n", argv[1]);
- exit(1);
- }
-
- optind = 2;
- while ((arg = getopt(argc, argv, "vh")) != -1) {
- switch (arg) {
- case 'v':
- verbose = 1;
- break;
- case 'h':
- usage(argv);
- }
- }
- if(optind != argc) {
- usage(argv);
- }
-
- /* Top level decode of signed cert into 3 components */
- item.data = certData;
- item.length = certDataLen;
- drtn = DERParseSequence(&item, DERNumSignedCertCrlItemSpecs, DERSignedCertCrlItemSpecs,
- &signedCert, sizeof(signedCert));
- if(drtn) {
- DERPerror("DERParseSequence(SignedCert)", drtn);
- exit(1);
- }
- printItem("TBSCert", IT_Branch, verbose, ASN1_CONSTR_SEQUENCE, &signedCert.tbs);
-
- incrIndent();
-
- /* decode the TBSCert - it was saved in full DER form */
- drtn = DERParseSequence(&signedCert.tbs,
- DERNumTBSCertItemSpecs, DERTBSCertItemSpecs,
- &tbs, sizeof(tbs));
- if(drtn) {
- DERPerror("DERParseSequenceContent(TBSCert)", drtn);
- exit(1);
- }
- if(tbs.version.data) {
- /* unwrap the explicitly tagged integer.... */
- decodePrintItem("version", IT_Leaf, verbose, &tbs.version);
- }
- printItem("serialNum", IT_Leaf, verbose, ASN1_INTEGER, &tbs.serialNum);
-
- printItem("tbsSigAlg", IT_Branch, verbose, ASN1_CONSTR_SEQUENCE, &tbs.tbsSigAlg);
- incrIndent();
- printAlgId(&tbs.tbsSigAlg, verbose);
- decrIndent();
-
- printItem("issuer", IT_Leaf, verbose, ASN1_CONSTR_SEQUENCE, &tbs.issuer);
- printItem("subject", IT_Leaf, verbose, ASN1_CONSTR_SEQUENCE, &tbs.subject);
-
- printItem("validity", IT_Branch, verbose, ASN1_CONSTR_SEQUENCE, &tbs.validity);
- incrIndent();
- printValidity(&tbs.validity, verbose);
- decrIndent();
-
- printItem("subjectPubKey", IT_Branch, verbose, ASN1_CONSTR_SEQUENCE,
- &tbs.subjectPubKey);
- incrIndent();
- printSubjPubKeyInfo(&tbs.subjectPubKey, verbose);
- decrIndent();
-
- if(tbs.issuerID.data) {
- /* found tag is implicit context specific: tell printItem what it really is */
- printItem("issuerID", IT_Leaf, verbose, ASN1_BIT_STRING, &tbs.issuerID);
- }
- if(tbs.subjectID.data) {
- printItem("subjectID", IT_Leaf, verbose, ASN1_BIT_STRING, &tbs.subjectID);
- }
- if(tbs.extensions.data) {
- printItem("extensions", IT_Leaf, verbose, ASN1_CONSTRUCTED | ASN1_CONTEXT_SPECIFIC | 3,
- &tbs.extensions);
- }
- decrIndent();
-
- printItem("sigAlg", IT_Branch, verbose, ASN1_CONSTR_SEQUENCE, &signedCert.sigAlg);
- incrIndent();
- printAlgId(&signedCert.sigAlg, verbose);
- decrIndent();
-
- printItem("sig", IT_Leaf, verbose, ASN1_BIT_STRING, &signedCert.sig);
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (c) 2005-2007,2010-2011 Apple Inc. All Rights Reserved.
- *
- * parseCrl.c - parse a DER-encoded X509 CRL using libDER.
- */
-
-#include <stdlib.h>
-#include <strings.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <libDER/libDER.h>
-#include <libDER/asn1Types.h>
-#include <libDER/DER_CertCrl.h>
-#include <libDER/DER_Keys.h>
-#include <libDERUtils/fileIo.h>
-#include <libDERUtils/libDERUtils.h>
-#include <libDERUtils/printFields.h>
-
-static void usage(char **argv)
-{
- printf("usage: %s crlFile [options]\n", argv[0]);
- printf("Options:\n");
- printf(" -v -- verbose \n");
- /* etc. */
- exit(1);
-}
-
-/*
- * This is a SEQUENCE OF so we use the low-level DERDecodeSeq* routines to snag one entry
- * at a time.
- */
-static void printRevokedCerts(
- DERItem *revokedCerts,
- int verbose)
-{
- DERReturn drtn;
- DERDecodedInfo currItem;
- DERSequence seq;
- unsigned certNum;
- DERRevokedCert revoked;
-
- drtn = DERDecodeSeqContentInit(revokedCerts, &seq);
- if(drtn) {
- DERPerror("DERDecodeSeqContentInit(revokedCerts)", drtn);
- return;
- }
-
- for(certNum=0; ; certNum++) {
- drtn = DERDecodeSeqNext(&seq, &currItem);
- switch(drtn) {
- case DR_EndOfSequence:
- /* normal termination */
- return;
- default:
- DERPerror("DERDecodeSeqNext", drtn);
- return;
- case DR_Success:
- doIndent();
- printf("revoked cert %u\n", certNum);
- incrIndent();
- drtn = DERParseSequenceContent(&currItem.content,
- DERNumRevokedCertItemSpecs, DERRevokedCertItemSpecs,
- &revoked, sizeof(revoked));
- if(drtn) {
- DERPerror("DERParseSequenceContent(RevokedCert)", drtn);
- decrIndent();
- return;
- }
- printItem("serialNum", IT_Leaf, verbose, ASN1_INTEGER, &revoked.serialNum);
- decodePrintItem("revocationDate", IT_Leaf, verbose, &revoked.revocationDate);
- printItem("extensions", IT_Branch, verbose, ASN1_CONSTR_SEQUENCE, &revoked.extensions);
- decrIndent();
- }
- }
-}
-
-int main(int argc, char **argv)
-{
- unsigned char *crlData = NULL;
- unsigned crlDataLen = 0;
- DERSignedCertCrl signedCrl;
- DERTBSCrl tbs;
- DERReturn drtn;
- DERItem item;
- int verbose = 0;
- extern char *optarg;
- int arg;
- extern int optind;
-
- if(argc < 2) {
- usage(argv);
- }
- if(readFile(argv[1], &crlData, &crlDataLen)) {
- printf("***Error reading CRL from %s. Aborting.\n", argv[1]);
- exit(1);
- }
-
- optind = 2;
- while ((arg = getopt(argc, argv, "vh")) != -1) {
- switch (arg) {
- case 'v':
- verbose = 1;
- break;
- case 'h':
- usage(argv);
- }
- }
- if(optind != argc) {
- usage(argv);
- }
-
- /* Top level decode of signed CRL into 3 components */
- item.data = crlData;
- item.length = crlDataLen;
- drtn = DERParseSequence(&item, DERNumSignedCertCrlItemSpecs, DERSignedCertCrlItemSpecs,
- &signedCrl, sizeof(signedCrl));
- if(drtn) {
- DERPerror("DERParseSequence(SignedCrl)", drtn);
- exit(1);
- }
- printItem("TBSCrl", IT_Branch, verbose, ASN1_CONSTR_SEQUENCE, &signedCrl.tbs);
-
- incrIndent();
-
- /* decode the TBSCrl - it was saved in full DER form */
- drtn = DERParseSequence(&signedCrl.tbs,
- DERNumTBSCrlItemSpecs, DERTBSCrlItemSpecs,
- &tbs, sizeof(tbs));
- if(drtn) {
- DERPerror("DERParseSequenceContent(TBSCrl)", drtn);
- exit(1);
- }
- if(tbs.version.data) {
- printItem("version", IT_Leaf, verbose, ASN1_INTEGER, &tbs.version);
- }
-
- printItem("tbsSigAlg", IT_Branch, verbose, ASN1_CONSTR_SEQUENCE, &tbs.tbsSigAlg);
- incrIndent();
- printAlgId(&tbs.tbsSigAlg, verbose);
- decrIndent();
-
- printItem("issuer", IT_Leaf, verbose, ASN1_CONSTR_SEQUENCE, &tbs.issuer);
-
- decodePrintItem("thisUpdate", IT_Leaf, verbose, &tbs.thisUpdate);
- decodePrintItem("nextUpdate", IT_Leaf, verbose, &tbs.nextUpdate);
-
- if(tbs.revokedCerts.data) {
- printItem("version", IT_Leaf, verbose, ASN1_CONSTR_SEQUENCE, &tbs.revokedCerts);
- incrIndent();
- printRevokedCerts(&tbs.revokedCerts, verbose);
- decrIndent();
- }
-
- if(tbs.extensions.data) {
- printItem("extensions", IT_Leaf, verbose, ASN1_CONSTRUCTED | ASN1_CONTEXT_SPECIFIC | 3,
- &tbs.extensions);
- }
-
- printItem("sigAlg", IT_Branch, verbose, ASN1_CONSTR_SEQUENCE, &signedCrl.sigAlg);
- incrIndent();
- printAlgId(&signedCrl.sigAlg, verbose);
- decrIndent();
-
- printItem("sig", IT_Leaf, verbose, ASN1_BIT_STRING, &signedCrl.sig);
-
- return 0;
-}
+++ /dev/null
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <openssl/pem.h>
-#include <openssl/rsa.h>
-#include <openssl/engine.h>
-#include <openssl/bio.h>
-#include <openssl/x509.h>
-#include <openssl/sha.h>
-#include "AppleMobilePersonalizedTicket.h"
-#include <libDER/libDER.h>
-#include <libDER/asn1Types.h>
-#include <libDER/DER_Decode.h>
-#include <libDER/DER_Encode.h>
-#include <libDER/DER_Keys.h>
-#include <libDER/oids.h>
-#include "DER_Ticket.h"
-
-const unsigned char GoldKeyCert[] = {
-0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
-0x00, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
-0x03, 0x00, 0x00, 0x00, 0x31, 0x6e, 0xc6, 0x4f,
-0xf1, 0xe8, 0x7d, 0x81, 0x38, 0x6d, 0xd8, 0xb3,
-0x2b, 0xe4, 0xb5, 0xa0, 0x09, 0xaf, 0x74, 0xdd,
-0xe9, 0x60, 0x27, 0x42, 0x07, 0xa3, 0xac, 0xef,
-0xe5, 0x75, 0x07, 0xa8, 0xc2, 0x2c, 0x25, 0x56,
-0x91, 0x37, 0xea, 0xdb, 0xdb, 0x68, 0x4a, 0x1c,
-0xe3, 0x29, 0x61, 0x85, 0xd7, 0xd8, 0x66, 0x86,
-0x66, 0xbf, 0xbf, 0x98, 0xae, 0xb4, 0xe9, 0x6c,
-0x13, 0x81, 0x97, 0x78, 0x2a, 0x8d, 0xdc, 0x37,
-0x9a, 0xf1, 0xa4, 0x0a, 0x9d, 0x74, 0xd5, 0x72,
-0xbc, 0xb5, 0x64, 0xa6, 0x1a, 0x62, 0xd5, 0x39,
-0xfb, 0x6f, 0xc0, 0xd1, 0xc7, 0x93, 0xc3, 0x20,
-0xda, 0x84, 0x69, 0x1e, 0xd9, 0x96, 0x2e, 0xc1,
-0x4f, 0x28, 0x96, 0x14, 0xa4, 0x68, 0x0a, 0x40,
-0xe5, 0x17, 0xe7, 0xd6, 0x76, 0x4d, 0x81, 0xd8,
-0xd2, 0xa6, 0x18, 0x82, 0x36, 0x40, 0x97, 0x31,
-0xd9, 0x88, 0xdf, 0xaf, 0x05, 0x3a, 0x4b, 0x4e,
-0x1b, 0x4a, 0x76, 0x6f, 0xb9, 0x6c, 0x18, 0x5d,
-0xd5, 0x98, 0xf0, 0xf1, 0xbe, 0x0a, 0xd9, 0x57,
-0x85, 0xc6, 0xc9, 0x63, 0xb3, 0xf5, 0x21, 0x26,
-0x07, 0xba, 0x6a, 0x05, 0xfb, 0x5a, 0x06, 0x87,
-0x2a, 0x30, 0x3f, 0xa9, 0xea, 0xab, 0x0e, 0x50,
-0x70, 0x3b, 0x7e, 0xd4, 0xd2, 0x8c, 0xf3, 0xa1,
-0xcf, 0x9a, 0x6c, 0x6b, 0xcf, 0x9b, 0x1b, 0x2a,
-0x97, 0x6a, 0x3c, 0x38, 0x40, 0x43, 0xb1, 0x97,
-0x19, 0x07, 0x64, 0x11, 0x94, 0x73, 0x14, 0xc9,
-0xa3, 0xfe, 0x7f, 0xf6, 0x64, 0x23, 0x73, 0xe3,
-0x76, 0xce, 0xf7, 0xf4, 0x2f, 0x6c, 0x9d, 0x0a,
-0xf6, 0x39, 0xe6, 0x1d, 0xb2, 0x17, 0x29, 0x39,
-0x98, 0x52, 0xda, 0xe0, 0x31, 0xa1, 0xfa, 0x85,
-0x52, 0xc2, 0x60, 0xb5, 0x11, 0x42, 0xc6, 0x9b,
-0x55, 0xd8, 0x40, 0x37, 0xf7, 0xdb, 0x01, 0x6a,
-0xd5, 0x26, 0x3b, 0x27, 0x07, 0x20, 0xf7, 0x58,
-0xd5, 0xa4, 0x1c, 0xe6, 0x2f, 0x74, 0x14, 0x6e,
-0xa4, 0xe8, 0xc8, 0xe8, 0x9a, 0x39, 0x6d, 0xde,
-0x7f, 0x67, 0x65, 0x40, 0x68, 0x26, 0x65, 0x62,
-0x95, 0x87, 0x45, 0x62, 0x0d, 0x8d, 0x42, 0xad,
-0x3b, 0x4f, 0xd3, 0x8f, 0x58, 0xcb, 0x61, 0x46,
-0xc9, 0x3d, 0x7d, 0x75, 0x3c, 0x6d, 0xac, 0xdf,
-0x53, 0xf4, 0x66, 0x9e, 0x14, 0x82, 0xc7, 0xd1,
-0xd0, 0xec, 0x92, 0x24, 0x97, 0x1e, 0xc9, 0x7a,
-0xfd, 0x8f, 0x75, 0xe2, 0xfd, 0x7e, 0x07, 0x44,
-0x46, 0x56, 0x64, 0x9b, 0x1b, 0x17, 0xfa, 0xd6,
-0xf5, 0xdb, 0xc9, 0x27, 0x3b, 0x60, 0x27, 0x2f,
-0x84, 0xd7, 0xac, 0x7f, 0xf3, 0xa7, 0x16, 0x31,
-0xfa, 0x19, 0x54, 0x57, 0x98, 0xb5, 0xdb, 0x9c,
-0xc3, 0xb5, 0x55, 0x72, 0x98, 0x2f, 0x56, 0x33,
-0x7c, 0x38, 0x1f, 0xb4, 0x8c, 0x94, 0x1a, 0x6a,
-0x26, 0x8a, 0x84, 0xfc, 0x8d, 0xb1, 0x49, 0xbb,
-0x6d, 0x11, 0x36, 0xc9, 0x05, 0x57, 0x87, 0xd2,
-0xdb, 0xd3, 0xe3, 0xea, 0x08, 0xb2, 0x9f, 0x44,
-0x85, 0xd7, 0xd4, 0x96, 0x25, 0xeb, 0x2b, 0xca,
-0x86, 0x0f, 0x33, 0x69, 0xc4, 0xda, 0x98, 0x68,
-0x21, 0xad, 0xd8, 0xc4, 0x4e, 0x46, 0x33, 0x43,
-0xad, 0xe7, 0xfe, 0x58, 0x10, 0x00, 0x76, 0x3c,
-0xd4, 0x14, 0x5a, 0x74, 0x43, 0x04, 0xc3, 0xdd,
-0x46, 0xc3, 0xe0, 0x4b, 0x46, 0xb5, 0x84, 0xcb,
-0xe6, 0x40, 0x71, 0xdf, 0x50, 0x16, 0x5f, 0xf0,
-0x0f, 0xc5, 0x9c, 0x50, 0x64, 0xe0, 0x64, 0x1c,
-0x58, 0x87, 0xae, 0x91, 0x9c, 0xb6, 0x57, 0x77,
-0xf0, 0xc4, 0x3e, 0xcf, 0xb6, 0xc5, 0x10, 0x0c,
-0xea, 0x5b, 0xcc, 0xaf, 0xee, 0x7b, 0x15, 0x4e,
-0x4e, 0x3c, 0x29, 0x9c, 0xf8, 0xe6, 0x59, 0xca,
-0xaf, 0x48, 0x12, 0x99, 0x76, 0xde, 0x54, 0xe2,
-0x75, 0x62, 0x31, 0x17,
-};
-
-#define SIG_LEN 128
-#define HASH_LEN 20
-
-const unsigned char fakeAlgOID[] = {
-0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x05
-};
-
-const unsigned char fakeG3Cert[] = {
-0x01,0x00,0x00,0x00,
-0x1c,0x00,0x00,0x00,
-0x01,0x08,0x00,0x00,
-0x10,0x0a,0x00,0x00,
-0x00,0x00,0x00,0x00,
-0x00,0x04,0x00,0x00,
-0x01,0x00,0x01,0x00,
-0x19,0xfc,0xb6,0x7b,0x4e,0xa8,0xd7,0xb1,0xeb,0xf9,0x19,0x28,0x07,0x7f,0x47,0x4c,
-0xe1,0x9f,0xbe,0x01,0x15,0x5c,0xea,0xda,0xc3,0xd1,0x59,0x3c,0x75,0xed,0x00,0x7b,
-0x22,0x67,0x22,0xd6,0xd3,0xee,0xc2,0x04,0xdf,0x47,0xc4,0x85,0xc0,0x56,0xa4,0x8d,
-0xf3,0xf0,0xcf,0x00,0x9d,0xd2,0x03,0xc1,0x23,0x3f,0xc8,0x9f,0xef,0xfe,0xea,0x50,
-0x33,0x6b,0xbe,0x74,0x1a,0xa3,0x3c,0x13,0xa2,0xc5,0xf6,0x75,0x88,0x1d,0x3f,0xba,
-0xff,0x0d,0x47,0xab,0xbe,0xfe,0x42,0xd3,0xea,0xe2,0xe2,0xb0,0x06,0xd7,0x88,0xf8,
-0x1d,0x93,0xdf,0x86,0xbd,0xd8,0xa8,0x5f,0x2b,0xe3,0x97,0x41,0xa1,0xc6,0x1d,0x69,
-0xe9,0x88,0xdf,0x87,0xbb,0x1b,0xff,0x31,0x82,0xae,0x8a,0x69,0x31,0xca,0xc3,0x90,
-0x0f,0x0d,0x22,0xa3,0xcf,0x8d,0xcc,0x23,0x03,0x10,0xed,0x74,0x8e,0x13,0x74,0x49,
-0x9d,0x9a,0x1c,0xf2,0x57,0x2d,0x18,0x89,0x6d,0xb8,0xcc,0xab,0xcf,0xd9,0xd8,0x0e,
-0x46,0x68,0x98,0xa3,0x81,0x5d,0x18,0xe8,0x4d,0x03,0x96,0x14,0xc5,0xaf,0x21,0x91,
-0xb7,0x8c,0x97,0xa1,0x85,0xde,0x85,0x78,0xa8,0xd7,0x25,0x20,0x9b,0x2b,0x98,0x36,
-0xd5,0xfe,0x14,0x9b,0x5d,0xe3,0x78,0xf4,0xd6,0xb2,0x15,0xc9,0xfd,0x13,0x77,0x7b,
-0x8a,0x5e,0x9e,0x85,0xff,0x53,0x6d,0x24,0x5d,0xc9,0x52,0x16,0x98,0x18,0xb1,0xaf,
-0xe1,0x6a,0xd6,0xe8,0xa9,0x7c,0x78,0x8e,0x9f,0x79,0x21,0xa1,0xde,0xf4,0xaf,0x9c,
-0xd4,0x61,0x52,0xf9,0xe7,0xfc,0xd7,0x10,0x1b,0x91,0x66,0x14,0x26,0xfd,0xda,0xee,
-0xe5,0xd9,0x4c,0xb7,0x9d,0x6d,0x17,0xf8,0xc2,0x21,0xb4,0x34,0x08,0x0c,0x44,0x79,
-0x53,0x9c,0x81,0xbf,0x1f,0x22,0x0a,0xa6,0xe7,0x22,0x5f,0x5c,0xcb,0x31,0x2e,0xf5,
-0x0c,0x1a,0xf1,0x67,0x13,0x7f,0xe6,0xb3,0xb2,0xfe,0x6b,0x09,0xac,0xa6,0xd4,0x14,
-0xe7,0xe9,0x11,0x0e,0x49,0x99,0x06,0x04,0xa4,0x43,0x22,0xec,0x9f,0x59,0x83,0xfb,
-0xef,0xa3,0x8f,0x6b,0xde,0x70,0x0c,0xbb,0x89,0xe9,0x88,0xbc,0xeb,0x36,0x42,0x42,
-0x50,0x84,0xf5,0x93,0x98,0x93,0xed,0xa2,0x1f,0x13,0x60,0x36,0xc8,0x2f,0x9f,0xd1,
-0xc4,0x23,0xf6,0xd0,0x49,0x40,0xab,0xbe,0xf7,0x43,0x02,0x96,0xf0,0x74,0xa5,0x7d,
-0x68,0x89,0xfa,0x58, 0xad, 0x7b, 0x2f, 0x7d, 0xe8, 0x21, 0x34, 0x5e, 0x6c, 0x20,
-0x97, 0x9e
-};
-
-const unsigned char fakeSig[] = {
-0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,
-0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,
-0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,
-0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,
-0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,
-0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,
-0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,
-0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10
-};
-
-static void
-dumpBytes( const char *title, const unsigned char *data, int len, int nonewline );
-
-static int
-rsa_sign(
- const char *keyFile,
- unsigned char *plain,
- unsigned plainLength,
- unsigned char signature[SIG_LEN] )
-{
- int rc = -1;
- FILE *fp = NULL;
- RSA *rsa = NULL;
- unsigned signatureLength = 0;
-
- fp = fopen ( (char*) keyFile, "r" );
- if ( !fp ) {
- fprintf( stderr, "failed to open file=%s\n", keyFile );
- goto cleanup;
- }
- rsa = RSA_new();
- if ( !rsa ) {
- fprintf( stderr, "RSA_new() failed\n" );
- goto cleanup;
- }
- rsa = (RSA*)PEM_read_RSAPrivateKey( fp, &rsa, NULL, NULL );
- if ( !rsa ) {
- fprintf( stderr, "PEM_read_RSAPrivateKey() failed\n" );
- goto cleanup;
- }
- signatureLength = SIG_LEN;
- char sha1[20] = {};
- (void) SHA1( plain, plainLength, (unsigned char*)sha1 );
- rc = RSA_sign( NID_sha1, (unsigned char*)sha1, sizeof( sha1),
- signature, &signatureLength, rsa );
- if ( rc != 1 ) {
- fprintf(stderr, "RSA_sign failed=%d\n", rc );
- rc = -1;
- goto cleanup;
- } else {
- rc = 0;
- }
-
-cleanup:
- if ( fp ) fclose( fp );
- RSA_free( rsa );
- return rc;
-}
-
-static int
-rsa_verify(
- const char *keyFile,
- unsigned char *plain,
- unsigned plainLength,
- unsigned char signature[SIG_LEN] )
-{
- int rc = -1;
- FILE *fp = NULL;
- RSA *rsa = NULL;
- unsigned signatureLength = 0;
-
- fp = fopen ( (char*) keyFile, "r" );
- if ( !fp ) {
- fprintf( stderr, "failed to open file=%s\n", keyFile );
- goto cleanup;
- }
- rsa = RSA_new();
- if ( !rsa ) {
- fprintf( stderr, "RSA_new() failed\n" );
- goto cleanup;
- }
- rsa = (RSA*)PEM_read_RSAPrivateKey( fp, &rsa, NULL, NULL );
- if ( !rsa ) {
- fprintf( stderr, "PEM_read_RSAPrivateKey() failed\n" );
- goto cleanup;
- }
- signatureLength = SIG_LEN;
- char sha1[20] = {};
- (void) SHA1( plain, plainLength, (unsigned char*)sha1 );
- rc = RSA_verify( NID_sha1, (unsigned char*)sha1, sizeof( sha1 ),
- signature, signatureLength, rsa );
- if ( rc != 1 ) {
- fprintf(stderr, "RSA_verify failed=%d\n", rc );
- rc = -1;
- goto cleanup;
- } else {
- rc = 0;
- }
-
-cleanup:
- if ( fp ) fclose( fp );
- RSA_free( rsa );
- return rc;
- return rc;
-}
-
-static void
-dumpBytes( const char *title, const unsigned char *data, int len, int nonewline )
-{
- int width = 16;
- int line = 0;
- int multiple = 0;
-
- multiple = ( len % width == 0 );
-
- printf( "[%s: %d bytes]\n", title, len );
- while ( len-- > 0 ) {
- line++;
- printf( "%02X ", *data++ );
- if ( line % width == 0 && len > 0 && !nonewline ) {
- printf( "\n" );
- }
- }
-
- printf("\n");
-}
-
-static void
-writeFile( char* filename, unsigned char* buf, int len )
-{
- FILE *file = NULL;
- file = fopen( filename, "w" );
- if ( file ) {
- fwrite( buf, len, 1, file );
- fclose( file );
- }
-}
-
-
-static void
-verify_bb_ticket(
- unsigned char *ticketData,
- unsigned ticketLength,
- const char *keyFile,
- bool doPrint )
-{
- if ( doPrint ) {
- dumpBytes( "Ticket (whole)", ticketData, ticketLength, 0 );
- printf( "\nBreakdown:\n" );
- }
-
- DERItem derTicket = { .data = ticketData, .length = ticketLength };
- DERReturn drtn;
- DERBbTicket ticket = {};
- DERSize ticketSize;
- drtn = DERDecodeBbTicket(&derTicket, &ticket, &ticketSize);
- if (drtn != DR_Success) goto badTicket;
- fprintf( stderr, "ticketSize=%u\n", ticketSize );
-
- // Verify signature if key file exists (we should really use the certificate or GPUK in the ticket here. */
- if ( keyFile ) {
- int status = rsa_verify(
- keyFile,
- (unsigned char *)ticket.body.data,
- ticket.body.length,
- (unsigned char *)ticket.signature.data );
- if ( status ) {
- fprintf( stderr, "rsa_verify failed=%d\n", status );
- } else {
- fprintf( stdout, "Signature verified successfully\n");
- }
- }
-
- /* Example of how to retrive fields from ticket. */
- DERItem snum;
- drtn = DERSetDecodeItemWithTag(&ticket.body,
- ASN1_CONTEXT_SPECIFIC | kBbSNUMTag, &snum);
- if (drtn != DR_Success) goto badTicket;
- DERItem chipId;
- drtn = DERSetDecodeItemWithTag(&ticket.body,
- ASN1_CONTEXT_SPECIFIC | kBbChipIDTag, &chipId);
- if (drtn != DR_Success) goto badTicket;
-
- return;
-badTicket:
- fprintf( stdout, "Bad ticket\n");
- return;
-}
-
-static void
-verify_ticket_file(
- const char *ticketFile,
- const char *keyFile )
-{
- unsigned char *ticket = NULL;
- unsigned ticketLength = 0;
- readFile( (char*)ticketFile, &ticket, &ticketLength );
- verify_bb_ticket( ticket, ticketLength, keyFile, false );
-}
-
-static void
-make_sample_ap_ticket( void )
-{
- unsigned char chipId[] = { 0x01, 0x02, 0x03, 0x04 };
- unsigned char ecid[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
-
- DERApTicket ticket = { };
-
- /* Encode the signatureAlgorithm field of the ticket. */
- DERAlgorithmId algorithmId = { .oid = oidSha1Rsa, .params = {} };
- ticket.signatureAlgorithm.length = DERLengthOfEncodedSequence(
- ASN1_CONSTR_SEQUENCE, &algorithmId, DERNumAlgorithmIdItemSpecs,
- DERAlgorithmIdItemSpecs);
- ticket.signatureAlgorithm.data = malloc(ticket.signatureAlgorithm.length);
- DEREncodeSequence(ASN1_CONSTR_SEQUENCE, &algorithmId, DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs, ticket.signatureAlgorithm.data, &ticket.signatureAlgorithm.length);
-
- /* Construct ticket body. */
- DERSize numBodyItemSpecs = 0;
- DERItemSpec bodyItemSpecs[50] = {};
- DERItem bodyItems[50] = {};
-
- /* Add tags in sorted order. */
- bodyItemSpecs[numBodyItemSpecs].offset = numBodyItemSpecs * sizeof(DERItem);
- bodyItemSpecs[numBodyItemSpecs].tag = ASN1_CONTEXT_SPECIFIC | kApECIDTag;
- bodyItemSpecs[numBodyItemSpecs].options = DER_ENC_NO_OPTS;
- bodyItems[numBodyItemSpecs].data = ecid;
- bodyItems[numBodyItemSpecs].length = sizeof(ecid);
- numBodyItemSpecs++;
-
- bodyItemSpecs[numBodyItemSpecs].offset = numBodyItemSpecs * sizeof(DERItem);
- bodyItemSpecs[numBodyItemSpecs].tag = ASN1_CONTEXT_SPECIFIC | kApChipIDTag;
- bodyItemSpecs[numBodyItemSpecs].options = DER_ENC_NO_OPTS;
- bodyItems[numBodyItemSpecs].data = chipId;
- bodyItems[numBodyItemSpecs].length = sizeof(chipId);
- numBodyItemSpecs++;
-
- /* Encode ticket body. */
- ticket.body.length = DERLengthOfEncodedSequence(ASN1_CONSTR_SET,
- &bodyItems, numBodyItemSpecs, bodyItemSpecs);
- ticket.body.data = malloc(ticket.body.length);
- DEREncodeSequence(ASN1_CONSTR_SET, &bodyItems, numBodyItemSpecs,
- bodyItemSpecs, ticket.body.data, &ticket.body.length);
-
- // Signature
- ticket.signature.data = (DERByte *)fakeSig;
- ticket.signature.length = sizeof(fakeSig);
-
- // Certificates
- DERItemSpec certItemSpecs[1];
- DERItem certItems[1];
- certItemSpecs[0].offset = 0;
- certItemSpecs[0].tag = ASN1_CONSTR_SEQUENCE;
- certItemSpecs[0].options = DER_ENC_WRITE_DER;
-
- // NOTE: The Certificate should be added to the ticket by the host. I'm just simulating that here
- // to generate the final ticket blob.
- readFile("S5L8920_TATSU_FAC_DARWIN_DEV_CHAIN.der", &certItems[0].data, &certItems[0].length);
-
- ticket.certificates.length = DERLengthOfEncodedSequence(
- ASN1_CONSTRUCTED | ASN1_CONTEXT_SPECIFIC | 1, &certItems,
- 1, certItemSpecs);
- ticket.certificates.data = malloc(ticket.certificates.length);
- DEREncodeSequence(ASN1_CONSTRUCTED | ASN1_CONTEXT_SPECIFIC | 1, &certItems,
- 1, certItemSpecs, ticket.certificates.data, &ticket.certificates.length);
-
- /* Encode the entire ticket. */
- DERSize ticketLength = DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE,
- &ticket, DERNumApTicketItemSpecs, DERApTicketItemSpecs);
- DERByte *ticketBytes = malloc(ticketLength);
- DEREncodeSequence(ASN1_CONSTR_SEQUENCE, &ticket, DERNumApTicketItemSpecs, DERApTicketItemSpecs,
- ticketBytes, &ticketLength);
-
- // save ticket to file
- writeFile("ApTicket.bin", ticketBytes, ticketLength);
-
-//cleanup:
- free(ticket.body.data);
- free(ticket.signatureAlgorithm.data);
- free(ticket.certificates.data);
- free(ticketBytes);
-}
-
-static void
-make_sample_bb_ticket( void )
-{
- int status = 0;
- unsigned char chipId[] = { 0x01, 0x02, 0x03, 0x04 };
- unsigned char snum[] = { 0x01, 0x02, 0x03, 0x04 };
- unsigned char signature[SIG_LEN] = {};
- DERByte *ticketBytes = NULL;
-
- DERBbTicket ticket = {};
-
- /* Encode the signatureAlgorithm field of the ticket. */
- DERAlgorithmId algorithmId = { .oid = oidSha1Rsa };
- ticket.signatureAlgorithm.length = DERLengthOfEncodedSequence(
- ASN1_CONSTR_SEQUENCE, &algorithmId, DERNumAlgorithmIdItemSpecs,
- DERAlgorithmIdItemSpecs);
- ticket.signatureAlgorithm.data = malloc(ticket.signatureAlgorithm.length);
- DEREncodeSequence(ASN1_CONSTR_SEQUENCE, &algorithmId,
- DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs,
- ticket.signatureAlgorithm.data, &ticket.signatureAlgorithm.length);
-
- /* Construct ticket body. */
- DERSize numBodyItemSpecs = 0;
- DERItemSpec bodyItemSpecs[50] = {};
- DERItem bodyItems[50] = {};
-
- /* Add tags in sorted order. */
- bodyItemSpecs[numBodyItemSpecs].offset = numBodyItemSpecs * sizeof(DERItem);
- bodyItemSpecs[numBodyItemSpecs].tag = ASN1_CONTEXT_SPECIFIC | kBbSNUMTag;
- bodyItemSpecs[numBodyItemSpecs].options = DER_ENC_NO_OPTS;
- bodyItems[numBodyItemSpecs].data = snum;
- bodyItems[numBodyItemSpecs].length = sizeof(snum);
- numBodyItemSpecs++;
-
- bodyItemSpecs[numBodyItemSpecs].offset = numBodyItemSpecs * sizeof(DERItem);
- bodyItemSpecs[numBodyItemSpecs].tag = ASN1_CONTEXT_SPECIFIC | kBbChipIDTag;
- bodyItemSpecs[numBodyItemSpecs].options = DER_ENC_NO_OPTS;
- bodyItems[numBodyItemSpecs].data = chipId;
- bodyItems[numBodyItemSpecs].length = sizeof(chipId);
- numBodyItemSpecs++;
-
- /* Encode ticket body. */
- ticket.body.length = DERLengthOfEncodedSequence(ASN1_CONSTR_SET,
- &bodyItems, numBodyItemSpecs, bodyItemSpecs);
- ticket.body.data = malloc(ticket.body.length);
- DEREncodeSequence(ASN1_CONSTR_SET, &bodyItems, numBodyItemSpecs, bodyItemSpecs,
- ticket.body.data, &ticket.body.length);
-
- // NOTE: In the SEE machine, the Body above will then be hashed/signed to generate signature
- status = rsa_sign(
- "G1_GPrK.pem",
- ticket.body.data,
- ticket.body.length,
- (unsigned char *)signature );
- if ( status ) {
- fprintf( stderr, "rsa_sign failed=%d\n", status );
- goto cleanup;
- } else {
- fprintf( stdout, "Signed successfully\n");
- }
-
- status = rsa_verify(
- "G1_GPrK.pem",
- ticket.body.data,
- ticket.body.length,
- (unsigned char *)signature );
- if ( status ) {
- fprintf( stderr, "rsa_verify failed=%d\n", status );
- goto cleanup;
- } else {
- fprintf( stdout, "Signature verified successfully\n");
- }
-
- // Signature
- ticket.signature.data = signature;
- ticket.signature.length = SIG_LEN;
-
- // Certificates
- ticket.gpuk.length = sizeof(GoldKeyCert);
- ticket.gpuk.data = (DERByte *)GoldKeyCert;
-
- /* Encode the entire ticket. */
- DERSize ticketLength = DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE,
- &ticket, DERNumBbTicketItemSpecs, DERBbTicketItemSpecs);
- ticketBytes = malloc(ticketLength);
- DEREncodeSequence(ASN1_CONSTR_SEQUENCE, &ticket, DERNumBbTicketItemSpecs,
- DERBbTicketItemSpecs, ticketBytes, &ticketLength);
-
- // save ticket to file
- writeFile("BbTicket.bin", ticketBytes, ticketLength);
-
-cleanup:
- free(ticket.body.data);
- free(ticket.signatureAlgorithm.data);
- free(ticketBytes);
-}
-
-static void
-long_tag_test(void)
-{
- printf("ASN1_TAG_MASK 0x%.016qx\n", (uint64_t)ASN1_TAG_MASK);
- printf("ASN1_TAGNUM_MASK 0x%.016qx\n", (uint64_t)ASN1_TAGNUM_MASK);
- printf("ASN1_METHOD_MASK 0x%.016qx\n", (uint64_t)ASN1_METHOD_MASK);
- printf("ASN1_PRIMITIVE 0x%.016qx\n", (uint64_t)ASN1_PRIMITIVE);
- printf("ASN1_CONSTRUCTED 0x%.016qx\n", (uint64_t)ASN1_CONSTRUCTED);
- printf("ASN1_CLASS_MASK 0x%.016qx\n", (uint64_t)ASN1_CLASS_MASK);
- printf("ASN1_UNIVERSAL 0x%.016qx\n", (uint64_t)ASN1_UNIVERSAL);
- printf("ASN1_APPLICATION 0x%.016qx\n", (uint64_t)ASN1_APPLICATION);
- printf("ASN1_CONTEXT_SPECIFIC 0x%.016qx\n", (uint64_t)ASN1_CONTEXT_SPECIFIC);
- printf("ASN1_PRIVATE 0x%.016qx\n", (uint64_t)ASN1_PRIVATE);
-
- DERByte buf[10];
- DERSize len = sizeof(buf);
- DERReturn drtn;
- DERTag tag = ASN1_CONTEXT_SPECIFIC | ASN1_TAGNUM_MASK;
- drtn = DEREncodeItem(tag, 0, 0, buf, &len);
- if (drtn)
- {
- printf("DEREncodeItem: %u\n", drtn);
- }
- DERItem der = { .data = buf, .length = len };
-
- dumpBytes("tlv", buf, len, 0);
-
- DERDecodedInfo decoded;
- drtn = DERDecodeItem(&der, &decoded);
- if (drtn)
- {
- printf("DERDecodeItem: %u\n", drtn);
- }
- if (decoded.tag != tag)
- {
- printf("DERDecodeItem tag: 0x%qx != 0x%qx\n", (uint64_t)decoded.tag, (uint64_t)tag);
- }
- printf("DERDecodeItem tag: 0x%qx encoded in %u bytes, decoded length: %u\n",
- (uint64_t)tag, len, decoded.content.length);
-}
-
-int main(int argc, char **argv)
-{
- long_tag_test();
- printf( "=> Making sample BB ticket...\n");
- make_sample_bb_ticket();
- printf( "=> Making sample AP ticket...\n");
- make_sample_ap_ticket();
- printf( "=> Verifying signature in bb_ticket.bin ...\n");
- verify_ticket_file( "bb_ticket.bin", "G1_GPrK.pem" );
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (c) 2005-2007,2010-2012 Apple Inc. All Rights Reserved.
- */
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "fileIo.h"
-
-int writeFile(
- const char *fileName,
- const unsigned char *bytes,
- unsigned numBytes)
-{
- int rtn;
- int fd;
-
- fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
- if(fd == -1) {
- return errno;
- }
- rtn = (int)write(fd, bytes, (size_t)numBytes);
- if(rtn != (int)numBytes) {
- if(rtn >= 0) {
- fprintf(stderr, "writeFile: short write\n");
- }
- rtn = EIO;
- }
- else {
- rtn = 0;
- }
- close(fd);
- return rtn;
-}
-
-/*
- * Read entire file.
- */
-int readFile(
- const char *fileName,
- unsigned char **bytes, // mallocd and returned
- unsigned *numBytes) // returned
-{
- int rtn;
- int fd;
- char *buf;
- struct stat sb;
- unsigned size;
-
- *numBytes = 0;
- *bytes = NULL;
- fd = open(fileName, O_RDONLY, 0);
- if(fd == -1) {
- return errno;
- }
- rtn = fstat(fd, &sb);
- if(rtn) {
- goto errOut;
- }
- size = (unsigned) sb.st_size;
- buf = (char *)malloc(size);
- if(buf == NULL) {
- rtn = ENOMEM;
- goto errOut;
- }
- rtn = (int)read(fd, buf, size);
- if(rtn != size) {
- if(rtn >= 0) {
- fprintf(stderr, "readFile: short read\n");
- }
- rtn = EIO;
- }
- else {
- rtn = 0;
- *bytes = (unsigned char *)buf;
- *numBytes = size;
- }
-errOut:
- close(fd);
- return rtn;
-}
+++ /dev/null
-/*
- * Copyright (c) 2005-2007,2010-2011 Apple Inc. All Rights Reserved.
- */
-
-#ifndef _DER_FILE_IO_H_
-#define _DER_FILE_IO_H_
-
-/*
- * Read entire file.
- */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int readFile(
- const char *fileName,
- unsigned char **bytes, // mallocd and returned
- unsigned *numBytes); // returned
-
-int writeFile(
- const char *fileName,
- const unsigned char *bytes,
- unsigned numBytes);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _DER_FILE_IO_H_ */
+++ /dev/null
-/*
- * Copyright (c) 2005-2007,2011,2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-/*
- * libDERUtils.c - support routines for libDER tests & examples
- *
- */
-
-#include <libDERUtils/libDERUtils.h>
-#include <stdio.h>
-
-const char *DERReturnString(
- DERReturn drtn)
-{
- static char unknown[128];
-
- switch(drtn) {
- case DR_Success: return "DR_Success";
- case DR_EndOfSequence: return "DR_EndOfSequence";
- case DR_UnexpectedTag: return "DR_UnexpectedTag";
- case DR_DecodeError: return "DR_DecodeError";
- case DR_Unimplemented: return "DR_Unimplemented";
- case DR_IncompleteSeq: return "DR_IncompleteSeq";
- default:
- sprintf(unknown, "Unknown error (%d)", (int)drtn);
- return unknown;
- }
-}
-
-void DERPerror(
- const char *op,
- DERReturn drtn)
-{
- fprintf(stderr, "*** %s: %s\n", op, DERReturnString(drtn));
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2005-2007,2011,2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-/*
- * libDERUtils.h - support routines for libDER tests & examples
- *
- */
-
-#ifndef _LIB_DER_UTILS_H_
-#define _LIB_DER_UTILS_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <libDER/libDER.h>
-
-const char *DERReturnString(
- DERReturn drtn);
-
-void DERPerror(
- const char *op,
- DERReturn rtn);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIB_DER_UTILS_H_ */
+++ /dev/null
-/*
- * Copyright (c) 2005-2007,2011-2012,2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-/*
- * printFeilds.h - print various DER objects
- *
- */
-
-#include <libDERUtils/printFields.h>
-#include <libDER/DER_Decode.h>
-#include <libDER/asn1Types.h>
-#include <libDER/DER_Keys.h>
-#include <libDERUtils/libDERUtils.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <stdio.h>
-
-static int indentLevel = 0;
-
-void doIndent(void)
-{
- int i;
- for (i = 0; i<indentLevel; i++) {
- putchar(' ');
- }
-} /* indent */
-
-void incrIndent(void)
-{
- indentLevel += 3;
-}
-
-void decrIndent(void)
-{
- indentLevel -= 3;
-}
-
-#define TO_PRINT_MAX 12
-
-void printHex(
- DERItem *item)
-{
- unsigned long dex;
- unsigned long toPrint = item->length;
-
- printf("<%lu> ", item->length);
- if(toPrint > TO_PRINT_MAX) {
- toPrint = TO_PRINT_MAX;
- }
- for(dex=0; dex<toPrint; dex++) {
- printf("%02x ", item->data[dex]);
- }
- if(item->length > TO_PRINT_MAX) {
- printf("...");
- }
- printf("\n");
-}
-
-void printBitString(
- DERItem *item)
-{
- DERSize dex;
- DERSize toPrint = item->length;
- DERItem bitStringBytes;
- DERByte numUnused;
- DERReturn drtn;
-
- drtn = DERParseBitString(item, &bitStringBytes, &numUnused);
- if(drtn) {
- DERPerror("DERParseBitString", drtn);
- return;
- }
-
- printf("<%lu, %lu> ", (unsigned long)bitStringBytes.length, (unsigned long)numUnused);
- toPrint = bitStringBytes.length;
- if(toPrint > TO_PRINT_MAX) {
- toPrint = TO_PRINT_MAX;
- }
- for(dex=0; dex<toPrint; dex++) {
- printf("%02x ", bitStringBytes.data[dex]);
- }
- if(item->length > TO_PRINT_MAX) {
- printf("...");
- }
- printf("\n");
-}
-
-void printString(
- DERItem *item)
-{
- unsigned dex;
- char *cp = (char *)item->data;
- printf("'");
- for(dex=0; dex<item->length; dex++) {
- putchar(*cp++);
- }
- printf("'\n");
-
-}
-
-#define COLON_COLUMN 20
-
-/*
- * Print line header, with current indent, followed by specified label, followed
- * by a ':' in column COLON_COLUMN, followed by one space.
- */
-void printHeader(
- const char *label)
-{
- size_t numPrinted;
-
- doIndent();
- printf("%s", label);
- numPrinted = indentLevel + strlen(label);
- if(numPrinted < COLON_COLUMN) {
- size_t numSpaces = COLON_COLUMN - numPrinted;
- size_t dex;
- for(dex=0; dex<numSpaces; dex++) {
- putchar(' ');
- }
- }
- printf(": ");
-}
-
-void printItem(
- const char *label,
- ItemType itemType,
- int verbose,
- DERTag tag, // maybe from decoding, maybe the real tag underlying
- // an implicitly tagged item
- DERItem *item) // content
-{
- DERTag tagClass = tag & ASN1_CLASS_MASK;
- DERTag tagNum = tag & ASN1_TAGNUM_MASK;
- char printable = 0;
- char *asnType = NULL;
-
- printHeader(label);
-
- if((itemType == IT_Branch) && !verbose) {
- printf("\n");
- return;
- }
- switch(tagClass) {
- case ASN1_UNIVERSAL:
- break; // proceed with normal tags */
- case ASN1_APPLICATION:
- printf("APPLICATION (tag %u) ", tagNum);
- printHex(item);
- return;
- case ASN1_CONTEXT_SPECIFIC:
- printf("CONTEXT SPECIFIC (tag %u) ", tagNum);
- printHex(item);
- return;
- case ASN1_PRIVATE:
- printf("PRIVATE (tag %u) ", tagNum);
- printHex(item);
- return;
- }
- switch(tagNum) {
- case ASN1_BOOLEAN:
- asnType = "BOOLEAN";
- break;
- case ASN1_INTEGER:
- asnType = "INTEGER";
- break;
- case ASN1_BIT_STRING:
- /* special case here... */
- printf("BIT STRING ");
- printBitString(item);
- return;
- case ASN1_OCTET_STRING:
- asnType = "OCTET STRING";
- break;
- case ASN1_NULL:
- asnType = "NULL";
- break;
- case ASN1_OBJECT_ID:
- asnType = "OID";
- break;
- case ASN1_OBJECT_DESCRIPTOR:
- asnType = "OBJECT_DESCRIPTOR";
- break;
- case ASN1_REAL:
- asnType = "REAL";
- break;
- case ASN1_ENUMERATED:
- asnType = "ENUM";
- break;
- case ASN1_EMBEDDED_PDV:
- asnType = "EMBEDDED_PDV";
- break;
- case ASN1_UTF8_STRING:
- asnType = "UTF8 STRING";
- /* FIXME print these too */
- break;
- case ASN1_SEQUENCE:
- asnType = "SEQ";
- break;
- case ASN1_SET:
- asnType = "SET";
- break;
- case ASN1_NUMERIC_STRING:
- asnType = "NUMERIC_STRING";
- break;
- case ASN1_PRINTABLE_STRING:
- asnType = "PRINTABLE_STRING";
- printable = 1;
- break;
- case ASN1_T61_STRING:
- asnType = "T61_STRING";
- printable = 1;
- break;
- case ASN1_VIDEOTEX_STRING:
- asnType = "VIDEOTEX_STRING";
- printable = 1;
- break;
- case ASN1_IA5_STRING:
- asnType = "IA5_STRING";
- printable = 1;
- break;
- case ASN1_UTC_TIME:
- asnType = "UTC_TIME";
- printable = 1;
- break;
- case ASN1_GENERALIZED_TIME:
- asnType = "GENERALIZED_TIME";
- printable = 1;
- break;
- case ASN1_GRAPHIC_STRING:
- asnType = "GRAPHIC_STRING";
- break;
- case ASN1_VISIBLE_STRING:
- asnType = "VISIBLE_STRING";
- break;
- case ASN1_GENERAL_STRING:
- asnType = "GENERAL_STRING";
- break;
- case ASN1_UNIVERSAL_STRING:
- asnType = "UNIVERSAL_STRING";
- break;
- case ASN1_BMP_STRING:
- asnType = "BMP_STRING";
- break;
- default:
- asnType = "[unknown]";
- break;
- }
- printf("%s ", asnType);
- if(printable) {
- printString(item);
- }
- else {
- printHex(item);
- }
-}
-
-void printAlgId(
- const DERItem *content,
- int verbose)
-{
- DERReturn drtn;
- DERAlgorithmId algId;
-
- drtn = DERParseSequenceContent(content,
- DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs,
- &algId, sizeof(algId));
- if(drtn) {
- DERPerror("DERParseSequenceContent(algId)", drtn);
- return;
- }
- printItem("alg", IT_Leaf, verbose, ASN1_OBJECT_ID, &algId.oid);
- if(algId.params.data) {
- printItem("params", IT_Leaf, verbose, algId.params.data[0], &algId.params);
- }
-}
-
-void printSubjPubKeyInfo(
- const DERItem *content,
- int verbose)
-{
- DERReturn drtn;
- DERSubjPubKeyInfo pubKeyInfo;
- DERRSAPubKeyPKCS1 pkcs1Key;
- DERItem bitStringContents;
- DERByte numUnused;
-
- drtn = DERParseSequence(content,
- DERNumSubjPubKeyInfoItemSpecs, DERSubjPubKeyInfoItemSpecs,
- &pubKeyInfo, sizeof(pubKeyInfo));
- if(drtn) {
- DERPerror("DERParseSequenceContent(pubKeyInfo)", drtn);
- return;
- }
- printItem("algId", IT_Branch, verbose, ASN1_CONSTR_SEQUENCE, &pubKeyInfo.algId);
- incrIndent();
- printAlgId(&pubKeyInfo.algId, verbose);
- decrIndent();
-
- printItem("pubKey", IT_Branch, verbose, ASN1_BIT_STRING, &pubKeyInfo.pubKey);
-
- /*
- * The contents of that bit string are a PKCS1 format RSA key.
- */
- drtn = DERParseBitString(&pubKeyInfo.pubKey, &bitStringContents, &numUnused);
- if(drtn) {
- DERPerror("DERParseBitString(pubKeyInfo.pubKey)", drtn);
- decrIndent();
- return;
- }
- drtn = DERParseSequence(&bitStringContents,
- DERNumRSAPubKeyPKCS1ItemSpecs, DERRSAPubKeyPKCS1ItemSpecs,
- &pkcs1Key, sizeof(pkcs1Key));
- if(drtn) {
- DERPerror("DERParseSequenceContent(pubKeyBits)", drtn);
- decrIndent();
- return;
- }
- incrIndent();
- printItem("modulus", IT_Leaf, verbose, ASN1_INTEGER, &pkcs1Key.modulus);
- printItem("pubExponent", IT_Leaf, verbose, ASN1_INTEGER, &pkcs1Key.pubExponent);
-
- decrIndent();
-}
-
-/* decode one item and print it */
-void decodePrintItem(
- const char *label,
- ItemType itemType,
- int verbose,
- DERItem *derItem)
-{
- DERDecodedInfo decoded;
- DERReturn drtn;
-
- drtn = DERDecodeItem(derItem, &decoded);
- if(drtn) {
- DERPerror("DERDecodeItem()", drtn);
- return;
- }
- printItem(label, IT_Leaf, 0, decoded.tag, &decoded.content);
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2005-2007,2011-2012,2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-/*
- * printFeilds.h - print various DER objects
- *
- */
-
-#ifndef _PRINT_FIELDS_H_
-#define _PRINT_FIELDS_H_
-
-#include <libDER/libDER.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void doIndent(void);
-void incrIndent(void);
-void decrIndent(void);
-void printHex(DERItem *item);
-void printBitString(DERItem *item);
-void printString(DERItem *item);
-void printHeader(const char *label);
-
-typedef enum {
- IT_Leaf, // leaf; always print contents
- IT_Branch // branch; print contents iff verbose
-} ItemType;
-
-void printItem(
- const char *label,
- ItemType itemType,
- int verbose,
- DERTag tag, // maybe from decoding, maybe the real tag underlying
- // an implicitly tagged item
- DERItem *item); // content
-
-void printAlgId(
- const DERItem *content,
- int verbose);
-void printSubjPubKeyInfo(
- const DERItem *content,
- int verbose);
-
-/* decode one item and print it */
-void decodePrintItem(
- const char *label,
- ItemType itemType,
- int verbose,
- DERItem *derItem);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _PRINT_FIELDS_H_ */
/*
- * Copyright (c) 2015 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2015-2017 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
};
+static CFStringRef copyIssuerCN(SecCertificateRef certificate)
+{
+ if (!certificate ||
+ CFGetTypeID(certificate) !=SecCertificateGetTypeID()) {
+ return NULL;
+ }
+
+ CFStringRef issuerCN = NULL; // do not release
+ CFDictionaryRef issuerDict = NULL; // do not release
+ CFArrayRef issuerArray = NULL; // do not release
+ CFDictionaryRef issuerInfo = NULL; // do not release
+ CFErrorRef error = NULL; // do not release
+
+ CFMutableArrayRef certificateKeys = NULL; // must release
+ CFDictionaryRef certificateDict = NULL; // must release
+
+ certificateKeys = CFArrayCreateMutable(kCFAllocatorDefault,
+ 1,
+ &kCFTypeArrayCallBacks);
+ if (!certificateKeys) {
+ goto finish;
+ }
+
+ CFArrayAppendValue(certificateKeys, kSecOIDX509V1IssuerName);
+
+ certificateDict = SecCertificateCopyValues(certificate,
+ certificateKeys,
+ &error);
+
+ if (error != errSecSuccess ||
+ !certificateDict ||
+ CFGetTypeID(certificateDict) != CFDictionaryGetTypeID()) {
+ goto finish;
+ }
+
+ issuerDict = (CFDictionaryRef) CFDictionaryGetValue(certificateDict,
+ kSecOIDX509V1IssuerName);
+ if (!issuerDict ||
+ CFGetTypeID(issuerDict) != CFDictionaryGetTypeID()) {
+ goto finish;
+ }
+
+ issuerArray = (CFArrayRef) CFDictionaryGetValue(issuerDict,
+ kSecPropertyKeyValue);
+ if (!issuerArray ||
+ CFGetTypeID(issuerArray) != CFArrayGetTypeID()) {
+ goto finish;
+ }
+
+ for (int index=0; index<CFArrayGetCount(issuerArray); index++) {
+ issuerInfo = (CFDictionaryRef) CFArrayGetValueAtIndex(issuerArray,
+ index);
+ if (!issuerInfo ||
+ CFGetTypeID(issuerInfo) != CFDictionaryGetTypeID()) {
+ continue;
+ }
+
+ CFStringRef label = NULL; // do not release
+ label = CFDictionaryGetValue(issuerInfo,
+ kSecPropertyKeyLabel);
+ if (kCFCompareEqualTo == CFStringCompare(label,
+ CFSTR("2.5.4.3"),
+ 0)) {
+ issuerCN = CFDictionaryGetValue(issuerInfo,
+ kSecPropertyKeyValue);
+ if (issuerCN &&
+ CFGetTypeID(issuerCN) == CFStringGetTypeID()) {
+ CFRetain(issuerCN);
+ goto finish;
+ }
+ else {
+ issuerCN = NULL;
+ }
+ }
+ }
+
+finish:
+ if (certificateKeys) { CFRelease(certificateKeys); }
+ if (certificateDict) { CFRelease(certificateDict); }
+ return issuerCN;
+}
+
+static void CertificateValuesTests(SecCertificateRef certificate)
+{
+ CFDictionaryRef subjectDict = NULL; // do not release
+ CFArrayRef subjectArray = NULL; // do not release
+ CFStringRef orgNameString = NULL; // do not release
+ CFErrorRef error = NULL; // do not release
+
+ CFMutableArrayRef certificateKeys = NULL; // must release
+ CFDictionaryRef certificateDict = NULL; // must release
+
+ bool hasAppleOrgName = false;
+ bool hasWWDRIssuerCN = false;
+
+ certificateKeys = CFArrayCreateMutable(kCFAllocatorDefault,
+ 1,
+ &kCFTypeArrayCallBacks);
+ if (!certificateKeys) {
+ goto finish;
+ }
+ CFArrayAppendValue(certificateKeys, kSecOIDX509V1SubjectName);
+
+ certificateDict = SecCertificateCopyValues(certificate, certificateKeys, &error);
+ if (error != errSecSuccess || !certificateDict ||
+ CFGetTypeID(certificateDict) != CFDictionaryGetTypeID()) {
+ goto finish;
+ }
+
+ subjectDict = (CFDictionaryRef) CFDictionaryGetValue(certificateDict,
+ kSecOIDX509V1SubjectName);
+ if (!subjectDict || CFGetTypeID(subjectDict) != CFDictionaryGetTypeID()) {
+ goto finish;
+ }
+
+ subjectArray = (CFArrayRef) CFDictionaryGetValue(subjectDict,
+ kSecPropertyKeyValue);
+ if (!subjectArray ||
+ CFGetTypeID(subjectArray) != CFArrayGetTypeID()) {
+ goto finish;
+ }
+
+ // look for Organization Name field ("2.5.4.10") in subject
+ for (int index=0; index<CFArrayGetCount(subjectArray); index++) {
+ CFDictionaryRef subjectInfo = (CFDictionaryRef) CFArrayGetValueAtIndex(subjectArray, index);
+ if (!subjectInfo || CFGetTypeID(subjectInfo) != CFDictionaryGetTypeID()) {
+ continue;
+ }
+ CFStringRef label = CFDictionaryGetValue(subjectInfo, kSecPropertyKeyLabel);
+ if (kCFCompareEqualTo == CFStringCompare(label, CFSTR("2.5.4.10"), 0)) {
+ orgNameString = CFDictionaryGetValue(subjectInfo, kSecPropertyKeyValue);
+ if (orgNameString && CFGetTypeID(orgNameString) == CFStringGetTypeID()) {
+ if (kCFCompareEqualTo == CFStringCompare(orgNameString, CFSTR("Apple Inc."), 0)) {
+ hasAppleOrgName = true;
+ }
+ }
+ }
+ }
+
+ // look for issuer common name
+ CFStringRef commonName = copyIssuerCN(certificate);
+ if (commonName) {
+ if (kCFCompareEqualTo == CFStringCompare(commonName,
+ CFSTR("Apple Worldwide Developer Relations Certification Authority"), 0)) {
+ hasWWDRIssuerCN = true;
+ }
+ CFRelease(commonName);
+ }
+
+finish:
+ if (certificateKeys) { CFRelease(certificateKeys); }
+ if (certificateDict) { CFRelease(certificateDict); }
+
+ /* and now, the actual tests... */
+ is(hasAppleOrgName, true, "O=Apple Inc.");
+ is(hasWWDRIssuerCN, true, "CN=Apple Worldwide Developer Relations Certification Authority");
+}
+
static void tests(void)
{
SecTrustRef trust = NULL;
is(signerStatus, kCMSSignerValid, "signer status valid");
is(verifyResult, errSecSuccess, "verify result valid");
+ /* Add some basic subject/issuer field value tests */
+ CertificateValuesTests(cert0);
+
CFReleaseSafe(decoder);
CFReleaseSafe(date);
CFReleaseSafe(trust);
int si_20_sectrust_provisioning(int argc, char *const *argv)
{
- plan_tests(15);
+ plan_tests(17);
tests();
} else if (tsaReply) {
reply = xpc_create_reply_with_format(event, "{TimeStampReply: %value}", tsaReply);
} else {
- reply = xpc_create_reply_with_format(event, "No TimeStampReply or TimeStampError");
+ reply = xpc_create_reply_with_format(event, "{}");
}
xpc_connection_send_message(peer, reply);
xpc_release(reply);
/*
- * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2012-2016 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include "tsaTemplates.h"
#include <Security/SecAsn1Coder.h>
#include <AssertMacros.h>
+#include <Security/SecBasePriv.h>
#include <Security/SecPolicy.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecImportExport.h>
#include <Security/SecCertificatePriv.h>
-#include <security_keychain/SecCertificateP.h>
-#include <security_keychain/SecCertificatePrivP.h>
#include <utilities/SecCFRelease.h>
#include <utilities/SecDispatchRelease.h>
return result;
}
-static OSStatus SecTSAValidateTimestamp(const SecAsn1TSATSTInfo *tstInfo, CSSM_DATA **signingCerts, CFAbsoluteTime *timestampTime)
+static OSStatus SecTSAValidateTimestamp(const SecAsn1TSATSTInfo *tstInfo, SecCertificateRef signerCert, CFAbsoluteTime *timestampTime)
{
// See <rdar://problem/11077708> Properly handle revocation information of timestamping certificate
OSStatus result = paramErr;
CFAbsoluteTime genTime = 0;
char timeStr[32] = {0,};
- SecCertificateRef signingCertificate = NULL;
- require(tstInfo && signingCerts && (tstInfo->genTime.Length < 16), xit);
-
- // Find the leaf signingCert
- require_noerr(result = SecCertificateCreateFromData(*signingCerts,
- CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &signingCertificate), xit);
+ require(tstInfo && signerCert && (tstInfo->genTime.Length < 16), xit);;
memcpy(timeStr, tstInfo->genTime.Data, tstInfo->genTime.Length);
timeStr[tstInfo->genTime.Length] = 0;
require_noerr(convertGeneralizedTimeToCFAbsoluteTime(timeStr, &genTime), xit);
- if (SecCertificateIsValidX(signingCertificate, genTime)) // iOS?
+ if (SecCertificateIsValidX(signerCert, genTime)) // iOS?
result = noErr;
else
result = errSecTimestampInvalid;
if (timestampTime)
*timestampTime = genTime;
xit:
- if (signingCertificate)
- CFReleaseNull(signingCertificate);
return result;
}
-static OSStatus verifyTSTInfo(const CSSM_DATA_PTR content, CSSM_DATA **signingCerts, SecAsn1TSATSTInfo *tstInfo, CFAbsoluteTime *timestampTime, uint64_t expectedNonce)
+static OSStatus verifyTSTInfo(const CSSM_DATA_PTR content, SecCertificateRef signerCert, SecAsn1TSATSTInfo *tstInfo, CFAbsoluteTime *timestampTime, uint64_t expectedNonce)
{
OSStatus status = paramErr;
SecAsn1CoderRef coder = NULL;
require_action(expectedNonce==nonce, xit, status = errSecTimestampRejection);
}
- status = SecTSAValidateTimestamp(tstInfo, signingCerts, timestampTime);
+ status = SecTSAValidateTimestamp(tstInfo, signerCert, timestampTime);
dtprintf("SecTSAValidateTimestamp result: %ld\n", (long)status);
xit:
static OSStatus setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo)
{
- OSStatus status = noErr;
+ SecCertificateRef tsaLeaf = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL);
- if (!signerinfo->timestampCertList || (CFArrayGetCount(signerinfo->timestampCertList) == 0))
+ if (!tsaLeaf)
return SecCmsVSSigningCertNotFound;
- SecCertificateRef tsaLeaf = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->timestampCertList, 0);
- require_action(tsaLeaf, xit, status = errSecCertificateCannotOperate);
-
signerinfo->tsaLeafNotBefore = SecCertificateNotValidBefore(tsaLeaf); /* Start date for Timestamp Authority leaf */
signerinfo->tsaLeafNotAfter = SecCertificateNotValidAfter(tsaLeaf); /* Expiration date for Timestamp Authority leaf */
free((void *)nbefore);free((void *)nafter);
}
-/*
- if(at < nb)
- status = errSecCertificateNotValidYet;
- else if (at > na)
- status = errSecCertificateExpired;
-*/
-
-xit:
- return status;
+ return errSecSuccess;
}
/*
case SEC_OID_PKCS9_ID_CT_TSTInfo:
{
SecAsn1TSATSTInfo tstInfo = {{0},};
- result = verifyTSTInfo(contentInfo->rawContent, signingCerts, &tstInfo, &signerinfo->timestampTime, expectedNonce);
+ SecCertificateRef signerCert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL);
+ result = verifyTSTInfo(contentInfo->rawContent, signerCert, &tstInfo, &signerinfo->timestampTime, expectedNonce);
if (signerinfo->timestampTime)
{
const char *tstamp = cfabsoluteTimeToString(signerinfo->timestampTime);
CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name);
-SOSPeerInfoRef SOSCreatePeerInfoFromName(CFStringRef name, SecKeyRef* outSigningKey, SecKeyRef* outOctagonSigningKey, CFErrorRef *error);
-SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(CFStringRef name, SecKeyRef* outSigningKey, SecKeyRef* outOctagonSigningKey, CFErrorRef *error);
+SOSPeerInfoRef
+SOSCreatePeerInfoFromName(CFStringRef name,
+ SecKeyRef* outSigningKey,
+ SecKeyRef* outOctagonSigningKey,
+ SecKeyRef* outOctagonEncryptionKey,
+ CFErrorRef *error);
+
+SOSFullPeerInfoRef
+SOSCreateFullPeerInfoFromName(CFStringRef name,
+ SecKeyRef* outSigningKey,
+ SecKeyRef* outOctagonSigningKey,
+ SecKeyRef* outOctagonEncryptionKey,
+ CFErrorRef *error);
__END_DECLS
}
-SOSPeerInfoRef SOSCreatePeerInfoFromName(CFStringRef name, SecKeyRef* outSigningKey, SecKeyRef* outOctagonSigningKey, CFErrorRef *error)
+SOSPeerInfoRef SOSCreatePeerInfoFromName(CFStringRef name,
+ SecKeyRef* outSigningKey,
+ SecKeyRef* outOctagonSigningKey,
+ SecKeyRef* outOctagonEncryptionKey,
+ CFErrorRef *error)
{
SOSPeerInfoRef result = NULL;
SecKeyRef publicKey = NULL;
- SecKeyRef octagonPublicKey = NULL;
+ SecKeyRef octagonSigningPublicKey = NULL;
+ SecKeyRef octagonEncryptionPublicKey = NULL;
CFDictionaryRef gestalt = NULL;
require(outSigningKey, exit);
require_quiet(SecError(GeneratePermanentECPair(256, &publicKey, outSigningKey), error, CFSTR("Failed To Create Key")), exit);
- require_quiet(SecError(GeneratePermanentECPair(384, &octagonPublicKey, outOctagonSigningKey), error, CFSTR("Failed To Creaete Key")), exit);
+ require_quiet(SecError(GeneratePermanentECPair(384, &octagonSigningPublicKey, outOctagonSigningKey), error, CFSTR("Failed to Create Octagon Signing Key")), exit);
+ require_quiet(SecError(GeneratePermanentECPair(384, &octagonEncryptionPublicKey, outOctagonEncryptionKey), error, CFSTR("Failed to Create Octagon Encryption Key")), exit);
gestalt = SOSCreatePeerGestaltFromName(name);
require(gestalt, exit);
- result = SOSPeerInfoCreate(NULL, gestalt, NULL, *outSigningKey, *outOctagonSigningKey, error);
+ result = SOSPeerInfoCreate(NULL, gestalt, NULL, *outSigningKey,
+ *outOctagonSigningKey, *outOctagonEncryptionKey, error);
exit:
CFReleaseNull(gestalt);
CFReleaseNull(publicKey);
- CFReleaseNull(octagonPublicKey);
+ CFReleaseNull(octagonSigningPublicKey);
+ CFReleaseNull(octagonEncryptionPublicKey);
return result;
}
-SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(CFStringRef name, SecKeyRef* outSigningKey, SecKeyRef* outOctagonSigningKey, CFErrorRef *error)
+SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(CFStringRef name,
+ SecKeyRef* outSigningKey,
+ SecKeyRef* outOctagonSigningKey,
+ SecKeyRef* outOctagonEncryptionKey,
+ CFErrorRef *error)
{
SOSFullPeerInfoRef result = NULL;
SecKeyRef publicKey = NULL;
*outOctagonSigningKey = GeneratePermanentFullECKey(384, name, error);
require(*outOctagonSigningKey, exit);
+ require(outOctagonEncryptionKey, exit);
+ *outOctagonEncryptionKey = GeneratePermanentFullECKey(384, name, error);
+ require(*outOctagonEncryptionKey, exit);
+
gestalt = SOSCreatePeerGestaltFromName(name);
require(gestalt, exit);
- result = SOSFullPeerInfoCreate(NULL, gestalt, NULL, *outSigningKey, *outOctagonSigningKey, error);
+ result = SOSFullPeerInfoCreate(NULL, gestalt,
+ NULL,
+ *outSigningKey,
+ *outOctagonSigningKey,
+ *outOctagonEncryptionKey,
+ error);
exit:
CFReleaseNull(gestalt);
typedef struct piStuff_t {
SecKeyRef signingKey;
SecKeyRef octagonSigningKey;
+ SecKeyRef octagonEncryptionKey;
SOSFullPeerInfoRef fpi;
SOSPeerInfoRef pi;
SOSPeerInfoRef resignation_ticket;
if(!pi) return NULL;
pi->signingKey = NULL;
CFStringRef cfName = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingMacRoman);
- pi->fpi = SOSCreateFullPeerInfoFromName(cfName, &pi->signingKey, &pi->octagonSigningKey, NULL);
+ pi->fpi = SOSCreateFullPeerInfoFromName(cfName, &pi->signingKey, &pi->octagonSigningKey, &pi->octagonEncryptionKey, NULL);
CFReleaseSafe(cfName);
pi->pi = SOSFullPeerInfoGetPeerInfo(pi->fpi);
pi->resignation_ticket = SOSPeerInfoCreateRetirementTicket(kCFAllocatorDefault, pi->signingKey, pi->pi, NULL);
CFReleaseSafe(pi->fpi);
CFReleaseSafe(pi->signingKey);
CFReleaseSafe(pi->octagonSigningKey);
+ CFReleaseSafe(pi->octagonEncryptionKey);
CFReleaseSafe(pi->resignation_ticket);
free(pi);
}
#include "SOSCircle_regressions.h"
#include "SOSRegressionUtilities.h"
-static SOSFullPeerInfoRef SOSCreateApplicantFullPeerInfoFromName(CFStringRef peerName, SecKeyRef user_private_key,
- SecKeyRef* outSigningKey, SecKeyRef* outOctagonSigningKey, CFErrorRef *error)
+static SOSFullPeerInfoRef SOSCreateApplicantFullPeerInfoFromName(CFStringRef peerName,
+ SecKeyRef user_private_key,
+ SecKeyRef* outSigningKey,
+ SecKeyRef* outOctagonSigningKey,
+ SecKeyRef* outOctagonEncryptionKey,
+ CFErrorRef *error)
{
SOSFullPeerInfoRef result = NULL;
- SOSFullPeerInfoRef fullPeer = SOSCreateFullPeerInfoFromName(peerName, outSigningKey, outOctagonSigningKey, error);
+ SOSFullPeerInfoRef fullPeer = SOSCreateFullPeerInfoFromName(peerName, outSigningKey, outOctagonSigningKey, outOctagonEncryptionKey, error);
if (fullPeer && SOSFullPeerInfoPromoteToApplication(fullPeer, user_private_key, error))
CFTransferRetained(result, fullPeer);
SecKeyRef dev_a_key = NULL;
SecKeyRef dev_b_key = NULL;
SecKeyRef dev_c_key = NULL;
- SecKeyRef oct_dev_a_key = NULL;
- SecKeyRef oct_dev_b_key = NULL;
- SecKeyRef oct_dev_c_key = NULL;
+ SecKeyRef oct_dev_as_key = NULL;
+ SecKeyRef oct_dev_aw_key = NULL;
+ SecKeyRef oct_dev_bs_key = NULL;
+ SecKeyRef oct_dev_bw_key = NULL;
+ SecKeyRef oct_dev_cs_key = NULL;
+ SecKeyRef oct_dev_cw_key = NULL;
CFErrorRef error = NULL;
CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
SecKeyRef user_pubkey = SecKeyCreatePublicFromPrivate(user_privkey);
- SOSFullPeerInfoRef peer_a_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer A"), user_privkey, &dev_a_key, &oct_dev_a_key, NULL);
- SOSFullPeerInfoRef peer_b_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer B"), user_privkey, &dev_b_key, &oct_dev_b_key, NULL);
- SOSFullPeerInfoRef peer_c_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer C"), user_privkey, &dev_c_key, &oct_dev_c_key, NULL);
+ SOSFullPeerInfoRef peer_a_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer A"), user_privkey, &dev_a_key, &oct_dev_as_key, &oct_dev_aw_key, NULL);
+ SOSFullPeerInfoRef peer_b_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer B"), user_privkey, &dev_b_key, &oct_dev_bs_key, &oct_dev_bw_key, NULL);
+ SOSFullPeerInfoRef peer_c_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer C"), user_privkey, &dev_c_key, &oct_dev_cs_key, &oct_dev_cw_key, NULL);
CFStringRef peerID_a = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(peer_a_full_info));
CFStringRef peerID_b = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(peer_b_full_info));
SOSRingRef Ring = SOSRingCreate(CFSTR("TESTRING"), peerID_a, kSOSRingBase, NULL);
CFReleaseNull(dev_a_key);
CFReleaseNull(dev_b_key);
CFReleaseNull(dev_c_key);
- CFReleaseNull(oct_dev_a_key);
- CFReleaseNull(oct_dev_b_key);
- CFReleaseNull(oct_dev_c_key);
+ CFReleaseNull(oct_dev_as_key);
+ CFReleaseNull(oct_dev_aw_key);
+ CFReleaseNull(oct_dev_bs_key);
+ CFReleaseNull(oct_dev_bw_key);
+ CFReleaseNull(oct_dev_cs_key);
+ CFReleaseNull(oct_dev_cw_key);
CFReleaseNull(cfpassword);
CFReleaseNull(user_privkey);
SecKeyRef peer1SigningKey = NULL;
SecKeyRef peer1OctagonSigningKey = NULL;
- SOSFullPeerInfoRef fullPeer1WithBackup = SOSCreateFullPeerInfoFromName(CFSTR("peer1WithBackupID"), &peer1SigningKey, &peer1OctagonSigningKey, &localError);
+ SecKeyRef peer1OctagonEncryptionKey = NULL;
+ SOSFullPeerInfoRef fullPeer1WithBackup = SOSCreateFullPeerInfoFromName(CFSTR("peer1WithBackupID"), &peer1SigningKey, &peer1OctagonSigningKey, &peer1OctagonEncryptionKey, &localError);
ok(fullPeer1WithBackup, "Allocate peer 1 (%@)", localError);
CFReleaseNull(localError);
SecKeyRef peer2SigningKey = NULL;
SecKeyRef peer2OctagonSigningKey = NULL;
- SOSFullPeerInfoRef fullPeer2WithBackup = SOSCreateFullPeerInfoFromName(CFSTR("peer2WithBackupID"), &peer2SigningKey, &peer2OctagonSigningKey, &localError);
+ SecKeyRef peer2OctagonEncryptionKey = NULL;
+ SOSFullPeerInfoRef fullPeer2WithBackup = SOSCreateFullPeerInfoFromName(CFSTR("peer2WithBackupID"), &peer2SigningKey, &peer2OctagonSigningKey, &peer2OctagonEncryptionKey, &localError);
ok(fullPeer2WithBackup, "Allocate peer 2 (%@)", localError);
CFReleaseNull(localError);
static void tests(void)
{
SecKeyRef publicKey = NULL;
- SecKeyRef octagonPublicKey = NULL;
-
+ SecKeyRef octagonSigningPublicKey = NULL;
+ SecKeyRef octagonEncryptionPublicKey = NULL;
+
CFErrorRef error = NULL;
SOSCircleRef circle = SOSCircleCreate(NULL, CFSTR("Test Circle"), &error);
CFReleaseNull(circle_name);
CFReleaseNull(circle_key);
- SOSPeerInfoRef pi = SOSCreatePeerInfoFromName(CFSTR("Test Peer"), &publicKey, &octagonPublicKey, &error);
+ SOSPeerInfoRef pi = SOSCreatePeerInfoFromName(CFSTR("Test Peer"), &publicKey, &octagonSigningPublicKey, &octagonEncryptionPublicKey, &error);
CFStringRef other_peer_id = CFSTR("OTHER PEER");
SOSPeerInfoGetPeerID(pi), retirement_peer_id);
CFReleaseNull(publicKey);
- CFReleaseNull(octagonPublicKey);
+ CFReleaseNull(octagonSigningPublicKey);
+ CFReleaseNull(octagonEncryptionPublicKey);
CFReleaseNull(circle);
CFReleaseNull(error);
CFReleaseNull(pi);
{
SecKeyRef signingKey = NULL;
SecKeyRef octagonSigningKey = NULL;
- SOSFullPeerInfoRef fpi = SOSCreateFullPeerInfoFromName(CFSTR("Test Peer"), &signingKey, &octagonSigningKey, NULL);
+ SecKeyRef octagonEncryptionKey = NULL;
+ SOSFullPeerInfoRef fpi = SOSCreateFullPeerInfoFromName(CFSTR("Test Peer"), &signingKey, &octagonSigningKey, &octagonEncryptionKey, NULL);
SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
ok(NULL != pi, "info creation");
CFReleaseNull(signingKey);
CFReleaseNull(octagonSigningKey);
+ CFReleaseNull(octagonEncryptionKey);
CFReleaseNull(fpi);
}
{
SecKeyRef signingKey = NULL;
SecKeyRef octagonSigningKey = NULL;
- SOSFullPeerInfoRef fpi = SOSCreateFullPeerInfoFromName(CFSTR("Test Peer"), &signingKey, &octagonSigningKey, NULL);
+ SecKeyRef octagonEncryptionKey = NULL;
+ SOSFullPeerInfoRef fpi = SOSCreateFullPeerInfoFromName(CFSTR("Test Peer"), &signingKey, &octagonSigningKey, &octagonEncryptionKey, NULL);
SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
unsigned long count;
errOut:
CFReleaseNull(signingKey);
CFReleaseNull(octagonSigningKey);
+ CFReleaseNull(octagonEncryptionKey);
CFReleaseNull(fpi);
}
SecKeyRef dev_b_key = NULL;
SecKeyRef dev_c_key = NULL;
SecKeyRef dev_d_key = NULL;
- SecKeyRef oct_dev_a_key = NULL;
- SecKeyRef oct_dev_b_key = NULL;
- SecKeyRef oct_dev_c_key = NULL;
- SecKeyRef oct_dev_d_key = NULL;
+ SecKeyRef oct_dev_as_key = NULL;
+ SecKeyRef oct_dev_aw_key = NULL;
+ SecKeyRef oct_dev_bs_key = NULL;
+ SecKeyRef oct_dev_bw_key = NULL;
+ SecKeyRef oct_dev_cs_key = NULL;
+ SecKeyRef oct_dev_cw_key = NULL;
+ SecKeyRef oct_dev_ds_key = NULL;
+ SecKeyRef oct_dev_dw_key = NULL;
CFErrorRef error = NULL;
CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
SecKeyRef user_privkey = SOSUserKeygen(cfpassword, parameters, &error);
CFReleaseNull(parameters);
- SOSFullPeerInfoRef peer_a_full_info = SOSCreateFullPeerInfoFromName(CFSTR("Peer A"), &dev_a_key, &oct_dev_a_key, NULL);
+ SOSFullPeerInfoRef peer_a_full_info = SOSCreateFullPeerInfoFromName(CFSTR("Peer A"), &dev_a_key, &oct_dev_as_key, &oct_dev_aw_key, NULL);
- SOSFullPeerInfoRef peer_b_full_info = SOSCreateFullPeerInfoFromName(CFSTR("Peer B"), &dev_b_key, &oct_dev_b_key, NULL);
+ SOSFullPeerInfoRef peer_b_full_info = SOSCreateFullPeerInfoFromName(CFSTR("Peer B"), &dev_b_key, &oct_dev_bs_key, &oct_dev_bw_key, NULL);
- SOSFullPeerInfoRef peer_c_full_info = SOSCreateFullPeerInfoFromName(CFSTR("Peer C"), &dev_c_key, &oct_dev_c_key, NULL);
+ SOSFullPeerInfoRef peer_c_full_info = SOSCreateFullPeerInfoFromName(CFSTR("Peer C"), &dev_c_key, &oct_dev_cs_key, &oct_dev_cw_key, NULL);
- SOSFullPeerInfoRef peer_d_full_info = SOSCreateFullPeerInfoFromName(CFSTR("Peer D"), &dev_d_key, &oct_dev_d_key, NULL);
+ SOSFullPeerInfoRef peer_d_full_info = SOSCreateFullPeerInfoFromName(CFSTR("Peer D"), &dev_d_key, &oct_dev_ds_key, &oct_dev_dw_key, NULL);
ok(SOSCircleRequestAdmission(circle, user_privkey, peer_a_full_info, NULL));
ok(SOSCircleRequestAdmission(circle, user_privkey, peer_a_full_info, NULL));
CFReleaseNull(dev_b_key);
CFReleaseNull(dev_c_key);
CFReleaseNull(dev_d_key);
- CFReleaseNull(oct_dev_a_key);
- CFReleaseNull(oct_dev_b_key);
- CFReleaseNull(oct_dev_c_key);
- CFReleaseNull(oct_dev_d_key);
+ CFReleaseNull(oct_dev_as_key);
+ CFReleaseNull(oct_dev_aw_key);
+ CFReleaseNull(oct_dev_bs_key);
+ CFReleaseNull(oct_dev_bw_key);
+ CFReleaseNull(oct_dev_cs_key);
+ CFReleaseNull(oct_dev_cw_key);
+ CFReleaseNull(oct_dev_ds_key);
+ CFReleaseNull(oct_dev_dw_key);
CFReleaseNull(cfpassword);
return SOSAccountInflateTransports(self, (__bridge CFStringRef) circle_name, NULL);
}
+-(void)ensureOctagonPeerKeys
+{
+ [self.trust ensureOctagonPeerKeys:self.circle_transport];
+}
+
-(id) initWithGestalt:(CFDictionaryRef)newGestalt factory:(SOSDataSourceFactoryRef)f
{
self = [super init];
CFReleaseNull(accountPrivateKey);
complete(true, NULL);
}];
+
+ // This makes getting the private key the same as Asserting the password - we read all the other things
+ // that we just expressed interest in.
+ CFErrorRef error = NULL;
+ if (!Flush(&error)) {
+ secnotice("pairing", "failed final flush: %@", error ? error : NULL);
+ return;
+ }
+ CFReleaseNull(error);
}
- (void)myPeerInfo:(void (^)(NSData *, NSError *))complete
SOSPeerInfoRef GenerateNewCloudIdentityPeerInfo(CFErrorRef *error) {
SecKeyRef cloud_key = GeneratePermanentFullECKeyForCloudIdentity(256, kicloud_identity_name, error);
- SecKeyRef octagon_key = GeneratePermanentFullECKeyForCloudIdentity(384, kicloud_identity_name, error);
SOSPeerInfoRef cloud_peer = NULL;
CFDictionaryRef gestalt = NULL;
NULL);
require_action_quiet(gestalt, fail, SecError(errSecAllocate, error, CFSTR("Can't allocate gestalt")));
- cloud_peer = SOSPeerInfoCreateCloudIdentity(kCFAllocatorDefault, gestalt, cloud_key, octagon_key, error);
+ cloud_peer = SOSPeerInfoCreateCloudIdentity(kCFAllocatorDefault, gestalt, cloud_key, error);
require(cloud_peer, fail);
UpdateKeyName(cloud_key, cloud_peer, error);
- UpdateKeyName(octagon_key, cloud_peer, error);
-
+
fail:
CFReleaseNull(gestalt);
CFReleaseNull(cloud_key);
- CFReleaseNull(octagon_key);
return cloud_peer;
}
}
bool SOSAccountGhostResultsInReset(SOSAccount* account) {
- return SOSTrustedCircleGhostSetCount(account) == SOSCircleCountActivePeers([account.trust getCircle:NULL]);
+ return SOSTrustedCircleGhostSetCount(account) == SOSCircleCountPeers([account.trust getCircle:NULL]);
}
SOSAccountSyncablePeersBlock changeBlock);
-(bool) ensureFactoryCircles;
+-(void) ensureOctagonPeerKeys;
-(void) flattenToSaveBlock;
self.initialKeyParameters = self.account.accountKeyDerivationParamters ? [NSData dataWithData:self.account.accountKeyDerivationParamters] : nil;
SOSPeerInfoRef mpi = self.account.peerInfo;
- self.initialViews = mpi ? (__bridge_transfer NSSet*) SOSPeerInfoCopyEnabledViews(mpi) : nil;
-
+ if (mpi) {
+ self.initialViews = CFBridgingRelease(SOSPeerInfoCopyEnabledViews(mpi));
+ [self.account ensureOctagonPeerKeys];
+ }
self.peersToRequestSync = nil;
CFStringSetPerformWithDescription((__bridge CFSetRef) self.initialViews, ^(CFStringRef description) {
-(bool) ghostBustingOK:(SOSCircleRef) oldCircle updatingTo:(SOSCircleRef) newCircle {
bool retval = false;
+ // Preliminaries - we must have a peer and it must be in the newCircle in order to attempt busting
SOSFullPeerInfoRef me_full = self.fullPeerInfo;
if(!me_full) return false;
SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(me_full);
+ if(!me || (!SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL))) return false;
+
CFStringRef myPid = SOSPeerInfoGetPeerID(me);
CFDictionaryRef newSigs = SOSCircleCopyAllSignatures(newCircle);
bool iSignedNew = CFDictionaryGetCountOfKey(newSigs, myPid);
long otherPeerSigCount = CFDictionaryGetCount(newSigs) - ((iSignedNew) ? 2: 1);
-
- if (me && SOSCircleHasPeer(oldCircle, me, NULL) && SOSCircleHasPeer(newCircle, me, NULL)) { // If we're already in the old one we're not PBing
+
+ if (SOSCircleHasPeer(oldCircle, me, NULL)) { // If we're already in the old one we're not PBing
retval = true;
} else if (!iSignedNew) { // Piggybacking peers always have signed as part of genSigning - so this indicates we're safe to bust.
retval = true;
return false;
}
+static bool SOSCirclePeerOctagonKeysChanged(SOSPeerInfoRef oldPeer, SOSPeerInfoRef newPeer) {
+ bool oldHasOctagonBits = oldPeer && (SOSPeerInfoHasOctagonSigningPubKey(oldPeer) || SOSPeerInfoHasOctagonEncryptionPubKey(oldPeer));
+ bool newHasOctagonBits = newPeer && (SOSPeerInfoHasOctagonSigningPubKey(newPeer) || SOSPeerInfoHasOctagonEncryptionPubKey(newPeer));
+
+ if(!oldPeer) {
+ if(newPeer && newHasOctagonBits) {
+ return true;
+ } else {
+ // New peer to circle has no octagon bits: no change
+ return false;
+ }
+ } else {
+ // Have an old peer
+ if(!newPeer) {
+ // We removed a peer. This is an octagon bits change if the old peer had octagon bits
+ return oldHasOctagonBits;
+ }
+
+ // This is a peer update: Did the keys change?
+ if(!oldHasOctagonBits && !newHasOctagonBits) {
+ // both peers have no keys: no change
+ return false;
+ }
+
+ bool signingKeyChanged = CFEqualSafe(SOSPeerInfoCopyOctagonSigningPublicKey(oldPeer, NULL), SOSPeerInfoCopyOctagonSigningPublicKey(newPeer, NULL));
+ bool encryptionKeyChanged = CFEqualSafe(SOSPeerInfoCopyOctagonEncryptionPublicKey(oldPeer, NULL), SOSPeerInfoCopyOctagonEncryptionPublicKey(newPeer, NULL));
+
+ return signingKeyChanged || encryptionKeyChanged;
+ }
+}
+
+static bool SOSCircleHasUpdatedPeerInfoWithOctagonKey(SOSCircleRef oldCircle, SOSCircleRef newCircle)
+{
+ __block bool hasUpdated = false;
+ SOSCircleForEachPeer(oldCircle, ^(SOSPeerInfoRef oldPeer) {
+ SOSPeerInfoRef equivalentNewPeer = SOSCircleCopyPeerWithID(newCircle, SOSPeerInfoGetPeerID(oldPeer), NULL);
+ hasUpdated |= SOSCirclePeerOctagonKeysChanged(oldPeer, equivalentNewPeer);
+ });
+
+ SOSCircleForEachPeer(newCircle, ^(SOSPeerInfoRef newPeer) {
+ SOSPeerInfoRef equivalentOldPeer = SOSCircleCopyPeerWithID(oldCircle, SOSPeerInfoGetPeerID(newPeer), NULL);
+ hasUpdated |= SOSCirclePeerOctagonKeysChanged(equivalentOldPeer, newPeer);
+ });
+
+ return hasUpdated;
+}
+
-(bool) handleUpdateCircle:(SOSCircleRef) prospective_circle transport:(SOSKVSCircleStorageTransport*)circleTransport update:(bool) writeUpdate err:(CFErrorRef*)error
{
bool success = true;
}
if (circle_action == accept) {
+ if(SOSCircleHasUpdatedPeerInfoWithOctagonKey(oldCircle, newCircle)){
+ notify_post(kSOSCCCircleOctagonKeysChangedNotification);
+ }
if (me && SOSCircleHasActivePeer(oldCircle, me, NULL) && !SOSCircleHasPeer(newCircle, me, NULL)) {
// Don't destroy evidence of other code determining reason for leaving.
if(![self hasLeft]) self.departureCode = kSOSMembershipRevoked;
-(bool) ensureFullPeerAvailable:(CFDictionaryRef)gestalt deviceID:(CFStringRef)deviceID backupKey:(CFDataRef)backup err:(CFErrorRef *) error;
-(bool) isMyPeerActive:(CFErrorRef*) error;
-(void) purgeIdentity;
+
+- (void)ensureOctagonPeerKeys:(SOSKVSCircleStorageTransport*)circleTransport;
+
@end
return SOSFullPeerInfoCopyFullPeerInfo(self.fullPeerInfo);
}
+- (SecKeyRef)randomPermanentFullECKey:(int)keysize name:(NSString *)name error:(CFErrorRef*)cferror
+{
+ return GeneratePermanentFullECKey(keysize, (__bridge CFStringRef)name, cferror);
+}
+
+- (void)ensureOctagonPeerKeys:(SOSKVSCircleStorageTransport*)circleTransport
+{
+ NSString* octagonKeyName;
+ SecKeyRef publicKey;
+
+ bool changedSelf = false;
+
+ CFErrorRef copyError = NULL;
+ publicKey = SOSFullPeerInfoCopyOctagonSigningKey(self.fullPeerInfo, ©Error);
+ if(copyError) {
+ secerror("circleChange: Error fetching Octagon signing key: %@", copyError);
+ CFReleaseNull(copyError);
+ }
+
+ if (publicKey == NULL) {
+ octagonKeyName = [NSString stringWithFormat:@"Octagon Peer Signing ID for %@", SOSCircleGetName(self.trustedCircle)];
+ CFErrorRef cferror = NULL;
+ SecKeyRef octagonSigningFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:&cferror];
+ if(cferror || !octagonSigningFullKey) {
+ secerror("circleChange: Error upgrading Octagon signing key: %@", cferror);
+ } else {
+ SOSFullPeerInfoUpdateOctagonSigningKey(self.fullPeerInfo, octagonSigningFullKey, &cferror);
+ if(cferror) {
+ secerror("circleChange: Error upgrading Octagon signing key: %@", cferror);
+ }
+ changedSelf = true;
+ }
+
+ CFReleaseNull(cferror);
+ CFReleaseNull(octagonSigningFullKey);
+ }
+ CFReleaseNull(publicKey);
+
+ CFReleaseNull(copyError);
+ publicKey = SOSFullPeerInfoCopyOctagonEncryptionKey(self.fullPeerInfo, ©Error);
+ if(copyError) {
+ secerror("circleChange: Error fetching Octagon encryption key: %@", copyError);
+ CFReleaseNull(copyError);
+ }
+
+ if (publicKey == NULL) {
+ octagonKeyName = [NSString stringWithFormat:@"Octagon Peer Encryption ID for %@", SOSCircleGetName(self.trustedCircle)];
+ CFErrorRef cferror = NULL;
+ SecKeyRef octagonEncryptionFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:&cferror];
+ if(cferror || !octagonEncryptionFullKey) {
+ secerror("circleChange: Error upgrading Octagon encryption key: %@", cferror);
+ } else {
+
+ SOSFullPeerInfoUpdateOctagonEncryptionKey(self.fullPeerInfo, octagonEncryptionFullKey, &cferror);
+ if(cferror) {
+ secerror("circleChange: Error upgrading Octagon encryption key: %@", cferror);
+ }
+ changedSelf = true;
+ }
+
+ CFReleaseNull(cferror);
+ CFReleaseNull(octagonEncryptionFullKey);
+ }
+ CFReleaseNull(publicKey);
+
+ if(changedSelf) {
+ [self modifyCircle:circleTransport err:NULL action:^bool (SOSCircleRef circle_to_change) {
+ return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(self.fullPeerInfo));
+ }];
+ }
+}
+
-(bool) ensureFullPeerAvailable:(CFDictionaryRef)gestalt deviceID:(CFStringRef)deviceID backupKey:(CFDataRef)backup err:(CFErrorRef *) error
{
require_action_quiet(self.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("Don't have circle")));
if (self.fullPeerInfo == NULL) {
+ NSString* octagonKeyName;
CFStringRef keyName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("ID for %@-%@"), SOSPeerGestaltGetName(gestalt), SOSCircleGetName(self.trustedCircle));
- SecKeyRef full_key = GeneratePermanentFullECKey(256, keyName, error);
-
- NSString* octagonKeyName = [@"Octagon " stringByAppendingString:(__bridge NSString*)keyName];
- SecKeyRef octagonFullKey = GeneratePermanentFullECKey(384, (__bridge CFStringRef)octagonKeyName, error);
+ SecKeyRef full_key = [self randomPermanentFullECKey:256 name:(__bridge NSString *)keyName error:NULL];
- if (full_key && octagonFullKey) {
+ octagonKeyName = [@"Octagon Peer Signing " stringByAppendingString:(__bridge NSString*)keyName];
+ SecKeyRef octagonSigningFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:NULL];
+
+ octagonKeyName = [@"Octagon Peer Encryption " stringByAppendingString:(__bridge NSString*)keyName];
+ SecKeyRef octagonEncryptionFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:NULL];
+
+ if (full_key && octagonSigningFullKey && octagonEncryptionFullKey) {
CFSetRef initialViews = SOSViewCopyViewSet(kViewSetInitial);
self.fullPeerInfo = nil;
- self.fullPeerInfo = SOSFullPeerInfoCreateWithViews(kCFAllocatorDefault, gestalt, backup, initialViews, full_key,octagonFullKey, error);
+ self.fullPeerInfo = SOSFullPeerInfoCreateWithViews(kCFAllocatorDefault, gestalt, backup, initialViews, full_key, octagonSigningFullKey, octagonEncryptionFullKey, error);
+
CFDictionaryRef v2dictionaryTestUpdates = [self getValueFromExpansion:kSOSTestV2Settings err:NULL];
if(v2dictionaryTestUpdates) SOSFullPeerInfoUpdateV2Dictionary(self.fullPeerInfo, v2dictionaryTestUpdates, NULL);
CFReleaseNull(initialViews);
secerror("No full_key: %@:", error ? *error : NULL);
}
-
+
+ CFReleaseNull(full_key);
+ CFReleaseNull(octagonSigningFullKey);
+ CFReleaseNull(octagonEncryptionFullKey);
CFReleaseNull(keyName);
}
kSOSCCNotInCircle = 1,
kSOSCCRequestPending = 2,
kSOSCCCircleAbsent = 3,
- kSOSCCError = -1,
+ kSOSCCError = -1, // unable to determine circle status, inspect CFError to find out why
};
typedef int SOSCCStatus;
extern const char * kSOSCCHoldLockForInitialSync;
extern const char * kSOSCCPeerAvailable;
extern const char * kSOSCCRecoveryKeyChanged;
+extern const char * kSOSCCCircleOctagonKeysChangedNotification;
/*!
@function SOSCCSetUserCredentials
extern const CFStringRef kCKKSViewManatee;
extern const CFStringRef kCKKSViewAutoUnlock;
extern const CFStringRef kCKKSViewHealth;
+extern const CFStringRef kCKKSViewApplePay;
/*!
const char * kSOSCCHoldLockForInitialSync = "com.apple.security.secureobjectsync.holdlock";
const char * kSOSCCPeerAvailable = "com.apple.security.secureobjectsync.peeravailable";
const char * kSOSCCRecoveryKeyChanged = "com.apple.security.secureobjectsync.recoverykeychanged";
+const char * kSOSCCCircleOctagonKeysChangedNotification = "com.apple.security.sosoctagonbitschanged";
#define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
{
secdebug("sosops", "enter - operation: %d", op);
return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
- xpc_object_t xData = _CFXPCCreateXPCObjectFromCFObject(data);
+ xpc_object_t xData = NULL;
+ if(data) {
+ xData = _CFXPCCreateXPCObjectFromCFObject(data);
+ } else {
+ uint8_t zero = 0;
+ CFDataRef nullData = CFDataCreate(kCFAllocatorDefault, &zero, 1);
+ xData = _CFXPCCreateXPCObjectFromCFObject(nullData);
+ CFReleaseNull(nullData);
+ }
bool success = false;
if (xData){
xpc_dictionary_set_value(message, kSecXPCKeyRecoveryPublicKey, xData);
sec_trace_return_bool_api(^{
bool retval = false;
do_if_registered(soscc_RegisterRecoveryPublicKey, recovery_key, error);
- if(!recovery_key) { // this is used to clear the rk
- CFDataRef empty = CFDataCreate(kCFAllocatorDefault, 0, 0);
- retval = recovery_and_bool_to_bool_error_request(kSecXPCOpRegisterRecoveryPublicKey, empty, error);
- CFReleaseNull(empty);
- } else {
- retval = recovery_and_bool_to_bool_error_request(kSecXPCOpRegisterRecoveryPublicKey, recovery_key, error);
- }
+ // NULL recovery_key is handled in recovery_and_bool_to_bool_error_request now.
+ retval = recovery_and_bool_to_bool_error_request(kSecXPCOpRegisterRecoveryPublicKey, recovery_key, error);
return retval;
}, NULL);
}
#include <corecrypto/ccder.h>
#include <utilities/iCloudKeychainTrace.h>
+#include <utilities/SecADWrapper.h>
#include "AssertMacros.h"
case errSecDecode:
CFStringAppend(action, CFSTR("resending dh"));
result = SOSCoderResendDH(coder, error);
+ SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.restartotrnegotiation"), 1);
break;
default:
SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot negotiate session (%ld)"), clientId, (long)ppstatus);
if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) {
CFStringAppend(action, CFSTR("not ready for data; resending DH packet"));
SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed, 1);
+ SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.restartotrnegotiation"), 1);
result = SOSCoderResendDH(coder, error);
} else {
if (coder->waitingForDataPacket) {
SecADClientPushValueForDistributionKey(known ? kSecADSecurityKnownItemSyncTimeKey : kSecADSecurityNewItemSyncTimeKey,
SecBucket2Significant(syncTime));
}
+ CFReleaseNull(itemModDate);
}
/* Handle incoming message from peer p. Return false if there was an error, true otherwise. */
_kSOSCCHoldLockForInitialSync
_kSOSCCPeerAvailable
_kSOSCCRecoveryKeyChanged
+_kSOSCCCircleOctagonKeysChangedNotification
_SOSCCSetLastDepartureReason
_SOSCCAccountSetToNew
kSOSFullPeerVersion = 1,
};
-SOSFullPeerInfoRef SOSFullPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backupKey, SecKeyRef signingKey, SecKeyRef octagonSigningKey, CFErrorRef *error);
+SOSFullPeerInfoRef SOSFullPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backupKey, SecKeyRef signingKey, SecKeyRef octagonSigningKey, SecKeyRef octagonEncryptionKey, CFErrorRef *error);
bool SOSFullPeerInfoUpdateToThisPeer(SOSFullPeerInfoRef peer, SOSPeerInfoRef pi, CFErrorRef *error);
SOSFullPeerInfoRef SOSFullPeerInfoCreateWithViews(CFAllocatorRef allocator,
CFDictionaryRef gestalt, CFDataRef backupKey, CFSetRef enabledViews,
- SecKeyRef signingKey, SecKeyRef octagonSigningKey, CFErrorRef *error);
+ SecKeyRef signingKey, SecKeyRef octagonSigningKey, SecKeyRef octagonEncryptionKey, CFErrorRef *error);
SOSFullPeerInfoRef SOSFullPeerInfoCopyFullPeerInfo(SOSFullPeerInfoRef toCopy);
SOSPeerInfoRef SOSFullPeerInfoGetPeerInfo(SOSFullPeerInfoRef fullPeer);
SecKeyRef SOSFullPeerInfoCopyDeviceKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error);
SecKeyRef SOSFullPeerInfoCopyPubKey(SOSFullPeerInfoRef fpi, CFErrorRef *error);
+
+/* octagon keys */
+SecKeyRef SOSFullPeerInfoCopyOctagonPublicSigningKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error);
+SecKeyRef SOSFullPeerInfoCopyOctagonPublicEncryptionKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error);
SecKeyRef SOSFullPeerInfoCopyOctagonSigningKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error);
+SecKeyRef SOSFullPeerInfoCopyOctagonEncryptionKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error);
bool SOSFullPeerInfoPurgePersistentKey(SOSFullPeerInfoRef peer, CFErrorRef* error);
bool SOSFullPeerInfoUpdateTransportFragmentationPreference(SOSFullPeerInfoRef peer, CFBooleanRef preference, CFErrorRef* error);
bool SOSFullPeerInfoUpdateTransportAckModelPreference(SOSFullPeerInfoRef peer, CFBooleanRef preference, CFErrorRef* error);
+bool SOSFullPeerInfoUpdateOctagonSigningKey(SOSFullPeerInfoRef peer, SecKeyRef octagonSigningKey, CFErrorRef* error);
+bool SOSFullPeerInfoUpdateOctagonEncryptionKey(SOSFullPeerInfoRef peer, SecKeyRef octagonEncryptionKey, CFErrorRef* error);
+
SOSSecurityPropertyResultCode SOSFullPeerInfoUpdateSecurityProperty(SOSFullPeerInfoRef peer, SOSViewActionCode action, CFStringRef property, CFErrorRef* error);
SOSSecurityPropertyResultCode SOSFullPeerInfoSecurityPropertyStatus(SOSFullPeerInfoRef peer, CFStringRef property, CFErrorRef *error);
CFDataRef SOSPeerInfoCopyData(SOSPeerInfoRef fpi, CFErrorRef *error);
SOSPeerInfoRef peer_info;
CFDataRef key_ref;
- CFDataRef octagon_sync_signing_key_ref;
+ CFDataRef octagon_peer_signing_key_ref;
+ CFDataRef octagon_peer_encryption_key_ref;
};
CFGiblisWithHashFor(SOSFullPeerInfo);
CFStringRef kSOSFullPeerInfoNameKey = CFSTR("SOSFullPeerInfoName");
-static bool SOSFullPeerInfoUpdate(SOSFullPeerInfoRef peer, CFErrorRef *error, SOSPeerInfoRef (^create_modification)(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error)) {
+static bool SOSFullPeerInfoUpdate(SOSFullPeerInfoRef fullPeerInfo, CFErrorRef *error, SOSPeerInfoRef (^create_modification)(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error)) {
bool result = false;
SOSPeerInfoRef newPeer = NULL;
- SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(peer, error);
+ SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(fullPeerInfo, error);
require_quiet(device_key, fail);
- newPeer = create_modification(peer->peer_info, device_key, error);
+ newPeer = create_modification(fullPeerInfo->peer_info, device_key, error);
require_quiet(newPeer, fail);
- CFTransferRetained(peer->peer_info, newPeer);
+ CFTransferRetained(fullPeerInfo->peer_info, newPeer);
result = true;
}
SOSFullPeerInfoRef SOSFullPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt,
- CFDataRef backupKey, SecKeyRef signingKey, SecKeyRef octagonSigningKey,
+ CFDataRef backupKey,
+ SecKeyRef signingKey,
+ SecKeyRef octagonPeerSigningKey,
+ SecKeyRef octagonPeerEncryptionKey,
CFErrorRef* error) {
- return SOSFullPeerInfoCreateWithViews(allocator, gestalt, backupKey, NULL, signingKey, octagonSigningKey, error);
+ return SOSFullPeerInfoCreateWithViews(allocator, gestalt, backupKey, NULL, signingKey,
+ octagonPeerSigningKey, octagonPeerEncryptionKey, error);
}
SOSFullPeerInfoRef SOSFullPeerInfoCreateWithViews(CFAllocatorRef allocator,
CFDictionaryRef gestalt, CFDataRef backupKey, CFSetRef initialViews,
- SecKeyRef signingKey, SecKeyRef octagonSigningKey, CFErrorRef* error) {
+ SecKeyRef signingKey,
+ SecKeyRef octagonPeerSigningKey,
+ SecKeyRef octagonPeerEncryptionKey,
+ CFErrorRef* error) {
SOSFullPeerInfoRef result = NULL;
SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator);
fpi->peer_info = SOSPeerInfoCreateWithTransportAndViews(allocator, gestalt, backupKey,
IDSID, transportType, preferIDS,
preferIDSFragmentation, preferACKModel, initialViews,
- signingKey, octagonSigningKey, error);
+ signingKey, octagonPeerSigningKey, octagonPeerEncryptionKey, error);
require_quiet(fpi->peer_info, exit);
OSStatus status = SecKeyCopyPersistentRef(signingKey, &fpi->key_ref);
require_quiet(SecError(status, error, CFSTR("Inflating persistent ref")), exit);
- status = SecKeyCopyPersistentRef(octagonSigningKey, &fpi->octagon_sync_signing_key_ref);
- require_quiet(SecError(status, error, CFSTR("Inflating octagon persistent ref")), exit);
+ status = SecKeyCopyPersistentRef(octagonPeerSigningKey, &fpi->octagon_peer_signing_key_ref);
+ require_quiet(SecError(status, error, CFSTR("Inflating octagon peer signing persistent ref")), exit);
+ status = SecKeyCopyPersistentRef(octagonPeerSigningKey, &fpi->octagon_peer_encryption_key_ref);
+ require_quiet(SecError(status, error, CFSTR("Inflating octagon peer encryption persistent ref")), exit);
CFTransferRetained(result, fpi);
});
}
+bool SOSFullPeerInfoUpdateOctagonSigningKey(SOSFullPeerInfoRef peer, SecKeyRef octagonSigningKey, CFErrorRef* error){
+ return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
+ return SOSPeerInfoSetOctagonSigningKey(kCFAllocatorDefault, peer, octagonSigningKey, key, error);
+ });
+}
+
+bool SOSFullPeerInfoUpdateOctagonEncryptionKey(SOSFullPeerInfoRef peer, SecKeyRef octagonEncryptionKey, CFErrorRef* error){
+ return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
+ return SOSPeerInfoSetOctagonEncryptionKey(kCFAllocatorDefault, peer, octagonEncryptionKey, key, error);
+ });
+}
+
+
+
CFDataRef SOSPeerInfoCopyData(SOSPeerInfoRef pi, CFErrorRef *error)
{
CFTypeRef vData = NULL;
CFReleaseNull(fpi->peer_info);
CFReleaseNull(fpi->key_ref);
+ CFReleaseNull(fpi->octagon_peer_signing_key_ref);
+ CFReleaseNull(fpi->octagon_peer_encryption_key_ref);
}
static Boolean SOSFullPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) {
return retval;
}
-static SecKeyRef SOSFullPeerInfoCopyOctagonPubKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
+SecKeyRef SOSFullPeerInfoCopyOctagonPublicSigningKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
{
SecKeyRef retval = NULL;
require_quiet(fpi, errOut);
SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
require_quiet(pi, errOut);
- retval = SOSPeerInfoCopyOctagonPubKey(pi, error);
+ retval = SOSPeerInfoCopyOctagonSigningPublicKey(pi, error);
errOut:
return retval;
}
+SecKeyRef SOSFullPeerInfoCopyOctagonPublicEncryptionKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
+{
+ SecKeyRef retval = NULL;
+ require_quiet(fpi, errOut);
+ SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
+ require_quiet(pi, errOut);
+ retval = SOSPeerInfoCopyOctagonEncryptionPublicKey(pi, error);
+
+errOut:
+ return retval;
+}
+
+
static SecKeyRef SOSFullPeerInfoCopyMatchingPrivateKey(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
SecKeyRef retval = NULL;
return retval;
}
-static SecKeyRef SOSFullPeerInfoCopyMatchingOctagonPrivateKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
+static SecKeyRef SOSFullPeerInfoCopyMatchingOctagonSigningPrivateKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
{
SecKeyRef retval = NULL;
- SecKeyRef pub = SOSFullPeerInfoCopyOctagonPubKey(fpi, error);
+ SecKeyRef pub = SOSFullPeerInfoCopyOctagonPublicSigningKey(fpi, error);
require_quiet(pub, exit);
retval = SecKeyCopyMatchingPrivateKey(pub, error);
CFReleaseNull(pub);
return retval;
}
+static SecKeyRef SOSFullPeerInfoCopyMatchingOctagonEncryptionPrivateKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
+{
+ SecKeyRef retval = NULL;
+ SecKeyRef pub = SOSFullPeerInfoCopyOctagonPublicEncryptionKey(fpi, error);
+ require_quiet(pub, exit);
+ retval = SecKeyCopyMatchingPrivateKey(pub, error);
+
+exit:
+ CFReleaseNull(pub);
+ return retval;
+}
+
static OSStatus SOSFullPeerInfoGetMatchingPrivateKeyStatus(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
OSStatus retval = errSecParam;
CFMutableDictionaryRef octagonQuery = NULL;
SecKeyRef pub = SOSFullPeerInfoCopyPubKey(fpi, error);
- SecKeyRef octagonPub = SOSFullPeerInfoCopyOctagonPubKey(fpi, error);
+ SecKeyRef octagonSigningPub = SOSFullPeerInfoCopyOctagonPublicSigningKey(fpi, error);
+ SecKeyRef octagonEncryptionPub = SOSFullPeerInfoCopyOctagonPublicEncryptionKey(fpi, error);
require_quiet(pub, fail);
- require_quiet(octagonPub, fail);
+ // iCloud Identities doesn't have either signing or encryption key here. Don't fail if they're not present.
privQuery = CreatePrivateKeyMatchingQuery(pub, false);
query = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, privQuery);
CFDictionaryAddValue(query, kSecUseTombstones, kCFBooleanFalse);
result = SecError(SecItemDelete(query), error, CFSTR("Deleting while purging"));
-
+
// do the same thing to also purge the octagon sync signing key
-
- octagonPrivQuery = CreatePrivateKeyMatchingQuery(octagonPub, false);
- octagonQuery = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, octagonPrivQuery);
- CFDictionaryAddValue(octagonQuery, kSecUseTombstones, kCFBooleanFalse);
-
- result &= SecError(SecItemDelete(octagonQuery), error, CFSTR("Deleting while purging"));
+ if(octagonSigningPub) {
+ octagonPrivQuery = CreatePrivateKeyMatchingQuery(octagonSigningPub, false);
+ octagonQuery = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, octagonPrivQuery);
+ CFDictionaryAddValue(octagonQuery, kSecUseTombstones, kCFBooleanFalse);
+
+ result &= SecError(SecItemDelete(octagonQuery), error, CFSTR("Deleting signing key while purging"));
+ }
+
+ CFReleaseNull(octagonPrivQuery);
+ CFReleaseNull(octagonQuery);
+
+ // do the same thing to also purge the octagon encryption key
+ if(octagonEncryptionPub) {
+ octagonPrivQuery = CreatePrivateKeyMatchingQuery(octagonEncryptionPub, false);
+ octagonQuery = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, octagonPrivQuery);
+ CFDictionaryAddValue(octagonQuery, kSecUseTombstones, kCFBooleanFalse);
+
+ result &= SecError(SecItemDelete(octagonQuery), error, CFSTR("Deleting encryption key while purging"));
+ }
fail:
CFReleaseNull(privQuery);
CFReleaseNull(pub);
CFReleaseNull(octagonPrivQuery);
CFReleaseNull(octagonQuery);
- CFReleaseNull(octagonPub);
+ CFReleaseNull(octagonSigningPub);
+ CFReleaseNull(octagonEncryptionPub);
return result;
}
-SecKeyRef SOSFullPeerInfoCopyDeviceKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error) {
+SecKeyRef SOSFullPeerInfoCopyDeviceKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error)
+{
return SOSFullPeerInfoCopyMatchingPrivateKey(fullPeer, error);
}
SecKeyRef SOSFullPeerInfoCopyOctagonSigningKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error)
{
- return SOSFullPeerInfoCopyMatchingOctagonPrivateKey(fullPeer, error);
+ return SOSFullPeerInfoCopyMatchingOctagonSigningPrivateKey(fullPeer, error);
+}
+
+SecKeyRef SOSFullPeerInfoCopyOctagonEncryptionKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error)
+{
+ return SOSFullPeerInfoCopyMatchingOctagonEncryptionPrivateKey(fullPeer, error);
}
//
return isSOSPeerInfo(obj) ? (SOSPeerInfoRef) obj : NULL;
}
-SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key, SecKeyRef signingKey, SecKeyRef octagonSigningKey, CFErrorRef* error);
+SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key, SecKeyRef signingKey, SecKeyRef octagonSigningKey, SecKeyRef octagonPeerEncryptionKey, CFErrorRef* error);
SOSPeerInfoRef SOSPeerInfoCreateWithTransportAndViews(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key,
CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS,
- CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel, CFSetRef enabledViews, SecKeyRef signingKey, SecKeyRef octagonSigningKey, CFErrorRef* error);
+ CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel, CFSetRef enabledViews, SecKeyRef signingKey, SecKeyRef octagonSigningKey, SecKeyRef octagonPeerEncryptionKey, CFErrorRef* error);
-SOSPeerInfoRef SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, SecKeyRef octagonSigningKey, CFErrorRef* error);
+SOSPeerInfoRef SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error);
SOSPeerInfoRef SOSPeerInfoCreateCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFErrorRef* error);
SOSPeerInfoRef SOSPeerInfoCreateCurrentCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy,
CFTypeRef SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt, CFStringRef question);
SecKeyRef SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer, CFErrorRef *error);
-SecKeyRef SOSPeerInfoCopyOctagonPubKey(SOSPeerInfoRef peer, CFErrorRef* error);
+SecKeyRef SOSPeerInfoCopyOctagonSigningPublicKey(SOSPeerInfoRef peer, CFErrorRef* error);
+SecKeyRef SOSPeerInfoCopyOctagonEncryptionPublicKey(SOSPeerInfoRef peer, CFErrorRef* error);
+bool SOSPeerInfoHasOctagonSigningPubKey(SOSPeerInfoRef peer);
+bool SOSPeerInfoHasOctagonEncryptionPubKey(SOSPeerInfoRef peer);
CFDataRef SOSPeerInfoGetAutoAcceptInfo(SOSPeerInfoRef peer);
CFStringRef SOSPeerInfoCopyDeviceID(SOSPeerInfoRef peer);
SOSPeerInfoRef SOSPeerInfoSetDeviceID(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFStringRef IDS, SecKeyRef signingKey, CFErrorRef *error);
+/* octagon keys */
+SOSPeerInfoRef CF_RETURNS_RETAINED
+SOSPeerInfoSetOctagonSigningKey(CFAllocatorRef allocator,
+ SOSPeerInfoRef toCopy,
+ SecKeyRef octagonSigningKey,
+ SecKeyRef signingKey,
+ CFErrorRef *error);
+
+SOSPeerInfoRef CF_RETURNS_RETAINED
+SOSPeerInfoSetOctagonEncryptionKey(CFAllocatorRef allocator,
+ SOSPeerInfoRef toCopy,
+ SecKeyRef octagonEncryptionKey,
+ SecKeyRef signingKey,
+ CFErrorRef *error);
+
+
CFStringRef SOSPeerInfoCopySerialNumber(SOSPeerInfoRef pi);
CFStringRef SOSPeerInfoCopyOSVersion(SOSPeerInfoRef pi);
// Description Dictionary Entries
static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey");
-static CFStringRef sOctagonPublicKeyKey = CFSTR("OctagonPublicSigningKey");
+static CFStringRef sOctagonPeerSigningPublicKeyKey = CFSTR("OctagonPublicSigningKey");
+static CFStringRef sOctagonPeerEncryptionPublicKeyKey = CFSTR("OctagonPublicEncryptionKey");
const CFStringRef sGestaltKey = CFSTR("DeviceGestalt");
const CFStringRef sVersionKey = CFSTR("ConflictVersion");
static CFStringRef sCloudIdentityKey = CFSTR("CloudIdentity");
return _SOSPeerInfoCopyPubKey(peer, sPublicKeyKey, error);
}
-SecKeyRef SOSPeerInfoCopyOctagonPubKey(SOSPeerInfoRef peer, CFErrorRef* error)
+SecKeyRef SOSPeerInfoCopyOctagonSigningPublicKey(SOSPeerInfoRef peer, CFErrorRef* error)
{
- return _SOSPeerInfoCopyPubKey(peer, sOctagonPublicKeyKey, error);
+ return _SOSPeerInfoCopyPubKey(peer, sOctagonPeerSigningPublicKeyKey, error);
}
+SecKeyRef SOSPeerInfoCopyOctagonEncryptionPublicKey(SOSPeerInfoRef peer, CFErrorRef* error)
+{
+ return _SOSPeerInfoCopyPubKey(peer, sOctagonPeerEncryptionPublicKeyKey, error);
+}
+
+
CFDataRef SOSPeerInfoGetAutoAcceptInfo(SOSPeerInfoRef peer) {
CFDataRef pubKeyBytes = NULL;
CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS,
CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel,
CFSetRef enabledViews,
- SecKeyRef signingKey, SecKeyRef octagonSigningKey, CFErrorRef* error,
+ SecKeyRef signingKey,
+ SecKeyRef octagonPeerSigningKey,
+ SecKeyRef octagonPeerEncryptionKey,
+ CFErrorRef* error,
void (^ description_modifier)(CFMutableDictionaryRef description)) {
SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
pi->gestalt = gestalt;
pi->version = SOSPeerInfoGetPeerProtocolVersion(pi);
CFDataRef publicBytes = NULL;
- CFDataRef octagonPublicBytes = NULL;
+ CFDataRef octagonPeerSigningPublicBytes = NULL;
+ CFDataRef octagonPeerEncryptionPublicBytes = NULL;
CFNumberRef versionNumber = NULL;
SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(signingKey);
CFReleaseNull(pi);
goto exit;
}
-
- SecKeyRef octagonPublicKey = SecKeyCreatePublicFromPrivate(octagonSigningKey);
- if (octagonPublicKey == NULL) {
- SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public key"), NULL, error);
- CFReleaseNull(pi);
- goto exit;
+
+ if (octagonPeerSigningKey) {
+ SecKeyRef octagonPeerSigningPublicKey = SecKeyCreatePublicFromPrivate(octagonPeerSigningKey);
+ if (octagonPeerSigningPublicKey == NULL) {
+ SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public key"), NULL, error);
+ CFReleaseNull(pi);
+ goto exit;
+ }
+
+ result = SecKeyCopyPublicBytes(octagonPeerSigningPublicKey, &octagonPeerSigningPublicBytes);
+ CFReleaseNull(octagonPeerSigningPublicKey);
+ if (result != errSecSuccess) {
+ SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
+ CFReleaseNull(pi);
+ goto exit;
+ }
}
-
- result = SecKeyCopyPublicBytes(octagonPublicKey, &octagonPublicBytes);
-
- if (result != errSecSuccess) {
- SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
- CFReleaseNull(pi);
- goto exit;
+
+ if (octagonPeerEncryptionKey) {
+ SecKeyRef octagonPeerEncryptionPublicKey = SecKeyCreatePublicFromPrivate(octagonPeerEncryptionKey);
+ if (octagonPeerEncryptionPublicKey == NULL) {
+ SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public key"), NULL, error);
+ CFReleaseNull(pi);
+ goto exit;
+ }
+
+ result = SecKeyCopyPublicBytes(octagonPeerEncryptionPublicKey, &octagonPeerEncryptionPublicBytes);
+ CFReleaseNull(octagonPeerEncryptionPublicKey);
+ if (result != errSecSuccess) {
+ SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
+ CFReleaseNull(pi);
+ goto exit;
+ }
}
-
+
pi->signature = CFDataCreateMutable(allocator, 0);
versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version);
pi->description = CFDictionaryCreateMutableForCFTypesWith(allocator,
sVersionKey, versionNumber,
sPublicKeyKey, publicBytes,
- sOctagonPublicKeyKey, octagonPublicBytes,
sGestaltKey, pi->gestalt,
NULL);
+ if (octagonPeerSigningPublicBytes) {
+ CFDictionarySetValue(pi->description, sOctagonPeerSigningPublicKeyKey, octagonPeerSigningPublicBytes);
+ }
+ if (octagonPeerEncryptionPublicBytes) {
+ CFDictionarySetValue(pi->description, sOctagonPeerEncryptionPublicKeyKey, octagonPeerEncryptionPublicBytes);
+ }
description_modifier(pi->description);
- pi->id = SOSCopyIDOfKey(publicKey, error);
+ pi->peerID = SOSCopyIDOfKey(publicKey, error);
+
CFReleaseNull(publicKey);
- CFReleaseNull(octagonPublicKey);
-
- require_quiet(pi->id, exit);
+
+ require_quiet(pi->peerID, exit);
// ================ V2 Additions Start
exit:
CFReleaseNull(versionNumber);
CFReleaseNull(publicBytes);
- CFReleaseNull(octagonPublicBytes);
+ CFReleaseNull(octagonPeerSigningPublicBytes);
+ CFReleaseNull(octagonPeerEncryptionPublicBytes);
return pi;
}
-SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key, SecKeyRef signingKey, SecKeyRef octagonSigningKey, CFErrorRef* error) {
- return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, NULL, NULL, NULL, NULL, NULL, NULL, signingKey, octagonSigningKey, error, ^(CFMutableDictionaryRef description) {});
+SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key, SecKeyRef signingKey, SecKeyRef octagonPeerSigningKey, SecKeyRef octagonPeerEncryptionKey, CFErrorRef* error) {
+ return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, NULL, NULL, NULL, NULL, NULL, NULL, signingKey, octagonPeerSigningKey, octagonPeerEncryptionKey, error, ^(CFMutableDictionaryRef description) {});
}
SOSPeerInfoRef SOSPeerInfoCreateWithTransportAndViews(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key,
CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS,
- CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel, CFSetRef enabledViews, SecKeyRef signingKey, SecKeyRef octagonSigningKey, CFErrorRef* error)
+ CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel, CFSetRef enabledViews,
+ SecKeyRef signingKey,
+ SecKeyRef octagonPeerSigningKey,
+ SecKeyRef octagonPeerEncryptionKey,
+ CFErrorRef* error)
{
- return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, IDSID, transportType, preferIDS, preferFragmentation, preferAckModel, enabledViews, signingKey, octagonSigningKey, error, ^(CFMutableDictionaryRef description) {});
+ return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, IDSID, transportType, preferIDS, preferFragmentation, preferAckModel, enabledViews, signingKey, octagonPeerSigningKey, octagonPeerEncryptionKey, error, ^(CFMutableDictionaryRef description) {});
}
-SOSPeerInfoRef SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, SecKeyRef octagonSigningKey, CFErrorRef* error) {
- return SOSPeerInfoCreate_Internal(allocator, gestalt, NULL, NULL, NULL, NULL, NULL, NULL, NULL, signingKey, octagonSigningKey, error, ^(CFMutableDictionaryRef description) {
+SOSPeerInfoRef SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
+ return SOSPeerInfoCreate_Internal(allocator, gestalt, NULL, NULL, NULL, NULL, NULL, NULL, NULL, signingKey, NULL, NULL, error, ^(CFMutableDictionaryRef description) {
CFDictionarySetValue(description, sCloudIdentityKey, kCFBooleanTrue);
});
pi->signature = CFDataCreateCopy(allocator, toCopy->signature);
pi->gestalt = CFDictionaryCreateCopy(allocator, toCopy->gestalt);
- pi->id = CFStringCreateCopy(allocator, toCopy->id);
+ pi->peerID = CFStringCreateCopy(allocator, toCopy->peerID);
pi->version = toCopy->version;
if(!SOSPeerInfoVersionHasV2Data(pi)) SOSPeerInfoExpandV2Data(pi, error);
SOSPeerInfoV2DictionarySetValue(pi, sPingKey, ping);
SecKeyRef pub_key = SOSPeerInfoCopyPubKey(pi, error);
require_quiet(pub_key, exit);
- pi->id = SOSCopyIDOfKey(pub_key, error);
- require_quiet(pi->id, exit);
+ pi->peerID = SOSCopyIDOfKey(pub_key, error);
+ require_quiet(pi->peerID, exit);
require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, CFReleaseNull(pi));
exit:
CFReleaseNull(ping);
CFReleaseNull(pi->description);
CFReleaseNull(pi->signature);
CFReleaseNull(pi->gestalt);
- CFReleaseNull(pi->id);
- if(pi->v2Dictionary) CFReleaseNull(pi->v2Dictionary);
+ CFReleaseNull(pi->peerID);
+ CFReleaseNull(pi->v2Dictionary);
}
static Boolean SOSPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) {
}
CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) {
- return pi ? pi->id : NULL;
+ return pi ? pi->peerID : NULL;
}
bool SOSPeerInfoPeerIDEqual(SOSPeerInfoRef pi, CFStringRef myPeerID) {
});
}
+static SOSPeerInfoRef CF_RETURNS_RETAINED
+SOSPeerInfoSetOctagonKey(CFAllocatorRef allocator,
+ SOSPeerInfoRef toCopy,
+ CFStringRef descriptionKey,
+ SecKeyRef octagonKey,
+ SecKeyRef signingKey,
+ CFErrorRef *error)
+{
+ CFDataRef publicKeyBytes = NULL;
+ SOSPeerInfoRef pi = NULL;
+
+ OSStatus copyResult = SecKeyCopyPublicBytes(octagonKey, &publicKeyBytes);
+ require_action_quiet(0 == copyResult, fail, SecError(copyResult, error, CFSTR("failed to copy public key bytes")));
+
+ pi = SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
+ ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
+ if(peerToModify && peerToModify->description && publicKeyBytes && descriptionKey) {
+ CFDictionarySetValue(peerToModify->description, descriptionKey, publicKeyBytes);
+ } else {
+ SecError(errSecParam, error, CFSTR("Bad key bytes or dictionary key"));
+ }
+ return true;
+ });
+ require(pi, fail);
+
+fail:
+ CFReleaseNull(publicKeyBytes);
+ return pi;
+}
+
+SOSPeerInfoRef CF_RETURNS_RETAINED
+SOSPeerInfoSetOctagonSigningKey(CFAllocatorRef allocator,
+ SOSPeerInfoRef toCopy,
+ SecKeyRef octagonSigningKey,
+ SecKeyRef signingKey,
+ CFErrorRef *error)
+{
+ return SOSPeerInfoSetOctagonKey(allocator, toCopy, sOctagonPeerSigningPublicKeyKey, octagonSigningKey, signingKey, error);
+}
+
+SOSPeerInfoRef CF_RETURNS_RETAINED
+SOSPeerInfoSetOctagonEncryptionKey(CFAllocatorRef allocator,
+ SOSPeerInfoRef toCopy,
+ SecKeyRef octagonEncryptionKey,
+ SecKeyRef signingKey,
+ CFErrorRef *error)
+{
+ return SOSPeerInfoSetOctagonKey(allocator, toCopy, sOctagonPeerEncryptionPublicKeyKey, octagonEncryptionKey, signingKey, error);
+}
+
bool SOSPeerInfoTransportTypeIs(SOSPeerInfoRef pi, CFStringRef transportType) {
return SOSPeerInfoV2DictionaryHasStringValue(pi, sTransportType, transportType);
}
errOut:
return retval;
}
+
+bool SOSPeerInfoHasOctagonSigningPubKey(SOSPeerInfoRef peer){
+ bool hasKey = false;
+ SecKeyRef pubKey = SOSPeerInfoCopyOctagonSigningPublicKey(peer, NULL);
+ if(pubKey)
+ hasKey = true;
+ CFReleaseNull(pubKey);
+ return hasKey;
+}
+
+bool SOSPeerInfoHasOctagonEncryptionPubKey(SOSPeerInfoRef peer){
+ bool hasKey = false;
+ SecKeyRef pubKey = SOSPeerInfoCopyOctagonEncryptionPublicKey(peer, NULL);
+ if(pubKey)
+ hasKey = true;
+ CFReleaseNull(pubKey);
+ return hasKey;
+}
pubKey = SOSPeerInfoCopyPubKey(pi, error);
require_quiet(pubKey, fail);
- pi->id = SOSCopyIDOfKey(pubKey, error);
- require_quiet(pi->id, fail);
+ pi->peerID = SOSCopyIDOfKey(pubKey, error);
+ require_quiet(pi->peerID, fail);
if(pi->version >= 2) SOSPeerInfoExpandV2Data(pi, error);
// Cached data
CFDictionaryRef gestalt;
- CFStringRef id;
+ CFStringRef peerID;
CFIndex version;
/* V2 and beyond are listed below */
CFMutableDictionaryRef v2Dictionary;
#include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
#include <SOSCircle/CKBridge/SOSCloudKeychainConstants.h>
#include <Security/SecureObjectSync/SOSInternal.h>
+#include <utilities/SecADWrapper.h>
#import "Security/SecureObjectSync/SOSAccountTrustClassic.h"
#define IDS "IDS transport"
dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
secnotice("IDS Transport", "Starting");
-
+ SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.sendids"), 1);
+
SOSCloudKeychainSendIDSMessage(messagetoSend, deviceID, ourPeerID, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [transport SOSTransportMessageIDSGetFragmentationPreference:transport], ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
success = (sync_error == NULL);
if (sync_error && error) {
#import <Security/SecureObjectSync/SOSTransportMessageKVS.h>
#include <Security/SecureObjectSync/SOSKVSKeys.h>
#include <utilities/SecCFWrappers.h>
+#include <utilities/SecADWrapper.h>
#include <SOSInternal.h>
#include <AssertMacros.h>
#include <SOSCloudKeychainClient.h>
static bool SOSTransportMessageKVSUpdateKVS(SOSMessageKVS* transport, CFDictionaryRef changes, CFErrorRef *error){
+ SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.sendkvs"), 1);
+
CloudKeychainReplyBlock log_error = ^(CFDictionaryRef returnedValues __unused, CFErrorRef block_error) {
if (block_error) {
secerror("Error putting: %@", block_error);
DOVIEWMACRO(Manatee, "Manatee", "manatee", CKKS, D, , A, , )
DOVIEWMACRO(AutoUnlock, "AutoUnlock", "autounlock", CKKS, D, , A, , )
DOVIEWMACRO(Health, "Health", "health", CKKS, D, , A, , )
+DOVIEWMACRO(ApplePay, "ApplePay", "applepay", CKKS, D, , A, , )
#include <SecurityTool/security_tool_commands.h>
SECURITY_COMMAND(
- "recovery-key", recovery_key,
+ "recovery", recovery_key,
"[options]\n"
- "Recovery Key\n"
- " -s <string turned into recovery key> //Set the recovery key\n"
- " -g //Get the recovery key\n"
- " -c //Clear the recovery key\n"
- " -V //Create Verifier Dictionary (printout)\n",
- "Recovery Key." )
+ " -R //generates and prints a string encoded key seed\n"
+ " -G <string encoded seed> //Generate a recovery key from string encoded seed, but don't register\n"
+ " -s <string encoded seed> //Set the recovery key\n"
+ " -g //Get the recovery key\n"
+ " -c //Clear the recovery key\n"
+ " -V //Create Verifier Dictionary (printout)\n"
+ " -F // prompt cdp to followup to repair the recovery key\n",
+ "Recovery Key Tool" )
static struct option long_options[] =
{
/* These options set a flag. */
+ {"recovery-string", no_argument, NULL, 'R' },
{"generate", required_argument, NULL, 'G'},
{"set", required_argument, NULL, 's'},
{"get", no_argument, NULL, 'g'},
{"clear", no_argument, NULL, 'c'},
{"follow-up", no_argument, NULL, 'F'},
+ {"verifier", no_argument, NULL, 'V'},
{0, 0, 0, 0}
};
int option_index = 0;
if(publicKey == nil)
return 2;
- printmsg(CFSTR("public recovery key: %@\n"), publicKey);
+ printmsg(CFSTR("example (not registered) public recovery key: %@\n"), publicKey);
break;
}
case 'R': {
printmsg(CFSTR("SecRKCreateRecoveryKeyWithError: %@\n"), nserror);
return 2;
}
-
- NSData *publicKey = SecRKCopyBackupPublicKey(rk);
- if(publicKey == nil)
+
+ CFErrorRef cferror = NULL;
+ if(!SecRKRegisterBackupPublicKey(rk, &cferror)) {
+ printmsg(CFSTR("Error from SecRKRegisterBackupPublicKey: %@\n"), cferror);
+ CFReleaseNull(cferror);
return 2;
-
- hadError = SOSCCRegisterRecoveryPublicKey((__bridge CFDataRef)(publicKey), &error) != true;
+ }
break;
}
case 'g':
#else
ok(cmsg = SecCmsMessageCreate(NULL), "create message");
#endif
+ require(cmsg, out);
ok(sigd = SecCmsSignedDataCreate(cmsg), "create signed message");
ok(cinfo = SecCmsMessageGetContentInfo(cmsg), "get content info");
#if TARGET_OS_IPHONE
is(SecCmsSignerInfoCreate(cmsg, signer, SEC_OID_SHA1), NULL, "set up signer with no private key");
#endif
+out:
CFReleaseNull(cert);
CFReleaseNull(publicKey);
CFReleaseNull(privateKey);
}
require_quiet(tbsCert.serialNum.data != NULL &&
tbsCert.serialNum.length >= 1 &&
- tbsCert.serialNum.length <= 36, badCert);
+ tbsCert.serialNum.length <= 37, badCert);
certificate->_serialNumber = CFDataCreate(allocator,
tbsCert.serialNum.data, tbsCert.serialNum.length);
}
static OSStatus appendRDNProperty(void *context, const DERItem *rdnType,
- const DERItem *rdnValue, CFIndex rdnIX) {
- CFMutableArrayRef properties = (CFMutableArrayRef)context;
- if (rdnIX > 0) {
- /* If there is more than one value pair we create a subsection for the
- second pair, and append things to the subsection for subsequent
- pairs. */
- CFIndex lastIX = CFArrayGetCount(properties) - 1;
- CFTypeRef lastValue = CFArrayGetValueAtIndex(properties, lastIX);
- if (rdnIX == 1) {
- /* Since this is the second rdn pair for a given rdn, we setup a
- new subsection for this rdn. We remove the first property
- from the properties array and make it the first element in the
- subsection instead. */
- CFMutableArrayRef rdn_props = CFArrayCreateMutable(
- CFGetAllocator(properties), 0, &kCFTypeArrayCallBacks);
- CFArrayAppendValue(rdn_props, lastValue);
- CFArrayRemoveValueAtIndex(properties, lastIX);
- appendProperty(properties, kSecPropertyTypeSection, NULL, NULL,
+ const DERItem *rdnValue, CFIndex rdnIX) {
+ CFMutableArrayRef properties = (CFMutableArrayRef)context;
+ if (rdnIX > 0) {
+ /* If there is more than one value pair we create a subsection for the
+ second pair, and append things to the subsection for subsequent
+ pairs. */
+ CFIndex lastIX = CFArrayGetCount(properties) - 1;
+ CFTypeRef lastValue = CFArrayGetValueAtIndex(properties, lastIX);
+ if (rdnIX == 1) {
+ /* Since this is the second rdn pair for a given rdn, we setup a
+ new subsection for this rdn. We remove the first property
+ from the properties array and make it the first element in the
+ subsection instead. */
+ CFMutableArrayRef rdn_props = CFArrayCreateMutable(
+ CFGetAllocator(properties), 0, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue(rdn_props, lastValue);
+ CFArrayRemoveValueAtIndex(properties, lastIX);
+ appendProperty(properties, kSecPropertyTypeSection, NULL, NULL,
rdn_props);
- properties = rdn_props;
- } else {
- /* Since this is the third or later rdn pair we have already
- created a subsection in the top level properties array. Instead
- of appending to that directly we append to the array inside the
- subsection. */
- properties = (CFMutableArrayRef)CFDictionaryGetValue(
- (CFDictionaryRef)lastValue, kSecPropertyKeyValue);
- }
- }
+ properties = rdn_props;
+ // rdn_props is now retained by the original properties array
+ CFReleaseSafe(rdn_props);
+ } else {
+ /* Since this is the third or later rdn pair we have already
+ created a subsection in the top level properties array. Instead
+ of appending to that directly we append to the array inside the
+ subsection. */
+ properties = (CFMutableArrayRef)CFDictionaryGetValue(
+ (CFDictionaryRef)lastValue, kSecPropertyKeyValue);
+ }
+ }
- /* Finally we append the new rdn value to the property array. */
- CFStringRef label = SecDERItemCopyOIDDecimalRepresentation(
- CFGetAllocator(properties), rdnType);
- CFStringRef localizedLabel =
- copyLocalizedOidDescription(CFGetAllocator(properties), rdnType);
+ /* Finally we append the new rdn value to the property array. */
+ CFStringRef label = SecDERItemCopyOIDDecimalRepresentation(CFGetAllocator(properties),
+ rdnType);
+ CFStringRef localizedLabel =
+ copyLocalizedOidDescription(CFGetAllocator(properties), rdnType);
appendDERThingProperty(properties, label, localizedLabel, rdnValue);
CFReleaseSafe(label);
CFReleaseSafe(localizedLabel);
return summary;
}
+CFArrayRef SecCertificateCopyLegacyProperties(SecCertificateRef certificate) {
+ /*
+ This function replicates the content returned by SecCertificateCopyProperties
+ prior to 10.12.4, providing stable return values for SecCertificateCopyValues.
+ Unlike SecCertificateCopyProperties, it does not cache the result and
+ assumes the caller will do so.
+ */
+ CFAllocatorRef allocator = CFGetAllocator(certificate);
+ CFMutableArrayRef properties = CFArrayCreateMutable(allocator,
+ 0, &kCFTypeArrayCallBacks);
+
+ /* Subject Name */
+ CFArrayRef subject_plist = createPropertiesForX501NameContent(allocator,
+ &certificate->_subject);
+ appendProperty(properties, kSecPropertyTypeSection, CFSTR("Subject Name"),
+ NULL, subject_plist);
+ CFRelease(subject_plist);
+
+ /* Issuer Name */
+ CFArrayRef issuer_plist = createPropertiesForX501NameContent(allocator,
+ &certificate->_issuer);
+ appendProperty(properties, kSecPropertyTypeSection, CFSTR("Issuer Name"),
+ NULL, issuer_plist);
+ CFRelease(issuer_plist);
+
+ /* Version */
+ CFStringRef versionString = CFStringCreateWithFormat(allocator,
+ NULL, CFSTR("%d"), certificate->_version + 1);
+ appendProperty(properties, kSecPropertyTypeString, CFSTR("Version"),
+ NULL, versionString);
+ CFRelease(versionString);
+
+ /* Serial Number */
+ if (certificate->_serialNum.length) {
+ appendIntegerProperty(properties, CFSTR("Serial Number"),
+ &certificate->_serialNum);
+ }
+
+ /* Signature Algorithm */
+ appendAlgorithmProperty(properties, CFSTR("Signature Algorithm"),
+ &certificate->_tbsSigAlg);
+
+ /* Validity dates */
+ appendDateProperty(properties, CFSTR("Not Valid Before"), certificate->_notBefore);
+ appendDateProperty(properties, CFSTR("Not Valid After"), certificate->_notAfter);
+
+ if (certificate->_subjectUniqueID.length) {
+ appendDataProperty(properties, CFSTR("Subject Unique ID"),
+ NULL, &certificate->_subjectUniqueID);
+ }
+ if (certificate->_issuerUniqueID.length) {
+ appendDataProperty(properties, CFSTR("Issuer Unique ID"),
+ NULL, &certificate->_issuerUniqueID);
+ }
+
+ /* Public Key Algorithm */
+ appendAlgorithmProperty(properties, CFSTR("Public Key Algorithm"),
+ &certificate->_algId);
+
+ /* Public Key Data */
+ appendDataProperty(properties, CFSTR("Public Key Data"),
+ NULL, &certificate->_pubKeyDER);
+
+ /* Signature */
+ appendDataProperty(properties, CFSTR("Signature"),
+ NULL, &certificate->_signature);
+
+ /* Extensions */
+ CFIndex ix;
+ for (ix = 0; ix < certificate->_extensionCount; ++ix) {
+ appendExtension(properties, &certificate->_extensions[ix]);
+ }
+
+ /* Fingerprints */
+ appendFingerprintsProperty(properties, CFSTR("Fingerprints"), certificate);
+
+ return properties;
+}
+
CFArrayRef SecCertificateCopyProperties(SecCertificateRef certificate) {
if (!certificate->_properties) {
CFAllocatorRef allocator = CFGetAllocator(certificate);
/*
- * Copyright (c) 2007-2015 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2007-2016 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
any. */
CFArrayRef SecCertificateGetPermittedSubtrees(SecCertificateRef certificate);
-
/* Returns array of CFDataRefs containing the generalNames that are
Excluded Subtree Name Constraints for this certificate if it has
any. */
/* Return the raw public key data for certificate. */
const DERItem *SecCertificateGetPublicKeyData(SecCertificateRef certificate);
+/* Return legacy property values for use by SecCertificateCopyValues. */
+CFArrayRef SecCertificateCopyLegacyProperties(SecCertificateRef certificate);
+
// MARK: -
// MARK: Certificate Operations
_SecCertificateCopyIssuerSequence
_SecCertificateCopyIssuerSummary
_SecCertificateCopyKeychainItem
+_SecCertificateCopyLegacyProperties
_SecCertificateCopyNormalizedIssuerSequence
_SecCertificateCopyNormalizedSubjectSequence
_SecCertificateCopyNTPrincipalNames
SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator,
SecCertificateRef certificate, SecKeyRef privateKey) {
+ if (!certificate || CFGetTypeID(certificate) != SecCertificateGetTypeID() ||
+ !privateKey || CFGetTypeID(privateKey) != SecKeyGetTypeID()) {
+ return NULL;
+ }
CFIndex size = sizeof(struct __SecIdentity);
SecIdentityRef result = (SecIdentityRef)_CFRuntimeCreateInstance(
allocator, SecIdentityGetTypeID(), size - sizeof(CFRuntimeBase), 0);
CFDataRef _SecItemCreatePersistentRef(CFTypeRef class, sqlite_int64 rowid, CFDictionaryRef attributes)
{
CFDataRef result = NULL;
- if (attributes && CFDictionaryContainsKey(attributes, kSecAttrTokenID)) {
+ if (attributes && CFDictionaryContainsKey(attributes, CFEqual(class, kSecClassIdentity) ? kSecAttrIdentityCertificateTokenID : kSecAttrTokenID)) {
CFDataRef tokenPersistentRef = CreateTokenPersistentRefData(class, attributes);
require(tokenPersistentRef, out);
CFMutableDataRef tmpData = CFDataCreateMutable(kCFAllocatorDefault, sizeof(tk_persistent_ref_id) + CFDataGetLength(tokenPersistentRef));
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks), errOut);
- SecPolicyAddBasicX509Options(options);
+ SecPolicyAddBasicCertOptions(options);
/* Anchored to the Apple Roots */
require_quiet(SecPolicyAddAppleAnchorOptions(options, kSecPolicyNameAppleSoftwareSigning),
}
+// This recreates the key pair using the recovery key string.
static NSData *
-RKBackupCreateECKey(SecRecoveryKey *rk, bool fullkey)
+RKBackupCreateECKey(SecRecoveryKey *rk, bool returnFullkey)
{
- CFMutableDataRef publicKeyData = NULL;
+ CFMutableDataRef keyData = NULL;
CFDataRef derivedSecret = NULL;
ccec_const_cp_t cp = ccec_cp_256();
CFDataRef result = NULL;
fullKey);
require_noerr(status, fail);
- size_t space = ccec_compact_export_size(fullkey, ccec_ctx_pub(fullKey));
- publicKeyData = CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), space);
- require_quiet(publicKeyData, fail);
+ size_t space = ccec_compact_export_size(returnFullkey, ccec_ctx_pub(fullKey));
+ keyData = CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), space);
+ require_quiet(keyData, fail);
- ccec_compact_export(fullkey, CFDataGetMutableBytePtr(publicKeyData), fullKey);
+ ccec_compact_export(returnFullkey, CFDataGetMutableBytePtr(keyData), fullKey);
- CFTransferRetained(result, publicKeyData);
+ CFTransferRetained(result, keyData);
fail:
CFReleaseNull(derivedSecret);
- CFReleaseNull(publicKeyData);
+ CFReleaseNull(keyData);
return (__bridge NSData *)result;
}
CFDataRef backupKey = (__bridge CFDataRef)SecRKCopyBackupPublicKey(rk);
bool res = false;
- require(backupKey, fail);
+ require_action_quiet(backupKey, fail, SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to create key from rk"), NULL, error));
res = SOSCCRegisterRecoveryPublicKey(backupKey, error);
{
if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementRestoreKeychain, &error)) {
CFDataRef recovery_key = SecXPCDictionaryCopyData(event, kSecXPCKeyRecoveryPublicKey, &error);
+ uint8_t zero = 0;
+ CFDataRef nullData = CFDataCreate(kCFAllocatorDefault, &zero, 1); // token we send if we really wanted to send NULL
+ if(CFEqual(recovery_key, nullData)) {
+ CFReleaseNull(recovery_key);
+ }
+ CFReleaseNull(nullData);
xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCRegisterRecoveryPublicKey_Server(recovery_key, &error));
CFReleaseNull(recovery_key);
}
#include <ipc/server_security_helpers.h>
#include <ipc/server_endpoint.h>
+#if OCTAGON
+#include <CloudKit/CloudKit_Private.h>
+// If your callbacks might pass back a CK error, you should use the XPCSanitizeError() spi on all branches at this layer.
+// Otherwise, XPC might crash on the other side if they haven't linked CloudKit.framework.
+#define XPCSanitizeError CKXPCSuitableError
+#else
+// This is a no-op: XPCSanitizeError(error) turns into (error)
+#define XPCSanitizeError
+#endif // OCTAGON
+
#include <Security/SecEntitlements.h>
#include <Security/SecItemPriv.h>
#include <securityd/SecItemServer.h>
- (void) SecItemAddAndNotifyOnSync:(NSDictionary*) attributes
syncCallback:(id<SecuritydXPCCallbackProtocol>) callback
- complete:(void (^) (NSDictionary* opDictResult, NSArray* opArrayResult, NSError* operror)) complete
+ complete:(void (^) (NSDictionary* opDictResult, NSArray* opArrayResult, NSError* operror))xpcComplete
{
+ // The calling client might not handle CK types well. Sanitize!
+ void (^complete)(NSDictionary*, NSArray*, NSError*) = ^(NSDictionary* opDictResult, NSArray* opArrayResult, NSError* operror){
+ xpcComplete(opDictResult, opArrayResult, XPCSanitizeError(operror));
+ };
+
CFErrorRef cferror = NULL;
if([self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementKeychainDeny]) {
SecError(errSecNotAvailable, &cferror, CFSTR("SecItemAddAndNotifyOnSync: %@ has entitlement %@"), _client.task, kSecEntitlementKeychainDeny);
viewHint:(NSString* _Nonnull)viewHint
oldCurrentItemReference:(NSData* _Nullable)oldCurrentItemPersistentRef
oldCurrentItemHash:(NSData* _Nullable)oldItemSHA1
- complete:(void (^) (NSError* _Nullable operror)) complete
+ complete:(void (^) (NSError* _Nullable operror))xpcComplete
{
#if OCTAGON
+ // The calling client might not handle CK types well. Sanitize!
+ void (^complete)(NSError*) = ^(NSError* error){
+ xpcComplete(XPCSanitizeError(error));
+ };
+
__block CFErrorRef cferror = NULL;
if([self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementKeychainDeny]) {
SecError(errSecNotAvailable, &cferror, CFSTR("SecItemSetCurrentItemAcrossAllDevices: %@ has entitlement %@"), _client.task, kSecEntitlementKeychainDeny);
1,
&cferror);
if(cferror) {
- secerror("couldn't create query: %@", cferror);
+ secerror("couldn't create query for new item pref: %@", cferror);
return false;
}
newItem = CFRetainSafe(item);
})) {
query_destroy(q, NULL);
+ secerror("couldn't run query for new item pref: %@", cferror);
return false;
}
if(!query_destroy(q, &cferror)) {
+ secerror("couldn't destroy query for new item pref: %@", cferror);
return false;
};
oldItem = CFRetainSafe(item);
})) {
query_destroy(q, NULL);
+ secerror("couldn't run query for old item pref: %@", cferror);
return false;
}
if(!query_destroy(q, &cferror)) {
+ secerror("couldn't destroy query for old item pref: %@", cferror);
return false;
};
}
}
CFReleaseNull(cferror);
#else // ! OCTAGON
- complete([NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"SecItemSetCurrentItemAcrossAllDevices not implemented on this platform"}]);
+ xpcComplete([NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"SecItemSetCurrentItemAcrossAllDevices not implemented on this platform"}]);
#endif // OCTAGON
}
identifier:(NSString*)identifier
viewHint:(NSString*)viewHint
fetchCloudValue:(bool)fetchCloudValue
- complete:(void (^) (NSData* persistentref, NSError* operror)) complete
+ complete:(void (^) (NSData* persistentref, NSError* operror))xpcComplete
{
#if OCTAGON
+ // The calling client might not handle CK types well. Sanitize!
+ void (^complete)(NSData*, NSError*) = ^(NSData* persistentref, NSError* error){
+ xpcComplete(persistentref, XPCSanitizeError(error));
+ };
+
CFErrorRef cferror = NULL;
if([self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementKeychainDeny]) {
SecError(errSecNotAvailable, &cferror, CFSTR("SecItemFetchCurrentItemAcrossAllDevices: %@ has entitlement %@"), _client.task, kSecEntitlementKeychainDeny);
fetchCloudValue:fetchCloudValue
complete:^(NSString* uuid, NSError* error) {
if(error || !uuid) {
+ secnotice("ckkscurrent", "CKKS didn't find a current item for (%@,%@): %@ %@", accessGroup, identifier, uuid, error);
complete(NULL, error);
return;
}
// Find the persisent ref and return it.
+ secnotice("ckkscurrent", "CKKS believes current item UUID for (%@,%@) is %@. Looking up persistent ref...", accessGroup, identifier, uuid);
[self findItemPersistentRefByUUID:uuid complete:complete];
}];
#else // ! OCTAGON
- complete(NULL, [NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"SecItemFetchCurrentItemAcrossAllDevices not implemented on this platform"}]);
+ xpcComplete(NULL, [NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"SecItemFetchCurrentItemAcrossAllDevices not implemented on this platform"}]);
#endif // OCTAGON
}
-(void)findItemPersistentRefByUUID:(NSString*)uuid
- complete:(void (^) (NSData* persistentref, NSError* operror)) complete
+ complete:(void (^) (NSData* persistentref, NSError* operror))xpcComplete
{
+ // The calling client might not handle CK types well. Sanitize!
+ void (^complete)(NSData*, NSError*) = ^(NSData* persistentref, NSError* error){
+ xpcComplete(persistentref, XPCSanitizeError(error));
+ };
+
CFErrorRef cferror = NULL;
CFTypeRef result = NULL;
// Must query per-class, so:
const SecDbSchema *newSchema = current_schema();
for (const SecDbClass *const *class = newSchema->classes; *class != NULL; class++) {
- CFReleaseNull(result);
- CFReleaseNull(cferror);
-
if(!((*class)->itemclass)) {
//Don't try to search non-item 'classes'
continue;
}
+ // Now that we're in an item class, reset any errSecItemNotFound errors from the last item class
+ CFReleaseNull(result);
+ CFReleaseNull(cferror);
+
_SecItemCopyMatching((__bridge CFDictionaryRef) @{
(__bridge NSString*) kSecClass: (__bridge NSString*) (*class)->name,
(id)kSecAttrSynchronizable: (id)kSecAttrSynchronizableAny,
}
}
+ if(result && !cferror) {
+ secnotice("ckkscurrent", "Found current item for (%@)", uuid);
+ } else {
+ secerror("ckkscurrent: No current item for (%@): %@ %@", uuid, result, cferror);
+ }
+
complete((__bridge NSData*) result, (__bridge NSError*) cferror);
CFReleaseNull(result);
CFReleaseNull(cferror);
}
+static inline bool SOSTestChangeAccountDeviceName(SOSAccount* account, CFStringRef name) {
+ bool retval = false;
+ CFMutableDictionaryRef mygestalt = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, SOSPeerGetGestalt(account.peerInfo));
+ require_quiet(mygestalt, retOut);
+ CFDictionarySetValue(mygestalt, kPIUserDefinedDeviceNameKey, name);
+ retval = [account.trust updateGestalt:account newGestalt:mygestalt];
+retOut:
+ CFReleaseNull(mygestalt);
+ return retval;
+}
+
+/*
+ * this simulates a piggy-back join at the account level
+ */
+
+static inline bool SOSTestJoinThroughPiggyBack(CFDataRef cfpassword, CFStringRef cfaccount, CFMutableDictionaryRef changes, SOSAccount* approver, SOSAccount* joiner, bool dropUserKey, int expectedCount, bool expectCleanup) {
+ // retval will return op failures, not count failures - we'll still report those from in here.
+ bool retval = false;
+ CFErrorRef error = NULL;
+
+ ok(SOSAccountAssertUserCredentialsAndUpdate(approver, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
+ CFReleaseNull(error);
+ // This makes sure the joiner sees the current key parameters
+ ProcessChangesUntilNoChange(changes, approver, joiner, NULL);
+
+ SecKeyRef privKey = SOSAccountGetPrivateCredential(approver, &error);
+ ok(privKey, "got privkey from approver (%@)", error);
+ CFReleaseNull(error);
+
+ ok(SOSAccountTryUserPrivateKey(joiner, privKey, &error), "assert same credentials on joiner (%@)", error);
+ CFReleaseNull(error);
+ // This gives the joiner a chance to see the current circle - this is the account-level equivalent of the Flush added to stashAccountCredential
+ ProcessChangesUntilNoChange(changes, approver, joiner, NULL);
+
+ SOSPeerInfoRef joinerPI = SOSAccountCopyApplication(joiner, &error);
+ ok(joinerPI, "Joiner peerinfo available as application %@", error);
+ CFReleaseNull(error);
+
+ CFDataRef theBlob = SOSAccountCopyCircleJoiningBlob(approver, joinerPI, &error);
+ ok(theBlob, "Made a joining blob (%@)", error);
+ CFReleaseNull(error);
+
+
+ bool joined = SOSAccountJoinWithCircleJoiningBlob(joiner, theBlob, kPiggyV1, &error);
+ ok(joined, "Joiner posted circle with itself in it (%@)", error);
+ CFReleaseNull(error);
+
+ CFReleaseNull(joinerPI);
+ CFReleaseNull(theBlob);
+
+ is(ProcessChangesUntilNoChange(changes, approver, joiner, NULL), 2, "updates");
+
+ ok((retval = [joiner.trust isInCircle:NULL]), "Joiner is in");
+
+ accounts_agree_internal("Successful join shows same circle view", joiner, approver, false);
+ is(countPeers(joiner), expectedCount, "There should be %d valid peers", expectedCount);
+ return retval;
+}
+
+
static inline SOSAccount* SOSTestCreateAccountAsSerialClone(CFStringRef name, SOSPeerInfoDeviceClass devClass, CFStringRef serial, CFStringRef idsID) {
return CreateAccountForLocalChangesWithStartingAttributes(name, CFSTR("TestSource"), devClass, serial, kCFBooleanTrue, kCFBooleanTrue, kCFBooleanTrue, SOSTransportMessageTypeIDSV2, idsID);
}
return retval;
}
-static inline bool SOSTestChangeAccountDeviceName(SOSAccount* account, CFStringRef name) {
- bool retval = false;
- CFMutableDictionaryRef mygestalt = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, SOSPeerGetGestalt(account.peerInfo));
- require_quiet(mygestalt, retOut);
- CFDictionarySetValue(mygestalt, kPIUserDefinedDeviceNameKey, name);
- retval = [account.trust updateGestalt:account newGestalt:mygestalt];
-retOut:
- CFReleaseNull(mygestalt);
- return retval;
-}
-
static inline void SOSTestCleanup() {
SOSUnregisterAllTransportMessages();
SOSUnregisterAllTransportCircles();
#include "SecdTestKeychainUtilities.h"
-static int kTestTestCount = 213;
+static int kTestTestCount = 161;
static bool purgeICloudIdentity(SOSAccount* account) {
bool retval = false;
#include "SOSAccountTesting.h"
#include "SecdTestKeychainUtilities.h"
-static int kTestTestCount = 530;
-
/*
Make a circle with two peers - alice and bob(bob is iOS and serial#"abababababab")
have alice leave the circle
// Make new bob - same as the old bob except peerID
SOSAccount* bobFinal = SOSTestCreateAccountAsSerialClone(CFSTR("BobFinal"), devClass, ghostSerialID, ghostIdsID);
+ is(ProcessChangesUntilNoChange(changes, bobFinal, NULL), 1, "updates");
+
ok(SOSTestJoinWith(cfpassword, cfaccount, changes, bobFinal), "Application Made");
CFReleaseNull(cfpassword);
SOSTestCleanup();
}
-static void multiBob(SOSPeerInfoDeviceClass devClass, bool expectGhostBusted, bool delayedPrivKey) {
+static void multiBob(SOSPeerInfoDeviceClass devClass, bool expectGhostBusted, bool delayedPrivKey, bool pairJoin) {
CFErrorRef error = NULL;
CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
CFStringRef cfaccount = CFSTR("test@test.org");
SOSTestMakeGhostInCircle(CFSTR("Bob4"), devClass, ghostSerialID, ghostIdsID, cfpassword, cfaccount, changes, alice_account, 5);
SOSAccount* bobFinal_account = SOSTestCreateAccountAsSerialClone(CFSTR("BobFinal"), devClass, ghostSerialID, ghostIdsID);
- if(delayedPrivKey) {
+
+ if(pairJoin) {
+ SOSTestJoinThroughPiggyBack(cfpassword, cfaccount, changes, alice_account, bobFinal_account, KEEP_USERKEY, 6, true);
+ is(countPeers(bobFinal_account), 6, "Expect ghosts still in circle");
+ } else if(delayedPrivKey) {
SOSTestJoinWithApproval(cfpassword, cfaccount, changes, alice_account, bobFinal_account, DROP_USERKEY, 6, true);
is(countPeers(bobFinal_account), 6, "Expect ghosts still in circle");
+ } else {
+ SOSTestJoinWithApproval(cfpassword, cfaccount, changes, alice_account, bobFinal_account, KEEP_USERKEY, 2, true);
+ }
+
+ if(pairJoin || delayedPrivKey) { // this allows the ghostbusting to be done in a delayed fashion for the instances where that is proper
SOSAccountTryUserCredentials(bobFinal_account, cfaccount, cfpassword, &error);
ok(SOSTestChangeAccountDeviceName(bobFinal_account, CFSTR("ThereCanBeOnlyOneBob")), "force an unrelated circle change");
is(ProcessChangesUntilNoChange(changes, alice_account, bobFinal_account, NULL), 3, "updates");
- } else {
- SOSTestJoinWithApproval(cfpassword, cfaccount, changes, alice_account, bobFinal_account, KEEP_USERKEY, 2, true);
}
+
CFReleaseNull(cfpassword);
+
+ ok([bobFinal_account.trust isInCircle:NULL], "bobFinal_account is in");
+
is(countPeers(bobFinal_account), 2, "Expect ghostBobs to be gone");
is(countPeers(alice_account), 2, "Expect ghostBobs to be gone");
accounts_agree_internal("Alice and ThereCanBeOnlyOneBob are the only circle peers and they agree", alice_account, bobFinal_account, false);
int secd_68_ghosts(int argc, char *const *argv)
{
- plan_tests(kTestTestCount);
+ plan_tests(573);
secd_test_setup_temp_keychain(__FUNCTION__, NULL);
hauntedCircle(SOSPeerInfo_iOS, true);
hauntedCircle(SOSPeerInfo_macOS, false);
- multiBob(SOSPeerInfo_iOS, true, false);
- multiBob(SOSPeerInfo_iOS, false, true);
-
+ multiBob(SOSPeerInfo_iOS, true, false, false);
+ multiBob(SOSPeerInfo_iOS, false, true, false);
+ multiBob(SOSPeerInfo_iOS, false, false, true); // piggyback join case
+
iosICloudIdentity();
return 0;
XPC_RETURNS_RETAINED xpc_endpoint_t SOSCCCreateSOSEndpoint_server(CFErrorRef *error);
void SOSCCPerformWithOctagonSigningKey(void (^action)(SecKeyRef octagonPrivKey, CFErrorRef error));
+void SOSCCPerformWithOctagonEncryptionKey(void (^action)(SecKeyRef octagonPrivEncryptionKey, CFErrorRef error));
+void SOSCCPerformWithTrustedPeers(void (^action)(CFSetRef sosPeerInfoRefs, CFErrorRef error));
+
void SOSCCResetOTRNegotiation_Server(CFStringRef peerid);
void SOSCCPeerRateLimiterSendNextMessage_Server(CFStringRef peerid, CFStringRef accessGroup);
return [account xpcControlEndpoint];
}
-void SOSCCPerformWithOctagonSigningKey(void (^action)(SecKeyRef octagonPrivKey, CFErrorRef error))
+void SOSCCPerformWithOctagonSigningKey(void (^action)(SecKeyRef octagonPrivSigningKey, CFErrorRef error))
{
CFErrorRef error = NULL;
do_with_account_if_after_first_unlock(&error, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
CFErrorRef errorArg = err ? *err : NULL;
action(signingKey, errorArg);
CFReleaseNull(signingKey);
- return error == NULL;
+ return true;
});
+ CFReleaseNull(error);
}
+
+void SOSCCPerformWithOctagonEncryptionKey(void (^action)(SecKeyRef octagonPrivEncryptionKey, CFErrorRef error))
+{
+ CFErrorRef error = NULL;
+ do_with_account_if_after_first_unlock(&error, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
+ SOSFullPeerInfoRef fpi = txn.account.trust.fullPeerInfo;
+ SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonEncryptionKey(fpi, err);
+ CFErrorRef errorArg = err ? *err : NULL;
+ action(signingKey, errorArg);
+ CFReleaseNull(signingKey);
+ return true;
+ });
+ CFReleaseNull(error);
+}
+
+void SOSCCPerformWithTrustedPeers(void (^action)(CFSetRef sosPeerInfoRefs, CFErrorRef error))
+{
+ CFErrorRef cfAccountError = NULL;
+ do_with_account_if_after_first_unlock(&cfAccountError, ^bool(SOSAccountTransaction *txn, CFErrorRef *cferror) {
+ CFSetRef sosPeerSet = [txn.account.trust copyPeerSetMatching:^bool(SOSPeerInfoRef peer) {
+ return true;
+ }];
+
+ CFErrorRef errorArg = cferror ? *cferror : NULL;
+ action(sosPeerSet, errorArg);
+ CFReleaseNull(sosPeerSet);
+ return true;
+ });
+ CFReleaseNull(cfAccountError);
+}
+
return kSecPathVerifySuccess;
}
+/* Is the the issuer of the last cert a subject of a previous cert in the chain.See <rdar://33136765>. */
+bool SecCertificatePathVCIsCycleInGraph(SecCertificatePathVCRef path) {
+ bool isCircle = false;
+ CFDataRef issuer = SecCertificateGetNormalizedIssuerContent(SecCertificatePathVCGetRoot(path));
+ if (!issuer) { return isCircle; }
+ CFIndex ix = path->count - 2;
+ for (; ix >= 0; ix--) {
+ SecCertificateVCRef cvc = path->certificates[ix];
+ CFDataRef subject = SecCertificateGetNormalizedSubjectContent(cvc->certificate);
+ if (subject && CFEqual(issuer, subject)) {
+ isCircle = true;
+ break;
+ }
+ }
+ return isCircle;
+}
+
bool SecCertificatePathVCIsValid(SecCertificatePathVCRef certificatePath, CFAbsoluteTime verifyTime) {
__block bool result = true;
SecCertificatePathVCForEachCertificate(certificatePath, ^(SecCertificateRef certificate, bool *stop) {
SecPathVerifyStatus SecCertificatePathVCVerify(SecCertificatePathVCRef certificatePath);
+bool SecCertificatePathVCIsCycleInGraph(SecCertificatePathVCRef path);
+
bool SecCertificatePathVCIsValid(SecCertificatePathVCRef certificatePath, CFAbsoluteTime verifyTime);
bool SecCertificatePathVCHasWeakHash(SecCertificatePathVCRef certificatePath);
/*
- * Copyright (c) 2016 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2016-2017 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
static bool SecCAIssuerCertificateSourceCopyParents(
SecCertificateSourceRef source, SecCertificateRef certificate,
void *context, SecCertificateSourceParents callback) {
- return SecCAIssuerCopyParents(certificate, SecPathBuilderGetQueue((SecPathBuilderRef)context), context, callback);
+ /* Some expired certs have dead domains. Let's not check them. */
+ SecPathBuilderRef builder = (SecPathBuilderRef)context;
+ CFAbsoluteTime verifyDate = SecPathBuilderGetVerifyTime(builder);
+ if (SecPathBuilderHasTemporalParentChecks(builder) && !SecCertificateIsValid(certificate, verifyDate)) {
+ secinfo("async", "skipping CAIssuer fetch for expired %@", certificate);
+ callback(context, NULL);
+ return true;
+ }
+ return SecCAIssuerCopyParents(certificate, SecPathBuilderGetQueue(builder), context, callback);
}
static bool SecCAIssuerCertificateSourceContains(
SECDB_ATTR(v10_1itempersistentref,"persistref", UUID, SecDbFlags( ,L,I, , , , , , , , , ,N,U, , ), NULL, NULL);
SECDB_ATTR(v10_1currentItemUUID,"currentItemUUID",String, SecDbFlags(P,L, , , , , , , , , , , , , , ), NULL, NULL);
+SECDB_ATTR(v10_4currentItemUUID,"currentItemUUID",String, SecDbFlags( ,L, , , , , , , , , , , , , , ), NULL, NULL);
SECDB_ATTR(v10_1currentPtrIdentifier,"identifier",String, SecDbFlags(P,L, , , , , , , , , , , , , , ), NULL, NULL);
SECDB_ATTR(v10_2device, "device", String, SecDbFlags(P,L, , , , , , , , , , , , , , ), NULL, NULL);
SECDB_ATTR(v10_2currentClassA,"currentClassA",String, SecDbFlags( ,L, , , , , , , , , , , , , , ), NULL, NULL);
SECDB_ATTR(v10_2currentClassC,"currentClassC",String, SecDbFlags( ,L, , , , , , , , , , , , , , ), NULL, NULL);
+SECDB_ATTR(v10_4lastFixup, "lastfixup", Number, SecDbFlags( ,L, , , , , , , , ,Z, , ,N, , ), NULL, NULL);
+
+SECDB_ATTR(v10_5senderPeerID,"senderpeerid", String, SecDbFlags(P,L, , , , , , , , , , , , , , ), NULL, NULL);
+SECDB_ATTR(v10_5recvPeerID, "recvpeerid", String, SecDbFlags(P,L, , , , , , , , , , , , , , ), NULL, NULL);
+SECDB_ATTR(v10_5recvPubKey, "recvpubenckey", Blob, SecDbFlags( ,L, , , , , , , , , , , , , , ), NULL, NULL);
+SECDB_ATTR(v10_5curve, "curve", Number, SecDbFlags( ,L, , , , , , , , , , , , , , ), NULL, NULL);
+SECDB_ATTR(v10_5poisoned, "poisoned", Number, SecDbFlags( ,L, , , , , , , , ,Z, ,N, , , ), NULL, NULL);
+SECDB_ATTR(v10_5epoch, "epoch", Number, SecDbFlags( ,L, , , , , , , , ,Z, ,N, , , ), NULL, NULL);
+SECDB_ATTR(v10_5signature, "signature", Blob, SecDbFlags( ,L, , , , , , , , , , , , , , ), NULL, NULL);
+SECDB_ATTR(v10_5version, "version", Number, SecDbFlags( ,L, , , , , , , , ,Z, ,N,U, , ), NULL, NULL);
+
+const SecDbClass v10_5_tlkshare_class = {
+ .name = CFSTR("tlkshare"),
+ .itemclass = false,
+ .attrs = {
+ &v10ckzone,
+ &v10syncuuid,
+ &v10_5senderPeerID,
+ &v10_5recvPeerID,
+ &v10_5recvPubKey,
+ &v10_5curve,
+ &v10_5poisoned,
+ &v10_5epoch,
+ &v10wrappedkey,
+ &v10_5signature,
+ &v10_1encRecord,
+ &v10_5version,
+ 0
+ }
+};
+
+
+const SecDbClass v10_4_current_item_class = {
+ .name = CFSTR("currentitems"),
+ .itemclass = false,
+ .attrs = {
+ &v10ckzone,
+ &v10_1currentPtrIdentifier,
+ &v10_4currentItemUUID,
+ &v10state,
+ &v10encodedCKRecord,
+ 0
+ }
+};
+
+const SecDbClass v10_4_ckstate_class = {
+ .name = CFSTR("ckstate"),
+ .itemclass = false,
+ .attrs = {
+ &v10ckzone,
+ &v10ckzonecreated,
+ &v10ckzonesubscribed,
+ &v10lastfetchtime,
+ &v10changetoken,
+ &v10ratelimiter,
+ &v10_4lastFixup,
+ 0
+ }
+};
+
const SecDbClass v10_3_ckdevicestate_class = {
.name = CFSTR("ckdevicestate"),
.itemclass = false,
},
};
+
+/*
+ * Version 10.5
+ */
+const SecDbSchema v10_5_schema = {
+ .majorVersion = 10,
+ .minorVersion = 5,
+ .classes = {
+ &v10_1_genp_class,
+ &v10_1_inet_class,
+ &v10_1_cert_class,
+ &v10_1_keys_class,
+ &v10_0_tversion_class,
+ &v10_2_outgoing_queue_class,
+ &v10_2_incoming_queue_class,
+ &v10_0_sync_key_class,
+ &v10_1_ckmirror_class,
+ &v10_0_current_key_class,
+ &v10_4_ckstate_class,
+ &v10_0_item_backup_class,
+ &v10_0_backup_keybag_class,
+ &v10_2_ckmanifest_class,
+ &v10_2_pending_manifest_class,
+ &v10_1_ckmanifest_leaf_class,
+ &v10_1_backup_keyarchive_class,
+ &v10_1_current_keyarchive_class,
+ &v10_1_current_archived_keys_class,
+ &v10_1_pending_manifest_leaf_class,
+ &v10_4_current_item_class,
+ &v10_3_ckdevicestate_class,
+ &v10_5_tlkshare_class,
+ 0
+ }
+};
+
+
+/*
+ * Version 10.4
+ */
+const SecDbSchema v10_4_schema = {
+ .majorVersion = 10,
+ .minorVersion = 4,
+ .classes = {
+ &v10_1_genp_class,
+ &v10_1_inet_class,
+ &v10_1_cert_class,
+ &v10_1_keys_class,
+ &v10_0_tversion_class,
+ &v10_2_outgoing_queue_class,
+ &v10_2_incoming_queue_class,
+ &v10_0_sync_key_class,
+ &v10_1_ckmirror_class,
+ &v10_0_current_key_class,
+ &v10_4_ckstate_class,
+ &v10_0_item_backup_class,
+ &v10_0_backup_keybag_class,
+ &v10_2_ckmanifest_class,
+ &v10_2_pending_manifest_class,
+ &v10_1_ckmanifest_leaf_class,
+ &v10_1_backup_keyarchive_class,
+ &v10_1_current_keyarchive_class,
+ &v10_1_current_archived_keys_class,
+ &v10_1_pending_manifest_leaf_class,
+ &v10_4_current_item_class,
+ &v10_3_ckdevicestate_class,
+ 0
+ }
+};
+
/*
* Version 10.3
*/
SecDbSchema const * const * kc_schemas = NULL;
const SecDbSchema *v10_kc_schemas[] = {
+ &v10_5_schema,
+ &v10_4_schema,
&v10_3_schema,
&v10_2_schema,
&v10_1_schema,
comma = true;
}
- CFStringAppendFormat(sql, NULL, CFSTR("INSERT INTO %@ (%@) SELECT %@ FROM %@;"), (*newClass)->name, columns, columns, renamedOldClass->name);
+ CFStringAppendFormat(sql, NULL, CFSTR("INSERT OR REPLACE INTO %@ (%@) SELECT %@ FROM %@;"), (*newClass)->name, columns, columns, renamedOldClass->name);
CFReleaseNull(columns);
require_quiet(ok &= SecDbExec(dbt, sql, error), out);
mode_t mode = 0644;
CFStringRef path = CFStringCreateWithCString(NULL, [_dbPath fileSystemRepresentation], kCFStringEncodingUTF8);
- SecDbRef result = SecDbCreateWithOptions(path, mode, readWrite, false, false,
+ SecDbRef result = SecDbCreateWithOptions(path, mode, readWrite, readWrite, false,
^bool (SecDbRef db, SecDbConnectionRef dbconn, bool didCreate, bool *callMeAgainForNextConnection, CFErrorRef *error) {
if (!isDbOwner()) {
/* Non-owner process can't update the db, but it should get a db connection.
zs.avail_out = (uInt)buf_sz;
rc = inflate(&zs, 0);
if (off < (int64_t)zs.total_out) {
- off = write(fd, buf, (int64_t)zs.total_out - off);
+ off = write(fd, buf, (size_t)zs.total_out - (size_t)off);
}
} while (rc == Z_OK);
close(fd);
CFIndex version = 0;
CFIndex interval = 0;
const UInt8* p = CFDataGetBytePtr(updateData);
- CFIndex bytesRemaining = (p) ? CFDataGetLength(updateData) : 0;
+ size_t bytesRemaining = (p) ? (size_t)CFDataGetLength(updateData) : 0;
/* make sure there is enough data to contain length and count */
if (bytesRemaining < ((CFIndex)sizeof(uint32_t) * 2)) {
secinfo("validupdate", "Skipping property list creation (length %ld is too short)", (long)bytesRemaining);
if (semPathBuf) {
struct stat sb;
int fd = open(semPathBuf, O_WRONLY | O_CREAT, DEFFILEMODE);
- if (fd == -1 || fstat(fd, &sb) || close(fd)) {
+ if (fd == -1 || fstat(fd, &sb)) {
secnotice("validupdate", "unable to write %s", semPathBuf);
}
+ if (fd >= 0) {
+ close(fd);
+ }
free(semPathBuf);
}
// exit as gracefully as possible so we can replace the database
});
NSURLSessionDataTask *dataTask = [self.backgroundSession dataTaskWithURL:validUrl];
- dataTask.taskDescription = [NSString stringWithFormat:@"%ld",version];
+ dataTask.taskDescription = [NSString stringWithFormat:@"%lu",(unsigned long)version];
[dataTask resume];
secnotice("validupdate", "scheduled background data task %@ at %f", dataTask, CFAbsoluteTimeGetCurrent());
});
#endif
#define MAX_CHAIN_LENGTH 15
+#define MAX_NUM_CHAINS 100
#define ACCEPT_PATH_SCORE 10000000
/* Forward declaration for use in SecCertificateSource. */
return builder->verifyTime;
}
+bool SecPathBuilderHasTemporalParentChecks(SecPathBuilderRef builder) {
+ __block bool validIntermediates = false;
+ SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool *stop) {
+ CFArrayForEach(pvc->policies, ^(const void *value) {
+ SecPolicyRef policy = (SecPolicyRef)value;
+ if (CFDictionaryContainsKey(policy->_options, kSecPolicyCheckValidIntermediates)) {
+ validIntermediates = true;
+ *stop = true;
+ }
+ });
+ });
+ return validIntermediates;
+}
+
CFIndex SecPathBuilderGetCertificateCount(SecPathBuilderRef builder) {
return SecCertificatePathVCGetCount(builder->path);
}
secdebug("trust", "Adding candidate %@", path);
CFArrayAppendValue(builder->candidatePaths, path);
}
- /* The path is not partial if the last cert is self-signed. */
- if ((SecCertificatePathVCSelfSignedIndex(path) >= 0) &&
- (SecCertificatePathVCSelfSignedIndex(path) == SecCertificatePathVCGetCount(path)-1)) {
+ /* The path is not partial if the last cert is self-signed.
+ * The path is also not partial if the issuer of the last cert was the subject
+ * of a previous cert in the chain, indicating a cycle in the graph. See <rdar://33136765>. */
+ if (((SecCertificatePathVCSelfSignedIndex(path) >= 0) &&
+ (SecCertificatePathVCSelfSignedIndex(path) == SecCertificatePathVCGetCount(path)-1)) ||
+ SecCertificatePathVCIsCycleInGraph(path)) {
+ if (!builder->considerRejected) {
+ secdebug("trust", "Adding non-partial non-anchored reject %@", path);
+ CFArrayAppendValue(builder->rejectedPaths, path);
+ } else {
+ /* This path was previously rejected as unanchored non-partial, but now that
+ * we're considering rejected paths, this is a candidate. */
+ secdebug("trust", "Adding non-partial non-anchored candidate %@", path);
+ CFArrayAppendValue(builder->candidatePaths, path);
+ }
return false;
}
}
directly.
FIXME we might not want to consider partial paths that
are subsets of other partial paths, or not consider them
- at all if we already have an anchored reject. */
+ at all if we already have an (unpreferred) accept or anchored reject */
if (!builder->considerRejected) {
builder->considerRejected = true;
secdebug("trust", "considering rejected paths");
return true;
}
+ /* Don't try to extend partials anymore if we already have too many chains. */
+ if (CFSetGetCount(builder->allPaths) > MAX_NUM_CHAINS) {
+ secnotice("trust", "not building any more paths, already have %" PRIdCFIndex,
+ CFSetGetCount(builder->allPaths));
+ builder->partialIX = -1;
+ return true;
+ }
+
/* Attempt to extend this partial path with another certificate. This
should give us a list of potential parents to consider. */
secdebug("trust", "looking for parents of partial %" PRIdCFIndex "/%" PRIdCFIndex ": %@",
SecTrustResultType result;
};
-/* Completion callback. You should call SecTrustSessionDestroy from this. */
+/* Completion callback. */
typedef void(*SecPathBuilderCompleted)(const void *userData,
SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
SecTrustResultType result);
CFIndex SecPathBuilderGetCertificateCount(SecPathBuilderRef builder);
SecCertificateRef SecPathBuilderGetCertificateAtIndex(SecPathBuilderRef builder, CFIndex ix);
CFArrayRef SecPathBuilderGetExceptions(SecPathBuilderRef builder);
+bool SecPathBuilderHasTemporalParentChecks(SecPathBuilderRef builder);
/* Returns the isAnchored status of the path. The path builder sets isAnchored
* based solely on whether the terminating cert has some sort of trust setting
#include <notify.h>
#include <xpc/private.h>
#include <xpc/xpc.h>
+#include <CoreFoundation/CFStream.h>
#include <Security/SecuritydXPC.h>
#include <Security/SecTrustStore.h>
}
#endif
+static void trustd_cfstream_init() {
+ /* <rdar://problem/33635964> Force legacy CFStream run loop initialization before any NSURLSession usage */
+ CFReadStreamRef rs = CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8*) "", 0, kCFAllocatorNull);
+ CFReadStreamSetDispatchQueue(rs, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
+ CFReadStreamSetDispatchQueue(rs, NULL);
+ CFRelease(rs);
+}
+
int main(int argc, char *argv[])
{
char *wait4debugger = getenv("WAIT4DEBUGGER");
#if TARGET_OS_OSX
SecTrustLegacySourcesListenForKeychainEvents();
#endif
+ trustd_cfstream_init();
trustd_xpc_init(serviceName);
dispatch_main();
return @{};
}
if (status != errSecSuccess) {
- printf("Error reading items to verify: %d\n", status);
+ printf("Error reading items to verify: %d\n", (int)status);
exit(1);
}
NSArray *arr = CFBridgingRelease(result);
- (void)unexpectedError:(OSStatus)status
{
- NSLog(@"Unexpected error %d at step %d", status, self.step);
+ NSLog(@"Unexpected error %d at step %d", (int)status, self.step);
exit(1);
}
NSLog(@"(mark was already there, fine)");
break;
default:
- NSLog(@"Error writing mark %@: %d", name, status);
+ NSLog(@"Error writing mark %@: %d", name, (int)status);
exit(1);
}
}];
NSLog(@"(updated mark was already there, fine)");
break;
default:
- NSLog(@"Error updating mark %@: %d", name, status);
+ NSLog(@"Error updating mark %@: %d", name, (int)status);
exit(1);
}
}];
_SecAbsoluteTimeFromDateContent
-/* Internal securityd RPC stuff for tests */
+/* Internal securityd RPC stuff */
+_CKKSSetupControlProtocol
+#if TARGET_OS_IPHONE || (TARGET_OS_OSX && __x86_64__)
+_OBJC_CLASS_$_CKKSControl
+#endif
_OBJC_CLASS_$_SecuritydXPCClient
_SecAccessGroupsGetCurrent
_SecAccessGroupsSetCurrent
buildPhases = (
);
dependencies = (
- DC71D9FD1D95BB440065FB93 /* PBXTargetDependency */,
DC71D9E11D95BAC40065FB93 /* PBXTargetDependency */,
DC5AC1341D835C2300CF422C /* PBXTargetDependency */,
DC178BF31D77ABE300B50D50 /* PBXTargetDependency */,
BE197F5C1911724900BA91D1 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7FCBE411314471B000DE34E /* UIKit.framework */; };
BE197F5E191173A800BA91D1 /* SWCViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BE197F5D191173A800BA91D1 /* SWCViewController.m */; };
BE197F61191173F200BA91D1 /* entitlements.plist in Resources */ = {isa = PBXBuildFile; fileRef = BE197F60191173F200BA91D1 /* entitlements.plist */; };
+ BE1F74D31F609D460068FA64 /* SecFramework.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78E4F1D8085FC00865A7C /* SecFramework.c */; };
BE22FBC61EE0E8AB00893431 /* Monkey.m in Sources */ = {isa = PBXBuildFile; fileRef = BE22FBC51EE0E8AB00893431 /* Monkey.m */; };
BE22FBCE1EE1E26600893431 /* Keychain.m in Sources */ = {isa = PBXBuildFile; fileRef = BE22FBCD1EE1E26600893431 /* Keychain.m */; };
BE22FBD11EE2084100893431 /* Config.m in Sources */ = {isa = PBXBuildFile; fileRef = BE22FBD01EE2084100893431 /* Config.m */; };
D4D886C01CEB9F7200DC7583 /* ssl-policy-certs in Resources */ = {isa = PBXBuildFile; fileRef = D4D886BE1CEB9F3B00DC7583 /* ssl-policy-certs */; };
D4D886E91CEBDD2A00DC7583 /* nist-certs in Resources */ = {isa = PBXBuildFile; fileRef = D4D886E81CEBDD2A00DC7583 /* nist-certs */; };
D4D886EA1CEBDE0800DC7583 /* nist-certs in Resources */ = {isa = PBXBuildFile; fileRef = D4D886E81CEBDD2A00DC7583 /* nist-certs */; };
+ D4D96ED51F478BAF004B5F01 /* libDER_not_installed.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC59E9EC1D91C9DC001BDDF5 /* libDER_not_installed.a */; };
D4EC94FB1CEA482D0083E753 /* si-20-sectrust-policies-data in Resources */ = {isa = PBXBuildFile; fileRef = D4EC94FA1CEA482D0083E753 /* si-20-sectrust-policies-data */; };
D4EC94FE1CEA48760083E753 /* si-20-sectrust-policies-data in Resources */ = {isa = PBXBuildFile; fileRef = D4EC94FA1CEA482D0083E753 /* si-20-sectrust-policies-data */; };
D4FBBD621DD661A7004408F7 /* CMSEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = D4FBBD601DD66196004408F7 /* CMSEncoder.h */; settings = {ATTRIBUTES = (Private, ); }; };
D4FBBD631DD661AD004408F7 /* CMSDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = D4FBBD611DD66196004408F7 /* CMSDecoder.h */; settings = {ATTRIBUTES = (Private, ); }; };
DA30D6851DF8CA4100EC6B43 /* KeychainSyncAccountUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30D6841DF8CA4100EC6B43 /* KeychainSyncAccountUpdater.m */; };
+ DAD3BD011F9830BB00DF29BA /* CKKSControlProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DCF7A8A21F0450EB00CABE89 /* CKKSControlProtocol.m */; };
+ DAD3BD021F9830BC00DF29BA /* CKKSControlProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DCF7A8A21F0450EB00CABE89 /* CKKSControlProtocol.m */; };
DC0067C11D87879D005AF8DB /* ucspServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC6A82811D87734600418608 /* ucspServer.cpp */; };
DC0067C21D8787A4005AF8DB /* ucspNotifyReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC6A82831D87734600418608 /* ucspNotifyReceiver.cpp */; };
DC0067D11D8788B7005AF8DB /* ucspClientC.c in Sources */ = {isa = PBXBuildFile; fileRef = DC6A82801D87734600418608 /* ucspClientC.c */; };
DC0BCDB51D8C6A5B00070CB0 /* not_on_this_platorm.c in Sources */ = {isa = PBXBuildFile; fileRef = DC0BCDB41D8C6A5B00070CB0 /* not_on_this_platorm.c */; };
DC1002AF1D8E18870025549C /* libsecurity_codesigning.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DCD0677F1D8CDF19007602F1 /* libsecurity_codesigning.a */; };
DC1002D81D8E1A670025549C /* SecTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 107226D10D91DB32003CF14F /* SecTask.h */; };
+ DC14478A1F5764C600236DB4 /* CKKSResultOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1447881F5764C600236DB4 /* CKKSResultOperation.h */; };
+ DC14478B1F5764C600236DB4 /* CKKSResultOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1447881F5764C600236DB4 /* CKKSResultOperation.h */; };
+ DC14478C1F5764C600236DB4 /* CKKSResultOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1447891F5764C600236DB4 /* CKKSResultOperation.m */; };
+ DC14478D1F5764C600236DB4 /* CKKSResultOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1447891F5764C600236DB4 /* CKKSResultOperation.m */; };
+ DC1447961F5766D200236DB4 /* NSOperationCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1447941F5766D200236DB4 /* NSOperationCategories.h */; };
+ DC1447971F5766D200236DB4 /* NSOperationCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1447941F5766D200236DB4 /* NSOperationCategories.h */; };
+ DC1447981F5766D200236DB4 /* NSOperationCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1447951F5766D200236DB4 /* NSOperationCategories.m */; };
+ DC1447991F5766D200236DB4 /* NSOperationCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1447951F5766D200236DB4 /* NSOperationCategories.m */; };
DC15F7661E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC15F7641E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h */; };
DC15F7671E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC15F7641E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h */; };
DC15F7681E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC15F7651E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m */; };
DC378B391DEFADB500A3DAFA /* CKKSZoneStateEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = DC378B371DEFADB500A3DAFA /* CKKSZoneStateEntry.m */; };
DC378B3C1DF0CA7200A3DAFA /* CKKSIncomingQueueEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = DC378B3A1DF0CA7200A3DAFA /* CKKSIncomingQueueEntry.h */; };
DC378B3D1DF0CA7200A3DAFA /* CKKSIncomingQueueEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = DC378B3B1DF0CA7200A3DAFA /* CKKSIncomingQueueEntry.m */; };
- DC3832DA1DB7050900385F63 /* module.modulemap in Headers */ = {isa = PBXBuildFile; fileRef = DC3832C01DB6E69800385F63 /* module.modulemap */; settings = {ATTRIBUTES = (Public, ); }; };
DC3A4B4B1D91E30400E46D4A /* sec_xdr.h in Headers */ = {isa = PBXBuildFile; fileRef = DC6A825A1D87732E00418608 /* sec_xdr.h */; settings = {ATTRIBUTES = (Public, ); }; };
DC3A4B531D91E8EB00E46D4A /* libsecurity_utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DCD06AB01D8E0D53007602F1 /* libsecurity_utilities.a */; };
DC3A4B641D91EADC00E46D4A /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC3A4B621D91EAC500E46D4A /* main.cpp */; };
DC4DB1511E24692100CD6769 /* CKKSKey.h in Headers */ = {isa = PBXBuildFile; fileRef = DC4DB14E1E24692100CD6769 /* CKKSKey.h */; };
DC4DB1521E24692100CD6769 /* CKKSKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4DB14F1E24692100CD6769 /* CKKSKey.m */; };
DC4DB1531E24692100CD6769 /* CKKSKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4DB14F1E24692100CD6769 /* CKKSKey.m */; };
- DC4DB15F1E2590B100CD6769 /* CKKSEncryptionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4DB15E1E2590B100CD6769 /* CKKSEncryptionTests.m */; };
+ DC4DB15F1E2590B100CD6769 /* CKKSAESSIVEncryptionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4DB15E1E2590B100CD6769 /* CKKSAESSIVEncryptionTests.m */; };
DC4DB1691E26E99E00CD6769 /* ProtocolBuffer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C0B0C441E2537CC007F95E5 /* ProtocolBuffer.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
DC4DB16A1E26E9F900CD6769 /* ProtocolBuffer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C0B0C441E2537CC007F95E5 /* ProtocolBuffer.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
DC4EA5961E70A237008840B4 /* libsecurity.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DCC78EA91D8088E200865A7C /* libsecurity.a */; };
DC59EA051D91CA0A001BDDF5 /* DER_Encode.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59E9F41D91CA0A001BDDF5 /* DER_Encode.c */; };
DC59EA0A1D91CA0A001BDDF5 /* DER_Digest.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59E9F91D91CA0A001BDDF5 /* DER_Digest.c */; };
DC59EA0B1D91CA0A001BDDF5 /* oids.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59E9FA1D91CA0A001BDDF5 /* oids.c */; };
- DC59EA2D1D91CA2C001BDDF5 /* libDERUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59EA261D91CA2C001BDDF5 /* libDERUtils.h */; };
- DC59EA2E1D91CA2C001BDDF5 /* libDERUtils.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59EA271D91CA2C001BDDF5 /* libDERUtils.c */; };
- DC59EA2F1D91CA2C001BDDF5 /* fileIo.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59EA281D91CA2C001BDDF5 /* fileIo.c */; };
- DC59EA301D91CA2C001BDDF5 /* fileIo.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59EA291D91CA2C001BDDF5 /* fileIo.h */; };
- DC59EA311D91CA2C001BDDF5 /* printFields.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59EA2A1D91CA2C001BDDF5 /* printFields.h */; };
- DC59EA321D91CA2C001BDDF5 /* printFields.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59EA2B1D91CA2C001BDDF5 /* printFields.c */; };
- DC59EA4E1D91CACE001BDDF5 /* parseCert.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59EA491D91CACE001BDDF5 /* parseCert.c */; };
- DC59EA501D91CAE3001BDDF5 /* libDER_not_installed.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC59E9EC1D91C9DC001BDDF5 /* libDER_not_installed.a */; };
- DC59EA511D91CAE8001BDDF5 /* libDERUtils.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC59EA251D91CA15001BDDF5 /* libDERUtils.a */; };
- DC59EA5A1D91CAF0001BDDF5 /* libDERUtils.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC59EA251D91CA15001BDDF5 /* libDERUtils.a */; };
- DC59EA5B1D91CAF0001BDDF5 /* libDER_not_installed.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC59E9EC1D91C9DC001BDDF5 /* libDER_not_installed.a */; };
- DC59EA611D91CAFD001BDDF5 /* parseCrl.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59EA4A1D91CACE001BDDF5 /* parseCrl.c */; };
- DC59EA6B1D91CB9F001BDDF5 /* libDER_not_installed.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC59E9EC1D91C9DC001BDDF5 /* libDER_not_installed.a */; };
- DC59EA711D91CBB9001BDDF5 /* DER_Ticket.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59EA461D91CACE001BDDF5 /* DER_Ticket.c */; };
- DC59EA721D91CBBD001BDDF5 /* parseTicket.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59EA481D91CACE001BDDF5 /* parseTicket.c */; };
- DC59EA741D91CBD0001BDDF5 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DC59EA731D91CBD0001BDDF5 /* libcrypto.dylib */; };
DC59EA771D91CC6D001BDDF5 /* libDER_not_installed.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC59E9EC1D91C9DC001BDDF5 /* libDER_not_installed.a */; };
DC59EA7B1D91CC9F001BDDF5 /* libDER_not_installed.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC59E9EC1D91C9DC001BDDF5 /* libDER_not_installed.a */; };
DC59EA7E1D91CCB2001BDDF5 /* libDER_not_installed.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC59E9EC1D91C9DC001BDDF5 /* libDER_not_installed.a */; };
DC71D9D81D95BA6C0065FB93 /* secasn1d.c in Sources */ = {isa = PBXBuildFile; fileRef = DC88343A1D8A21AA00CE0ACA /* secasn1d.c */; };
DC71D9D91D95BA6C0065FB93 /* SecAsn1Coder.c in Sources */ = {isa = PBXBuildFile; fileRef = DC88340A1D8A21AA00CE0ACA /* SecAsn1Coder.c */; };
DC71D9DA1D95BA6C0065FB93 /* secasn1u.c in Sources */ = {isa = PBXBuildFile; fileRef = DC88343D1D8A21AA00CE0ACA /* secasn1u.c */; };
- DC71D9E61D95BB0A0065FB93 /* oidsPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59E9FC1D91CA0A001BDDF5 /* oidsPriv.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DC71D9E71D95BB0A0065FB93 /* libDER.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59E9F71D91CA0A001BDDF5 /* libDER.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DC71D9E81D95BB0A0065FB93 /* DER_Decode.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59E9F31D91CA0A001BDDF5 /* DER_Decode.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DC71D9E91D95BB0A0065FB93 /* DER_Keys.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59E9EE1D91CA0A001BDDF5 /* DER_Keys.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DC71D9EA1D95BB0A0065FB93 /* DER_Encode.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59E9F51D91CA0A001BDDF5 /* DER_Encode.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DC71D9EB1D95BB0A0065FB93 /* DER_Digest.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59E9F81D91CA0A001BDDF5 /* DER_Digest.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DC71D9ED1D95BB0A0065FB93 /* asn1Types.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59E9EF1D91CA0A001BDDF5 /* asn1Types.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DC71D9EE1D95BB0A0065FB93 /* libDER_config.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59E9F61D91CA0A001BDDF5 /* libDER_config.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DC71D9EF1D95BB0A0065FB93 /* DER_CertCrl.h in Headers */ = {isa = PBXBuildFile; fileRef = DC59E9F11D91CA0A001BDDF5 /* DER_CertCrl.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DC71D9F11D95BB0A0065FB93 /* DER_Decode.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59E9F21D91CA0A001BDDF5 /* DER_Decode.c */; };
- DC71D9F21D95BB0A0065FB93 /* DER_Encode.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59E9F41D91CA0A001BDDF5 /* DER_Encode.c */; };
- DC71D9F31D95BB0A0065FB93 /* DER_Keys.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59E9ED1D91CA0A001BDDF5 /* DER_Keys.c */; };
- DC71D9F41D95BB0A0065FB93 /* DER_Digest.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59E9F91D91CA0A001BDDF5 /* DER_Digest.c */; };
- DC71D9F51D95BB0A0065FB93 /* oids.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59E9FA1D91CA0A001BDDF5 /* oids.c */; };
- DC71D9F61D95BB0A0065FB93 /* DER_CertCrl.c in Sources */ = {isa = PBXBuildFile; fileRef = DC59E9F01D91CA0A001BDDF5 /* DER_CertCrl.c */; };
+ DC7341F31F8447AB00AB9BDF /* CKKSTLKShare.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7341F11F8447AB00AB9BDF /* CKKSTLKShare.h */; };
+ DC7341F41F8447AB00AB9BDF /* CKKSTLKShare.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7341F11F8447AB00AB9BDF /* CKKSTLKShare.h */; };
+ DC7341F51F8447AB00AB9BDF /* CKKSTLKShare.m in Sources */ = {isa = PBXBuildFile; fileRef = DC7341F21F8447AB00AB9BDF /* CKKSTLKShare.m */; };
+ DC7341F61F8447AB00AB9BDF /* CKKSTLKShare.m in Sources */ = {isa = PBXBuildFile; fileRef = DC7341F21F8447AB00AB9BDF /* CKKSTLKShare.m */; };
+ DC7341FE1F84642C00AB9BDF /* CKKSTLKSharingEncryptionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC7341FD1F84642C00AB9BDF /* CKKSTLKSharingEncryptionTests.m */; };
DC762A9E1E57A86A00B03A2C /* CKKSRecordHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = DC762A9C1E57A86A00B03A2C /* CKKSRecordHolder.h */; };
DC762A9F1E57A86A00B03A2C /* CKKSRecordHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = DC762A9C1E57A86A00B03A2C /* CKKSRecordHolder.h */; };
DC762AA01E57A86A00B03A2C /* CKKSRecordHolder.m in Sources */ = {isa = PBXBuildFile; fileRef = DC762A9D1E57A86A00B03A2C /* CKKSRecordHolder.m */; };
DC963E801D95EBD1008A153E /* libsecurity_apple_csp.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = DCF784F91D88B63800E694BB /* libsecurity_apple_csp.txt */; };
DC963E821D95EC1C008A153E /* libsecurity_codesigning.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = DCD068CB1D8CDFFE007602F1 /* libsecurity_codesigning.plist */; };
DC963E841D95EC31008A153E /* libsecurity_codesigning.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = DCD068CC1D8CDFFE007602F1 /* libsecurity_codesigning.txt */; };
- DC963EC51D95F52C008A153E /* oids.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1785421D778A7400B50D50 /* oids.h */; settings = {ATTRIBUTES = (Public, ); }; };
DC963EC61D95F646008A153E /* der_plist.h in Headers */ = {isa = PBXBuildFile; fileRef = 524492931AFD6D480043695A /* der_plist.h */; };
DC9A2C5F1EB3F557008FAC27 /* CKKSTests+Coalesce.m in Sources */ = {isa = PBXBuildFile; fileRef = DC9A2C5E1EB3F556008FAC27 /* CKKSTests+Coalesce.m */; };
DC9A2C7F1EB40A76008FAC27 /* OCMock.framework in Embed OCMock */ = {isa = PBXBuildFile; fileRef = DC3502E81E02172C00BC0587 /* OCMock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
DC9C75161E4BCE1800F1CA0D /* CKKSOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC9C750F1E4BCC5100F1CA0D /* CKKSOperationTests.m */; };
+ DC9C95971F748D0B000D19E5 /* CKKSServerValidationRecoveryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC9C95951F748D0B000D19E5 /* CKKSServerValidationRecoveryTests.m */; };
+ DC9C95B41F79CFD1000D19E5 /* CKKSControl.h in Headers */ = {isa = PBXBuildFile; fileRef = DC9C95B21F79CFD1000D19E5 /* CKKSControl.h */; };
+ DC9C95B51F79CFD1000D19E5 /* CKKSControl.h in Headers */ = {isa = PBXBuildFile; fileRef = DC9C95B21F79CFD1000D19E5 /* CKKSControl.h */; };
+ DC9C95B61F79CFD1000D19E5 /* CKKSControl.m in Sources */ = {isa = PBXBuildFile; fileRef = DC9C95B31F79CFD1000D19E5 /* CKKSControl.m */; };
+ DC9C95B71F79CFD1000D19E5 /* CKKSControl.m in Sources */ = {isa = PBXBuildFile; fileRef = DC9C95B31F79CFD1000D19E5 /* CKKSControl.m */; };
+ DC9C95BD1F79DC5A000D19E5 /* CKKSControl.h in Headers */ = {isa = PBXBuildFile; fileRef = DC9C95B21F79CFD1000D19E5 /* CKKSControl.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ DC9C95BE1F79DC5F000D19E5 /* CKKSControl.h in Headers */ = {isa = PBXBuildFile; fileRef = DC9C95B21F79CFD1000D19E5 /* CKKSControl.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ DC9C95BF1F79DC88000D19E5 /* CKKSControl.m in Sources */ = {isa = PBXBuildFile; fileRef = DC9C95B31F79CFD1000D19E5 /* CKKSControl.m */; };
+ DC9C95C01F79DC89000D19E5 /* CKKSControl.m in Sources */ = {isa = PBXBuildFile; fileRef = DC9C95B31F79CFD1000D19E5 /* CKKSControl.m */; };
+ DC9C95C11F79DD4B000D19E5 /* CKKSControlProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DCF7A8A21F0450EB00CABE89 /* CKKSControlProtocol.m */; };
+ DC9C95C21F79DD4D000D19E5 /* CKKSControlProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DCF7A8A21F0450EB00CABE89 /* CKKSControlProtocol.m */; };
+ DC9FD3231F8587A500C8AAC8 /* CKKSSerializedKey.proto in Sources */ = {isa = PBXBuildFile; fileRef = DC4D49D81F857728007AF2B8 /* CKKSSerializedKey.proto */; };
+ DC9FD32C1F85990A00C8AAC8 /* CKKSPeer.m in Sources */ = {isa = PBXBuildFile; fileRef = DC9FD3291F8598F300C8AAC8 /* CKKSPeer.m */; };
+ DC9FD32D1F85990B00C8AAC8 /* CKKSPeer.m in Sources */ = {isa = PBXBuildFile; fileRef = DC9FD3291F8598F300C8AAC8 /* CKKSPeer.m */; };
+ DC9FD3361F86A34F00C8AAC8 /* CKKSSerializedKey.proto in Sources */ = {isa = PBXBuildFile; fileRef = DC4D49D81F857728007AF2B8 /* CKKSSerializedKey.proto */; };
DCA4D1FF1E552DD50056214F /* CKKSCurrentKeyPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA4D1F41E5520550056214F /* CKKSCurrentKeyPointer.m */; };
DCA4D2001E552DD50056214F /* CKKSCurrentKeyPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA4D1F41E5520550056214F /* CKKSCurrentKeyPointer.m */; };
DCA4D2151E5684220056214F /* CKKSReencryptOutgoingItemsOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DCA4D2131E5684220056214F /* CKKSReencryptOutgoingItemsOperation.h */; };
DCA85B9A1E8D981100BA7241 /* client_endpoint.m in Sources */ = {isa = PBXBuildFile; fileRef = DC844AEC1E81F315007AAB71 /* client_endpoint.m */; };
DCA85B9B1E8D981200BA7241 /* client_endpoint.m in Sources */ = {isa = PBXBuildFile; fileRef = DC844AEC1E81F315007AAB71 /* client_endpoint.m */; };
DCAB14271E40039600C81511 /* libASN1_not_installed.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8834081D8A218F00CE0ACA /* libASN1_not_installed.a */; };
+ DCAD9B441F8D939C00C5E2AE /* CKKSFixups.h in Headers */ = {isa = PBXBuildFile; fileRef = DCAD9B421F8D939C00C5E2AE /* CKKSFixups.h */; };
+ DCAD9B451F8D939C00C5E2AE /* CKKSFixups.h in Headers */ = {isa = PBXBuildFile; fileRef = DCAD9B421F8D939C00C5E2AE /* CKKSFixups.h */; };
+ DCAD9B461F8D939C00C5E2AE /* CKKSFixups.m in Sources */ = {isa = PBXBuildFile; fileRef = DCAD9B431F8D939C00C5E2AE /* CKKSFixups.m */; };
+ DCAD9B471F8D939C00C5E2AE /* CKKSFixups.m in Sources */ = {isa = PBXBuildFile; fileRef = DCAD9B431F8D939C00C5E2AE /* CKKSFixups.m */; };
+ DCAD9B491F8D95F200C5E2AE /* CloudKitKeychainSyncingFixupTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DCAD9B481F8D95F200C5E2AE /* CloudKitKeychainSyncingFixupTests.m */; };
DCB221501E8B08A5001598BC /* server_xpc.m in Sources */ = {isa = PBXBuildFile; fileRef = DCB2214A1E8B0861001598BC /* server_xpc.m */; };
DCB221511E8B08A6001598BC /* server_xpc.m in Sources */ = {isa = PBXBuildFile; fileRef = DCB2214A1E8B0861001598BC /* server_xpc.m */; };
DCB221531E8B08BC001598BC /* server_xpc.m in Sources */ = {isa = PBXBuildFile; fileRef = DCB2214A1E8B0861001598BC /* server_xpc.m */; };
DCB343851D8A32A20054D16E /* UnlockReferralItem.h in Headers */ = {isa = PBXBuildFile; fileRef = DCB342D21D8A32A20054D16E /* UnlockReferralItem.h */; };
DCB343861D8A32A20054D16E /* TrustSettingsUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DCB342D31D8A32A20054D16E /* TrustSettingsUtils.cpp */; };
DCB343871D8A32A20054D16E /* TrustSettingsUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DCB342D41D8A32A20054D16E /* TrustSettingsUtils.h */; };
- DCB343881D8A32A20054D16E /* SecCertificatePrivP.h in Headers */ = {isa = PBXBuildFile; fileRef = DCB342D51D8A32A20054D16E /* SecCertificatePrivP.h */; settings = {ATTRIBUTES = (Private, ); }; };
- DCB343891D8A32A20054D16E /* SecBase64P.c in Sources */ = {isa = PBXBuildFile; fileRef = DCB342D61D8A32A20054D16E /* SecBase64P.c */; };
- DCB3438A1D8A32A20054D16E /* SecFrameworkP.c in Sources */ = {isa = PBXBuildFile; fileRef = DCB342D71D8A32A20054D16E /* SecFrameworkP.c */; };
- DCB3438B1D8A32A20054D16E /* SecCertificateP.c in Sources */ = {isa = PBXBuildFile; fileRef = DCB342D81D8A32A20054D16E /* SecCertificateP.c */; };
- DCB3438C1D8A32A20054D16E /* SecCertificateP.h in Headers */ = {isa = PBXBuildFile; fileRef = DCB342D91D8A32A20054D16E /* SecCertificateP.h */; settings = {ATTRIBUTES = (Private, ); }; };
- DCB3438D1D8A32A20054D16E /* SecCertificateInternalP.h in Headers */ = {isa = PBXBuildFile; fileRef = DCB342DA1D8A32A20054D16E /* SecCertificateInternalP.h */; };
DCB3438E1D8A32A20054D16E /* tsaDERUtilities.c in Sources */ = {isa = PBXBuildFile; fileRef = DCB342DC1D8A32A20054D16E /* tsaDERUtilities.c */; };
DCB3438F1D8A32A20054D16E /* tsaDERUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = DCB342DD1D8A32A20054D16E /* tsaDERUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; };
DCB343901D8A32A20054D16E /* TokenLogin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DCB342DE1D8A32A20054D16E /* TokenLogin.cpp */; };
DCBDB3BC1E57CA7A00B61300 /* CKKSViewManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DCBDB3B91E57CA7A00B61300 /* CKKSViewManager.h */; };
DCBDB3BD1E57CA7A00B61300 /* CKKSViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DCBDB3BA1E57CA7A00B61300 /* CKKSViewManager.m */; };
DCBDB3BE1E57CA7A00B61300 /* CKKSViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DCBDB3BA1E57CA7A00B61300 /* CKKSViewManager.m */; };
+ DCBF2F7D1F90084D00ED0CA4 /* CKKSTLKSharingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DCBF2F7C1F90084D00ED0CA4 /* CKKSTLKSharingTests.m */; };
+ DCBF2F851F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DCBF2F831F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.h */; };
+ DCBF2F861F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DCBF2F831F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.h */; };
+ DCBF2F871F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DCBF2F841F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.m */; };
+ DCBF2F881F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DCBF2F841F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.m */; };
DCC093791D80B02100F984E4 /* SecOnOSX.h in Headers */ = {isa = PBXBuildFile; fileRef = DCC78E671D8085FC00865A7C /* SecOnOSX.h */; };
DCC0937A1D80B07200F984E4 /* SecOTRSessionPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AF7FFFC15AFB73800B9D400 /* SecOTRSessionPriv.h */; };
DCC0937B1D80B07B00F984E4 /* SecOTRSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AF7FFFB15AFB73800B9D400 /* SecOTRSession.h */; };
DCF789481D88D17C00E694BB /* AppleX509TPBuiltin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DCF789471D88D17C00E694BB /* AppleX509TPBuiltin.cpp */; };
DCF7A8A01F04502400CABE89 /* CKKSControlProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF7A89F1F04502300CABE89 /* CKKSControlProtocol.h */; };
DCF7A8A11F04502400CABE89 /* CKKSControlProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF7A89F1F04502300CABE89 /* CKKSControlProtocol.h */; };
- DCF7A8A31F0450EB00CABE89 /* CKKSControlProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DCF7A8A21F0450EB00CABE89 /* CKKSControlProtocol.m */; };
- DCF7A8A41F0450EB00CABE89 /* CKKSControlProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DCF7A8A21F0450EB00CABE89 /* CKKSControlProtocol.m */; };
DCF7A8A51F0451AC00CABE89 /* CKKSControlProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DCF7A8A21F0450EB00CABE89 /* CKKSControlProtocol.m */; };
DCFAEDCF1D999859005187E4 /* SOSAccountGhost.m in Sources */ = {isa = PBXBuildFile; fileRef = DCFAEDC81D999851005187E4 /* SOSAccountGhost.m */; };
DCFAEDD21D99991F005187E4 /* secd-668-ghosts.m in Sources */ = {isa = PBXBuildFile; fileRef = DCFAEDD11D9998DD005187E4 /* secd-668-ghosts.m */; };
F667EC611E96E9E700203D5C /* authdtests.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A0971F1E953ABD00B1E7D6 /* authdtests.m */; };
F667EC621E96EAD200203D5C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F667EC551E96E94800203D5C /* main.m */; };
F667EC631E96EDC500203D5C /* libregressionBase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC0BCBFD1D8C648C00070CB0 /* libregressionBase.a */; };
+ F682C1D41F4486F700F1B029 /* libctkloginhelper.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F682C1CE1F4486F600F1B029 /* libctkloginhelper.a */; };
F6AF96681E646CAF00917214 /* libcoreauthd_client.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4432AF6A1A01458F000958DC /* libcoreauthd_client.a */; };
F93C493B1AB8FF530047E01A /* ckcdiagnose.sh in CopyFiles */ = {isa = PBXBuildFile; fileRef = F93C493A1AB8FF530047E01A /* ckcdiagnose.sh */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
F964772C1E5832540019E4EB /* SecCodePriv.h in Headers */ = {isa = PBXBuildFile; fileRef = DCD0678E1D8CDF7E007602F1 /* SecCodePriv.h */; settings = {ATTRIBUTES = (Private, ); }; };
);
script = "#!/bin/sh\n\nfor file in ${HEADER_SEARCH_PATHS[@]} ; do\nHEADER_SEARCH_OPTIONS=\"${HEADER_SEARCH_OPTIONS} -I${file}\"\ndone\n\nxcrun clang -E -Xpreprocessor -P -x c -arch ${CURRENT_ARCH} ${HEADER_SEARCH_OPTIONS} ${OTHER_INPUT_FILE_FLAGS} ${INPUT_FILE_PATH} -o ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.${CURRENT_ARCH}.exp\n";
};
+ DC9FD3201F85818000C8AAC8 /* PBXBuildRule */ = {
+ isa = PBXBuildRule;
+ compilerSpec = com.apple.compilers.proxy.script;
+ filePatterns = "*.proto";
+ fileType = pattern.proxy;
+ isEditable = 1;
+ outputFiles = (
+ "$(INPUT_FILE_DIR)/source/${INPUT_FILE_BASE}.h",
+ "$(INPUT_FILE_DIR)/source/${INPUT_FILE_BASE}.m",
+ );
+ script = "set -x\n\nmkdir -p ${INPUT_FILE_DIR}/source\nprotocompiler --arc --strict --emitDeprecated=NO --generics=YES --outputDir ${INPUT_FILE_DIR}/source --proto ${INPUT_FILE_DIR}/${INPUT_FILE_NAME}\n";
+ };
+ DC9FD3221F85877000C8AAC8 /* PBXBuildRule */ = {
+ isa = PBXBuildRule;
+ compilerSpec = com.apple.compilers.proxy.script;
+ filePatterns = "*.proto";
+ fileType = pattern.proxy;
+ isEditable = 1;
+ outputFiles = (
+ "$(INPUT_FILE_DIR)/source/${INPUT_FILE_BASE}.h",
+ "$(INPUT_FILE_DIR)/source/${INPUT_FILE_BASE}.m",
+ );
+ script = "set -x\n\nmkdir -p ${INPUT_FILE_DIR}/source\nprotocompiler --arc --strict --emitDeprecated=NO --generics=YES --outputDir ${INPUT_FILE_DIR}/source --proto ${INPUT_FILE_DIR}/${INPUT_FILE_NAME}\n";
+ };
E7B006FF170B56E700B27966 /* PBXBuildRule */ = {
isa = PBXBuildRule;
compilerSpec = com.apple.compilers.proxy.script;
remoteGlobalIDString = 79DC33610D4E6EEA0039E4BC;
remoteInfo = libCMS;
};
- DC59EA401D91CAAA001BDDF5 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = DC59E9AC1D91C9DC001BDDF5;
- remoteInfo = DER;
- };
- DC59EA421D91CAAE001BDDF5 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = DC59EA0E1D91CA15001BDDF5;
- remoteInfo = DERUtils;
- };
- DC59EA541D91CAF0001BDDF5 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = DC59EA0E1D91CA15001BDDF5;
- remoteInfo = DERUtils;
- };
- DC59EA561D91CAF0001BDDF5 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = DC59E9AC1D91C9DC001BDDF5;
- remoteInfo = DER;
- };
- DC59EA661D91CB9F001BDDF5 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = DC59E9AC1D91C9DC001BDDF5;
- remoteInfo = DER;
- };
DC59EA751D91CC5E001BDDF5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
remoteGlobalIDString = DC71D99F1D95BA6C0065FB93;
remoteInfo = ASN1;
};
- DC71D9FC1D95BB440065FB93 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = DC71D9E41D95BB0A0065FB93;
- remoteInfo = DER;
- };
DC71DA021D95BDEA0065FB93 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
);
runOnlyForDeploymentPostprocessing = 1;
};
- DC59EA351D91CA82001BDDF5 /* CopyFiles */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 2147483647;
- dstPath = /usr/share/man/man1/;
- dstSubfolderSpec = 0;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 1;
- };
- DC59EA5C1D91CAF0001BDDF5 /* CopyFiles */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 2147483647;
- dstPath = /usr/share/man/man1/;
- dstSubfolderSpec = 0;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 1;
- };
- DC59EA6C1D91CB9F001BDDF5 /* CopyFiles */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 2147483647;
- dstPath = /usr/share/man/man1/;
- dstSubfolderSpec = 0;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 1;
- };
DC5ABDC31D832DAB00CF422C /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
DC0BCD551D8C697100070CB0 /* su-40-secdb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "su-40-secdb.c"; sourceTree = "<group>"; };
DC0BCD561D8C697100070CB0 /* su-41-secdb-stress.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "su-41-secdb-stress.c"; sourceTree = "<group>"; };
DC0BCDB41D8C6A5B00070CB0 /* not_on_this_platorm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = not_on_this_platorm.c; sourceTree = "<group>"; };
+ DC1447881F5764C600236DB4 /* CKKSResultOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSResultOperation.h; sourceTree = "<group>"; };
+ DC1447891F5764C600236DB4 /* CKKSResultOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSResultOperation.m; sourceTree = "<group>"; };
+ DC1447941F5766D200236DB4 /* NSOperationCategories.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSOperationCategories.h; sourceTree = "<group>"; };
+ DC1447951F5766D200236DB4 /* NSOperationCategories.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSOperationCategories.m; sourceTree = "<group>"; };
DC15F7641E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKKSHealKeyHierarchyOperation.h; sourceTree = "<group>"; };
DC15F7651E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSHealKeyHierarchyOperation.m; sourceTree = "<group>"; };
DC15F79B1E68EAD5003B9A40 /* CKKSTests+API.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CKKSTests+API.m"; sourceTree = "<group>"; };
DC4269031E82EDAC002B7110 /* SecItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecItem.m; sourceTree = "<group>"; };
DC4269061E82FBDF002B7110 /* server_security_helpers.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = server_security_helpers.c; sourceTree = "<group>"; };
DC4269071E82FBDF002B7110 /* server_security_helpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = server_security_helpers.h; sourceTree = "<group>"; };
+ DC4D49D81F857728007AF2B8 /* CKKSSerializedKey.proto */ = {isa = PBXFileReference; lastKnownFileType = text; path = CKKSSerializedKey.proto; sourceTree = "<group>"; };
DC4DB14E1E24692100CD6769 /* CKKSKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKKSKey.h; sourceTree = "<group>"; };
DC4DB14F1E24692100CD6769 /* CKKSKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSKey.m; sourceTree = "<group>"; };
- DC4DB15E1E2590B100CD6769 /* CKKSEncryptionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSEncryptionTests.m; sourceTree = "<group>"; };
+ DC4DB15E1E2590B100CD6769 /* CKKSAESSIVEncryptionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSAESSIVEncryptionTests.m; sourceTree = "<group>"; };
DC5225091E402D8B0021640A /* PlatformLibraries.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = PlatformLibraries.xcconfig; path = xcconfig/PlatformLibraries.xcconfig; sourceTree = "<group>"; };
DC52E7C21D80BC8000B0A59C /* libsecurityd_ios.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libsecurityd_ios.a; sourceTree = BUILT_PRODUCTS_DIR; };
DC52E8C61D80C25800B0A59C /* libSecureObjectSyncServer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libSecureObjectSyncServer.a; sourceTree = BUILT_PRODUCTS_DIR; };
DC59E9F91D91CA0A001BDDF5 /* DER_Digest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DER_Digest.c; sourceTree = "<group>"; };
DC59E9FA1D91CA0A001BDDF5 /* oids.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = oids.c; sourceTree = "<group>"; };
DC59E9FC1D91CA0A001BDDF5 /* oidsPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = oidsPriv.h; sourceTree = "<group>"; };
- DC59EA251D91CA15001BDDF5 /* libDERUtils.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libDERUtils.a; sourceTree = BUILT_PRODUCTS_DIR; };
- DC59EA261D91CA2C001BDDF5 /* libDERUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libDERUtils.h; sourceTree = "<group>"; };
- DC59EA271D91CA2C001BDDF5 /* libDERUtils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libDERUtils.c; sourceTree = "<group>"; };
- DC59EA281D91CA2C001BDDF5 /* fileIo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fileIo.c; sourceTree = "<group>"; };
- DC59EA291D91CA2C001BDDF5 /* fileIo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fileIo.h; sourceTree = "<group>"; };
- DC59EA2A1D91CA2C001BDDF5 /* printFields.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = printFields.h; sourceTree = "<group>"; };
- DC59EA2B1D91CA2C001BDDF5 /* printFields.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = printFields.c; sourceTree = "<group>"; };
- DC59EA371D91CA82001BDDF5 /* parseCert */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = parseCert; sourceTree = BUILT_PRODUCTS_DIR; };
- DC59EA451D91CACE001BDDF5 /* AppleMobilePersonalizedTicket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleMobilePersonalizedTicket.h; sourceTree = "<group>"; };
- DC59EA461D91CACE001BDDF5 /* DER_Ticket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DER_Ticket.c; sourceTree = "<group>"; };
- DC59EA471D91CACE001BDDF5 /* DER_Ticket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DER_Ticket.h; sourceTree = "<group>"; };
- DC59EA481D91CACE001BDDF5 /* parseTicket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parseTicket.c; sourceTree = "<group>"; };
- DC59EA491D91CACE001BDDF5 /* parseCert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parseCert.c; sourceTree = "<group>"; };
- DC59EA4A1D91CACE001BDDF5 /* parseCrl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parseCrl.c; sourceTree = "<group>"; };
- DC59EA601D91CAF0001BDDF5 /* parseCrl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = parseCrl; sourceTree = BUILT_PRODUCTS_DIR; };
- DC59EA701D91CB9F001BDDF5 /* parseTicket */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = parseTicket; sourceTree = BUILT_PRODUCTS_DIR; };
DC59EA731D91CBD0001BDDF5 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
DC5ABD781D832D5800CF422C /* srCdsaUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = srCdsaUtils.cpp; sourceTree = "<group>"; };
DC5ABD791D832D5800CF422C /* srCdsaUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = srCdsaUtils.h; sourceTree = "<group>"; };
DC6ACC401E81DF9400125DC5 /* server_endpoint.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = server_endpoint.m; sourceTree = "<group>"; };
DC71D8DD1D94CF3C0065FB93 /* lib_ios_shim.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = lib_ios_shim.xcconfig; path = xcconfig/lib_ios_shim.xcconfig; sourceTree = "<group>"; };
DC71D9DF1D95BA6C0065FB93 /* libASN1.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libASN1.a; sourceTree = BUILT_PRODUCTS_DIR; };
- DC71D9FB1D95BB0A0065FB93 /* libDER.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libDER.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ DC7341F11F8447AB00AB9BDF /* CKKSTLKShare.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSTLKShare.h; sourceTree = "<group>"; };
+ DC7341F21F8447AB00AB9BDF /* CKKSTLKShare.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSTLKShare.m; sourceTree = "<group>"; };
+ DC7341FD1F84642C00AB9BDF /* CKKSTLKSharingEncryptionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSTLKSharingEncryptionTests.m; sourceTree = "<group>"; };
DC762A9C1E57A86A00B03A2C /* CKKSRecordHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKKSRecordHolder.h; sourceTree = "<group>"; };
DC762A9D1E57A86A00B03A2C /* CKKSRecordHolder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSRecordHolder.m; sourceTree = "<group>"; };
DC797E131DD3F88300CC9E42 /* CKKSSQLDatabaseObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSSQLDatabaseObject.m; sourceTree = "<group>"; };
DC9B7AE41DCBF604004E9385 /* CKKSOutgoingQueueEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSOutgoingQueueEntry.m; sourceTree = "<group>"; };
DC9B7AE61DCBF651004E9385 /* CKKSOutgoingQueueEntry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSOutgoingQueueEntry.h; sourceTree = "<group>"; };
DC9C750F1E4BCC5100F1CA0D /* CKKSOperationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSOperationTests.m; sourceTree = "<group>"; };
+ DC9C95951F748D0B000D19E5 /* CKKSServerValidationRecoveryTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSServerValidationRecoveryTests.m; sourceTree = "<group>"; };
+ DC9C95B21F79CFD1000D19E5 /* CKKSControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSControl.h; sourceTree = "<group>"; };
+ DC9C95B31F79CFD1000D19E5 /* CKKSControl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSControl.m; sourceTree = "<group>"; };
DC9EBA231DEE36FE00D0F733 /* ApplePushService.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplePushService.framework; path = System/Library/PrivateFrameworks/ApplePushService.framework; sourceTree = SDKROOT; };
DC9EBA2F1DEE651500D0F733 /* Info-macOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-macOS.plist"; sourceTree = "<group>"; };
DC9EBA311DEE768000D0F733 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
+ DC9FD3261F858D3E00C8AAC8 /* CKKSSerializedKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSSerializedKey.m; sourceTree = "<group>"; };
+ DC9FD3271F858D3F00C8AAC8 /* CKKSSerializedKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKKSSerializedKey.h; sourceTree = "<group>"; };
+ DC9FD3281F8598F300C8AAC8 /* CKKSPeer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSPeer.h; sourceTree = "<group>"; };
+ DC9FD3291F8598F300C8AAC8 /* CKKSPeer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSPeer.m; sourceTree = "<group>"; };
DCA4D1F31E5520550056214F /* CKKSCurrentKeyPointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKKSCurrentKeyPointer.h; sourceTree = "<group>"; };
DCA4D1F41E5520550056214F /* CKKSCurrentKeyPointer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSCurrentKeyPointer.m; sourceTree = "<group>"; };
DCA4D2131E5684220056214F /* CKKSReencryptOutgoingItemsOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKKSReencryptOutgoingItemsOperation.h; sourceTree = "<group>"; };
DCA4D2141E5684220056214F /* CKKSReencryptOutgoingItemsOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSReencryptOutgoingItemsOperation.m; sourceTree = "<group>"; };
+ DCAD9B421F8D939C00C5E2AE /* CKKSFixups.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSFixups.h; sourceTree = "<group>"; };
+ DCAD9B431F8D939C00C5E2AE /* CKKSFixups.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSFixups.m; sourceTree = "<group>"; };
+ DCAD9B481F8D95F200C5E2AE /* CloudKitKeychainSyncingFixupTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CloudKitKeychainSyncingFixupTests.m; sourceTree = "<group>"; };
DCB2214A1E8B0861001598BC /* server_xpc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = server_xpc.m; sourceTree = "<group>"; };
DCB2215B1E8B098D001598BC /* server_endpoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = server_endpoint.h; sourceTree = "<group>"; };
DCB3406D1D8A24DF0054D16E /* libsecurity_authorization.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libsecurity_authorization.a; sourceTree = BUILT_PRODUCTS_DIR; };
DCB342D21D8A32A20054D16E /* UnlockReferralItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnlockReferralItem.h; sourceTree = "<group>"; };
DCB342D31D8A32A20054D16E /* TrustSettingsUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrustSettingsUtils.cpp; sourceTree = "<group>"; };
DCB342D41D8A32A20054D16E /* TrustSettingsUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrustSettingsUtils.h; sourceTree = "<group>"; };
- DCB342D51D8A32A20054D16E /* SecCertificatePrivP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecCertificatePrivP.h; sourceTree = "<group>"; };
- DCB342D61D8A32A20054D16E /* SecBase64P.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SecBase64P.c; sourceTree = "<group>"; };
- DCB342D71D8A32A20054D16E /* SecFrameworkP.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SecFrameworkP.c; sourceTree = "<group>"; };
- DCB342D81D8A32A20054D16E /* SecCertificateP.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = SecCertificateP.c; sourceTree = "<group>"; };
- DCB342D91D8A32A20054D16E /* SecCertificateP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecCertificateP.h; sourceTree = "<group>"; };
- DCB342DA1D8A32A20054D16E /* SecCertificateInternalP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecCertificateInternalP.h; sourceTree = "<group>"; };
DCB342DB1D8A32A20054D16E /* generateErrStrings.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = generateErrStrings.pl; sourceTree = "<group>"; };
DCB342DC1D8A32A20054D16E /* tsaDERUtilities.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tsaDERUtilities.c; sourceTree = "<group>"; };
DCB342DD1D8A32A20054D16E /* tsaDERUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tsaDERUtilities.h; sourceTree = "<group>"; };
DCBDB3B11E57C67500B61300 /* CKKSKeychainView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSKeychainView.m; sourceTree = "<group>"; };
DCBDB3B91E57CA7A00B61300 /* CKKSViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKKSViewManager.h; sourceTree = "<group>"; };
DCBDB3BA1E57CA7A00B61300 /* CKKSViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSViewManager.m; sourceTree = "<group>"; };
+ DCBF2F7C1F90084D00ED0CA4 /* CKKSTLKSharingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSTLKSharingTests.m; sourceTree = "<group>"; };
+ DCBF2F831F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSHealTLKSharesOperation.h; sourceTree = "<group>"; };
+ DCBF2F841F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSHealTLKSharesOperation.m; sourceTree = "<group>"; };
DCC0800D1CFF7903005C35C8 /* CSSMOID.exp-in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "CSSMOID.exp-in"; sourceTree = "<group>"; };
DCC78C371D8085D800865A7C /* ios6_1_keychain_2_db.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ios6_1_keychain_2_db.h; sourceTree = "<group>"; };
DCC78C381D8085D800865A7C /* ios8-inet-keychain-2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ios8-inet-keychain-2.h"; sourceTree = "<group>"; };
EB10557A1E14DF640003C309 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
EB108F121E6CE48B003B0456 /* KCParing.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = KCParing.plist; path = Tests/KCParing.plist; sourceTree = "<group>"; };
EB108F411E6CE4D2003B0456 /* KCPairingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KCPairingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
- EB27FF0B1E402C8000EC9E3A /* ckksctl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ckksctl.h; sourceTree = "<group>"; };
EB27FF0C1E402C8000EC9E3A /* ckksctl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ckksctl.m; sourceTree = "<group>"; };
EB27FF111E402CD300EC9E3A /* ckksctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ckksctl; sourceTree = BUILT_PRODUCTS_DIR; };
EB27FF2F1E408CC900EC9E3A /* ckksctl-Entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "ckksctl-Entitlements.plist"; sourceTree = "<group>"; };
EBF3747F1DC057FE0065D840 /* security-sysdiagnose.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = "security-sysdiagnose.1"; sourceTree = "<group>"; };
EBF3749A1DC064200065D840 /* SecADWrapper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = SecADWrapper.c; path = src/SecADWrapper.c; sourceTree = "<group>"; };
EBF3749B1DC064200065D840 /* SecADWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SecADWrapper.h; path = src/SecADWrapper.h; sourceTree = "<group>"; };
+ EBF9AE171F536D0300FECBF7 /* Version.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Version.xcconfig; path = xcconfig/Version.xcconfig; sourceTree = "<group>"; };
F619D71D1ED70BB0005B5F46 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = OSX/authorizationdump/main.m; sourceTree = "<group>"; };
F621D07F1ED6DCE7000EA569 /* authorizationdump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = authorizationdump; sourceTree = BUILT_PRODUCTS_DIR; };
F667EC551E96E94800203D5C /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = OSX/authd/tests/main.m; sourceTree = "<group>"; };
F667EC601E96E9B100203D5C /* authdtest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = authdtest; sourceTree = BUILT_PRODUCTS_DIR; };
+ F682C1CE1F4486F600F1B029 /* libctkloginhelper.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libctkloginhelper.a; path = usr/local/lib/libctkloginhelper.a; sourceTree = SDKROOT; };
F6A0971E1E953A1500B1E7D6 /* authdtestlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = authdtestlist.h; path = OSX/authd/tests/authdtestlist.h; sourceTree = "<group>"; };
F6A0971F1E953ABD00B1E7D6 /* authdtests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = authdtests.m; path = OSX/authd/tests/authdtests.m; sourceTree = "<group>"; };
F6A3CB0D1E7062BA00E7821F /* authd-Entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "authd-Entitlements.plist"; path = "OSX/authd/authd-Entitlements.plist"; sourceTree = "<group>"; };
files = (
CD9F2AFB1DF24BAF00AD3577 /* Foundation.framework in Frameworks */,
DCD22D4B1D8CBF54001C9B81 /* libASN1_not_installed.a in Frameworks */,
+ D4D96ED51F478BAF004B5F01 /* libDER_not_installed.a in Frameworks */,
DC00AB6F1D821C3400513D74 /* libSecItemShimOSX.a in Frameworks */,
DC00AB701D821C3800513D74 /* libSecOtrOSX.a in Frameworks */,
DC00AB6B1D821C1A00513D74 /* libSecTrustOSX.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
- DC59EA211D91CA15001BDDF5 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- DC59EA341D91CA82001BDDF5 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DC59EA511D91CAE8001BDDF5 /* libDERUtils.a in Frameworks */,
- DC59EA501D91CAE3001BDDF5 /* libDER_not_installed.a in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- DC59EA591D91CAF0001BDDF5 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DC59EA5A1D91CAF0001BDDF5 /* libDERUtils.a in Frameworks */,
- DC59EA5B1D91CAF0001BDDF5 /* libDER_not_installed.a in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- DC59EA691D91CB9F001BDDF5 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DC59EA741D91CBD0001BDDF5 /* libcrypto.dylib in Frameworks */,
- DC59EA6B1D91CB9F001BDDF5 /* libDER_not_installed.a in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
DC5ABDC21D832DAB00CF422C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
- DC71D9F71D95BB0A0065FB93 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
DC8834041D8A218F00CE0ACA /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
DCE4E8C61D7F354700AFB96E /* CoreFoundation.framework in Frameworks */,
DCE4E8C51D7F354300AFB96E /* IOKit.framework in Frameworks */,
F6AF96681E646CAF00917214 /* libcoreauthd_client.a in Frameworks */,
+ F682C1D41F4486F700F1B029 /* libctkloginhelper.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DCD06A741D8CE2D5007602F1 /* gkunpack */,
DCD06AB01D8E0D53007602F1 /* libsecurity_utilities.a */,
DC59E9EC1D91C9DC001BDDF5 /* libDER_not_installed.a */,
- DC59EA251D91CA15001BDDF5 /* libDERUtils.a */,
- DC59EA371D91CA82001BDDF5 /* parseCert */,
- DC59EA601D91CAF0001BDDF5 /* parseCrl */,
- DC59EA701D91CB9F001BDDF5 /* parseTicket */,
DC3A4B581D91E9FB00E46D4A /* com.apple.CodeSigningHelper.xpc */,
DC71D9DF1D95BA6C0065FB93 /* libASN1.a */,
- DC71D9FB1D95BB0A0065FB93 /* libDER.a */,
EBF374721DC055580065D840 /* security-sysdiagnose */,
DA30D6761DF8C8FB00EC6B43 /* KeychainSyncAccountUpdater.bundle */,
DCD8A1991E09EE0F00E4FA0A /* libSecureObjectSyncFramework.a */,
DC15F79B1E68EAD5003B9A40 /* CKKSTests+API.m */,
DC6593C91ED8DA9200C19462 /* CKKSTests+CurrentPointerAPI.m */,
DC9A2C5E1EB3F556008FAC27 /* CKKSTests+Coalesce.m */,
+ DCAD9B481F8D95F200C5E2AE /* CloudKitKeychainSyncingFixupTests.m */,
+ DCBF2F7C1F90084D00ED0CA4 /* CKKSTLKSharingTests.m */,
DC08D1CB1E64FCC5006237DA /* CKKSSOSTests.m */,
DC9C750F1E4BCC5100F1CA0D /* CKKSOperationTests.m */,
DC222C891E089BAE00B09171 /* CKKSSQLTests.m */,
- DC4DB15E1E2590B100CD6769 /* CKKSEncryptionTests.m */,
+ DC4DB15E1E2590B100CD6769 /* CKKSAESSIVEncryptionTests.m */,
+ DC7341FD1F84642C00AB9BDF /* CKKSTLKSharingEncryptionTests.m */,
6C34462F1E24F6BE00F9522B /* CKKSRateLimiterTests.m */,
DCD6C4B61EC5319600414FEE /* CKKSNearFutureSchedulerTests.m */,
DCFE1C3C1F17EFB5007640C8 /* CKKSConditionTests.m */,
6C588D791EAA149F00D7E322 /* RateLimiterTests.m */,
4723C9D11F1531970082882F /* CKKSLoggerTests.m */,
DCE7F2081F21726500DDB0F7 /* CKKSAPSReceiverTests.m */,
+ DC9C95951F748D0B000D19E5 /* CKKSServerValidationRecoveryTests.m */,
);
name = "Tests (Local)";
path = tests;
isa = PBXGroup;
children = (
DC59E9FD1D91CA0A001BDDF5 /* libDER */,
- DC59EA2C1D91CA2C001BDDF5 /* libDERUtils */,
);
name = DER;
sourceTree = "<group>";
path = OSX/libsecurity_keychain/libDER/libDER;
sourceTree = "<group>";
};
- DC59EA2C1D91CA2C001BDDF5 /* libDERUtils */ = {
- isa = PBXGroup;
- children = (
- DC59EA261D91CA2C001BDDF5 /* libDERUtils.h */,
- DC59EA271D91CA2C001BDDF5 /* libDERUtils.c */,
- DC59EA281D91CA2C001BDDF5 /* fileIo.c */,
- DC59EA291D91CA2C001BDDF5 /* fileIo.h */,
- DC59EA2A1D91CA2C001BDDF5 /* printFields.h */,
- DC59EA2B1D91CA2C001BDDF5 /* printFields.c */,
- );
- name = libDERUtils;
- path = OSX/libsecurity_keychain/libDER/libDERUtils;
- sourceTree = "<group>";
- };
- DC59EA4B1D91CACE001BDDF5 /* libDER */ = {
- isa = PBXGroup;
- children = (
- DC59EA451D91CACE001BDDF5 /* AppleMobilePersonalizedTicket.h */,
- DC59EA461D91CACE001BDDF5 /* DER_Ticket.c */,
- DC59EA471D91CACE001BDDF5 /* DER_Ticket.h */,
- DC59EA481D91CACE001BDDF5 /* parseTicket.c */,
- DC59EA491D91CACE001BDDF5 /* parseCert.c */,
- DC59EA4A1D91CACE001BDDF5 /* parseCrl.c */,
- );
- name = libDER;
- path = OSX/libsecurity_keychain/libDER/Tests;
- sourceTree = "<group>";
- };
DC5ABD281D832D4C00CF422C /* SecurityTool macOS */ = {
isa = PBXGroup;
children = (
F667EC541E96E8C800203D5C /* authdtests */,
EB1055641E14DB370003C309 /* secfuzzer */,
DC0BCBD81D8C646700070CB0 /* regressionBase */,
- DC59EA4B1D91CACE001BDDF5 /* libDER */,
DC0BCCB81D8C68F000070CB0 /* utilitiesRegressions */,
DC0BC5CD1D8B72FE00070CB0 /* test-checkpw */,
DC610AB81D7910E5002223DE /* gk_reset_check */,
DC9B7AD31DCBF336004E9385 /* CloudKit Syncing */ = {
isa = PBXGroup;
children = (
+ DC9FD3161F857FF800C8AAC8 /* Protocol Buffers */,
DCD662F21E3294DE00188186 /* CloudKit Support */,
DCFE1C311F17ECC3007640C8 /* dispatch Support */,
DCD662EB1E32946000188186 /* Sync Objects */,
DCBDB3BA1E57CA7A00B61300 /* CKKSViewManager.m */,
DCBDB3B01E57C67500B61300 /* CKKSKeychainView.h */,
DCBDB3B11E57C67500B61300 /* CKKSKeychainView.m */,
+ DC9FD3281F8598F300C8AAC8 /* CKKSPeer.h */,
+ DC9FD3291F8598F300C8AAC8 /* CKKSPeer.m */,
DC1ED8C01DD51890002BDCFA /* CKKSItemEncrypter.h */,
DC1ED8BA1DD51883002BDCFA /* CKKSItemEncrypter.m */,
6CC185971E24E87D009657D8 /* CKKSRateLimiter.h */,
6CC185981E24E87D009657D8 /* CKKSRateLimiter.m */,
6CA2B9431E9F9F5700C43444 /* RateLimiter.h */,
6CC7F5B31E9F99EE0014AE63 /* RateLimiter.m */,
+ DC9C95B21F79CFD1000D19E5 /* CKKSControl.h */,
+ DC9C95B31F79CFD1000D19E5 /* CKKSControl.m */,
);
name = "CloudKit Syncing";
path = ckks;
sourceTree = "<group>";
};
+ DC9FD3161F857FF800C8AAC8 /* Protocol Buffers */ = {
+ isa = PBXGroup;
+ children = (
+ DC9FD3251F858BAD00C8AAC8 /* derived source */,
+ DC4D49D81F857728007AF2B8 /* CKKSSerializedKey.proto */,
+ );
+ name = "Protocol Buffers";
+ path = proto;
+ sourceTree = "<group>";
+ };
+ DC9FD3251F858BAD00C8AAC8 /* derived source */ = {
+ isa = PBXGroup;
+ children = (
+ DC9FD3271F858D3F00C8AAC8 /* CKKSSerializedKey.h */,
+ DC9FD3261F858D3E00C8AAC8 /* CKKSSerializedKey.m */,
+ );
+ name = "derived source";
+ path = source;
+ sourceTree = "<group>";
+ };
DCA4D2121E5651950056214F /* Tests (Live CloudKit) */ = {
isa = PBXGroup;
children = (
DC207EB71ED4EAB600D46873 /* CKKSLockStateTracker.m */,
DCCD88E61E42622200F5AA71 /* CKKSGroupOperation.h */,
DCCD88E71E42622200F5AA71 /* CKKSGroupOperation.m */,
+ DC1447881F5764C600236DB4 /* CKKSResultOperation.h */,
+ DC1447891F5764C600236DB4 /* CKKSResultOperation.m */,
+ DC1447941F5766D200236DB4 /* NSOperationCategories.h */,
+ DC1447951F5766D200236DB4 /* NSOperationCategories.m */,
);
name = Helpers;
sourceTree = "<group>";
DCB342D21D8A32A20054D16E /* UnlockReferralItem.h */,
DCB342D31D8A32A20054D16E /* TrustSettingsUtils.cpp */,
DCB342D41D8A32A20054D16E /* TrustSettingsUtils.h */,
- DCB342D51D8A32A20054D16E /* SecCertificatePrivP.h */,
- DCB342D61D8A32A20054D16E /* SecBase64P.c */,
- DCB342D71D8A32A20054D16E /* SecFrameworkP.c */,
- DCB342D81D8A32A20054D16E /* SecCertificateP.c */,
- DCB342D91D8A32A20054D16E /* SecCertificateP.h */,
- DCB342DA1D8A32A20054D16E /* SecCertificateInternalP.h */,
DCB342DB1D8A32A20054D16E /* generateErrStrings.pl */,
DCB342DC1D8A32A20054D16E /* tsaDERUtilities.c */,
DCB342DD1D8A32A20054D16E /* tsaDERUtilities.h */,
DCEA5D541E2826DB0089CF55 /* CKKSSIV.m */,
DCE278DB1ED789EF0083B485 /* CKKSCurrentItemPointer.h */,
DCE278DC1ED789EF0083B485 /* CKKSCurrentItemPointer.m */,
+ DC7341F11F8447AB00AB9BDF /* CKKSTLKShare.h */,
+ DC7341F21F8447AB00AB9BDF /* CKKSTLKShare.m */,
);
name = "Sync Objects";
sourceTree = "<group>";
DC15F7651E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m */,
DCD662F31E329B6800188186 /* CKKSNewTLKOperation.h */,
DCD662F41E329B6800188186 /* CKKSNewTLKOperation.m */,
+ DCBF2F831F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.h */,
+ DCBF2F841F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.m */,
DC7A17EB1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.h */,
DC7A17EC1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.m */,
DCA4D2131E5684220056214F /* CKKSReencryptOutgoingItemsOperation.h */,
DCE278E71ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m */,
DCFE1C4F1F1825F7007640C8 /* CKKSUpdateDeviceStateOperation.h */,
DCFE1C501F1825F7007640C8 /* CKKSUpdateDeviceStateOperation.m */,
+ DCAD9B421F8D939C00C5E2AE /* CKKSFixups.h */,
+ DCAD9B431F8D939C00C5E2AE /* CKKSFixups.m */,
);
name = Operations;
sourceTree = "<group>";
E7FCBE401314471B000DE34E /* Frameworks */ = {
isa = PBXGroup;
children = (
+ F682C1CE1F4486F600F1B029 /* libctkloginhelper.a */,
5EAFA4CD1EF16059002DC188 /* LocalAuthentication.framework */,
D41D36701EB14D87007FA978 /* libDiagnosticMessagesClient.tbd */,
D47CA65C1EB036450038E2BB /* libMobileGestalt.dylib */,
EB27FF051E402C3C00EC9E3A /* ckksctl */ = {
isa = PBXGroup;
children = (
- EB27FF0B1E402C8000EC9E3A /* ckksctl.h */,
EB27FF0C1E402C8000EC9E3A /* ckksctl.m */,
EB27FF2F1E408CC900EC9E3A /* ckksctl-Entitlements.plist */,
);
D47C56AF1DCA841D00E18518 /* lib_ios_x64_shim.xcconfig */,
DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */,
BE8351D41EC0EEDD00ACD5FD /* framework_requiring_modern_objc_runtime.xcconfig */,
+ EBF9AE171F536D0300FECBF7 /* Version.xcconfig */,
);
name = xcconfig;
sourceTree = "<group>";
724340BA1ED3FEC800F8F566 /* SecSMIME.h in Headers */,
22A23B3E1E3AAC9800C41830 /* SecRequirement.h in Headers */,
22A23B3F1E3AAC9800C41830 /* SecCodeHost.h in Headers */,
+ DC9C95BE1F79DC5F000D19E5 /* CKKSControl.h in Headers */,
DC3C7AB61D838C2D00F6A832 /* SecAsn1Types.h in Headers */,
DC3C73551D837B2C00F6A832 /* SOSPeerInfoPriv.h in Headers */,
7901791912D51F7200CA4D44 /* SecCmsContentInfo.h in Headers */,
files = (
4723C9CB1F152ECF0082882F /* SFSQLiteStatement.h in Headers */,
4723C9C31F152EB60082882F /* SFObjCType.h in Headers */,
+ DC9C95BD1F79DC5A000D19E5 /* CKKSControl.h in Headers */,
DC3C73561D837B9B00F6A832 /* SOSPeerInfoPriv.h in Headers */,
EB6928C61D9C9C6F00062A18 /* SecRecoveryKey.h in Headers */,
4723C9DD1F1540CE0082882F /* SFAnalyticsLogger.h in Headers */,
DC2C5F5E1F0EB97E00FEBDA7 /* CKKSNotifier.h in Headers */,
DC5BB4FF1E0C98320010F836 /* CKKSOutgoingQueueOperation.h in Headers */,
DC222C651E034D1F00B09171 /* SOSChangeTracker.h in Headers */,
+ DC14478B1F5764C600236DB4 /* CKKSResultOperation.h in Headers */,
DCFE1C521F1825F7007640C8 /* CKKSUpdateDeviceStateOperation.h in Headers */,
DCBDB3BC1E57CA7A00B61300 /* CKKSViewManager.h in Headers */,
DC762A9F1E57A86A00B03A2C /* CKKSRecordHolder.h in Headers */,
DCFE1C281F17E455007640C8 /* CKKSDeviceStateEntry.h in Headers */,
DCFB12C61E95A4C000510F5F /* CKKSCKAccountStateTracker.h in Headers */,
DC222C6B1E034D1F00B09171 /* SecItemDataSource.h in Headers */,
+ DC7341F41F8447AB00AB9BDF /* CKKSTLKShare.h in Headers */,
DC18F7701E43E116006B8B43 /* CKKSFetchAllRecordZoneChangesOperation.h in Headers */,
DC222C6C1E034D1F00B09171 /* CKKSIncomingQueueEntry.h in Headers */,
DC9082C71EA027DC00D0C1C5 /* CKKSZoneChangeFetcher.h in Headers */,
DCA4D2161E5684220056214F /* CKKSReencryptOutgoingItemsOperation.h in Headers */,
DC222C6D1E034D1F00B09171 /* SecItemDb.h in Headers */,
DC222C6E1E034D1F00B09171 /* SecItemSchema.h in Headers */,
+ DCAD9B451F8D939C00C5E2AE /* CKKSFixups.h in Headers */,
+ DC9C95B51F79CFD1000D19E5 /* CKKSControl.h in Headers */,
DC222C6F1E034D1F00B09171 /* SecKeybagSupport.h in Headers */,
DC222C701E034D1F00B09171 /* iCloudTrace.h in Headers */,
DCEA5D861E2F14810089CF55 /* CKKSAPSReceiver.h in Headers */,
DCCD88E91E42622200F5AA71 /* CKKSGroupOperation.h in Headers */,
DC15F7671E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h in Headers */,
DCD6C4B31EC5302500414FEE /* CKKSNearFutureScheduler.h in Headers */,
+ DCBF2F861F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.h in Headers */,
DCE278E91ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.h in Headers */,
DCD662F61E329B6800188186 /* CKKSNewTLKOperation.h in Headers */,
+ DC1447971F5766D200236DB4 /* NSOperationCategories.h in Headers */,
DC4DB1511E24692100CD6769 /* CKKSKey.h in Headers */,
DCE278DE1ED789EF0083B485 /* CKKSCurrentItemPointer.h in Headers */,
DC222C731E034D1F00B09171 /* CKKSItem.h in Headers */,
DC2C5F5D1F0EB97E00FEBDA7 /* CKKSNotifier.h in Headers */,
DCCD88E81E42622200F5AA71 /* CKKSGroupOperation.h in Headers */,
6CC1859E1E24E8EB009657D8 /* CKKSRateLimiter.h in Headers */,
+ DC14478A1F5764C600236DB4 /* CKKSResultOperation.h in Headers */,
DCFE1C511F1825F7007640C8 /* CKKSUpdateDeviceStateOperation.h in Headers */,
DCBDB3BB1E57CA7A00B61300 /* CKKSViewManager.h in Headers */,
DC762A9E1E57A86A00B03A2C /* CKKSRecordHolder.h in Headers */,
DCFE1C271F17E455007640C8 /* CKKSDeviceStateEntry.h in Headers */,
DCFB12C51E95A4C000510F5F /* CKKSCKAccountStateTracker.h in Headers */,
DC378B381DEFADB500A3DAFA /* CKKSZoneStateEntry.h in Headers */,
+ DC7341F31F8447AB00AB9BDF /* CKKSTLKShare.h in Headers */,
DC52E7E71D80BE8100B0A59C /* SecItemDataSource.h in Headers */,
DC18F76F1E43E116006B8B43 /* CKKSFetchAllRecordZoneChangesOperation.h in Headers */,
DC9082C61EA027DB00D0C1C5 /* CKKSZoneChangeFetcher.h in Headers */,
DCA4D2151E5684220056214F /* CKKSReencryptOutgoingItemsOperation.h in Headers */,
DC378B3C1DF0CA7200A3DAFA /* CKKSIncomingQueueEntry.h in Headers */,
DC52E7E61D80BE7B00B0A59C /* SecItemDb.h in Headers */,
+ DCAD9B441F8D939C00C5E2AE /* CKKSFixups.h in Headers */,
+ DC9C95B41F79CFD1000D19E5 /* CKKSControl.h in Headers */,
DC52E7EA1D80BE9500B0A59C /* SecItemSchema.h in Headers */,
DC52E7E91D80BE8D00B0A59C /* SecKeybagSupport.h in Headers */,
DCD662F51E329B6800188186 /* CKKSNewTLKOperation.h in Headers */,
DC6D2C931DD2836500BE372D /* CKKSOutgoingQueueEntry.h in Headers */,
DC15F7661E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h in Headers */,
DCD6C4B21EC5302500414FEE /* CKKSNearFutureScheduler.h in Headers */,
+ DCBF2F851F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.h in Headers */,
DCE278E81ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.h in Headers */,
DCEA5D851E2F14810089CF55 /* CKKSAPSReceiver.h in Headers */,
+ DC1447961F5766D200236DB4 /* NSOperationCategories.h in Headers */,
DC4DB1501E24692100CD6769 /* CKKSKey.h in Headers */,
DCE278DD1ED789EF0083B485 /* CKKSCurrentItemPointer.h in Headers */,
DCEA5D551E2826DB0089CF55 /* CKKSSIV.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
- DC59EA0F1D91CA15001BDDF5 /* Headers */ = {
- isa = PBXHeadersBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DC59EA311D91CA2C001BDDF5 /* printFields.h in Headers */,
- DC59EA2D1D91CA2C001BDDF5 /* libDERUtils.h in Headers */,
- DC59EA301D91CA2C001BDDF5 /* fileIo.h in Headers */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
DC5ABDEF1D832E5C00CF422C /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
- DC71D9E51D95BB0A0065FB93 /* Headers */ = {
- isa = PBXHeadersBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DC3832DA1DB7050900385F63 /* module.modulemap in Headers */,
- DC71D9E61D95BB0A0065FB93 /* oidsPriv.h in Headers */,
- DC71D9E71D95BB0A0065FB93 /* libDER.h in Headers */,
- DC71D9E81D95BB0A0065FB93 /* DER_Decode.h in Headers */,
- DC71D9E91D95BB0A0065FB93 /* DER_Keys.h in Headers */,
- DC71D9EA1D95BB0A0065FB93 /* DER_Encode.h in Headers */,
- DC963EC51D95F52C008A153E /* oids.h in Headers */,
- DC71D9EB1D95BB0A0065FB93 /* DER_Digest.h in Headers */,
- DC71D9ED1D95BB0A0065FB93 /* asn1Types.h in Headers */,
- DC71D9EE1D95BB0A0065FB93 /* libDER_config.h in Headers */,
- DC71D9EF1D95BB0A0065FB93 /* DER_CertCrl.h in Headers */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
DC8834021D8A218F00CE0ACA /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
buildActionMask = 2147483647;
files = (
DCB3438F1D8A32A20054D16E /* tsaDERUtilities.h in Headers */,
- DCB3438C1D8A32A20054D16E /* SecCertificateP.h in Headers */,
- DCB343881D8A32A20054D16E /* SecCertificatePrivP.h in Headers */,
DCB343651D8A32A20054D16E /* TrustSettings.h in Headers */,
DCB343831D8A32A20054D16E /* TrustStore.h in Headers */,
DCB343941D8A32A20054D16E /* SecExternalRep.h in Headers */,
DCB343851D8A32A20054D16E /* UnlockReferralItem.h in Headers */,
DCB343A41D8A32A20054D16E /* SecNetscapeTemplates.h in Headers */,
DCB343481D8A32A20054D16E /* Globals.h in Headers */,
- DCB3438D1D8A32A20054D16E /* SecCertificateInternalP.h in Headers */,
DCB3437F1D8A32A20054D16E /* TrustAdditions.h in Headers */,
DCB3436D1D8A32A20054D16E /* cssmdatetime.h in Headers */,
DCB343711D8A32A20054D16E /* DLDBListCFPref.h in Headers */,
6C0B0C481E2537E2007F95E5 /* CopyFiles */,
);
buildRules = (
+ DC9FD3221F85877000C8AAC8 /* PBXBuildRule */,
);
dependencies = (
);
6C0B0C4A1E253840007F95E5 /* CopyFiles */,
);
buildRules = (
+ DC9FD3201F85818000C8AAC8 /* PBXBuildRule */,
);
dependencies = (
);
productReference = DC59E9EC1D91C9DC001BDDF5 /* libDER_not_installed.a */;
productType = "com.apple.product-type.library.static";
};
- DC59EA0E1D91CA15001BDDF5 /* DERUtils */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = DC59EA221D91CA15001BDDF5 /* Build configuration list for PBXNativeTarget "DERUtils" */;
- buildPhases = (
- DC59EA0F1D91CA15001BDDF5 /* Headers */,
- DC59EA1A1D91CA15001BDDF5 /* Sources */,
- DC59EA211D91CA15001BDDF5 /* Frameworks */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = DERUtils;
- productName = libsecurityd_client_macos;
- productReference = DC59EA251D91CA15001BDDF5 /* libDERUtils.a */;
- productType = "com.apple.product-type.library.static";
- };
- DC59EA361D91CA82001BDDF5 /* parseCert */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = DC59EA3B1D91CA82001BDDF5 /* Build configuration list for PBXNativeTarget "parseCert" */;
- buildPhases = (
- DC59EA331D91CA82001BDDF5 /* Sources */,
- DC59EA341D91CA82001BDDF5 /* Frameworks */,
- DC59EA351D91CA82001BDDF5 /* CopyFiles */,
- );
- buildRules = (
- );
- dependencies = (
- DC59EA431D91CAAE001BDDF5 /* PBXTargetDependency */,
- DC59EA411D91CAAA001BDDF5 /* PBXTargetDependency */,
- );
- name = parseCert;
- productName = parseCert;
- productReference = DC59EA371D91CA82001BDDF5 /* parseCert */;
- productType = "com.apple.product-type.tool";
- };
- DC59EA521D91CAF0001BDDF5 /* parseCrl */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = DC59EA5D1D91CAF0001BDDF5 /* Build configuration list for PBXNativeTarget "parseCrl" */;
- buildPhases = (
- DC59EA571D91CAF0001BDDF5 /* Sources */,
- DC59EA591D91CAF0001BDDF5 /* Frameworks */,
- DC59EA5C1D91CAF0001BDDF5 /* CopyFiles */,
- );
- buildRules = (
- );
- dependencies = (
- DC59EA531D91CAF0001BDDF5 /* PBXTargetDependency */,
- DC59EA551D91CAF0001BDDF5 /* PBXTargetDependency */,
- );
- name = parseCrl;
- productName = parseCert;
- productReference = DC59EA601D91CAF0001BDDF5 /* parseCrl */;
- productType = "com.apple.product-type.tool";
- };
- DC59EA621D91CB9F001BDDF5 /* parseTicket */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = DC59EA6D1D91CB9F001BDDF5 /* Build configuration list for PBXNativeTarget "parseTicket" */;
- buildPhases = (
- DC59EA671D91CB9F001BDDF5 /* Sources */,
- DC59EA691D91CB9F001BDDF5 /* Frameworks */,
- DC59EA6C1D91CB9F001BDDF5 /* CopyFiles */,
- );
- buildRules = (
- );
- dependencies = (
- DC59EA651D91CB9F001BDDF5 /* PBXTargetDependency */,
- );
- name = parseTicket;
- productName = parseCert;
- productReference = DC59EA701D91CB9F001BDDF5 /* parseTicket */;
- productType = "com.apple.product-type.tool";
- };
DC5ABDC41D832DAB00CF422C /* securitytool_macos */ = {
isa = PBXNativeTarget;
buildConfigurationList = DC5ABDC91D832DAB00CF422C /* Build configuration list for PBXNativeTarget "securitytool_macos" */;
productReference = DC71D9DF1D95BA6C0065FB93 /* libASN1.a */;
productType = "com.apple.product-type.library.static";
};
- DC71D9E41D95BB0A0065FB93 /* DER */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = DC71D9F81D95BB0A0065FB93 /* Build configuration list for PBXNativeTarget "DER" */;
- buildPhases = (
- DC71DA001D95BD320065FB93 /* Why is this here? */,
- DC71D9E51D95BB0A0065FB93 /* Headers */,
- DC71D9F01D95BB0A0065FB93 /* Sources */,
- DC71D9F71D95BB0A0065FB93 /* Frameworks */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = DER;
- productName = libsecurityd_client_macos;
- productReference = DC71D9FB1D95BB0A0065FB93 /* libDER.a */;
- productType = "com.apple.product-type.library.static";
- };
DC8834011D8A218F00CE0ACA /* ASN1_not_installed */ = {
isa = PBXNativeTarget;
buildConfigurationList = DC8834051D8A218F00CE0ACA /* Build configuration list for PBXNativeTarget "ASN1_not_installed" */;
CreatedOnToolsVersion = 8.0;
ProvisioningStyle = Automatic;
};
- DC59EA361D91CA82001BDDF5 = {
- CreatedOnToolsVersion = 8.0;
- ProvisioningStyle = Automatic;
- };
DC5ABDC41D832DAB00CF422C = {
CreatedOnToolsVersion = 8.0;
DevelopmentTeam = XPSUQMMH5W;
DC8834011D8A218F00CE0ACA /* ASN1_not_installed */,
DC71D99F1D95BA6C0065FB93 /* ASN1 */,
DC59E9AC1D91C9DC001BDDF5 /* DER_not_installed */,
- DC71D9E41D95BB0A0065FB93 /* DER */,
- DC59EA0E1D91CA15001BDDF5 /* DERUtils */,
DCF782BA1D88B44300E694BB /* ==== macOS Libraries ====== */,
DCF7830A1D88B4DE00E694BB /* security_apple_csp */,
DCF785021D88B95500E694BB /* security_apple_cspdl */,
7913B1FF0D172B3900601FE9 /* sslServer */,
4C9DE9D11181AC4800CF5C27 /* sslEcdsa */,
4CE5A54C09C796E100D27A3F /* sslViewer */,
- DC59EA361D91CA82001BDDF5 /* parseCert */,
- DC59EA521D91CAF0001BDDF5 /* parseCrl */,
- DC59EA621D91CB9F001BDDF5 /* parseTicket */,
DC0BC5C51D8B72E700070CB0 /* test-checkpw */,
DC0BC5D51D8B73B000070CB0 /* perf-checkpw */,
6C98082C1E788AEB00E70590 /* CKKSCloudKitTests_mac */,
shellScript = "# The build system requires that we don't install these headers and .as in multiple phases.\n# This target will not install anything, so feel free to depend on it whenever you use it.\n\n# If you make changes to this target, please make them to ASN1 as well.";
showEnvVarsInLog = 0;
};
- DC71DA001D95BD320065FB93 /* Why is this here? */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 8;
- files = (
- );
- inputPaths = (
- "$(SRCROOT)/OSX/libsecurity_keychain/libDER/libDER/libDER.h",
- );
- name = "Why is this here?";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 1;
- shellPath = /bin/sh;
- shellScript = "# The build system requires that we don't install these headers and .as in multiple phases. This target will install libDER (as needed per platform).\n\n# If you make changes to this target, please make them to DER_not_installed as well.";
- showEnvVarsInLog = 0;
- };
DC71DA011D95BD670065FB93 /* Why is this here? */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
files = (
220179E91E3BF03200EFB6F3 /* dummy.cpp in Sources */,
4723C9CC1F152ED30082882F /* SFSQLiteStatement.m in Sources */,
+ DC9C95C11F79DD4B000D19E5 /* CKKSControlProtocol.m in Sources */,
DCA85B931E8D97E400BA7241 /* client.c in Sources */,
+ DC9C95BF1F79DC88000D19E5 /* CKKSControl.m in Sources */,
18F7F67914D77F4400F88A12 /* NtlmGenerator.c in Sources */,
0CD8CB051ECA50780076F37F /* SOSPeerOTRTimer.m in Sources */,
DCA85B981E8D980A00BA7241 /* client_endpoint.m in Sources */,
DC1789A51D779E3B00B50D50 /* dummy.cpp in Sources */,
4723C9C51F152EBC0082882F /* SFObjCType.m in Sources */,
4723C9CD1F152ED40082882F /* SFSQLiteStatement.m in Sources */,
+ DC9C95C01F79DC89000D19E5 /* CKKSControl.m in Sources */,
4723C9E11F1540CE0082882F /* SFAnalyticsLogger.m in Sources */,
B61577E81F20151C004A3930 /* SecPaddingConfigurations.c in Sources */,
DC1789A21D779DF400B50D50 /* SecBreadcrumb.c in Sources */,
+ DC9C95C21F79DD4D000D19E5 /* CKKSControlProtocol.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ DC9FD3231F8587A500C8AAC8 /* CKKSSerializedKey.proto in Sources */,
DC222C3A1E034D1F00B09171 /* CKKSItemEncrypter.m in Sources */,
DC7A17F01E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.m in Sources */,
DCEA5D581E2826DB0089CF55 /* CKKSSIV.m in Sources */,
DC15F7691E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m in Sources */,
DC222C461E034D1F00B09171 /* SecItemDb.c in Sources */,
DC222C471E034D1F00B09171 /* SecItemSchema.c in Sources */,
+ DC9FD32D1F85990B00C8AAC8 /* CKKSPeer.m in Sources */,
DCEA5D881E2F14810089CF55 /* CKKSAPSReceiver.m in Sources */,
DC2C5F611F0EB97E00FEBDA7 /* CKKSNotifier.m in Sources */,
DC222C481E034D1F00B09171 /* SecItemServer.c in Sources */,
+ DC9C95B71F79CFD1000D19E5 /* CKKSControl.m in Sources */,
DC18F7721E43E116006B8B43 /* CKKSFetchAllRecordZoneChangesOperation.m in Sources */,
DC222C491E034D1F00B09171 /* SecKeybagSupport.c in Sources */,
DC1DA6691E4555D80094CE7F /* CKKSScanLocalItemsOperation.m in Sources */,
6C8CC3B41E2F913D009025C5 /* AWDKeychainCKKSRateLimiterOverload.m in Sources */,
DC222C4A1E034D1F00B09171 /* SecLogSettingsServer.m in Sources */,
+ DC14478D1F5764C600236DB4 /* CKKSResultOperation.m in Sources */,
479DA1781EBBA8D30065C98F /* CKKSManifest.m in Sources */,
DCD662F81E329B6800188186 /* CKKSNewTLKOperation.m in Sources */,
DC222C4D1E034D1F00B09171 /* CKKSOutgoingQueueEntry.m in Sources */,
+ DCBF2F881F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.m in Sources */,
DC222C4E1E034D1F00B09171 /* CKKS.m in Sources */,
DC762AA11E57A86A00B03A2C /* CKKSRecordHolder.m in Sources */,
DC222C501E034D1F00B09171 /* SecOTRRemote.m in Sources */,
479108BA1EE879F9008CEFA0 /* CKKSAnalyticsLogger.m in Sources */,
+ DC1447991F5766D200236DB4 /* NSOperationCategories.m in Sources */,
DC222C511E034D1F00B09171 /* CKKSItem.m in Sources */,
DCBDB3BE1E57CA7A00B61300 /* CKKSViewManager.m in Sources */,
DCFE1C2A1F17E455007640C8 /* CKKSDeviceStateEntry.m in Sources */,
+ DCAD9B471F8D939C00C5E2AE /* CKKSFixups.m in Sources */,
DCA4D2181E5684220056214F /* CKKSReencryptOutgoingItemsOperation.m in Sources */,
DCE278EB1ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m in Sources */,
DC222C541E034D1F00B09171 /* CKKSSQLDatabaseObject.m in Sources */,
DCEA5D981E3015840089CF55 /* CKKSZone.m in Sources */,
DCB837381ED5045100015C07 /* CKKSLockStateTracker.m in Sources */,
- DCF7A8A41F0450EB00CABE89 /* CKKSControlProtocol.m in Sources */,
+ DAD3BD021F9830BC00DF29BA /* CKKSControlProtocol.m in Sources */,
DC4DB1531E24692100CD6769 /* CKKSKey.m in Sources */,
DC9082C51EA0277700D0C1C5 /* CKKSZoneChangeFetcher.m in Sources */,
DC222C571E034D1F00B09171 /* SecuritydXPC.c in Sources */,
+ DC7341F61F8447AB00AB9BDF /* CKKSTLKShare.m in Sources */,
6C8CC3B51E2F913D009025C5 /* AWDKeychainCKKSRateLimiterTopWriters.m in Sources */,
DCBDB3B81E57C82300B61300 /* CKKSKeychainView.m in Sources */,
DC222C5A1E034D1F00B09171 /* iCloudTrace.c in Sources */,
DC08D1C41E64FA8C006237DA /* CloudKitKeychainSyncingMockXCTest.m in Sources */,
47E553741EDF674700749715 /* CKKSManifestTests.m in Sources */,
6C588D7F1EAA14AA00D7E322 /* RateLimiterTests.m in Sources */,
- DC4DB15F1E2590B100CD6769 /* CKKSEncryptionTests.m in Sources */,
+ DC4DB15F1E2590B100CD6769 /* CKKSAESSIVEncryptionTests.m in Sources */,
DC3502E71E0214C800BC0587 /* MockCloudKit.m in Sources */,
DC6593D11ED8DAB900C19462 /* CKKSTests+CurrentPointerAPI.m in Sources */,
DCA85B9A1E8D981100BA7241 /* client_endpoint.m in Sources */,
+ DCAD9B491F8D95F200C5E2AE /* CloudKitKeychainSyncingFixupTests.m in Sources */,
DC9A2C5F1EB3F557008FAC27 /* CKKSTests+Coalesce.m in Sources */,
DC222C8A1E089BAE00B09171 /* CKKSSQLTests.m in Sources */,
DC15F79C1E68EAD5003B9A40 /* CKKSTests+API.m in Sources */,
4723C9D41F1531A30082882F /* CKKSLoggerTests.m in Sources */,
+ DCBF2F7D1F90084D00ED0CA4 /* CKKSTLKSharingTests.m in Sources */,
DC3502B81E0208BE00BC0587 /* CKKSTests.m in Sources */,
6C3446301E24F6BE00F9522B /* CKKSRateLimiterTests.m in Sources */,
DCA85B961E8D980100BA7241 /* client.c in Sources */,
DC4268FE1E820371002B7110 /* server_endpoint.m in Sources */,
DCFE1C3D1F17EFB5007640C8 /* CKKSConditionTests.m in Sources */,
DCCD33C91E3FE95900AA4AD1 /* spi.c in Sources */,
+ DC9C95971F748D0B000D19E5 /* CKKSServerValidationRecoveryTests.m in Sources */,
+ DC7341FE1F84642C00AB9BDF /* CKKSTLKSharingEncryptionTests.m in Sources */,
DC5F35AC1EE0F27900900966 /* server_entitlement_helpers.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ DC9FD3361F86A34F00C8AAC8 /* CKKSSerializedKey.proto in Sources */,
DC797E1A1DD3F9A400CC9E42 /* CKKSSQLDatabaseObject.m in Sources */,
6CC1859F1E24E8EB009657D8 /* CKKSRateLimiter.m in Sources */,
DCFB12C71E95A4C000510F5F /* CKKSCKAccountStateTracker.m in Sources */,
DCD6C4B41EC5302500414FEE /* CKKSNearFutureScheduler.m in Sources */,
DC378B2F1DEF9E0E00A3DAFA /* CKKSMirrorEntry.m in Sources */,
DC94BCCC1F10448600E07CEB /* CloudKitCategories.m in Sources */,
+ DC9FD32C1F85990A00C8AAC8 /* CKKSPeer.m in Sources */,
DC1ED8C61DD55476002BDCFA /* CKKS.m in Sources */,
DCB5D93D1E4A9A3400BE22AB /* CKKSSynchronizeOperation.m in Sources */,
DC762AA01E57A86A00B03A2C /* CKKSRecordHolder.m in Sources */,
DC18F7711E43E116006B8B43 /* CKKSFetchAllRecordZoneChangesOperation.m in Sources */,
DC2C5F601F0EB97E00FEBDA7 /* CKKSNotifier.m in Sources */,
DC52E7CF1D80BCFD00B0A59C /* SOSEngine.c in Sources */,
+ DC9C95B61F79CFD1000D19E5 /* CKKSControl.m in Sources */,
DC4DB1521E24692100CD6769 /* CKKSKey.m in Sources */,
DCBDB3BD1E57CA7A00B61300 /* CKKSViewManager.m in Sources */,
DC52E7C41D80BCAD00B0A59C /* SecDbItem.c in Sources */,
DC52E7D31D80BD1800B0A59C /* SecDbKeychainItem.c in Sources */,
DC52E7CC1D80BCDF00B0A59C /* SecDbQuery.c in Sources */,
+ DC14478C1F5764C600236DB4 /* CKKSResultOperation.m in Sources */,
479DA1721EBBA8D10065C98F /* CKKSManifest.m in Sources */,
DC52E7CB1D80BCD800B0A59C /* SecItemBackupServer.c in Sources */,
DC52E7CD1D80BCE700B0A59C /* SecItemDataSource.c in Sources */,
+ DCBF2F871F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.m in Sources */,
DC52E7DE1D80BD7F00B0A59C /* SecItemDb.c in Sources */,
DC52E7E01D80BD8D00B0A59C /* SecItemSchema.c in Sources */,
DC52E7D71D80BD2D00B0A59C /* SecItemServer.c in Sources */,
479108B91EE879F9008CEFA0 /* CKKSAnalyticsLogger.m in Sources */,
+ DC1447981F5766D200236DB4 /* NSOperationCategories.m in Sources */,
DCD8A0CF1E09EA1800E4FA0A /* SecKeybagSupport.c in Sources */,
DC52E7E11D80BD9300B0A59C /* SecLogSettingsServer.m in Sources */,
DCFE1C291F17E455007640C8 /* CKKSDeviceStateEntry.m in Sources */,
+ DCAD9B461F8D939C00C5E2AE /* CKKSFixups.m in Sources */,
6C8CC3AC1E2F913C009025C5 /* AWDKeychainCKKSRateLimiterOverload.m in Sources */,
DC52E7DC1D80BD4F00B0A59C /* SecOTRRemote.m in Sources */,
DCE278EA1ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m in Sources */,
DCD662F71E329B6800188186 /* CKKSNewTLKOperation.m in Sources */,
DCB837321ED5045000015C07 /* CKKSLockStateTracker.m in Sources */,
- DCF7A8A31F0450EB00CABE89 /* CKKSControlProtocol.m in Sources */,
+ DAD3BD011F9830BB00DF29BA /* CKKSControlProtocol.m in Sources */,
DCBDB3B71E57C82300B61300 /* CKKSKeychainView.m in Sources */,
DC52E7D61D80BD2800B0A59C /* SecuritydXPC.c in Sources */,
DC7A17EF1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.m in Sources */,
+ DC7341F51F8447AB00AB9BDF /* CKKSTLKShare.m in Sources */,
DCA4D2171E5684220056214F /* CKKSReencryptOutgoingItemsOperation.m in Sources */,
5269658D1E6A154700627F9D /* SecBackupKeybagEntry.m in Sources */,
DC52E7D41D80BD1D00B0A59C /* iCloudTrace.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
- DC59EA1A1D91CA15001BDDF5 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DC59EA2F1D91CA2C001BDDF5 /* fileIo.c in Sources */,
- DC59EA321D91CA2C001BDDF5 /* printFields.c in Sources */,
- DC59EA2E1D91CA2C001BDDF5 /* libDERUtils.c in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- DC59EA331D91CA82001BDDF5 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DC59EA4E1D91CACE001BDDF5 /* parseCert.c in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- DC59EA571D91CAF0001BDDF5 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DC59EA611D91CAFD001BDDF5 /* parseCrl.c in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- DC59EA671D91CB9F001BDDF5 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DC59EA721D91CBBD001BDDF5 /* parseTicket.c in Sources */,
- DC59EA711D91CBB9001BDDF5 /* DER_Ticket.c in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
DC5ABDC11D832DAB00CF422C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
- DC71D9F01D95BB0A0065FB93 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DC71D9F11D95BB0A0065FB93 /* DER_Decode.c in Sources */,
- DC71D9F21D95BB0A0065FB93 /* DER_Encode.c in Sources */,
- DC71D9F31D95BB0A0065FB93 /* DER_Keys.c in Sources */,
- DC71D9F41D95BB0A0065FB93 /* DER_Digest.c in Sources */,
- DC71D9F51D95BB0A0065FB93 /* oids.c in Sources */,
- DC71D9F61D95BB0A0065FB93 /* DER_CertCrl.c in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
DC8834031D8A218F00CE0ACA /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
DCB342FA1D8A32A20054D16E /* SecACL.cpp in Sources */,
DCB342F91D8A32A20054D16E /* SecAccess.cpp in Sources */,
DCB342FB1D8A32A20054D16E /* SecBase.cpp in Sources */,
- DCB343891D8A32A20054D16E /* SecBase64P.c in Sources */,
DCB3435B1D8A32A20054D16E /* SecCFTypes.cpp in Sources */,
DCB342FD1D8A32A20054D16E /* SecCertificate.cpp in Sources */,
DCB342FE1D8A32A20054D16E /* SecCertificateBundle.cpp in Sources */,
- DCB3438B1D8A32A20054D16E /* SecCertificateP.c in Sources */,
DCB343921D8A32A20054D16E /* SecExport.cpp in Sources */,
DCB343931D8A32A20054D16E /* SecExternalRep.cpp in Sources */,
DCB343371D8A32A20054D16E /* SecFDERecoveryAsymmetricCrypto.cpp in Sources */,
- DCB3438A1D8A32A20054D16E /* SecFrameworkP.c in Sources */,
DCB343001D8A32A20054D16E /* SecIdentity.cpp in Sources */,
DCB343011D8A32A20054D16E /* SecIdentitySearch.cpp in Sources */,
DCB343951D8A32A20054D16E /* SecImport.cpp in Sources */,
BEEB47DA1EA189F5004AA5C6 /* SecTrustStatusCodes.c in Sources */,
DCD66DBE1D82053700DB1393 /* SecBase64.c in Sources */,
DCD66DBD1D82053100DB1393 /* SecCertificatePath.c in Sources */,
+ BE1F74D31F609D460068FA64 /* SecFramework.c in Sources */,
DCD66DB61D82050900DB1393 /* SecKey.c in Sources */,
DCD66DBC1D82052B00DB1393 /* SecKeyAdaptors.c in Sources */,
DCD66DBB1D82052700DB1393 /* SecPolicy.c in Sources */,
name = libCMS;
targetProxy = DC59E9A81D91C7CC001BDDF5 /* PBXContainerItemProxy */;
};
- DC59EA411D91CAAA001BDDF5 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = DC59E9AC1D91C9DC001BDDF5 /* DER_not_installed */;
- targetProxy = DC59EA401D91CAAA001BDDF5 /* PBXContainerItemProxy */;
- };
- DC59EA431D91CAAE001BDDF5 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = DC59EA0E1D91CA15001BDDF5 /* DERUtils */;
- targetProxy = DC59EA421D91CAAE001BDDF5 /* PBXContainerItemProxy */;
- };
- DC59EA531D91CAF0001BDDF5 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = DC59EA0E1D91CA15001BDDF5 /* DERUtils */;
- targetProxy = DC59EA541D91CAF0001BDDF5 /* PBXContainerItemProxy */;
- };
- DC59EA551D91CAF0001BDDF5 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = DC59E9AC1D91C9DC001BDDF5 /* DER_not_installed */;
- targetProxy = DC59EA561D91CAF0001BDDF5 /* PBXContainerItemProxy */;
- };
- DC59EA651D91CB9F001BDDF5 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = DC59E9AC1D91C9DC001BDDF5 /* DER_not_installed */;
- targetProxy = DC59EA661D91CB9F001BDDF5 /* PBXContainerItemProxy */;
- };
DC59EA761D91CC5E001BDDF5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DC59E9AC1D91C9DC001BDDF5 /* DER_not_installed */;
target = DC71D99F1D95BA6C0065FB93 /* ASN1 */;
targetProxy = DC71D9E21D95BAD50065FB93 /* PBXContainerItemProxy */;
};
- DC71D9FD1D95BB440065FB93 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = DC71D9E41D95BB0A0065FB93 /* DER */;
- targetProxy = DC71D9FC1D95BB440065FB93 /* PBXContainerItemProxy */;
- };
DC71DA031D95BDEA0065FB93 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DC8834011D8A218F00CE0ACA /* ASN1_not_installed */;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/keychain/ckks/tests/testrunner/KeychainEntitledTestRunner-Entitlements.plist";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-";
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/keychain/ckks/tests/testrunner/KeychainEntitledTestRunner-Entitlements.plist";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-";
COPY_PHASE_STRIP = NO;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
INFOPLIST_FILE = keychain/trust/TrustedPeersTests/Info.plist;
PRODUCT_BUNDLE_IDENTIFIER = com.apple.TrustedPeersTests;
PRODUCT_NAME = "$(TARGET_NAME)";
- VALID_ARCHS = "armv6 armv7 arm64 x86_64 x86_64h";
};
name = Debug;
};
INFOPLIST_FILE = keychain/trust/TrustedPeersTests/Info.plist;
PRODUCT_BUNDLE_IDENTIFIER = com.apple.TrustedPeersTests;
PRODUCT_NAME = "$(TARGET_NAME)";
- VALID_ARCHS = "armv6 armv7 arm64 x86_64 x86_64h";
};
name = Release;
};
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COMBINE_HIDPI_IMAGES = YES;
GCC_DYNAMIC_NO_PIC = NO;
- GCC_PREPROCESSOR_DEFINITIONS = (
- "DEBUG=1",
- "$(inherited)",
- );
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
"-Wl,-upward_framework,Foundation",
"$(OTHER_LDFLAGS_APPLEIDAUTHSUPPORT)",
);
- VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
- GCC_PREPROCESSOR_DEFINITIONS = (
- "NDEBUG=1",
- "$(inherited)",
- );
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
"-Wl,-upward_framework,Foundation",
"$(OTHER_LDFLAGS_APPLEIDAUTHSUPPORT)",
);
- VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
};
name = Release;
};
- DC59EA231D91CA15001BDDF5 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_SUSPICIOUS_MOVES = YES;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- MTL_ENABLE_DEBUG_INFO = YES;
- PRODUCT_NAME = "$(TARGET_NAME)";
- PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/security_asn1;
- SKIP_INSTALL = NO;
- };
- name = Debug;
- };
- DC59EA241D91CA15001BDDF5 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_SUSPICIOUS_MOVES = YES;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- MTL_ENABLE_DEBUG_INFO = NO;
- PRODUCT_NAME = "$(TARGET_NAME)";
- PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/security_asn1;
- SKIP_INSTALL = NO;
- };
- name = Release;
- };
- DC59EA3C1D91CA82001BDDF5 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_SUSPICIOUS_MOVES = YES;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- MTL_ENABLE_DEBUG_INFO = YES;
- PRODUCT_NAME = "$(TARGET_NAME)";
- };
- name = Debug;
- };
- DC59EA3D1D91CA82001BDDF5 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_SUSPICIOUS_MOVES = YES;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- MTL_ENABLE_DEBUG_INFO = NO;
- PRODUCT_NAME = "$(TARGET_NAME)";
- };
- name = Release;
- };
- DC59EA5E1D91CAF0001BDDF5 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_SUSPICIOUS_MOVES = YES;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- MTL_ENABLE_DEBUG_INFO = YES;
- PRODUCT_NAME = "$(TARGET_NAME)";
- };
- name = Debug;
- };
- DC59EA5F1D91CAF0001BDDF5 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_SUSPICIOUS_MOVES = YES;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- MTL_ENABLE_DEBUG_INFO = NO;
- PRODUCT_NAME = "$(TARGET_NAME)";
- };
- name = Release;
- };
- DC59EA6E1D91CB9F001BDDF5 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_SUSPICIOUS_MOVES = YES;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- MTL_ENABLE_DEBUG_INFO = YES;
- PRODUCT_NAME = "$(TARGET_NAME)";
- };
- name = Debug;
- };
- DC59EA6F1D91CB9F001BDDF5 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_SUSPICIOUS_MOVES = YES;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- MTL_ENABLE_DEBUG_INFO = NO;
- PRODUCT_NAME = "$(TARGET_NAME)";
- };
- name = Release;
- };
DC5ABDCA1D832DAB00CF422C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Release;
};
- DC71D9F91D95BB0A0065FB93 /* Debug */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = D47C56FB1DCA8F4900E18518 /* all_arches.xcconfig */;
- buildSettings = {
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_SUSPICIOUS_MOVES = YES;
- DEFINES_MODULE = YES;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- GENERATE_TEXT_BASED_STUBS = NO;
- INLINE_PRIVATE_FRAMEWORKS = NO;
- MODULEMAP_FILE = OSX/libsecurity_keychain/libDER/libDER/module.modulemap;
- MTL_ENABLE_DEBUG_INFO = YES;
- PRODUCT_MODULE_NAME = libDER;
- PRODUCT_NAME = "$(TARGET_NAME)";
- PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/security_libDER/libDER;
- SKIP_INSTALL = YES;
- "SKIP_INSTALL[sdk=macosx*]" = NO;
- SUPPORTS_TEXT_BASED_API = NO;
- };
- name = Debug;
- };
- DC71D9FA1D95BB0A0065FB93 /* Release */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = D47C56FB1DCA8F4900E18518 /* all_arches.xcconfig */;
- buildSettings = {
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_SUSPICIOUS_MOVES = YES;
- DEFINES_MODULE = YES;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- GENERATE_TEXT_BASED_STUBS = NO;
- INLINE_PRIVATE_FRAMEWORKS = NO;
- MODULEMAP_FILE = OSX/libsecurity_keychain/libDER/libDER/module.modulemap;
- MTL_ENABLE_DEBUG_INFO = NO;
- PRODUCT_MODULE_NAME = libDER;
- PRODUCT_NAME = "$(TARGET_NAME)";
- PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/security_libDER/libDER;
- SKIP_INSTALL = YES;
- "SKIP_INSTALL[sdk=macosx*]" = NO;
- SUPPORTS_TEXT_BASED_API = NO;
- };
- name = Release;
- };
DC82FFE81D90D3F60085674B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- DC59EA221D91CA15001BDDF5 /* Build configuration list for PBXNativeTarget "DERUtils" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- DC59EA231D91CA15001BDDF5 /* Debug */,
- DC59EA241D91CA15001BDDF5 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- DC59EA3B1D91CA82001BDDF5 /* Build configuration list for PBXNativeTarget "parseCert" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- DC59EA3C1D91CA82001BDDF5 /* Debug */,
- DC59EA3D1D91CA82001BDDF5 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- DC59EA5D1D91CAF0001BDDF5 /* Build configuration list for PBXNativeTarget "parseCrl" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- DC59EA5E1D91CAF0001BDDF5 /* Debug */,
- DC59EA5F1D91CAF0001BDDF5 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- DC59EA6D1D91CB9F001BDDF5 /* Build configuration list for PBXNativeTarget "parseTicket" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- DC59EA6E1D91CB9F001BDDF5 /* Debug */,
- DC59EA6F1D91CB9F001BDDF5 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
DC5ABDC91D832DAB00CF422C /* Build configuration list for PBXNativeTarget "securitytool_macos" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- DC71D9F81D95BB0A0065FB93 /* Build configuration list for PBXNativeTarget "DER" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- DC71D9F91D95BB0A0065FB93 /* Debug */,
- DC71D9FA1D95BB0A0065FB93 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
DC82FFE71D90D3F60085674B /* Build configuration list for PBXAggregateTarget "security_utilities_DTrace" */ = {
isa = XCConfigurationList;
buildConfigurations = (
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ enableAddressSanitizer = "YES"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
- argument = "secd_668_ghosts"
+ argument = "secd_68_ghosts"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
CFTypeID SecAccessControlGetTypeID(void)
__OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
-#if RC_HIDE_J79 || RC_HIDE_J80
-
-typedef CF_OPTIONS(CFOptionFlags, SecAccessControlCreateFlags) {
- kSecAccessControlUserPresence = 1 << 0, // User presence policy using Touch ID or Passcode. Touch ID does not have to be available or enrolled. Item is still accessible by Touch ID even if fingers are added or removed.
- kSecAccessControlTouchIDAny CF_ENUM_AVAILABLE(NA, 9_0) = 1u << 1, // Constraint: Touch ID (any finger). Touch ID must be available and at least one finger must be enrolled. Item is still accessible by Touch ID even if fingers are added or removed.
- kSecAccessControlTouchIDCurrentSet CF_ENUM_AVAILABLE(NA, 9_0) = 1u << 3, // Constraint: Touch ID from the set of currently enrolled fingers. Touch ID must be available and at least one finger must be enrolled. When fingers are added or removed, the item is invalidated.
- kSecAccessControlDevicePasscode CF_ENUM_AVAILABLE(10_11, 9_0) = 1u << 4, // Constraint: Device passcode
- kSecAccessControlOr CF_ENUM_AVAILABLE(NA, 9_0) = 1u << 14, // Constraint logic operation: when using more than one constraint, at least one of them must be satisfied.
- kSecAccessControlAnd CF_ENUM_AVAILABLE(NA, 9_0) = 1u << 15, // Constraint logic operation: when using more than one constraint, all must be satisfied.
- kSecAccessControlPrivateKeyUsage CF_ENUM_AVAILABLE(NA, 9_0) = 1u << 30, // Create access control for private key operations (i.e. sign operation)
- kSecAccessControlApplicationPassword CF_ENUM_AVAILABLE(NA, 9_0) = 1u << 31, // Security: Application provided password for data encryption key generation. This is not a constraint but additional item encryption mechanism.
-} __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
-
-#else
-
typedef CF_OPTIONS(CFOptionFlags, SecAccessControlCreateFlags) {
- kSecAccessControlUserPresence = 1 << 0, // User presence policy using Touch ID or Passcode. Touch ID does not have to be available or enrolled. Item is still accessible by Touch ID even if fingers are added or removed.
- kSecAccessControlTouchIDAny CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 1, // Constraint: Touch ID (any finger). Touch ID must be available and at least one finger must be enrolled. Item is still accessible by Touch ID even if fingers are added or removed.
- kSecAccessControlTouchIDCurrentSet CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 3, // Constraint: Touch ID from the set of currently enrolled fingers. Touch ID must be available and at least one finger must be enrolled. When fingers are added or removed, the item is invalidated.
+ kSecAccessControlUserPresence = 1 << 0, // User presence policy using biometry or Passcode. Biometry does not have to be available or enrolled. Item is still accessible by Touch ID even if fingers are added or removed. Item is still accessible by Face ID if user is re-enrolled.
+ kSecAccessControlTouchIDAny CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 1, // Constraint: Touch ID (any finger) or Face ID. Touch ID or Face ID must be available. With Touch ID at least one finger must be enrolled. With Face ID user has to be enrolled. Item is still accessible by Touch ID even if fingers are added or removed. Item is still accessible by Face ID if user is re-enrolled.
+ kSecAccessControlTouchIDCurrentSet CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 3, // Constraint: Touch ID from the set of currently enrolled fingers. Touch ID must be available and at least one finger must be enrolled. When fingers are added or removed, the item is invalidated. When Face ID is re-enrolled this item is invalidated.
kSecAccessControlDevicePasscode CF_ENUM_AVAILABLE(10_11, 9_0) = 1u << 4, // Constraint: Device passcode
kSecAccessControlOr CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 14, // Constraint logic operation: when using more than one constraint, at least one of them must be satisfied.
kSecAccessControlAnd CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 15, // Constraint logic operation: when using more than one constraint, all must be satisfied.
kSecAccessControlApplicationPassword CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 31, // Security: Application provided password for data encryption key generation. This is not a constraint but additional item encryption mechanism.
} __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
-#endif
/*!
@function SecAccessControlCreateWithFlags
extern const CFStringRef kSecAttrViewHintManatee;
extern const CFStringRef kSecAttrViewHintAutoUnlock;
extern const CFStringRef kSecAttrViewHintHealth;
+extern const CFStringRef kSecAttrViewHintApplePay;
#if SEC_OS_IPHONE
/*!
@function SecKeyGetBlockSize
- @abstract Decrypt a block of ciphertext.
+ @abstract Returns block length of the key in bytes.
@param key The key for which the block length is requested.
@result The block length of the key in bytes.
@discussion If for example key is an RSA key the value returned by
/* Item CKRecords */
extern NSString* const SecCKRecordItemType;
-extern NSString* const SecCKRecordVersionKey;
+extern NSString* const SecCKRecordHostOSVersionKey;
extern NSString* const SecCKRecordEncryptionVersionKey;
extern NSString* const SecCKRecordParentKeyRefKey;
extern NSString* const SecCKRecordDataKey;
//extern NSString* const SecCKRecordWrappedKeyKey;
//extern NSString* const SecCKRecordParentKeyRefKey;
+/* TLK Share CKRecord Keys */
+// These are a bit special; they can't use the record ID as information without parsing.
+extern NSString* const SecCKRecordTLKShareType;
+extern NSString* const SecCKRecordSenderPeerID;
+extern NSString* const SecCKRecordReceiverPeerID;
+extern NSString* const SecCKRecordReceiverPublicEncryptionKey;
+extern NSString* const SecCKRecordCurve;
+extern NSString* const SecCKRecordEpoch;
+extern NSString* const SecCKRecordPoisoned;
+extern NSString* const SecCKRecordSignature;
+extern NSString* const SecCKRecordVersion;
+//extern NSString* const SecCKRecordParentKeyRefKey; // reference to the key contained by this record
+//extern NSString* const SecCKRecordWrappedKeyKey; // key material
+
/* Current Key CKRecord Keys */
extern NSString* const SecCKRecordCurrentKeyType;
// The key class will be the record name.
/* Current Item CKRecord Keys */
extern NSString* const SecCKRecordCurrentItemType;
extern NSString* const SecCKRecordItemRefKey;
-//extern NSString* const SecCKRecordVersionKey; <-- the OS version which last updated the record
+//extern NSString* const SecCKRecordHostOSVersionKey; <-- the OS version which last updated the record
/* Device State CKRexord Keys */
extern NSString* const SecCKRecordDeviceStateType;
extern CKKSZoneKeyState* const SecCKKSZoneKeyStateBadCurrentPointers;
// Something has gone wrong creating new TLKs.
extern CKKSZoneKeyState* const SecCKKSZoneKeyStateNewTLKsFailed;
+// Something isn't quite right with the TLK shares.
+extern CKKSZoneKeyState* const SecCKKSZoneKeyStateHealTLKShares;
+// Something has gone wrong fixing TLK shares.
+extern CKKSZoneKeyState* const SecCKKSZoneKeyStateHealTLKSharesFailed;
+// The key hierarchy state machine needs to wait for the fixup operation to complete
+extern CKKSZoneKeyState* const SecCKKSZoneKeyStateWaitForFixupOperation;
+
// Fatal error. Will not proceed unless fixed from outside class.
extern CKKSZoneKeyState* const SecCKKSZoneKeyStateError;
// This CKKS instance has been cancelled.
extern NSString* const SecCKKSUserDefaultsSuite;
+extern NSString* const CKKSErrorDomain;
+extern NSString* const CKKSServerExtensionErrorDomain;
+
/* Queue limits: these should likely be configurable via plist */
#define SecCKKSOutgoingQueueItemsAtOnce 100
#define SecCKKSIncomingQueueItemsAtOnce 10
bool SecCKKSEnableEnforceManifests(void);
bool SecCKKSSetEnforceManifests(bool value);
+bool SecCKKSShareTLKs(void);
+bool SecCKKSEnableShareTLKs(void);
+bool SecCKKSSetShareTLKs(bool value);
+
// Testing support
bool SecCKKSTestsEnabled(void);
bool SecCKKSTestsEnable(void);
// TODO: handle errors better
typedef CF_ENUM(CFIndex, CKKSErrorCode) {
+ CKKSNotLoggedIn = 10,
+ CKKSNoSuchView = 11,
+
+ CKKSRemoteItemChangePending = 12,
+ CKKSLocalItemChangePending = 13,
+ CKKSItemChanged = 14,
CKKSNoUUIDOnItem = 15,
+ CKKSItemCreationFailure = 16,
+ CKKSInvalidKeyClass = 17,
+ CKKSKeyNotSelfWrapped = 18,
+ CKKSNoTrustedPeer = 19,
+ CKKSDataMismatch = 20,
+ CKKSProtobufFailure = 21,
+ CKKSNoSuchRecord = 22,
+ CKKSMissingTLKShare = 23,
+ CKKSNoPeersAvailable = 24,
+};
+
+// These errors are returned by the CKKS server extension.
+// Commented out codes here indicate that we don't currently handle them on the client side.
+typedef CF_ENUM(CFIndex, CKKSServerExtensionErrorCode) {
+ // Generic Errors
+ //CKKSServerMissingField = 1,
+ //CKKSServerMissingRecord = 2,
+ //CKKSServerUnexpectedFieldType = 3,
+ //CKKSServerUnexpectedRecordType = 4,
+ //CKKSServerUnepxectedRecordID = 5,
+
+ // Chain errors:
+ //CKKSServerMissingCurrentKeyPointer = 6,
+ //CKKSServerMissingCurrentKey = 7,
+ //CKKSServerUnexpectedSyncKeyClassInChain = 8,
+ CKKSServerUnexpectedSyncKeyInChain = 9,
+
+ // Item/Currentitem record errors:
+ //CKKSServerKeyrollingNotAllowed = 10,
+ //CKKSServerInvalidPublicIdentity = 11,
+ //CKKSServerPublicKeyMismatch = 12,
+ //CKKSServerServiceNumberMismatch = 13,
+ //CKKSServerUnknownServiceNumber = 14,
+ //CKKSServerEncverLessThanMinVal = 15,
+ //CKKSServerCannotModifyWasCurrent = 16,
+ //CKKSServerInvalidCurrentItem = 17,
};
#define SecTranslateError(nserrorptr, cferror) \
NSString* const SecCKKSAPSNamedPort = @"com.apple.securityd.aps";
NSString* const SecCKRecordItemType = @"item";
-NSString* const SecCKRecordVersionKey = @"uploadver";
+NSString* const SecCKRecordHostOSVersionKey = @"uploadver";
NSString* const SecCKRecordEncryptionVersionKey = @"encver";
NSString* const SecCKRecordDataKey = @"data";
NSString* const SecCKRecordParentKeyRefKey = @"parentkeyref";
NSString* const SecCKRecordIntermediateKeyType = @"synckey";
NSString* const SecCKRecordKeyClassKey = @"class";
+NSString* const SecCKRecordTLKShareType = @"tlkshare";
+NSString* const SecCKRecordSenderPeerID = @"sender";
+NSString* const SecCKRecordReceiverPeerID = @"receiver";
+NSString* const SecCKRecordReceiverPublicEncryptionKey = @"receiverPublicEncryptionKey";
+NSString* const SecCKRecordCurve = @"curve";
+NSString* const SecCKRecordEpoch = @"epoch";
+NSString* const SecCKRecordPoisoned = @"poisoned";
+NSString* const SecCKRecordSignature = @"signature";
+NSString* const SecCKRecordVersion = @"version";
+
NSString* const SecCKRecordCurrentKeyType = @"currentkey";
NSString* const SecCKRecordCurrentItemType = @"currentitem";
CKKSZoneKeyState* const SecCKKSZoneKeyStateUnhealthy = (CKKSZoneKeyState*) @"unhealthy";
CKKSZoneKeyState* const SecCKKSZoneKeyStateBadCurrentPointers = (CKKSZoneKeyState*) @"badcurrentpointers";
CKKSZoneKeyState* const SecCKKSZoneKeyStateNewTLKsFailed = (CKKSZoneKeyState*) @"newtlksfailed";
+CKKSZoneKeyState* const SecCKKSZoneKeyStateHealTLKShares = (CKKSZoneKeyState*) @"healtlkshares";
+CKKSZoneKeyState* const SecCKKSZoneKeyStateHealTLKSharesFailed = (CKKSZoneKeyState*) @"healtlksharesfailed";
+CKKSZoneKeyState* const SecCKKSZoneKeyStateWaitForFixupOperation = (CKKSZoneKeyState*) @"waitforfixupoperation";
NSDictionary<CKKSZoneKeyState*, NSNumber*>* CKKSZoneKeyStateMap(void) {
static NSDictionary<CKKSZoneKeyState*, NSNumber*>* map = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
map = @{
- SecCKKSZoneKeyStateReady: [NSNumber numberWithUnsignedInt: 0],
- SecCKKSZoneKeyStateError: [NSNumber numberWithUnsignedInt: 1],
- SecCKKSZoneKeyStateCancelled: [NSNumber numberWithUnsignedInt: 2],
-
- SecCKKSZoneKeyStateInitializing: [NSNumber numberWithUnsignedInt: 3],
- SecCKKSZoneKeyStateInitialized: [NSNumber numberWithUnsignedInt: 4],
- SecCKKSZoneKeyStateFetchComplete: [NSNumber numberWithUnsignedInt: 5],
- SecCKKSZoneKeyStateWaitForTLK: [NSNumber numberWithUnsignedInt: 6],
- SecCKKSZoneKeyStateWaitForUnlock: [NSNumber numberWithUnsignedInt: 7],
- SecCKKSZoneKeyStateUnhealthy: [NSNumber numberWithUnsignedInt: 8],
- SecCKKSZoneKeyStateBadCurrentPointers: [NSNumber numberWithUnsignedInt: 9],
- SecCKKSZoneKeyStateNewTLKsFailed: [NSNumber numberWithUnsignedInt:10],
+ SecCKKSZoneKeyStateReady: @0U,
+ SecCKKSZoneKeyStateError: @1U,
+ SecCKKSZoneKeyStateCancelled: @2U,
+
+ SecCKKSZoneKeyStateInitializing: @3U,
+ SecCKKSZoneKeyStateInitialized: @4U,
+ SecCKKSZoneKeyStateFetchComplete: @5U,
+ SecCKKSZoneKeyStateWaitForTLK: @6U,
+ SecCKKSZoneKeyStateWaitForUnlock: @7U,
+ SecCKKSZoneKeyStateUnhealthy: @8U,
+ SecCKKSZoneKeyStateBadCurrentPointers: @9U,
+ SecCKKSZoneKeyStateNewTLKsFailed: @10U,
+ SecCKKSZoneKeyStateNeedFullRefetch: @11U,
+ SecCKKSZoneKeyStateHealTLKShares: @12U,
+ SecCKKSZoneKeyStateHealTLKSharesFailed:@13U,
+ SecCKKSZoneKeyStateWaitForFixupOperation:@14U,
};
});
return map;
NSString* const SecCKKSUserDefaultsSuite = @"com.apple.security.ckks";
+NSString* const CKKSErrorDomain = @"CKKSErrorDomain";
+NSString* const CKKSServerExtensionErrorDomain = @"CKKSServerExtensionErrorDomain";
+
#if OCTAGON
static bool enableCKKS = true;
static bool testCKKS = false;
return CKKSEnforceManifests;
}
+static bool CKKSShareTLKs = true;
+bool SecCKKSShareTLKs(void) {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ // Use the default value as above, or apply the preferences value if it exists
+ NSUserDefaults* defaults = [[NSUserDefaults alloc] initWithSuiteName:SecCKKSUserDefaultsSuite];
+ [defaults registerDefaults: @{@"tlksharing": CKKSShareTLKs ? @YES : @NO}];
+
+ CKKSShareTLKs = !![defaults boolForKey:@"tlksharing"];
+ secnotice("ckksshare", "TLK sharing is %@", CKKSShareTLKs ? @"on" : @"off");
+ });
+
+ return CKKSShareTLKs;
+}
+bool SecCKKSEnableShareTLKs(void) {
+ return SecCKKSSetShareTLKs(true);
+}
+bool SecCKKSSetShareTLKs(bool value) {
+ // Call this to do the dispatch_once first
+ SecCKKSShareTLKs();
+
+ CKKSShareTLKs = value;
+ return CKKSShareTLKs;
+}
+
// Feature flags to twiddle behavior for tests
static bool CKKSDisableAutomaticUUID = false;
bool SecCKKSTestDisableAutomaticUUID(void) {
extern CKKSAnalyticsFailableEvent* const CKKSEventProcessIncomingQueueClassA;
extern CKKSAnalyticsFailableEvent* const CKKSEventProcessIncomingQueueClassC;
extern CKKSAnalyticsFailableEvent* const CKKSEventUploadChanges;
+extern CKKSAnalyticsFailableEvent* const CKKSEventStateError;
@protocol CKKSAnalyticsSignpostEvent
@end
+ (instancetype)logger;
- (void)logSuccessForEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view;
-- (void)logRecoverableError:(NSError*)error forEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view;
-- (void)logUnrecoverableError:(NSError*)error forEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view;
+- (void)logRecoverableError:(NSError*)error forEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view withAttributes:(NSDictionary *)attributes;
+- (void)logUnrecoverableError:(NSError*)error forEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view withAttributes:(NSDictionary *)attributes;
- (void)noteEvent:(CKKSAnalyticsSignpostEvent*)event inView:(CKKSKeychainView*)view;
#import "Analytics/SFAnalyticsLogger.h"
#import <os/log.h>
-NSString* const CKKSAnalyticsAttributeRecoverableError = @"recoverableError";
-NSString* const CKKSAnalyticsAttributeZoneName = @"zone";
-NSString* const CKKSAnalyticsAttributeErrorDomain = @"errorDomain";
-NSString* const CKKSAnalyticsAttributeErrorCode = @"errorCode";
-
-NSString* const CKKSAnalyticsHasTLKs = @"TLKs";
-NSString* const CKKSAnalyticsSyncedClassARecently = @"inSyncA";
-NSString* const CKKSAnalyticsSyncedClassCRecently = @"inSyncC";
-NSString* const CKKSAnalyticsIncomingQueueIsErrorFree = @"IQNOE";
-NSString* const CKKSAnalyticsOutgoingQueueIsErrorFree = @"OQNOE";
-NSString* const CKKSAnalyticsInSync = @"inSync";
+static NSString* const CKKSAnalyticsAttributeRecoverableError = @"recoverableError";
+static NSString* const CKKSAnalyticsAttributeZoneName = @"zone";
+static NSString* const CKKSAnalyticsAttributeErrorDomain = @"errorDomain";
+static NSString* const CKKSAnalyticsAttributeErrorCode = @"errorCode";
+
+static NSString* const CKKSAnalyticsInCircle = @"inCircle";
+static NSString* const CKKSAnalyticsDeviceID = @"ckdeviceID";
+static NSString* const CKKSAnalyticsHasTLKs = @"TLKs";
+static NSString* const CKKSAnalyticsSyncedClassARecently = @"inSyncA";
+static NSString* const CKKSAnalyticsSyncedClassCRecently = @"inSyncC";
+static NSString* const CKKSAnalyticsIncomingQueueIsErrorFree = @"IQNOE";
+static NSString* const CKKSAnalyticsOutgoingQueueIsErrorFree = @"OQNOE";
+static NSString* const CKKSAnalyticsInSync = @"inSync";
CKKSAnalyticsFailableEvent* const CKKSEventProcessIncomingQueueClassA = (CKKSAnalyticsFailableEvent*)@"CKKSEventProcessIncomingQueueClassA";
CKKSAnalyticsFailableEvent* const CKKSEventProcessIncomingQueueClassC = (CKKSAnalyticsFailableEvent*)@"CKKSEventProcessIncomingQueueClassC";
CKKSAnalyticsFailableEvent* const CKKSEventUploadChanges = (CKKSAnalyticsFailableEvent*)@"CKKSEventUploadChanges";
+CKKSAnalyticsFailableEvent* const CKKSEventStateError = (CKKSAnalyticsFailableEvent*)@"CKKSEventStateError";
CKKSAnalyticsSignpostEvent* const CKKSEventPushNotificationReceived = (CKKSAnalyticsSignpostEvent*)@"CKKSEventPushNotificationReceived";
CKKSAnalyticsSignpostEvent* const CKKSEventItemAddedToOutgoingQueue = (CKKSAnalyticsSignpostEvent*)@"CKKSEventItemAddedToOutgoingQueue";
[self setDateProperty:[NSDate date] forKey:[NSString stringWithFormat:@"last_success_%@-%@", view.zoneName, event]];
}
-- (void)logRecoverableError:(NSError*)error forEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view
+- (void)logRecoverableError:(NSError*)error forEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view withAttributes:(NSDictionary *)attributes
{
- NSDictionary* attributes = @{ CKKSAnalyticsAttributeRecoverableError : @(YES),
- CKKSAnalyticsAttributeZoneName : view.zoneName,
- CKKSAnalyticsAttributeErrorDomain : error.domain,
- CKKSAnalyticsAttributeErrorCode : @(error.code) };
+ NSDictionary* eventAttributes = @{ CKKSAnalyticsAttributeRecoverableError : @(YES),
+ CKKSAnalyticsAttributeZoneName : view.zoneName,
+ CKKSAnalyticsAttributeErrorDomain : error.domain,
+ CKKSAnalyticsAttributeErrorCode : @(error.code) };
+
+ if (attributes) {
+ /* Don't allow caller to overwrite our attributes */
+ NSMutableDictionary *mergedAttributes = [attributes mutableCopy];
+ [mergedAttributes setValuesForKeysWithDictionary:eventAttributes];
+ eventAttributes = mergedAttributes;
+ }
- [super logSoftFailureForEventNamed:event withAttributes:attributes];
+ [super logSoftFailureForEventNamed:event withAttributes:eventAttributes];
}
-- (void)logUnrecoverableError:(NSError*)error forEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view
+- (void)logUnrecoverableError:(NSError*)error forEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view withAttributes:(NSDictionary *)attributes
{
- NSDictionary* attributes = @{ CKKSAnalyticsAttributeRecoverableError : @(NO),
- CKKSAnalyticsAttributeZoneName : view.zoneName,
- CKKSAnalyticsAttributeErrorDomain : error.domain,
- CKKSAnalyticsAttributeErrorCode : @(error.code) };
+ if (error == nil)
+ return;
+ NSDictionary* eventAttributes = @{ CKKSAnalyticsAttributeRecoverableError : @(NO),
+ CKKSAnalyticsAttributeZoneName : view.zoneName,
+ CKKSAnalyticsAttributeErrorDomain : error.domain,
+ CKKSAnalyticsAttributeErrorCode : @(error.code) };
+
+ if (attributes) {
+ /* Don't allow caller to overwrite our attributes */
+ NSMutableDictionary *mergedAttributes = [attributes mutableCopy];
+ [mergedAttributes setValuesForKeysWithDictionary:eventAttributes];
+ eventAttributes = mergedAttributes;
+ }
- [self logHardFailureForEventNamed:event withAttributes:attributes];
+ [self logHardFailureForEventNamed:event withAttributes:eventAttributes];
}
- (void)noteEvent:(CKKSAnalyticsSignpostEvent*)event inView:(CKKSKeychainView*)view
- (NSDictionary*)extraValuesToUploadToServer
{
NSMutableDictionary* values = [NSMutableDictionary dictionary];
- for (NSString* viewName in [CKKSViewManager viewList]) {
+ CKKSCKAccountStateTracker* accountTracker = [[CKKSViewManager manager] accountTracker];
+ BOOL inCircle = accountTracker && accountTracker.currentCircleStatus == kSOSCCInCircle;
+ values[CKKSAnalyticsInCircle] = @(inCircle);
+
+ NSString *ckdeviceID = accountTracker.ckdeviceID;
+ if (ckdeviceID)
+ values[CKKSAnalyticsDeviceID] = ckdeviceID;
+ for (NSString* viewName in [[CKKSViewManager manager] viewList]) {
CKKSKeychainView* view = [CKKSViewManager findOrCreateView:viewName];
NSDate* dateOfLastSyncClassA = [self dateOfLastSuccessForEvent:CKKSEventProcessIncomingQueueClassA inView:view];
NSDate* dateOfLastSyncClassC = [self dateOfLastSuccessForEvent:CKKSEventProcessIncomingQueueClassC inView:view];
values[incomingQueueIsErrorFreeKey] = @(incomingQueueIsErrorFree);
values[outgoingQueueIsErrorFreeKey] = @(outgoingQueueIsErrorFree);
- BOOL weThinkWeAreInSync = hasTLKs && syncedClassARecently && syncedClassCRecently && incomingQueueIsErrorFree && outgoingQueueIsErrorFree;
+ BOOL weThinkWeAreInSync = inCircle && hasTLKs && syncedClassARecently && syncedClassCRecently && incomingQueueIsErrorFree && outgoingQueueIsErrorFree;
NSString* inSyncKey = [NSString stringWithFormat:@"%@-%@", viewName, CKKSAnalyticsInSync];
values[inSyncKey] = @(weThinkWeAreInSync);
}
@property SOSCCStatus currentCircleStatus;
// Fetched and memoized from CloudKit; we can't afford deadlocks with their callbacks
-@property NSString* ckdeviceID;
-@property NSError* ckdeviceIDError;
-@property CKKSCondition* ckdeviceIDInitialized;
+@property (copy) NSString* ckdeviceID;
+@property NSError* ckdeviceIDError;
+@property CKKSCondition* ckdeviceIDInitialized;
// Fetched and memoized from the Account when we're in-circle; our threading model is strange
@property NSString* accountCirclePeerID;
dispatch_semaphore_t finishedSema = dispatch_semaphore_create(0);
SOSCCStatus circleStatus = [CKKSCKAccountStateTracker getCircleStatus];
- if(circleStatus == kSOSCCError) {
- dispatch_semaphore_signal(finishedSema);
- return finishedSema;
- }
-
dispatch_sync(self.queue, ^{
self.firstSOSCircleFetch = true;
}];
} else {
// Not in-circle, reset circle ID
+ secnotice("ckksaccount", "out of circle(%d): resetting peer ID", sosccstatus);
self.accountCirclePeerID = nil;
self.accountCirclePeerIDError = nil;
self.accountCirclePeerIDInitialized = [[CKKSCondition alloc] init];
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+// You must be 64-bit to use this class.
+#if __OBJC2__
+
+#import <Foundation/Foundation.h>
+
+@interface CKKSControl : NSObject
+
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithConnection:(NSXPCConnection*)connection;
+
+- (void)rpcStatus: (NSString*)viewName reply:(void(^)(NSArray<NSDictionary*>* result, NSError* error)) reply;
+- (void)rpcResetLocal: (NSString*)viewName reply:(void(^)(NSError* error))reply;
+- (void)rpcResetCloudKit: (NSString*)viewName reply:(void(^)(NSError* error))reply;
+- (void)rpcResync: (NSString*)viewName reply:(void(^)(NSError* error))reply;
+- (void)rpcFetchAndProcessChanges: (NSString*)viewName reply:(void(^)(NSError* error))reply;
+- (void)rpcFetchAndProcessClassAChanges: (NSString*)viewName reply:(void(^)(NSError* error))reply;
+- (void)rpcPushOutgoingChanges: (NSString*)viewName reply:(void(^)(NSError* error))reply;
+
+- (void)rpcPerformanceCounters: (void(^)(NSDictionary <NSString *,NSNumber *> *,NSError*))reply;
+- (void)rpcGetAnalyticsSysdiagnoseWithReply:(void (^)(NSString* sysdiagnose, NSError* error))reply;
+- (void)rpcGetAnalyticsJSONWithReply: (void (^)(NSData* json, NSError* error))reply;
+- (void)rpcForceUploadAnalyticsWithReply: (void (^)(BOOL success, NSError* error))reply;
+
+// convenience wrapper for rpcStatus:reply:
+- (void)rpcTLKMissing: (NSString*)viewName reply:(void(^)(bool missing))reply;
+
++ (CKKSControl*)controlObject:(NSError* __autoreleasing *)error;
+
+@end
+
+#endif // __OBJC__
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if __OBJC2__
+
+#import <Foundation/NSXPCConnection_Private.h>
+#import <xpc/xpc.h>
+
+#import <Security/SecItemPriv.h>
+
+#import "keychain/ckks/CKKSControl.h"
+#import "keychain/ckks/CKKSControlProtocol.h"
+
+#include <security_utilities/debugging.h>
+
+@interface CKKSControl ()
+@property xpc_endpoint_t endpoint;
+@property NSXPCConnection *connection;
+@end
+
+@implementation CKKSControl
+
+- (instancetype)initWithConnection:(NSXPCConnection*)connection {
+ if(self = [super init]) {
+ _connection = connection;
+ }
+ return self;
+}
+
+- (void)rpcStatus:(NSString*)viewName reply:(void(^)(NSArray<NSDictionary*>* result, NSError* error)) reply {
+ [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
+ reply(nil, error);
+
+ }] rpcStatus:viewName reply:^(NSArray<NSDictionary*>* result, NSError* error){
+ reply(result, error);
+ }];
+}
+
+- (void)rpcResetLocal:(NSString*)viewName reply:(void(^)(NSError* error))reply {
+ [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
+ reply(error);
+ }] rpcResetLocal:viewName reply:^(NSError* error){
+ reply(error);
+ }];
+}
+
+- (void)rpcResetCloudKit:(NSString*)viewName reply:(void(^)(NSError* error))reply {
+ [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
+ reply(error);
+ }] rpcResetCloudKit:viewName reply:^(NSError* error){
+ reply(error);
+ }];
+}
+
+- (void)rpcResync:(NSString*)viewName reply:(void(^)(NSError* error))reply {
+ [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
+ reply(error);
+ }] rpcResync:viewName reply:^(NSError* error){
+ reply(error);
+ }];
+}
+- (void)rpcFetchAndProcessChanges:(NSString*)viewName reply:(void(^)(NSError* error))reply {
+ [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
+ reply(error);
+ }] rpcFetchAndProcessChanges:viewName reply:^(NSError* error){
+ reply(error);
+ }];
+}
+- (void)rpcFetchAndProcessClassAChanges:(NSString*)viewName reply:(void(^)(NSError* error))reply {
+ [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
+ reply(error);
+ }] rpcFetchAndProcessClassAChanges:viewName reply:^(NSError* error){
+ reply(error);
+ }];
+}
+- (void)rpcPushOutgoingChanges:(NSString*)viewName reply:(void(^)(NSError* error))reply {
+ [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
+ reply(error);
+ }] rpcPushOutgoingChanges:viewName reply:^(NSError* error){
+ reply(error);
+ }];
+}
+
+- (void)rpcPerformanceCounters:(void(^)(NSDictionary <NSString *,NSNumber *> *,NSError*))reply {
+ [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
+ reply(nil, error);
+ }] performanceCounters:^(NSDictionary <NSString *, NSNumber *> *counters){
+ reply(counters, nil);
+ }];
+}
+
+- (void)rpcGetAnalyticsSysdiagnoseWithReply:(void (^)(NSString* sysdiagnose, NSError* error))reply {
+ [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
+ reply(nil, error);
+ }] rpcGetAnalyticsSysdiagnoseWithReply:^(NSString* sysdiagnose, NSError* error){
+ reply(sysdiagnose, error);
+ }];
+}
+
+- (void)rpcGetAnalyticsJSONWithReply:(void (^)(NSData* json, NSError* error))reply {
+ [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
+ reply(nil, error);
+ }] rpcGetAnalyticsJSONWithReply:^(NSData* json, NSError* error){
+ reply(json, error);
+ }];
+}
+
+- (void)rpcForceUploadAnalyticsWithReply: (void (^)(BOOL success, NSError* error))reply {
+ [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
+ reply(false, error);
+ }] rpcForceUploadAnalyticsWithReply:^(BOOL success, NSError* error){
+ reply(success, error);
+ }];
+}
+
+- (void)rpcTLKMissing:(NSString*)viewName reply:(void(^)(bool missing))reply {
+ [self rpcStatus:viewName reply:^(NSArray<NSDictionary*>* results, NSError* blockError) {
+ bool missing = false;
+
+ // Until PCS fixes [<rdar://problem/35103941> PCS: Remove PCS's use of CKKSControlProtocol], we can't add things to the protocol
+ // Use this hack
+ for(NSDictionary* result in results) {
+ NSString* name = result[@"view"];
+ NSString* keystate = result[@"keystate"];
+
+ if([name isEqualToString:@"global"]) {
+ // this is global status; no view implicated
+ continue;
+ }
+
+ if ([keystate isEqualToString:@"waitfortlk"] || [keystate isEqualToString:@"error"]) {
+ missing = true;
+ }
+ }
+
+ reply(missing);
+ }];
+}
+
++ (CKKSControl*)controlObject:(NSError* __autoreleasing *)error {
+
+ CFErrorRef cferror = NULL;
+ xpc_endpoint_t endpoint = _SecSecuritydCopyCKKSEndpoint(&cferror);
+ if (endpoint == NULL) {
+ NSString* errorstr = NULL;
+
+ if(cferror) {
+ errorstr = CFBridgingRelease(CFErrorCopyDescription(cferror));
+ }
+
+ NSString* errorDescription = [NSString stringWithFormat:@"no CKKSControl endpoint available: %@", errorstr ? errorstr : @"unknown error"];
+ if(error) {
+ *error = [NSError errorWithDomain:@"securityd" code:-1 userInfo:@{NSLocalizedDescriptionKey: errorDescription}];
+ }
+ return nil;
+ }
+
+ NSXPCListenerEndpoint *listenerEndpoint = [[NSXPCListenerEndpoint alloc] init];
+ [listenerEndpoint _setEndpoint:endpoint];
+ NSXPCConnection* connection = [[NSXPCConnection alloc] initWithListenerEndpoint:listenerEndpoint];
+
+ if (connection == nil) {
+ if(error) {
+ *error = [NSError errorWithDomain:@"securityd" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Couldn't create connection (no reason given)"}];
+ }
+ return nil;
+ }
+
+ NSXPCInterface *interface = CKKSSetupControlProtocol([NSXPCInterface interfaceWithProtocol:@protocol(CKKSControlProtocol)]);
+ connection.remoteObjectInterface = interface;
+ [connection resume];
+
+ CKKSControl* c = [[CKKSControl alloc] initWithConnection:connection];
+ return c;
+}
+
+@end
+
+#endif // __OBJC2__
char *classes[] = {
"CKPrettyError",
"CKRecordID",
+ "NSArray",
+ "NSData",
+ "NSDate",
+ "NSDictionary",
+ "NSError",
+ "NSNull",
+ "NSNumber",
+ "NSOrderedSet",
+ "NSSet",
+ "NSString",
"NSURL",
- "NSError"
};
for (unsigned n = 0; n < sizeof(classes)/sizeof(classes[0]); n++) {
[interface setClasses:errClasses forSelector:@selector(rpcResetLocal:reply:) argumentIndex:0 ofReply:YES];
[interface setClasses:errClasses forSelector:@selector(rpcResetCloudKit:reply:) argumentIndex:0 ofReply:YES];
[interface setClasses:errClasses forSelector:@selector(rpcResync:reply:) argumentIndex:0 ofReply:YES];
+ [interface setClasses:errClasses forSelector:@selector(rpcStatus:reply:) argumentIndex:0 ofReply:YES];
[interface setClasses:errClasses forSelector:@selector(rpcStatus:reply:) argumentIndex:1 ofReply:YES];
[interface setClasses:errClasses forSelector:@selector(rpcFetchAndProcessChanges:reply:) argumentIndex:0 ofReply:YES];
[interface setClasses:errClasses forSelector:@selector(rpcFetchAndProcessClassAChanges:reply:) argumentIndex:0 ofReply:YES];
+ (NSArray<CKKSCurrentItemPointer*>*)remoteItemPointers: (CKRecordZoneID*)zoneID error:(NSError * __autoreleasing *)error;
+ (bool) deleteAll:(CKRecordZoneID*)zoneID error:(NSError * __autoreleasing *) error;
++ (NSArray<CKKSCurrentItemPointer*>*)allInZone:(CKRecordZoneID*)zoneID error:(NSError * __autoreleasing *)error;
@end
return [self allWhere: @{@"state": SecCKKSProcessedStateRemote, @"ckzone":zoneID.zoneName} error:error];
}
++ (NSArray<CKKSCurrentItemPointer*>*)allInZone:(CKRecordZoneID*)zoneID error:(NSError * __autoreleasing *)error {
+ return [self allWhere: @{@"ckzone":zoneID.zoneName} error:error];
+}
+
+ (bool)deleteAll:(CKRecordZoneID*)zoneID error:(NSError * __autoreleasing *)error {
bool ok = [CKKSSQLDatabaseObject deleteFromTable:[self sqlTable] where: @{@"ckzone":zoneID.zoneName} connection:nil error: error];
}
- (NSDictionary<NSString*,NSString*>*) whereClauseToFindSelf {
- return @{@"identifier": self.identifier, @"ckzone":self.zoneID.zoneName, @"currentItemUUID": CKKSNilToNSNull(self.currentItemUUID), @"state":self.state};
+ return @{@"identifier": self.identifier, @"ckzone":self.zoneID.zoneName, @"state":self.state};
}
- (NSDictionary<NSString*,NSString*>*)sqlValues {
#import "keychain/ckks/CKKS.h"
#import "keychain/ckks/CKKSItem.h"
#import "keychain/ckks/CKKSKey.h"
+#import "keychain/ckks/CKKSTLKShare.h"
+
#if OCTAGON
@property CKKSCurrentKeyPointer* currentClassAPointer;
@property CKKSCurrentKeyPointer* currentClassCPointer;
+@property NSArray<CKKSTLKShare*>* tlkShares;
+
-(instancetype)init;
-(instancetype)initForZone:(CKRecordZoneID*)zoneID;
@end
_classA = _currentClassAPointer.currentKeyUUID ? [CKKSKey tryFromDatabase:_currentClassAPointer.currentKeyUUID zoneID:zoneID error:&error] : nil;
_classC = _currentClassCPointer.currentKeyUUID ? [CKKSKey tryFromDatabase:_currentClassCPointer.currentKeyUUID zoneID:zoneID error:&error] : nil;
+ _tlkShares = [CKKSTLKShare allForUUID:_currentTLKPointer.currentKeyUUID zoneID:zoneID error:&error];
+
_error = error;
+
}
return self;
NSMutableDictionary<NSString*, NSMutableArray*>* changedRecordsDict = [[NSMutableDictionary alloc] init];
- [blockCKKS dispatchSyncWithAccountQueue:^bool{
+ [blockCKKS dispatchSyncWithAccountKeys:^bool{
// let's process records in a specific order by type
// 1. Manifest leaf records, without which the manifest master records are meaningless
// 2. Manifest master records, which will be used to validate incoming items
[strongSelf _onqueueRecordsChanged:changedRecordsDict[SecCKRecordItemType]];
[strongSelf _onqueueRecordsChanged:changedRecordsDict[SecCKRecordCurrentItemType]];
[strongSelf _onqueueRecordsChanged:changedRecordsDict[SecCKRecordDeviceStateType]];
+ [strongSelf _onqueueRecordsChanged:changedRecordsDict[SecCKRecordTLKShareType]];
[strongSelf _onqueueProcessRecordDeletions];
[strongSelf _onqueueScanForExtraneousLocalItems];
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import <Foundation/Foundation.h>
+#import "keychain/ckks/CKKSResultOperation.h"
+#import "keychain/ckks/CKKSGroupOperation.h"
+#import "keychain/ckks/CKKSKeychainView.h"
+
+// Sometimes things go wrong.
+// Sometimes you have to clean up after your past self.
+// This contains the fixes.
+
+typedef NS_ENUM(NSUInteger, CKKSFixup) {
+ CKKSFixupNever,
+ CKKSFixupRefetchCurrentItemPointers,
+ CKKSFixupFetchTLKShares,
+};
+#define CKKSCurrentFixupNumber (SecCKKSShareTLKs() ? CKKSFixupFetchTLKShares : CKKSFixupRefetchCurrentItemPointers)
+
+@interface CKKSFixups : NSObject
++(CKKSGroupOperation*)fixup:(CKKSFixup)lastfixup for:(CKKSKeychainView*)keychainView;
+@end
+
+// Fixup declarations. You probably don't need to look at these
+@interface CKKSFixupRefetchAllCurrentItemPointers : CKKSGroupOperation
+@property (weak) CKKSKeychainView* ckks;
+- (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*)keychainView ckoperationGroup:(CKOperationGroup *)ckoperationGroup;
+@end
+
+@interface CKKSFixupFetchAllTLKShares : CKKSGroupOperation
+@property (weak) CKKSKeychainView* ckks;
+- (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*)keychainView ckoperationGroup:(CKOperationGroup *)ckoperationGroup;
+@end
+
+#endif // OCTAGON
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import "keychain/ckks/CKKSFixups.h"
+#import "keychain/ckks/CloudKitCategories.h"
+#import "keychain/ckks/CKKSCurrentItemPointer.h"
+#import "keychain/ckks/CKKSZoneStateEntry.h"
+
+@implementation CKKSFixups
++(CKKSGroupOperation*)fixup:(CKKSFixup)lastfixup for:(CKKSKeychainView*)keychainView
+{
+ if(lastfixup == CKKSCurrentFixupNumber) {
+ return nil;
+ }
+
+ CKOperationGroup* fixupCKGroup = [CKOperationGroup CKKSGroupWithName:@"fixup"];
+ CKKSGroupOperation* fixups = [[CKKSGroupOperation alloc] init];
+ fixups.name = @"ckks-fixups";
+
+ CKKSResultOperation* previousOp = nil;
+
+ if(lastfixup < CKKSFixupRefetchCurrentItemPointers) {
+ CKKSResultOperation* refetch = [[CKKSFixupRefetchAllCurrentItemPointers alloc] initWithCKKSKeychainView:keychainView
+ ckoperationGroup:fixupCKGroup];
+ [refetch addNullableSuccessDependency:previousOp];
+ [fixups runBeforeGroupFinished:refetch];
+ previousOp = refetch;
+ }
+
+ if(SecCKKSShareTLKs() && lastfixup < CKKSFixupFetchTLKShares) {
+ CKKSResultOperation* fetchShares = [[CKKSFixupFetchAllTLKShares alloc] initWithCKKSKeychainView:keychainView
+ ckoperationGroup:fixupCKGroup];
+ [fetchShares addNullableSuccessDependency:previousOp];
+ [fixups runBeforeGroupFinished:fetchShares];
+ previousOp = fetchShares;
+ }
+
+ return fixups;
+}
+@end
+
+#pragma mark - CKKSFixupRefetchAllCurrentItemPointers
+
+@interface CKKSFixupRefetchAllCurrentItemPointers ()
+@property CKOperationGroup* group;
+@end
+
+// In <rdar://problem/34916549> CKKS: current item pointer CKRecord resurrection,
+// We found that some devices could end up with multiple current item pointer records for a given record ID.
+// This fixup will fetch all CKRecords matching any existing current item pointer records, and then trigger processing.
+@implementation CKKSFixupRefetchAllCurrentItemPointers
+- (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*)keychainView
+ ckoperationGroup:(CKOperationGroup *)ckoperationGroup
+{
+ if((self = [super init])) {
+ _ckks = keychainView;
+ _group = ckoperationGroup;
+ }
+ return self;
+}
+
+- (NSString*)description {
+ return [NSString stringWithFormat:@"<CKKSFixup:RefetchAllCurrentItemPointers (%@)>", self.ckks];
+}
+- (void)groupStart {
+ CKKSKeychainView* ckks = self.ckks;
+ if(!ckks) {
+ ckkserror("ckksfixup", ckks, "no CKKS object");
+ self.error = [NSError errorWithDomain:@"securityd" code:errSecInternalError userInfo:@{NSLocalizedDescriptionKey: @"no CKKS object"}];
+ return;
+ }
+
+ [ckks dispatchSyncWithAccountKeys:^bool {
+ NSError* error = nil;
+
+ NSArray<CKKSCurrentItemPointer*>* cips = [CKKSCurrentItemPointer allInZone: ckks.zoneID error:&error];
+ if(error) {
+ ckkserror("ckksfixup", ckks, "Couldn't fetch current item pointers: %@", error);
+ return false;
+ }
+
+ NSMutableSet<CKRecordID*>* recordIDs = [NSMutableSet set];
+ for(CKKSCurrentItemPointer* cip in cips) {
+ CKRecordID* recordID = cip.storedCKRecord.recordID;
+ if(recordID) {
+ ckksnotice("ckksfixup", ckks, "Re-fetching %@ for %@", recordID, cip);
+ [recordIDs addObject:recordID];
+ } else {
+ ckkserror("ckksfixup", ckks, "No record ID for stored %@", cip);
+ }
+ }
+
+ if(recordIDs.count == 0) {
+ ckksnotice("ckksfixup", ckks, "No existing CIPs; fixup complete");
+ }
+
+ __weak __typeof(self) weakSelf = self;
+ NSBlockOperation* doneOp = [NSBlockOperation named:@"fetch-records-operation-complete" withBlock:^{}];
+ id<CKKSFetchRecordsOperation> fetch = [[ckks.fetchRecordsOperationClass alloc] initWithRecordIDs: [recordIDs allObjects]];
+ fetch.fetchRecordsCompletionBlock = ^(NSDictionary<CKRecordID *,CKRecord *> * _Nullable recordsByRecordID, NSError * _Nullable error) {
+ __strong __typeof(self) strongSelf = weakSelf;
+ CKKSKeychainView* strongCKKS = strongSelf.ckks;
+
+ [strongCKKS dispatchSync:^bool{
+ if(error) {
+ ckkserror("ckksfixup", strongCKKS, "Finished record fetch with error: %@", error);
+
+ NSDictionary<CKRecordID*,NSError*>* partialErrors = error.userInfo[CKPartialErrorsByItemIDKey];
+ if([error.domain isEqualToString:CKErrorDomain] && error.code == CKErrorPartialFailure && partialErrors) {
+ // Check if any of these records no longer exist on the server
+ for(CKRecordID* recordID in partialErrors.keyEnumerator) {
+ NSError* recordError = partialErrors[recordID];
+ if(recordError && [recordError.domain isEqualToString:CKErrorDomain] && recordError.code == CKErrorUnknownItem) {
+ ckkserror("ckksfixup", strongCKKS, "CloudKit believes %@ no longer exists", recordID);
+ [strongCKKS _onqueueCKRecordDeleted: recordID recordType:SecCKRecordCurrentItemType resync:true];
+ } else {
+ ckkserror("ckksfixup", strongCKKS, "Unknown error for %@: %@", recordID, error);
+ strongSelf.error = error;
+ }
+ }
+ } else {
+ strongSelf.error = error;
+ }
+ } else {
+ ckksnotice("ckksfixup", strongCKKS, "Finished record fetch successfully");
+ }
+
+ for(CKRecordID* recordID in recordsByRecordID) {
+ CKRecord* record = recordsByRecordID[recordID];
+ ckksnotice("ckksfixup", strongCKKS, "Recieved record %@", record);
+ [self.ckks _onqueueCKRecordChanged:record resync:true];
+ }
+
+ if(!strongSelf.error) {
+ // Now, update the zone state entry to be at this level
+ NSError* localerror = nil;
+ CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry fromDatabase:strongCKKS.zoneName error:&localerror];
+ ckse.lastFixup = CKKSFixupRefetchCurrentItemPointers;
+ [ckse saveToDatabase:&localerror];
+ if(localerror) {
+ ckkserror("ckksfixup", strongCKKS, "Couldn't save CKKSZoneStateEntry(%@): %@", ckse, localerror);
+ } else {
+ ckksnotice("ckksfixup", strongCKKS, "Updated zone fixup state to CKKSFixupRefetchCurrentItemPointers");
+ }
+ }
+
+ [strongSelf runBeforeGroupFinished:doneOp];
+ return true;
+ }];
+ };
+ [ckks.database addOperation: fetch];
+ [self dependOnBeforeGroupFinished:fetch];
+ [self dependOnBeforeGroupFinished:doneOp];
+
+ return true;
+ }];
+}
+@end
+
+#pragma mark - CKKSFixupFetchAllTLKShares
+
+@interface CKKSFixupFetchAllTLKShares ()
+@property CKOperationGroup* group;
+@end
+
+// In <rdar://problem/34901306> CKKSTLK: TLKShare CloudKit upload/download on TLK change, trust set addition
+// We introduced TLKShare records.
+// Older devices will throw them away, so on upgrade, they must refetch them
+@implementation CKKSFixupFetchAllTLKShares
+- (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*)keychainView
+ ckoperationGroup:(CKOperationGroup *)ckoperationGroup
+{
+ if((self = [super init])) {
+ _ckks = keychainView;
+ _group = ckoperationGroup;
+ }
+ return self;
+}
+
+- (NSString*)description {
+ return [NSString stringWithFormat:@"<CKKSFixup:FetchAllTLKShares (%@)>", self.ckks];
+}
+- (void)groupStart {
+ CKKSKeychainView* ckks = self.ckks;
+ if(!ckks) {
+ ckkserror("ckksfixup", ckks, "no CKKS object");
+ self.error = [NSError errorWithDomain:@"securityd" code:errSecInternalError userInfo:@{NSLocalizedDescriptionKey: @"no CKKS object"}];
+ return;
+ }
+
+ [ckks dispatchSyncWithAccountKeys:^bool {
+ __weak __typeof(self) weakSelf = self;
+ NSBlockOperation* doneOp = [NSBlockOperation named:@"fetch-records-operation-complete" withBlock:^{}];
+
+ NSPredicate *yes = [NSPredicate predicateWithValue:YES];
+ CKQuery *query = [[CKQuery alloc] initWithRecordType:SecCKRecordTLKShareType predicate:yes];
+
+ id<CKKSQueryOperation> fetch = [[ckks.queryOperationClass alloc] initWithQuery:query];
+ fetch.zoneID = ckks.zoneID;
+ fetch.desiredKeys = nil;
+
+ fetch.recordFetchedBlock = ^(CKRecord * _Nonnull record) {
+ __strong __typeof(self) strongSelf = weakSelf;
+ CKKSKeychainView* strongCKKS = strongSelf.ckks;
+ [strongCKKS dispatchSync:^bool{
+ ckksnotice("ckksfixup", strongCKKS, "Recieved tlk share record from query: %@", record);
+
+ [strongCKKS _onqueueCKRecordChanged:record resync:true];
+ return true;
+ }];
+ };
+
+ fetch.queryCompletionBlock = ^(CKQueryCursor * _Nullable cursor, NSError * _Nullable error) {
+ __strong __typeof(self) strongSelf = weakSelf;
+ CKKSKeychainView* strongCKKS = strongSelf.ckks;
+
+ [strongCKKS dispatchSync:^bool{
+ if(error) {
+ ckkserror("ckksfixup", strongCKKS, "Couldn't fetch all TLKShare records: %@", error);
+ strongSelf.error = error;
+ return false;
+ }
+
+ ckksnotice("ckksfixup", strongCKKS, "Successfully fetched TLKShare records (%@)", cursor);
+
+ NSError* localerror = nil;
+ CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry fromDatabase:strongCKKS.zoneName error:&localerror];
+ ckse.lastFixup = CKKSFixupFetchTLKShares;
+ [ckse saveToDatabase:&localerror];
+ if(localerror) {
+ ckkserror("ckksfixup", strongCKKS, "Couldn't save CKKSZoneStateEntry(%@): %@", ckse, localerror);
+ } else {
+ ckksnotice("ckksfixup", strongCKKS, "Updated zone fixup state to CKKSFixupFetchTLKShares");
+ }
+ return true;
+ }];
+ [strongSelf runBeforeGroupFinished:doneOp];
+ };
+
+ [ckks.database addOperation: fetch];
+ [self dependOnBeforeGroupFinished:fetch];
+ [self dependOnBeforeGroupFinished:doneOp];
+
+ return true;
+ }];
+}
+@end
+
+
+#endif // OCTAGON
#import <Foundation/Foundation.h>
#include <dispatch/dispatch.h>
-
-@class CKKSCondition;
-
-@interface NSOperation (CKKSUsefulPrintingOperation)
-- (NSString*)description;
-- (BOOL)isPending;
-
-// If op is nonnull, op becomes a dependency of this operation
-- (void)addNullableDependency: (NSOperation*) op;
-
-// Add all operations in this collection as dependencies, then add yourself to the collection
--(void)linearDependencies:(NSHashTable*)collection;
-
-// Insert yourself as high up the linearized list of dependencies as possible
--(void)linearDependenciesWithSelfFirst: (NSHashTable*) collection;
-@end
-
-@interface NSBlockOperation (CKKSUsefulConstructorOperation)
-+(instancetype)named: (NSString*)name withBlock: (void(^)(void)) block;
-@end
-
-
-#define CKKSResultErrorDomain @"CKKSResultOperationError"
-enum {
- CKKSResultSubresultError = 1,
- CKKSResultSubresultCancelled = 2,
- CKKSResultTimedOut = 3,
-};
-
-@interface CKKSResultOperation : NSBlockOperation
-@property NSError* error;
-@property NSDate* finishDate;
-@property CKKSCondition* completionHandlerDidRunCondition;
-
-// Very similar to addDependency, but:
-// if the dependent operation has an error or is canceled, cancel this operation
-- (void)addSuccessDependency: (CKKSResultOperation*) operation;
-
-// Call to check if you should run.
-// Note: all subclasses must call this if they'd like to comply with addSuccessDependency
-// Also sets your .error property to encapsulate the upstream error
-- (bool)allDependentsSuccessful;
-
-// Allows you to time out CKKSResultOperations: if they haven't started by now, they'll cancel themselves
-// and set their error to indicate the timeout
-- (instancetype)timeout:(dispatch_time_t)timeout;
-
-// Convenience constructor.
-+(instancetype)operationWithBlock:(void (^)(void))block;
-+(instancetype)named: (NSString*)name withBlock: (void(^)(void)) block;
-@end
+#import "keychain/ckks/CKKSResultOperation.h"
@interface CKKSGroupOperation : CKKSResultOperation {
BOOL executing;
*/
#import "CKKSGroupOperation.h"
-#import "CKKSCondition.h"
#include <utilities/debugging.h>
-@implementation NSOperation (CKKSUsefulPrintingOperation)
-- (NSString*)selfname {
- if(self.name) {
- return [NSString stringWithFormat: @"%@(%@)", NSStringFromClass([self class]), self.name];
- } else {
- return NSStringFromClass([self class]);
- }
-}
-
--(void)linearDependencies: (NSHashTable*) collection {
- @synchronized(collection) {
- for(NSOperation* existingop in collection) {
- if(existingop == self) {
- // don't depend on yourself
- continue;
- }
- [self addDependency: existingop];
- }
- [collection addObject:self];
- }
-}
-
--(void)linearDependenciesWithSelfFirst: (NSHashTable*) collection {
- @synchronized(collection) {
- for(NSOperation* existingop in collection) {
- if(existingop == self) {
- // don't depend on yourself
- continue;
- }
-
- if([existingop isPending]) {
- [existingop addDependency: self];
- if([existingop isPending]) {
- // Good, we're ahead of this one.
- } else {
- // It started before we told it to wait on us. Reverse the dependency.
- [existingop removeDependency: self];
- [self addDependency:existingop];
- }
- } else {
- // Not a pending op? We depend on it.
- [self addDependency: existingop];
- }
- }
- [collection addObject:self];
- }
-}
-
--(NSString*)pendingDependenciesString:(NSString*)prefix {
- NSArray* dependencies = [self.dependencies copy];
- dependencies = [dependencies objectsAtIndexes: [dependencies indexesOfObjectsPassingTest: ^BOOL (id obj,
- NSUInteger idx,
- BOOL* stop) {
- return [obj isPending] ? YES : NO;
- }]];
-
- if(dependencies.count == 0u) {
- return @"";
- }
-
- return [NSString stringWithFormat: @"%@%@", prefix, [dependencies componentsJoinedByString: @", "]];
-}
-
-- (NSString*)description {
- NSString* state = ([self isFinished] ? @"finished" :
- [self isCancelled] ? @"cancelled" :
- [self isExecuting] ? @"executing" :
- [self isReady] ? @"ready" :
- @"pending");
-
- return [NSString stringWithFormat: @"<%@: %@%@>", [self selfname], state, [self pendingDependenciesString: @" dep:"]];
-}
-- (NSString*)debugDescription {
- NSString* state = ([self isFinished] ? @"finished" :
- [self isCancelled] ? @"cancelled" :
- [self isExecuting] ? @"executing" :
- [self isReady] ? @"ready" :
- @"pending");
-
- return [NSString stringWithFormat: @"<%@ (%p): %@%@>", [self selfname], self, state, [self pendingDependenciesString: @" dep:"]];
-}
-
-- (BOOL)isPending {
- return (!([self isExecuting] || [self isFinished])) ? YES : NO;
-}
-
-- (void)addNullableDependency: (NSOperation*) op {
- if(op) {
- [self addDependency:op];
- }
-}
-@end
-
-@implementation NSBlockOperation (CKKSUsefulConstructorOperation)
-+(instancetype)named: (NSString*)name withBlock: (void(^)(void)) block {
- // How many blocks could a block block if a block could block blocks?
- NSBlockOperation* blockOp = [NSBlockOperation blockOperationWithBlock: block];
- blockOp.name = name;
- return blockOp;
-}
-@end
-
-
-@interface CKKSResultOperation()
-@property NSMutableArray<CKKSResultOperation*>* successDependencies;
-@property bool timeoutCanOccur;
-@property dispatch_queue_t timeoutQueue;
-@property void (^finishingBlock)(void);
-@end
-
-@implementation CKKSResultOperation
-- (instancetype)init {
- if(self = [super init]) {
- _error = nil;
- _successDependencies = [[NSMutableArray alloc] init];
- _timeoutCanOccur = true;
- _timeoutQueue = dispatch_queue_create("result-operation-timeout", DISPATCH_QUEUE_SERIAL);
- _completionHandlerDidRunCondition = [[CKKSCondition alloc] init];
-
- __weak __typeof(self) weakSelf = self;
- _finishingBlock = ^(void) {
- weakSelf.finishDate = [NSDate dateWithTimeIntervalSinceNow:0];
- };
- self.completionBlock = ^{}; // our _finishing block gets added in the method override
- }
- return self;
-}
-
-- (NSString*)description {
- NSString* state = ([self isFinished] ? [NSString stringWithFormat:@"finished %@", self.finishDate] :
- [self isCancelled] ? @"cancelled" :
- [self isExecuting] ? @"executing" :
- [self isReady] ? @"ready" :
- @"pending");
-
- if(self.error) {
- return [NSString stringWithFormat: @"<%@: %@ error:%@>", [self selfname], state, self.error];
- } else {
- return [NSString stringWithFormat: @"<%@: %@%@>", [self selfname], state, [self pendingDependenciesString:@" dep:"]];
- }
-}
-
-- (NSString*)debugDescription {
- return [self description];
-}
-
-- (void)setCompletionBlock:(void (^)(void))completionBlock
-{
- __weak __typeof(self) weakSelf = self;
- [super setCompletionBlock:^(void) {
- __strong __typeof(self) strongSelf = weakSelf;
- if (!strongSelf) {
- secerror("ckksresultoperation: completion handler called on deallocated operation instance");
- completionBlock(); // go ahead and still behave as things would if this method override were not here
- return;
- }
-
- strongSelf.finishingBlock();
- completionBlock();
- [strongSelf.completionHandlerDidRunCondition fulfill];
- }];
-}
-
-- (void)start {
- if(![self allDependentsSuccessful]) {
- secdebug("ckksresultoperation", "Not running due to some failed dependent: %@", self.error);
- [self cancel];
- } else {
- dispatch_sync(self.timeoutQueue, ^{
- if(![self isCancelled]) {
- self.timeoutCanOccur = false;
- };
- });
- }
-
- [super start];
-}
-
-- (instancetype)timeout:(dispatch_time_t)timeout {
- __weak __typeof(self) weakSelf = self;
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeout), self.timeoutQueue, ^{
- __strong __typeof(self) strongSelf = weakSelf;
- if(strongSelf.timeoutCanOccur) {
- strongSelf.error = [NSError errorWithDomain:CKKSResultErrorDomain code: CKKSResultTimedOut userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Operation timed out waiting to start for [%@]", [self pendingDependenciesString:@""]]}];
- strongSelf.timeoutCanOccur = false;
- [strongSelf cancel];
- }
- });
-
- return self;
-}
-
-- (void)addSuccessDependency: (CKKSResultOperation*) operation {
- if(!operation) {
- return;
- }
- @synchronized(self) {
- [self.successDependencies addObject: operation];
- [self addDependency: operation];
- }
-}
-
-- (bool)allDependentsSuccessful {
- return [self allSuccessful: self.successDependencies];
-}
-
-- (bool)allSuccessful: (NSArray<CKKSResultOperation*>*) operations {
- @synchronized(self) {
- bool result = false;
-
- bool finished = true; // all dependents must be finished
- bool cancelled = false; // no dependents can be cancelled
- bool failed = false; // no dependents can have failed
-
- for(CKKSResultOperation* op in operations) {
- finished &= !!([op isFinished]);
- cancelled |= !!([op isCancelled]);
- failed |= (op.error != nil);
-
- // TODO: combine suberrors
- if(op.error != nil) {
- if([op.error.domain isEqual: CKKSResultErrorDomain] && op.error.code == CKKSResultSubresultError) {
- // Already a subresult, just copy it on in
- self.error = op.error;
- } else {
- self.error = [NSError errorWithDomain:CKKSResultErrorDomain code: CKKSResultSubresultError userInfo:@{ NSUnderlyingErrorKey: op.error}];
- }
- }
- }
-
- result = finished && !( cancelled || failed );
-
- if(!result && self.error == nil) {
- self.error = [NSError errorWithDomain:CKKSResultErrorDomain code: CKKSResultSubresultCancelled userInfo:nil];
- }
- return result;
- }
-}
-
-+ (CKKSResultOperation*)operationWithBlock:(void (^)(void))block {
- CKKSResultOperation* op = [[CKKSResultOperation alloc] init];
- [op addExecutionBlock: block];
- return op;
-}
-
-+(instancetype)named:(NSString*)name withBlock:(void(^)(void)) block {
- CKKSResultOperation* blockOp = [CKKSResultOperation operationWithBlock: block];
- blockOp.name = name;
- return blockOp;
-}
-@end
-
-
@interface CKKSGroupOperation()
+@property bool fillInError;
@property NSBlockOperation* startOperation;
@property NSBlockOperation* finishOperation;
+@property dispatch_queue_t queue;
@property NSMutableArray<CKKSResultOperation*>* internalSuccesses;
@end
-
@implementation CKKSGroupOperation
- (instancetype)init {
if(self = [super init]) {
__weak __typeof(self) weakSelf = self;
+ _fillInError = true;
+
_operationQueue = [[NSOperationQueue alloc] init];
_internalSuccesses = [[NSMutableArray alloc] init];
+ _queue = dispatch_queue_create("CKKSGroupOperationDispatchQueue", DISPATCH_QUEUE_SERIAL);
+
// At start, we'll call this method (for subclasses)
_startOperation = [NSBlockOperation blockOperationWithBlock:^{
__strong __typeof(weakSelf) strongSelf = weakSelf;
}
}
+// We are pending if our start operation is pending but we are also not cancelled yet
- (BOOL)isPending {
- return [self.startOperation isPending];
+ return [self.startOperation isPending] && ![self isCancelled];
}
- (void)setName:(NSString*) name {
}
- (BOOL)isExecuting {
- return self->executing;
+ __block BOOL ret = FALSE;
+ dispatch_sync(self.queue, ^{
+ ret = self->executing;
+ });
+ return ret;
}
- (BOOL)isFinished {
- return self->finished;
+ __block BOOL ret = FALSE;
+ dispatch_sync(self.queue, ^{
+ ret = self->finished;
+ });
+ return ret;
}
- (void)start {
+ [self invalidateTimeout];
+
if([self isCancelled]) {
[self willChangeValueForKey:@"isFinished"];
- finished = YES;
+ dispatch_sync(self.queue, ^{
+ self->finished = YES;
+ });
[self didChangeValueForKey:@"isFinished"];
return;
}
[self.operationQueue addOperation: self.startOperation];
[self willChangeValueForKey:@"isExecuting"];
- executing = YES;
+ dispatch_sync(self.queue, ^{
+ self->executing = YES;
+ });
[self didChangeValueForKey:@"isExecuting"];
}
- (void)cancel {
- [self.operationQueue cancelAllOperations];
- [self.startOperation cancel];
+
+ // Block off the start operation
+ NSBlockOperation* block = [NSBlockOperation blockOperationWithBlock:^{}];
+ [self.startOperation addDependency: block];
[super cancel];
- // Our finishoperation might not fire (as we cancelled it above), so let's help it out
- [self completeOperation];
+ // Cancel all operations currently on the queue, except for the finish operation
+ NSArray<NSOperation*>* ops = [self.operationQueue.operations copy];
+ for(NSOperation* op in ops) {
+ if(![op isEqual: self.finishOperation]) {
+ [op cancel];
+ }
+ }
+
+ NSArray<NSOperation*>* finishDependencies = [self.finishOperation.dependencies copy];
+ for(NSOperation* finishDep in finishDependencies) {
+ if(![ops containsObject: finishDep]) {
+ // This is finish dependency that we don't control.
+ // Since we're cancelled, don't wait for it.
+ [self.finishOperation removeDependency: finishDep];
+ }
+ }
+
+ if([self.startOperation isPending]) {
+ // If we were cancelled before starting, don't fill in our error later; we'll probably just get subresult cancelled
+ self.fillInError = false;
+ }
+
+ // Now, we're in a position where either:
+ // 1. This operation hasn't been started, and is now 'cancelled'
+ // 2. This operation has beens started, and is now cancelled, and has delivered a 'cancel' message to all its suboperations,
+ // which may or may not comply
+ //
+ // In either case, this operation will complete its finish operation whenever it is 'started' and all of its cancelled suboperations finish.
+
+ [self.operationQueue addOperation: block];
}
- (void)completeOperation {
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
- // Run through all the failable operations in this group, and determine if we should be considered successful ourselves
- [self allSuccessful: self.internalSuccesses];
+ dispatch_sync(self.queue, ^{
+ if(self.fillInError) {
+ // Run through all the failable operations in this group, and determine if we should be considered successful ourselves
+ [self allSuccessful: self.internalSuccesses];
+ }
- executing = NO;
- finished = YES;
+ self->executing = NO;
+ self->finished = YES;
+ });
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
- (void)runBeforeGroupFinished: (NSOperation*) suboperation {
if([self isCancelled]) {
// Cancelled operations can't add anything.
- secnotice("ckksgroup", "Not adding operation to cancelled group");
+ secnotice("ckksgroup", "Not adding operation to cancelled group %@", self);
return;
}
return;
}
- if([self.finishOperation isExecuting] || [self.finishOperation isFinished]) {
- @throw @"Attempt to add operation to completed group";
+ // Make sure we wait for it.
+ [self.finishOperation addDependency: suboperation];
+ if([self.finishOperation isFinished]) {
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"Attempt to add operation(%@) to completed group(%@)", suboperation, self] userInfo:nil];
}
+ // And it waits for us.
+ [suboperation addDependency: self.startOperation];
+
// If this is a CKKSResultOperation, then its result impacts our result.
if([suboperation isKindOfClass: [CKKSResultOperation class]]) {
// don't use addSuccessDependency, because it's not a dependency for The Group Operation, but rather a suboperation
[self.internalSuccesses addObject: (CKKSResultOperation*) suboperation];
}
}
-
- // Make sure it waits for us...
- [suboperation addDependency: self.startOperation];
- // and we wait for it.
- [self.finishOperation addDependency: suboperation];
}
@end
}
// Synchronous, on some thread. Get back on the CKKS queue for SQL thread-safety.
- [ckks dispatchSync: ^bool{
+ [ckks dispatchSyncWithAccountKeys: ^bool{
if(self.cancelled) {
ckksnotice("ckksheal", ckks, "CKKSHealKeyHierarchyOperation cancelled, quitting");
return false;
// 1. Current key pointers are nil.
// 2. Keys do not exist in local keychain (but TLK does)
// 3. Keys do not exist in local keychain (including TLK)
+ // 4. Class A or Class C keys do not wrap immediately to top TLK.
//
if(keyset.currentTLKPointer && keyset.currentClassAPointer && keyset.currentClassCPointer &&
if(keyset.currentTLKPointer.currentKeyUUID == nil || keyset.currentClassAPointer.currentKeyUUID == nil || keyset.currentClassCPointer.currentKeyUUID == nil ||
- keyset.tlk == nil || keyset.classA == nil || keyset.classC == nil) {
+ keyset.tlk == nil || keyset.classA == nil || keyset.classC == nil ||
+ ![keyset.classA.parentKeyUUID isEqualToString: keyset.tlk.uuid] || ![keyset.classC.parentKeyUUID isEqualToString: keyset.tlk.uuid]) {
// The records exist, but are broken. Point them at something reasonable.
NSArray<CKKSKey*>* keys = [CKKSKey allKeys:ckks.zoneID error:&error];
}
}
- if(![ckks checkTLK: newTLK error: &error]) {
+ if(![ckks _onqueueWithAccountKeysCheckTLK: newTLK error: &error]) {
// Was this error "I've never seen that TLK before in my life"? If so, enter the "wait for TLK sync" state.
if(error && [error.domain isEqualToString: @"securityd"] && error.code == errSecItemNotFound) {
ckksnotice("ckksheal", ckks, "Received a TLK which we don't have in the local keychain(%@). Entering waitfortlk.", newTLK);
}
// We have our new TLK.
- keyset.currentTLKPointer.currentKeyUUID = newTLK.uuid;
- changedCurrentTLK = true;
+ if(![keyset.currentTLKPointer.currentKeyUUID isEqualToString: newTLK.uuid]) {
+ // And it's even actually new!
+ keyset.tlk = newTLK;
+ keyset.currentTLKPointer.currentKeyUUID = newTLK.uuid;
+ changedCurrentTLK = true;
+ }
// Find some class A and class C keys directly under this one.
for(CKKSKey* key in keys) {
if([key.parentKeyUUID isEqualToString: newTLK.uuid]) {
- if((keyset.currentClassAPointer.currentKeyUUID == nil || keyset.classA == nil) &&
- [key.keyclass isEqualToString: SecCKKSKeyClassA]) {
+ if([key.keyclass isEqualToString: SecCKKSKeyClassA] &&
+ (keyset.currentClassAPointer.currentKeyUUID == nil ||
+ ![keyset.classA.parentKeyUUID isEqualToString: newTLK.uuid] ||
+ keyset.classA == nil)
+ ) {
+ keyset.classA = key;
keyset.currentClassAPointer.currentKeyUUID = key.uuid;
changedCurrentClassA = true;
}
- if((keyset.currentClassCPointer.currentKeyUUID == nil || keyset.classC == nil) &&
- [key.keyclass isEqualToString: SecCKKSKeyClassC]) {
+ if([key.keyclass isEqualToString: SecCKKSKeyClassC] &&
+ (keyset.currentClassCPointer.currentKeyUUID == nil ||
+ ![keyset.classC.parentKeyUUID isEqualToString: newTLK.uuid] ||
+ keyset.classC == nil)
+ ) {
+ keyset.classC = key;
keyset.currentClassCPointer.currentKeyUUID = key.uuid;
changedCurrentClassC = true;
}
[recordsToSave addObject: [keyset.currentClassCPointer CKRecordWithZoneID: ckks.zoneID]];
}
+ // We've selected a new TLK. Compute any TLKShares that should go along with it.
+ NSSet<CKKSTLKShare*>* tlkShares = [ckks _onqueueCreateMissingKeyShares:keyset.tlk
+ error:&error];
+ if(error) {
+ ckkserror("ckksshare", ckks, "Unable to create TLK shares for new tlk: %@", error);
+ return false;
+ }
+
+ for(CKKSTLKShare* share in tlkShares) {
+ CKRecord* record = [share CKRecordWithZoneID:ckks.zoneID];
+ [recordsToSave addObject: record];
+ }
+
+ // Kick off the CKOperation
+
ckksinfo("ckksheal", ckks, "Saving new keys %@ to cloudkit %@", recordsToSave, ckks.database);
// Use the spare operation trick to wait for the CKModifyRecordsOperation to complete
CKModifyRecordsOperation* modifyRecordsOp = nil;
+ NSMutableDictionary<CKRecordID*, CKRecord*>* attemptedRecords = [[NSMutableDictionary alloc] init];
+ for(CKRecord* record in recordsToSave) {
+ attemptedRecords[record.recordID] = record;
+ }
+
// Get the CloudKit operation ready...
modifyRecordsOp = [[CKModifyRecordsOperation alloc] initWithRecordsToSave:recordsToSave recordIDsToDelete:recordIDsToDelete];
modifyRecordsOp.atomic = YES;
[keyset.currentClassAPointer saveToDatabase: &localerror];
[keyset.currentClassCPointer saveToDatabase: &localerror];
+ // save all the TLKShares, too
+ for(CKKSTLKShare* share in tlkShares) {
+ [share saveToDatabase:&localerror];
+ }
+
if(localerror != nil) {
ckkserror("ckksheal", strongCKKS, "couldn't save new key hierarchy to database; this is very bad: %@", localerror);
[strongCKKS _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateError withError: localerror];
} else {
// ERROR. This isn't a total-failure error state, but one that should kick off a healing process.
ckkserror("ckksheal", strongCKKS, "couldn't save new key hierarchy to CloudKit: %@", error);
+ [strongCKKS _onqueueCKWriteFailed:error attemptedRecordsChanged:attemptedRecords];
[strongCKKS _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateNewTLKsFailed withError: nil];
}
return true;
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#import <Foundation/Foundation.h>
+#import "keychain/ckks/CKKSGroupOperation.h"
+
+#if OCTAGON
+
+@class CKKSKeychainView;
+
+@interface CKKSHealTLKSharesOperation : CKKSGroupOperation
+@property (weak) CKKSKeychainView* ckks;
+
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*)ckks
+ ckoperationGroup:(CKOperationGroup*)ckoperationGroup;
+@end
+
+#endif // OCTAGON
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import "keychain/ckks/CKKSKeychainView.h"
+#import "keychain/ckks/CKKSCurrentKeyPointer.h"
+#import "keychain/ckks/CKKSKey.h"
+#import "keychain/ckks/CKKSHealTLKSharesOperation.h"
+#import "keychain/ckks/CKKSGroupOperation.h"
+#import "keychain/ckks/CKKSTLKShare.h"
+
+@interface CKKSHealTLKSharesOperation ()
+@property NSBlockOperation* cloudkitModifyOperationFinished;
+@property CKOperationGroup* ckoperationGroup;
+@end
+
+@implementation CKKSHealTLKSharesOperation
+
+- (instancetype)init {
+ return nil;
+}
+- (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*)ckks ckoperationGroup:(CKOperationGroup*)ckoperationGroup {
+ if(self = [super init]) {
+ _ckks = ckks;
+ _ckoperationGroup = ckoperationGroup;
+ }
+ return self;
+}
+
+- (void)groupStart {
+ /*
+ * We've been invoked because something is wonky with the tlk shares.
+ *
+ * Attempt to figure out what it is, and what we can do about it.
+ */
+
+ __weak __typeof(self) weakSelf = self;
+
+ CKKSKeychainView* ckks = self.ckks;
+ if(!ckks) {
+ ckkserror("ckksshare", ckks, "no CKKS object");
+ return;
+ }
+
+ if(self.cancelled) {
+ ckksnotice("ckksshare", ckks, "CKKSHealTLKSharesOperation cancelled, quitting");
+ return;
+ }
+
+ [ckks dispatchSyncWithAccountKeys: ^bool{
+ if(self.cancelled) {
+ ckksnotice("ckksshare", ckks, "CKKSHealTLKSharesOperation cancelled, quitting");
+ return false;
+ }
+
+ NSError* error = nil;
+
+ CKKSCurrentKeySet* keyset = [[CKKSCurrentKeySet alloc] initForZone:ckks.zoneID];
+
+ if(keyset.error) {
+ self.error = keyset.error;
+ ckkserror("ckksshare", ckks, "couldn't load current keys: can't fix TLK shares");
+ [ckks _onqueueAdvanceKeyStateMachineToState:SecCKKSZoneKeyStateUnhealthy withError:nil];
+ return true;
+ } else {
+ ckksnotice("ckksshare", ckks, "Key set is %@", keyset);
+ }
+
+ // Okay! Perform the checks.
+ if(![keyset.tlk loadKeyMaterialFromKeychain:&error] || error) {
+ // Well, that's no good. We can't share a TLK we don't have.
+ if([ckks.lockStateTracker isLockedError: error]) {
+ ckkserror("ckksshare", ckks, "Keychain is locked: can't fix shares yet: %@", error);
+ [ckks _onqueueAdvanceKeyStateMachineToState:SecCKKSZoneKeyStateWaitForUnlock withError:nil];
+ } else {
+ // TODO go to waitfortlk
+ ckkserror("ckksshare", ckks, "couldn't load current tlk from keychain: %@", error);
+ [ckks _onqueueAdvanceKeyStateMachineToState:SecCKKSZoneKeyStateUnhealthy withError:nil];
+ }
+ return true;
+ }
+
+ NSSet<CKKSTLKShare*>* newShares = [ckks _onqueueCreateMissingKeyShares:keyset.tlk
+ error:&error];
+ if(error) {
+ ckkserror("ckksshare", ckks, "Unable to create shares: %@", error);
+ return false;
+ }
+
+ if(newShares.count == 0u) {
+ ckksnotice("ckksshare", ckks, "Don't believe we need to change any TLKShares, stopping");
+ [ckks _onqueueAdvanceKeyStateMachineToState:SecCKKSZoneKeyStateReady withError:nil];
+ return true;
+ }
+
+ // Fire up our CloudKit operation!
+
+ NSMutableArray<CKRecord *>* recordsToSave = [[NSMutableArray alloc] init];
+ NSMutableArray<CKRecordID *>* recordIDsToDelete = [[NSMutableArray alloc] init];
+ NSMutableDictionary<CKRecordID*, CKRecord*>* attemptedRecords = [[NSMutableDictionary alloc] init];
+
+ for(CKKSTLKShare* share in newShares) {
+ CKRecord* record = [share CKRecordWithZoneID:ckks.zoneID];
+ [recordsToSave addObject: record];
+ attemptedRecords[record.recordID] = record;
+ }
+
+ // Use the spare operation trick to wait for the CKModifyRecordsOperation to complete
+ self.cloudkitModifyOperationFinished = [NSBlockOperation named:@"heal-tlkshares-modify-operation-finished" withBlock:^{}];
+ [self dependOnBeforeGroupFinished: self.cloudkitModifyOperationFinished];
+
+
+ // Get the CloudKit operation ready...
+ CKModifyRecordsOperation* modifyRecordsOp = [[CKModifyRecordsOperation alloc] initWithRecordsToSave:recordsToSave
+ recordIDsToDelete:recordIDsToDelete];
+ modifyRecordsOp.atomic = YES;
+ modifyRecordsOp.longLived = NO;
+ modifyRecordsOp.timeoutIntervalForRequest = 10;
+ modifyRecordsOp.qualityOfService = NSQualityOfServiceUtility; // relatively important. Use Utility.
+ modifyRecordsOp.group = self.ckoperationGroup;
+ ckksnotice("ckksshare", ckks, "Operation group is %@", self.ckoperationGroup);
+
+ modifyRecordsOp.perRecordCompletionBlock = ^(CKRecord *record, NSError * _Nullable error) {
+ __strong __typeof(weakSelf) strongSelf = weakSelf;
+ __strong __typeof(strongSelf.ckks) blockCKKS = strongSelf.ckks;
+
+ // These should all fail or succeed as one. Do the hard work in the records completion block.
+ if(!error) {
+ ckksinfo("ckksshare", blockCKKS, "Successfully completed upload for record %@", record.recordID.recordName);
+ } else {
+ ckkserror("ckksshare", blockCKKS, "error on row: %@ %@", record.recordID, error);
+ }
+ };
+
+ modifyRecordsOp.modifyRecordsCompletionBlock = ^(NSArray<CKRecord *> *savedRecords, NSArray<CKRecordID *> *deletedRecordIDs, NSError *error) {
+ __strong __typeof(weakSelf) strongSelf = weakSelf;
+ __strong __typeof(strongSelf.ckks) strongCKKS = strongSelf.ckks;
+ if(!strongSelf) {
+ secerror("ckks: received callback for released object");
+ return;
+ }
+
+ [strongCKKS dispatchSyncWithAccountKeys: ^bool {
+ if(error == nil) {
+ // Success. Persist the records to the CKKS database
+ ckksnotice("ckksshare", strongCKKS, "Completed TLK Share heal operation with success");
+ NSError* localerror = nil;
+
+ // Save the new CKRecords to the database
+ for(CKRecord* record in savedRecords) {
+ CKKSTLKShare* savedShare = [[CKKSTLKShare alloc] initWithCKRecord:record];
+ [savedShare saveToDatabase:&localerror];
+
+ if(localerror) {
+ // No recovery from this, really...
+ ckkserror("ckksshare", strongCKKS, "Couldn't save new TLKShare record to database: %@", localerror);
+ localerror = nil;
+ } else {
+ ckksnotice("ckksshare", strongCKKS, "Successfully completed upload for %@", savedShare);
+ }
+ }
+
+ // Successfully sharing TLKs means we're now in ready!
+ [strongCKKS _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateReady withError: nil];
+ } else {
+ ckkserror("ckksshare", strongCKKS, "Completed TLK Share heal operation with error: %@", error);
+ [strongCKKS _onqueueCKWriteFailed:error attemptedRecordsChanged:attemptedRecords];
+ // Send the key state machine into tlksharesfailed
+ [strongCKKS _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateHealTLKSharesFailed withError: nil];
+ }
+ return true;
+ }];
+
+ // Notify that we're done
+ [strongSelf.operationQueue addOperation: strongSelf.cloudkitModifyOperationFinished];
+ };
+
+ [ckks.database addOperation: modifyRecordsOp];
+ return true;
+ }];
+}
+
+- (void)cancel {
+ [self.cloudkitModifyOperationFinished cancel];
+ [super cancel];
+}
+
+@end;
+
+#endif
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#import "CKKSSQLDatabaseObject.h"
#import "CKKSItem.h"
#import "CKKSMirrorEntry.h"
#ifndef CKKSIncomingQueueEntry_h
#define CKKSIncomingQueueEntry_h
-#if OCTAGON
+
#import <CloudKit/CloudKit.h>
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#include <AssertMacros.h>
#import <Foundation/Foundation.h>
#include <securityd/SecDbItem.h>
#include <securityd/SecItemSchema.h>
-#if OCTAGON
-
#import <CloudKit/CloudKit.h>
#import "CKKSIncomingQueueEntry.h"
#import "CKKSItemEncrypter.h"
return;
}
- [strongCKKS dispatchSyncWithAccountQueue:^bool{
+ [strongCKKS dispatchSyncWithAccountKeys:^bool{
strongCKKS.latestManifest = [CKKSManifest latestTrustedManifestForZone:strongCKKS.zoneName error:&error];
if (error) {
strongSelf.error = error;
return;
}
- [ckks dispatchSyncWithAccountQueue: ^bool{
+ [ckks dispatchSyncWithAccountKeys: ^bool{
if(self.cancelled) {
ckksnotice("ckksincoming", ckks, "CKKSIncomingQueueOperation cancelled, quitting");
return false;
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#import "keychain/ckks/CKKS.h"
#import "keychain/ckks/CKKSSQLDatabaseObject.h"
#import "keychain/ckks/CKKSRecordHolder.h"
#ifndef CKKSItem_h
#define CKKSItem_h
-#if OCTAGON
-
#import <CloudKit/CloudKit.h>
@class CKKSWrappedAESSIVKey;
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#include <AssertMacros.h>
#import <Foundation/Foundation.h>
#include <securityd/SecDbItem.h>
#include <securityd/SecItemSchema.h>
-#if OCTAGON
-
#import <CloudKit/CloudKit.h>
#import <CloudKit/CloudKit_Private.h>
// subtly improve osversion (but it's okay if that does nothing)
NSString* finalversion = [platform stringByAppendingString: [osversion stringByReplacingOccurrencesOfString:@"Version" withString:@""]];
- record[SecCKRecordVersionKey] = finalversion;
+ record[SecCKRecordHostOSVersionKey] = finalversion;
}
- (CKRecord*) updateCKRecord: (CKRecord*) record zoneID: (CKRecordZoneID*) zoneID {
if(record) {
for(NSString* key in record.allKeys) {
if([key isEqualToString:@"UUID"] ||
- [key isEqualToString:SecCKRecordVersionKey] ||
+ [key isEqualToString:SecCKRecordHostOSVersionKey] ||
[key isEqualToString:SecCKRecordDataKey] ||
[key isEqualToString:SecCKRecordWrappedKeyKey] ||
[key isEqualToString:SecCKRecordGenerationCountKey] ||
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#import <Foundation/Foundation.h>
#import "keychain/ckks/CKKSItem.h"
#import "keychain/ckks/CKKSSIV.h"
-#if OCTAGON
+#import "keychain/ckks/proto/source/CKKSSerializedKey.h"
+#import "keychain/ckks/CKKSPeer.h"
@interface CKKSKey : CKKSItem
// Attempts to unwrap this key via unwrapping its wrapping keys via the key hierarchy.
- (CKKSAESSIVKey*)unwrapViaKeyHierarchy: (NSError * __autoreleasing *) error;
+// On a self-wrapped key, determine if this AES-SIV key is the self-wrapped key.
+// If it is, save the key as this CKKSKey's unwrapped key.
+- (bool)trySelfWrappedKeyCandidate:(CKKSAESSIVKey*)candidate error:(NSError * __autoreleasing *) error;
+
- (CKKSWrappedAESSIVKey*)wrapAESKey: (CKKSAESSIVKey*) keyToWrap error: (NSError * __autoreleasing *) error;
- (CKKSAESSIVKey*)unwrapAESKey: (CKKSWrappedAESSIVKey*) keyToUnwrap error: (NSError * __autoreleasing *) error;
- (NSData*)encryptData: (NSData*) plaintext authenticatedData: (NSDictionary<NSString*, NSData*>*) ad error: (NSError * __autoreleasing *) error;
- (NSData*)decryptData: (NSData*) ciphertext authenticatedData: (NSDictionary<NSString*, NSData*>*) ad error: (NSError * __autoreleasing *) error;
+- (NSData*)serializeAsProtobuf:(NSError* __autoreleasing *)error;
++ (CKKSKey*)loadFromProtobuf:(NSData*)data error:(NSError* __autoreleasing *)error;
+ (NSDictionary<NSString*,NSNumber*>*)countsByClass:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error;
@end
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#import "CKKSViewManager.h"
#import "CKKSKeychainView.h"
#import "CKKSCurrentKeyPointer.h"
#import "CKKSKey.h"
+#import "keychain/ckks/CloudKitCategories.h"
#include <securityd/SecItemSchema.h>
#include <Security/SecItem.h>
#include <Security/SecItemPriv.h>
-#if OCTAGON
-
#include <CloudKit/CloudKit.h>
#include <CloudKit/CloudKit_Private.h>
return self.aessivkey;
}
+- (bool)trySelfWrappedKeyCandidate:(CKKSAESSIVKey*)candidate error:(NSError * __autoreleasing *) error {
+ if(![self wrapsSelf]) {
+ if(error) {
+ *error = [NSError errorWithDomain:CKKSErrorDomain code:CKKSKeyNotSelfWrapped userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"%@ is not self-wrapped", self]}];
+ }
+ return false;
+ }
+
+ CKKSAESSIVKey* unwrapped = [candidate unwrapAESKey:self.wrappedkey error:error];
+ if(unwrapped && [unwrapped isEqual: candidate]) {
+ _aessivkey = unwrapped;
+ return true;
+ }
+
+ return false;
+}
+
- (CKKSWrappedAESSIVKey*)wrapAESKey: (CKKSAESSIVKey*) keyToWrap error: (NSError * __autoreleasing *) error {
CKKSAESSIVKey* key = [self ensureKeyLoaded: error];
CKKSWrappedAESSIVKey* wrappedkey = [key wrapAESKey: keyToWrap error:error];
#pragma mark - Utility
- (NSString*)description {
- return [NSString stringWithFormat: @"<%@(%@): %@ (%@,%@:%d) %p>",
+ return [NSString stringWithFormat: @"<%@(%@): %@ (%@,%@:%d)>",
NSStringFromClass([self class]),
self.zoneID.zoneName,
self.uuid,
self.keyclass,
self.state,
- self.currentkey,
- &self];
+ self.currentkey];
}
#pragma mark - CKKSSQLDatabaseObject methods
return keyCopy;
}
+- (NSData*)serializeAsProtobuf: (NSError * __autoreleasing *) error {
+ if(![self ensureKeyLoaded:error]) {
+ return nil;
+ }
+ CKKSSerializedKey* proto = [[CKKSSerializedKey alloc] init];
+
+ proto.uuid = self.uuid;
+ proto.zoneName = self.zoneID.zoneName;
+ proto.keyclass = self.keyclass;
+ proto.key = [[NSData alloc] initWithBytes:self.aessivkey->key length:self.aessivkey->size];
+
+ return proto.data;
+}
+
++ (CKKSKey*)loadFromProtobuf:(NSData*)data error:(NSError* __autoreleasing *)error {
+ CKKSSerializedKey* key = [[CKKSSerializedKey alloc] initWithData: data];
+ if(key && key.uuid && key.zoneName && key.keyclass && key.key) {
+ return [[CKKSKey alloc] initSelfWrappedWithAESKey:[[CKKSAESSIVKey alloc] initWithBytes:(uint8_t*)key.key.bytes len:key.key.length]
+ uuid:key.uuid
+ keyclass:(CKKSKeyClass*)key.keyclass // TODO sanitize
+ state:SecCKKSProcessedStateRemote
+ zoneID:[[CKRecordZoneID alloc] initWithZoneName:key.zoneName
+ ownerName:CKCurrentUserDefaultName]
+ encodedCKRecord:nil
+ currentkey:false];
+ }
+
+ if(error) {
+ *error = [NSError errorWithDomain:CKKSErrorDomain code:CKKSProtobufFailure description:@"Data failed to parse as a CKKSSerializedKey"];
+ }
+ return nil;
+}
+
@end
#endif // OCTAGON
#import "keychain/ckks/CKKSZone.h"
#import "keychain/ckks/CKKSZoneChangeFetcher.h"
#import "keychain/ckks/CKKSNotifier.h"
+#import "keychain/ckks/CKKSPeer.h"
+#import "keychain/ckks/CKKSTLKShare.h"
#include "CKKS.h"
@class CKKSOutgoingQueueEntry;
@class CKKSZoneChangeFetcher;
-@interface CKKSKeychainView : CKKSZone <CKKSZoneUpdateReceiver, CKKSChangeFetcherErrorOracle> {
+@interface CKKSKeychainView : CKKSZone <CKKSZoneUpdateReceiver,
+ CKKSChangeFetcherErrorOracle,
+ CKKSPeerUpdateListener> {
CKKSZoneKeyState* _keyHierarchyState;
}
@property CKKSReencryptOutgoingItemsOperation* lastReencryptOutgoingItemsOperation;
@property CKKSScanLocalItemsOperation* lastScanLocalItemsOperation;
@property CKKSSynchronizeOperation* lastSynchronizeOperation;
+@property CKKSResultOperation* lastFixupOperation;
/* Used for testing: pause operation types by adding operations here */
@property NSOperation* holdReencryptOutgoingItemsOperation;
/* Trigger this to tell the whole machine that this view has changed */
@property CKKSNearFutureScheduler* notifyViewChangedScheduler;
+// These are available when you're in a dispatchSyncWithAccountKeys call, but at no other time
+// These must be pre-fetched before you get on the CKKS queue, otherwise we end up with CKKS<->SQLite<->SOSAccountQueue deadlocks
+@property (nonatomic, readonly) CKKSSelves* currentSelfPeers;
+@property (nonatomic, readonly) NSError* currentSelfPeersError;
+@property (nonatomic, readonly) NSSet<id<CKKSPeer>>* currentTrustedPeers;
+@property (nonatomic, readonly) NSError* currentTrustedPeersError;
+
- (instancetype)initWithContainer: (CKContainer*) container
zoneName: (NSString*) zoneName
accountTracker:(CKKSCKAccountStateTracker*) accountTracker
lockStateTracker:(CKKSLockStateTracker*) lockStateTracker
savedTLKNotifier:(CKKSNearFutureScheduler*) savedTLKNotifier
+ peerProvider:(id<CKKSPeerProvider>)peerProvider
fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
+ fetchRecordsOperationClass: (Class<CKKSFetchRecordsOperation>)fetchRecordsOperationClass
+ queryOperationClass:(Class<CKKSQueryOperation>)queryOperationClass
modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
- (CKKSKey*) keyForItem: (SecDbItemRef) item error: (NSError * __autoreleasing *) error;
-- (bool)checkTLK: (CKKSKey*) proposedTLK error: (NSError * __autoreleasing *) error;
+- (bool)_onqueueWithAccountKeysCheckTLK: (CKKSKey*) proposedTLK error: (NSError * __autoreleasing *) error;
/* Asynchronous kickoffs */
// Schedules an operation to update this device's state record in CloudKit
// If rateLimit is true, the operation will abort if it's updated the record in the past 3 days
-- (CKKSUpdateDeviceStateOperation*)updateDeviceState:(bool)rateLimit ckoperationGroup:(CKOperationGroup*)ckoperationGroup;
+- (CKKSUpdateDeviceStateOperation*)updateDeviceState:(bool)rateLimit
+ waitForKeyHierarchyInitialization:(uint64_t)timeout
+ ckoperationGroup:(CKOperationGroup*)ckoperationGroup;
- (CKKSSynchronizeOperation*) resyncWithCloud;
// Use these helper methods to make sure those exist.
- (void) dispatchAsync: (bool (^)(void)) block;
- (void) dispatchSync: (bool (^)(void)) block;
-- (void)dispatchSyncWithAccountQueue:(bool (^)(void))block;
+- (void)dispatchSyncWithAccountKeys:(bool (^)(void))block;
/* Synchronous operations which must be called from inside a dispatchAsync or dispatchSync block */
- (bool) _onqueueCKRecordChanged:(CKRecord*)record resync:(bool)resync;
- (bool) _onqueueCKRecordDeleted:(CKRecordID*)recordID recordType:(NSString*)recordType resync:(bool)resync;
+// For this key, who doesn't yet have a CKKSTLKShare for it?
+// Note that we really want a record sharing the TLK to ourselves, so this function might return
+// a non-empty set even if all peers have the TLK: it wants us to make a record for ourself.
+- (NSSet<id<CKKSPeer>>*)_onqueueFindPeersMissingShare:(CKKSKey*)key error:(NSError* __autoreleasing*)error;
+
+// For this key, share it to all trusted peers who don't have it yet
+- (NSSet<CKKSTLKShare*>*)_onqueueCreateMissingKeyShares:(CKKSKey*)key error:(NSError* __autoreleasing*)error;
+
- (bool)_onQueueUpdateLatestManifestWithError:(NSError**)error;
- (CKKSDeviceStateEntry*)_onqueueCurrentDeviceStateEntry: (NSError* __autoreleasing*)error;
#import "CKKSManifest.h"
#import "CKKSManifestLeafRecord.h"
#import "CKKSZoneChangeFetcher.h"
+#import "CKKSAnalyticsLogger.h"
#import "keychain/ckks/CKKSDeviceStateEntry.h"
#import "keychain/ckks/CKKSNearFutureScheduler.h"
#import "keychain/ckks/CKKSCurrentItemPointer.h"
#import "keychain/ckks/CKKSLockStateTracker.h"
#import "keychain/ckks/CKKSNotifier.h"
#import "keychain/ckks/CloudKitCategories.h"
+#import "keychain/ckks/CKKSTLKShare.h"
+#import "keychain/ckks/CKKSHealTLKSharesOperation.h"
#include <utilities/SecCFWrappers.h>
#include <utilities/SecDb.h>
@property CKKSResultOperation* processIncomingQueueAfterNextUnlockOperation;
@property NSMutableDictionary<NSString*, SecBoolNSErrorCallback>* pendingSyncCallbacks;
+
+@property id<CKKSPeerProvider> currentPeerProvider;
+
+// Make these readwrite
+@property (nonatomic, readwrite) CKKSSelves* currentSelfPeers;
+@property (nonatomic, readwrite) NSError* currentSelfPeersError;
+@property (nonatomic, readwrite) NSSet<id<CKKSPeer>>* currentTrustedPeers;
+@property (nonatomic, readwrite) NSError* currentTrustedPeersError;
@end
#endif
accountTracker:(CKKSCKAccountStateTracker*) accountTracker
lockStateTracker:(CKKSLockStateTracker*) lockStateTracker
savedTLKNotifier:(CKKSNearFutureScheduler*) savedTLKNotifier
+ peerProvider:(id<CKKSPeerProvider>)peerProvider
fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
+ fetchRecordsOperationClass: (Class<CKKSFetchRecordsOperation>)fetchRecordsOperationClass
+ queryOperationClass:(Class<CKKSQueryOperation>)queryOperationClass
modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
zoneName:zoneName
accountTracker:accountTracker
fetchRecordZoneChangesOperationClass:fetchRecordZoneChangesOperationClass
+ fetchRecordsOperationClass:fetchRecordsOperationClass
+ queryOperationClass:queryOperationClass
modifySubscriptionsOperationClass:modifySubscriptionsOperationClass
modifyRecordZonesOperationClass:modifyRecordZonesOperationClass
apsConnectionClass:apsConnectionClass]) {
keepProcessAlive:true
block:^{
__strong __typeof(self) strongSelf = weakSelf;
- ckksnotice("ckks", strongSelf, "");
[strongSelf.notifierClass post:[NSString stringWithFormat:@"com.apple.security.view-change.%@", strongSelf.zoneName]];
// Ugly, but: the Manatee and Engram views need to send a fake 'PCS' view change.
_lockStateTracker = lockStateTracker;
_savedTLKNotifier = savedTLKNotifier;
+ _currentPeerProvider = peerProvider;
+ [_currentPeerProvider registerForPeerChangeUpdates:self];
_setupSuccessful = false;
_keyStateFetchRequested = false;
_keyStateProcessRequested = false;
- _keyStateReadyDependency = [CKKSResultOperation operationWithBlock:^{
- ckksnotice("ckkskey", weakSelf, "Key state has become ready for the first time.");
- }];
- self.keyStateReadyDependency.name = [NSString stringWithFormat: @"%@-key-state-ready", self.zoneName];
+ _keyStateReadyDependency = [self createKeyStateReadyDependency: @"Key state has become ready for the first time." ckoperationGroup:[CKOperationGroup CKKSGroupWithName:@"initial-key-state-ready-scan"]];
dispatch_time_t initializeDelay = SecCKKSTestsEnabled() ? NSEC_PER_MSEC * 500 : NSEC_PER_SEC * 30;
_initializeScheduler = [[CKKSNearFutureScheduler alloc] initWithName:[NSString stringWithFormat: @"%@-zone-initializer", self.zoneName]
}
// We can't enter the account queue until an account exists. Before this point, we don't know if one does.
- [strongSelf dispatchSyncWithAccountQueue: ^bool{
+ [strongSelf dispatchSyncWithAccountKeys: ^bool{
CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state: strongSelf.zoneName];
// Check if we believe we've synced this zone before.
} else {
// Likely a restart of securityd!
+ // Are there any fixups to run first?
+ strongSelf.lastFixupOperation = [CKKSFixups fixup:ckse.lastFixup for:strongSelf];
+ if(strongSelf.lastFixupOperation) {
+ ckksnotice("ckksfixup", strongSelf, "We have a fixup to perform: %@", strongSelf.lastFixupOperation);
+ [strongSelf scheduleOperation:strongSelf.lastFixupOperation];
+ }
+
strongSelf.keyHierarchyOperationGroup = [CKOperationGroup CKKSGroupWithName:@"restart-setup"];
if ([CKKSManifest shouldSyncManifests]) {
NSOperation* initialProcess = nil;
if(ckse.lastFetchTime == nil || [ckse.lastFetchTime compare: deadline] == NSOrderedAscending) {
- initialProcess = [strongSelf fetchAndProcessCKChanges:CKKSFetchBecauseSecuritydRestart];
+ initialProcess = [strongSelf fetchAndProcessCKChanges:CKKSFetchBecauseSecuritydRestart after:strongSelf.lastFixupOperation];
} else {
- initialProcess = [strongSelf processIncomingQueue:false];
+ initialProcess = [strongSelf processIncomingQueue:false after:strongSelf.lastFixupOperation];
}
if(!strongSelf.egoManifest) {
ckksnotice("ckksmanifest", strongSelf, "No ego manifest on restart; rescanning");
strongSelf.initialScanOperation = [[CKKSScanLocalItemsOperation alloc] initWithCKKSKeychainView:strongSelf ckoperationGroup:strongSelf.keyHierarchyOperationGroup];
strongSelf.initialScanOperation.name = @"initial-scan-operation";
+ [strongSelf.initialScanOperation addNullableDependency:strongSelf.lastFixupOperation];
[strongSelf.initialScanOperation addNullableDependency:strongSelf.lockStateTracker.unlockDependency];
[strongSelf.initialScanOperation addDependency: initialProcess];
[strongSelf scheduleOperation: strongSelf.initialScanOperation];
}
- [strongSelf processOutgoingQueue:strongSelf.keyHierarchyOperationGroup];
+ [strongSelf processOutgoingQueueAfter:strongSelf.lastFixupOperation ckoperationGroup:strongSelf.keyHierarchyOperationGroup];
}
- // Tell the key state machine to fire off.
- [strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateInitialized withError: nil];
+ // Tell the key state machine to fire off. It should either:
+ // Wait for the fixup operation to occur
+ // Be initialized
+ if(strongSelf.lastFixupOperation) {
+ [strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateWaitForFixupOperation withError: nil];
+ } else {
+ [strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateInitialized withError: nil];
+ }
return true;
}];
}];
}
}
+ [CKKSTLKShare deleteAll:self.zoneID error: &localerror];
+ if(localerror) {
+ ckkserror("ckks", self, "couldn't delete all CKKSTLKShare: %@", localerror);
+ if(error && !setError) {
+ *error = localerror; setError = true;
+ }
+ }
+
[CKKSCurrentKeyPointer deleteAll:self.zoneID error: &localerror];
if(localerror) {
ckkserror("ckks", self, "couldn't delete all CKKSCurrentKeyPointer: %@", localerror);
}
}
+ [CKKSCurrentItemPointer deleteAll:self.zoneID error: &localerror];
+ if(localerror) {
+ ckkserror("ckks", self, "couldn't delete all CKKSCurrentItemPointer: %@", localerror);
+ if(error && !setError) {
+ *error = localerror; setError = true;
+ }
+ }
+
[CKKSDeviceStateEntry deleteAll:self.zoneID error:&localerror];
if(localerror) {
ckkserror("ckks", self, "couldn't delete all CKKSDeviceStateEntry: %@", localerror);
__block NSError* error = nil;
[strongSelf dispatchSync: ^bool{
- [self _onqueueResetLocalData: &error];
+ [strongSelf _onqueueResetLocalData: &error];
return true;
}];
}
}];
+ // On a reset, all other operations should be cancelled
+ [self cancelAllOperations];
[resetFollowUp runBeforeGroupFinished:op];
[self scheduleOperationWithoutDependencies:resetFollowUp];
return resetFollowUp;
return nil;
}
+ // On a reset, we should cancel all existing operations
+ [self cancelAllOperations];
CKKSResultOperation* reset = [super beginResetCloudKitZoneOperation];
__weak __typeof(self) weakSelf = self;
}];
}
+- (CKKSResultOperation*)createKeyStateReadyDependency:(NSString*)message ckoperationGroup:(CKOperationGroup*)group {
+ __weak __typeof(self) weakSelf = self;
+ CKKSResultOperation* keyStateReadyDependency = [CKKSResultOperation operationWithBlock:^{
+ __strong __typeof(self) strongSelf = weakSelf;
+ if(!strongSelf) {
+ return;
+ }
+ ckksnotice("ckkskey", strongSelf, "%@", message);
+
+ // While we weren't in 'ready', keychain modifications might have come in and were dropped on the floor. Find them!
+ CKKSScanLocalItemsOperation* scanOperation = [[CKKSScanLocalItemsOperation alloc] initWithCKKSKeychainView: strongSelf ckoperationGroup:group];
+ [strongSelf scheduleOperation: scanOperation];
+ }];
+ keyStateReadyDependency.name = [NSString stringWithFormat: @"%@-key-state-ready", self.zoneName];
+ return keyStateReadyDependency;
+}
+
- (void)_onqueueKeyStateMachineRequestProcess {
dispatch_assert_queue(self.queue);
// Set the request flag, then nudge the key state machine.
- // If it was idle, then it should launch a fetch. If there was an active process, this flag will stay high
- // and the fetch will be launched later.
+ // If it was idle, then it should launch a process. If there was an active process, this flag will stay high
+ // and the process will be launched later.
self.keyStateProcessRequested = true;
[self _onqueueAdvanceKeyStateMachineToState: nil withError: nil];
self.keyStateProcessRequested = false;
self.keyHierarchyOperationGroup = [CKOperationGroup CKKSGroupWithName:@"key-state-reset"];
- self.keyStateReadyDependency = [CKKSResultOperation operationWithBlock:^{
- ckksnotice("ckkskey", weakSelf, "Key state has become ready for the first time (after reset).");
- }];
- self.keyStateReadyDependency.name = [NSString stringWithFormat: @"%@-key-state-ready", self.zoneName];
+ self.keyStateReadyDependency = [self createKeyStateReadyDependency:@"Key state has become ready for the first time (after reset)." ckoperationGroup:self.keyHierarchyOperationGroup];
return;
}
} else {
// Error state: record the error and exit early
ckkserror("ckkskey", self, "advised of error: coming from state (%@): %@", self.keyHierarchyState, error);
+
+ [[CKKSAnalyticsLogger logger] logUnrecoverableError:error
+ forEvent:CKKSEventStateError
+ inView:self
+ withAttributes:@{ @"previousKeyHierarchyState" : self.keyHierarchyState }];
+
+
self.keyHierarchyState = SecCKKSZoneKeyStateError;
self.keyHierarchyError = error;
+
return;
}
}
if(!self.keyStateMachineOperation) {
// We think we're ready. Double check.
- bool ready = [self _onqueueEnsureKeyHierarchyHealth:&hierarchyError];
- if(!ready || hierarchyError) {
+ CKKSZoneKeyState* checkedstate = [self _onqueueEnsureKeyHierarchyHealth:keyset error:&hierarchyError];
+ if(![checkedstate isEqualToString:SecCKKSZoneKeyStateReady] || hierarchyError) {
// Things is bad. Kick off a heal to fix things up.
- ckksnotice("ckkskey", self, "Thought we were ready, but the key hierarchy is unhealthy: %@", hierarchyError);
- self.keyHierarchyState = SecCKKSZoneKeyStateUnhealthy;
+ ckksnotice("ckkskey", self, "Thought we were ready, but the key hierarchy is %@: %@", checkedstate, hierarchyError);
+ self.keyHierarchyState = checkedstate;
} else {
// In ready, nothing to do. Notify waiters and quit.
if(tlk && classA && classC && !error) {
// This is likely a restart of securityd, and we think we're ready. Double check.
- bool ready = [self _onqueueEnsureKeyHierarchyHealth:&hierarchyError];
- if(ready && !hierarchyError) {
+
+ CKKSZoneKeyState* checkedstate = [self _onqueueEnsureKeyHierarchyHealth:keyset error:&hierarchyError];
+ if([checkedstate isEqualToString:SecCKKSZoneKeyStateReady] && !hierarchyError) {
ckksnotice("ckkskey", self, "Already have existing key hierarchy for %@; using it.", self.zoneID.zoneName);
} else if(hierarchyError && [self.lockStateTracker isLockedError:hierarchyError]) {
ckksnotice("ckkskey", self, "Initial scan shows key hierarchy is unavailable since keychain is locked: %@", hierarchyError);
self.keyHierarchyState = SecCKKSZoneKeyStateWaitForUnlock;
} else {
- ckksnotice("ckkskey", self, "Initial scan shows key hierarchy is unhealthy: %@", hierarchyError);
- self.keyHierarchyState = SecCKKSZoneKeyStateUnhealthy;
+ ckksnotice("ckkskey", self, "Initial scan shows key hierarchy is %@: %@", checkedstate, hierarchyError);
+ self.keyHierarchyState = checkedstate;
}
} else {
[self _onqueueKeyHierarchyFetch];
}
+ } else if([self.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateWaitForFixupOperation]) {
+ // We should enter 'initialized' when the fixup operation completes
+ ckksnotice("ckkskey", self, "Waiting for the fixup operation: %@", self.lastFixupOperation);
+
+ self.keyStateMachineOperation = [NSBlockOperation named:@"key-state-after-fixup" withBlock:^{
+ __strong __typeof(self) strongSelf = weakSelf;
+ [strongSelf dispatchSync:^bool{
+ [strongSelf _onqueueAdvanceKeyStateMachineToState:SecCKKSZoneKeyStateInitialized withError:nil];
+ return true;
+ }];
+ }];
+ [self.keyStateMachineOperation addNullableDependency:self.lastFixupOperation];
+
} else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateFetchComplete]) {
- // We're initializing this zone, and just completed a fetch of everything. Are there any remote keys?
+ // We've just completed a fetch of everything. Are there any remote keys?
if(remoteKeys.count > 0u) {
// Process the keys we received.
self.keyStateMachineOperation = [[CKKSProcessReceivedKeysOperation alloc] initWithCKKSKeychainView: self];
// Transfer to the "unhealthy" state to request a fix
ckksnotice("ckkskey", self, "We appear to have current key pointers but no keys to match them. Moving to 'unhealthy'");
self.keyHierarchyState = SecCKKSZoneKeyStateUnhealthy;
-
- } else if([remoteKeys count] == 0) {
- // No keys, no pointers? make some new ones!
- self.keyStateMachineOperation = [[CKKSNewTLKOperation alloc] initWithCKKSKeychainView: self ckoperationGroup:self.keyHierarchyOperationGroup];
+ } else {
+ // No remote keys, and the pointers look sane? Do we have an existing key hierarchy?
+ CKKSZoneKeyState* checkedstate = [self _onqueueEnsureKeyHierarchyHealth:keyset error:&hierarchyError];
+ if([checkedstate isEqualToString:SecCKKSZoneKeyStateReady] && !hierarchyError) {
+ ckksnotice("ckkskey", self, "After fetch, everything looks good.");
+ } else if(hierarchyError && [self.lockStateTracker isLockedError:hierarchyError]) {
+ ckksnotice("ckkskey", self, "After fetch, we're locked. Key hierarchy is unavailable since keychain is locked: %@", hierarchyError);
+ self.keyHierarchyState = SecCKKSZoneKeyStateWaitForUnlock;
+ } else if(localKeys.count == 0 && remoteKeys.count == 0) {
+ ckksnotice("ckkskey", self, "After fetch, we don't have any key hierarchy. Making a new one: %@", hierarchyError);
+ self.keyStateMachineOperation = [[CKKSNewTLKOperation alloc] initWithCKKSKeychainView: self ckoperationGroup:self.keyHierarchyOperationGroup];
+ } else {
+ ckksnotice("ckkskey", self, "After fetch, we have an unhealthy key hierarchy. Moving to %@: %@", checkedstate, hierarchyError);
+ self.keyHierarchyState = checkedstate;
+ }
}
} else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateWaitForTLK]) {
ckksnotice("ckkskey", self, "Creating new TLKs didn't work. Attempting to refetch!");
[self _onqueueKeyHierarchyFetch];
+ } else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateHealTLKSharesFailed]) {
+ if(!SecCKKSShareTLKs()) {
+ ckkserror("ckkskey", self, "In SecCKKSZoneKeyStateHealTLKSharesFailed, but TLK sharing is disabled.");
+ }
+ ckksnotice("ckkskey", self, "Creating new TLK shares didn't work. Attempting to refetch!");
+ [self _onqueueKeyHierarchyFetch];
+
} else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateNeedFullRefetch]) {
ckksnotice("ckkskey", self, "Informed of request for full refetch");
[self _onqueueKeyHierarchyRefetch];
}
if(self.keyStateMachineOperation) {
-
if(self.keyStateReadyDependency == nil || [self.keyStateReadyDependency isFinished]) {
ckksnotice("ckkskey", self, "reloading keyStateReadyDependency due to operation %@", self.keyStateMachineOperation);
- __weak __typeof(self) weakSelf = self;
self.keyHierarchyOperationGroup = [CKOperationGroup CKKSGroupWithName:@"key-state-broken"];
- self.keyStateReadyDependency = [CKKSResultOperation operationWithBlock:^{
- ckksnotice("ckkskey", weakSelf, "Key state has become ready again.");
- }];
- self.keyStateReadyDependency.name = [NSString stringWithFormat: @"%@-key-state-ready", self.zoneName];
+ self.keyStateReadyDependency = [self createKeyStateReadyDependency:@"Key state has become ready again." ckoperationGroup:self.keyHierarchyOperationGroup];
}
[self scheduleOperation: self.keyStateMachineOperation];
self.keyStateMachineOperation = [[CKKSHealKeyHierarchyOperation alloc] initWithCKKSKeychainView:self ckoperationGroup:self.keyHierarchyOperationGroup];
[self scheduleOperation: self.keyStateMachineOperation];
+ } else if([self.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateHealTLKShares]) {
+
+ if(!SecCKKSShareTLKs()) {
+ // This is an invalid state, since we haven't enabled TLK fixing. Set ourselves to ready!
+ ckkserror("ckksshare", self, "In SecCKKSZoneKeyStateHealTLKShares, but TLK sharing is disabled.");
+ self.keyHierarchyState = SecCKKSZoneKeyStateReady;
+ } else {
+ ckksnotice("ckksshare", self, "Key hierarchy is okay, but not shared appropriately. Launching fix.");
+ self.keyStateMachineOperation = [[CKKSHealTLKSharesOperation alloc] initWithCKKSKeychainView:self
+ ckoperationGroup:self.keyHierarchyOperationGroup];
+ [self scheduleOperation: self.keyStateMachineOperation];
+ }
+
} else {
// Nothing to do and not in a waiting state? Awesome; we must be in the ready state.
if(![self.keyHierarchyState isEqual: SecCKKSZoneKeyStateReady]) {
}
}
-- (bool)_onqueueEnsureKeyHierarchyHealth:(NSError* __autoreleasing *)error {
+// For this key, who doesn't yet have a CKKSTLKShare for it?
+// Note that we really want a record sharing the TLK to ourselves, so this function might return
+// a non-empty set even if all peers have the TLK: it wants us to make a record for ourself.
+- (NSSet<id<CKKSPeer>>*)_onqueueFindPeersMissingShare:(CKKSKey*)key error:(NSError* __autoreleasing*)error {
dispatch_assert_queue(self.queue);
+ if(!SecCKKSShareTLKs()) {
+ return [NSSet set];
+ }
+
+ if(self.currentTrustedPeersError) {
+ ckkserror("ckksshare", self, "Couldn't find missing shares because trusted peers aren't available: %@", self.currentTrustedPeersError);
+ if(error) {
+ *error = self.currentTrustedPeersError;
+ }
+ return nil;
+ }
+ if(self.currentSelfPeersError) {
+ ckkserror("ckksshare", self, "Couldn't find missing shares because self peers aren't available: %@", self.currentSelfPeersError);
+ if(error) {
+ *error = self.currentSelfPeersError;
+ }
+ return nil;
+ }
+
+ NSMutableSet<id<CKKSPeer>>* peersMissingShares = [NSMutableSet set];
+
+ NSMutableSet<NSString*>* trustedPeerIDs = [NSMutableSet set];
+ for(id<CKKSPeer> peer in self.currentTrustedPeers) {
+ [trustedPeerIDs addObject:peer.peerID];
+ }
+
+ for(id<CKKSPeer> peer in self.currentTrustedPeers) {
+ NSError* peerError = nil;
+ // Find all the shares for this peer for this key
+ NSArray<CKKSTLKShare*>* currentPeerShares = [CKKSTLKShare allFor:peer.peerID
+ keyUUID:key.uuid
+ zoneID:self.zoneID
+ error:&peerError];
+
+ if(peerError) {
+ ckkserror("ckksshare", self, "Couldn't load shares for peer %@: %@", peer, peerError);
+ if(error) {
+ *error = peerError;
+ }
+ return nil;
+ }
+
+ // Determine if we think this peer has enough things shared to them
+ bool alreadyShared = false;
+ for(CKKSTLKShare* existingPeerShare in currentPeerShares) {
+ if([existingPeerShare.tlkUUID isEqualToString: key.uuid] && [trustedPeerIDs containsObject:existingPeerShare.senderPeerID]) {
+
+ // Was this shared to us?
+ if([peer.peerID isEqualToString: self.currentSelfPeers.currentSelf.peerID]) {
+ // We only count this as 'found' if we did the sharing
+ if([existingPeerShare.senderPeerID isEqualToString:self.currentSelfPeers.currentSelf.peerID]) {
+ ckksnotice("ckksshare", self, "Local peer %@ is shared %@ via self: %@", peer, key, existingPeerShare);
+ alreadyShared = true;
+ } else {
+ ckksnotice("ckksshare", self, "Local peer %@ is shared %@ via trusted %@, but that's not good enough", peer, key, existingPeerShare);
+ }
+
+ } else {
+ // Some other peer has a trusted share. Cool!
+ ckksnotice("ckksshare", self, "Peer %@ is shared %@ via trusted %@", peer, key, existingPeerShare);
+ alreadyShared = true;
+ }
+ }
+ }
+
+ if(!alreadyShared) {
+ // Add this peer to our set
+ [peersMissingShares addObject:peer];
+ }
+ }
+
+ if(peersMissingShares.count > 0u) {
+ // Log each and every one of the things
+ ckksnotice("ckksshare", self, "Missing TLK shares for %lu peers: %@", (unsigned long)peersMissingShares.count, peersMissingShares);
+ ckksnotice("ckksshare", self, "Self peers are (%@) %@", self.currentSelfPeersError ?: @"no error", self.currentSelfPeers);
+ ckksnotice("ckksshare", self, "Trusted peers are (%@) %@", self.currentTrustedPeersError ?: @"no error", self.currentTrustedPeers);
+ }
+
+ return peersMissingShares;
+}
+
+- (NSSet<CKKSTLKShare*>*)_onqueueCreateMissingKeyShares:(CKKSKey*)key error:(NSError* __autoreleasing*)error {
+ dispatch_assert_queue(self.queue);
+
+ if(!SecCKKSShareTLKs()) {
+ return [NSSet set];
+ }
+
+ if(self.currentTrustedPeersError) {
+ ckkserror("ckksshare", self, "Couldn't create missing shares because trusted peers aren't available: %@", self.currentTrustedPeersError);
+ if(error) {
+ *error = self.currentTrustedPeersError;
+ }
+ return nil;
+ }
+ if(self.currentSelfPeersError) {
+ ckkserror("ckksshare", self, "Couldn't create missing shares because self peers aren't available: %@", self.currentSelfPeersError);
+ if(error) {
+ *error = self.currentSelfPeersError;
+ }
+ return nil;
+ }
+
+ NSSet<id<CKKSPeer>>* remainingPeers = [self _onqueueFindPeersMissingShare:key error:error];
+ NSMutableSet<CKKSTLKShare*>* newShares = [NSMutableSet set];
+
+ if(!remainingPeers) {
+ return nil;
+ }
+
NSError* localerror = nil;
- // Check if we have an existing key hierarchy
- CKKSKey* tlk = [CKKSKey currentKeyForClass:SecCKKSKeyClassTLK zoneID:self.zoneID error:&localerror];
- CKKSKey* classA = [CKKSKey currentKeyForClass:SecCKKSKeyClassA zoneID:self.zoneID error:&localerror];
- CKKSKey* classC = [CKKSKey currentKeyForClass:SecCKKSKeyClassC zoneID:self.zoneID error:&localerror];
+ if(![key ensureKeyLoaded:error]) {
+ return nil;
+ }
+
+ for(id<CKKSPeer> peer in remainingPeers) {
+ // Create a share for this peer.
+ ckksnotice("ckksshare", self, "Creating share of %@ as %@ for %@", key, self.currentSelfPeers.currentSelf, peer);
+ CKKSTLKShare* newShare = [CKKSTLKShare share:key
+ as:self.currentSelfPeers.currentSelf
+ to:peer
+ epoch:-1
+ poisoned:0
+ error:&localerror];
+
+ if(localerror) {
+ ckkserror("ckksshare", self, "Couldn't create new share for %@: %@", peer, localerror);
+ if(error) {
+ *error = localerror;
+ }
+ return nil;
+ }
+
+ [newShares addObject: newShare];
+ }
+
+ return newShares;
+}
+
+- (CKKSZoneKeyState*)_onqueueEnsureKeyHierarchyHealth:(CKKSCurrentKeySet*)set error:(NSError* __autoreleasing *)error {
+ dispatch_assert_queue(self.queue);
- if(localerror || !tlk || !classA || !classC) {
- ckkserror("ckkskey", self, "Error examining existing key hierarchy: %@", localerror);
- ckkserror("ckkskey", self, "Keys are: %@ %@ %@", tlk, classA, classC);
+ // Check keyset
+ if(!set.tlk || !set.classA || !set.classC) {
+ ckkserror("ckkskey", self, "Error examining existing key hierarchy: %@", set);
if(error) {
- *error = localerror;
+ *error = set.error;
}
- return false;
+ return SecCKKSZoneKeyStateUnhealthy;
}
+ NSError* localerror = nil;
+
// keychain being locked is not a fatal error here
- [tlk loadKeyMaterialFromKeychain:&localerror];
+ [set.tlk loadKeyMaterialFromKeychain:&localerror];
if(localerror && !([localerror.domain isEqual: @"securityd"] && localerror.code == errSecInteractionNotAllowed)) {
- ckksinfo("ckkskey", self, "Error loading TLK(%@): %@", tlk, localerror);
+ ckkserror("ckkskey", self, "Error loading TLK(%@): %@", set.tlk, localerror);
if(error) {
*error = localerror;
}
- return false;
+ return SecCKKSZoneKeyStateUnhealthy;
} else if(localerror) {
- ckksinfo("ckkskey", self, "Error loading TLK(%@), maybe locked: %@", tlk, localerror);
+ ckkserror("ckkskey", self, "Soft error loading TLK(%@), maybe locked: %@", set.tlk, localerror);
}
localerror = nil;
// keychain being locked is not a fatal error here
- [classA loadKeyMaterialFromKeychain:&localerror];
+ [set.classA loadKeyMaterialFromKeychain:&localerror];
if(localerror && !([localerror.domain isEqual: @"securityd"] && localerror.code == errSecInteractionNotAllowed)) {
- ckksinfo("ckkskey", self, "Error loading classA key(%@): %@", classA, localerror);
+ ckkserror("ckkskey", self, "Error loading classA key(%@): %@", set.classA, localerror);
if(error) {
*error = localerror;
}
- return false;
+ return SecCKKSZoneKeyStateUnhealthy;
} else if(localerror) {
- ckksinfo("ckkskey", self, "Error loading classA key(%@), maybe locked: %@", classA, localerror);
+ ckkserror("ckkskey", self, "Soft error loading classA key(%@), maybe locked: %@", set.classA, localerror);
}
localerror = nil;
// keychain being locked is a fatal error here, since this is class C
- [classA loadKeyMaterialFromKeychain:&localerror];
+ [set.classC loadKeyMaterialFromKeychain:&localerror];
if(localerror) {
- ckksinfo("ckkskey", self, "Error loading classC(%@): %@", classC, localerror);
+ ckkserror("ckkskey", self, "Error loading classC(%@): %@", set.classC, localerror);
if(error) {
*error = localerror;
}
- return false;
+ return SecCKKSZoneKeyStateUnhealthy;
+ }
+
+ // Check that the classA and classC keys point to the current TLK
+ if(![set.classA.parentKeyUUID isEqualToString: set.tlk.uuid]) {
+ localerror = [NSError errorWithDomain:CKKSServerExtensionErrorDomain
+ code:CKKSServerUnexpectedSyncKeyInChain
+ userInfo:@{
+ NSLocalizedDescriptionKey: @"Current class A key does not wrap to current TLK",
+ }];
+ ckkserror("ckkskey", self, "Key hierarchy unhealthy: %@", localerror);
+ if(error) {
+ *error = localerror;
+ }
+ return SecCKKSZoneKeyStateUnhealthy;
+ }
+ if(![set.classC.parentKeyUUID isEqualToString: set.tlk.uuid]) {
+ localerror = [NSError errorWithDomain:CKKSServerExtensionErrorDomain
+ code:CKKSServerUnexpectedSyncKeyInChain
+ userInfo:@{
+ NSLocalizedDescriptionKey: @"Current class C key does not wrap to current TLK",
+ }];
+ ckkserror("ckkskey", self, "Key hierarchy unhealthy: %@", localerror);
+ if(error) {
+ *error = localerror;
+ }
+ return SecCKKSZoneKeyStateUnhealthy;
}
- self.activeTLK = [tlk uuid];
+ self.activeTLK = [set.tlk uuid];
+
+ // Now that we're pretty sure we have the keys, are they shared appropriately?
+ if(SecCKKSShareTLKs()) {
+ // Check that every trusted peer has at least one TLK share
+ NSSet<id<CKKSPeer>>* missingShares = [self _onqueueFindPeersMissingShare:set.tlk error:&localerror];
+ if(localerror) {
+ if(error) {
+ *error = localerror;
+ }
+ return SecCKKSZoneKeyStateError;
+ }
+
+ if(!missingShares || missingShares.count != 0u) {
+ localerror = [NSError errorWithDomain:CKKSErrorDomain code:CKKSMissingTLKShare
+ description:[NSString stringWithFormat:@"Missing shares for %lu peers", (unsigned long)missingShares.count]];
+ if(error) {
+ *error = localerror;
+ }
+ return SecCKKSZoneKeyStateHealTLKShares;
+ } else {
+ ckksnotice("ckksshare", self, "TLK (%@) is shared correctly", set.tlk);
+ }
+ }
// Got to the bottom? Cool! All keys are present and accounted for.
- return true;
+ return SecCKKSZoneKeyStateReady;
}
- (void)_onqueueKeyHierarchyFetch {
return;
}
- [strongSelf dispatchSync: ^bool{
+ [strongSelf dispatchSyncWithAccountKeys: ^bool{
[strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateFetchComplete withError: nil];
return true;
}];
return;
}
- [strongSelf dispatchSync: ^bool{
+ [strongSelf dispatchSyncWithAccountKeys: ^bool{
[strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateFetchComplete withError: nil];
return true;
}];
rateLimiter: (CKKSRateLimiter*) rateLimiter
syncCallback: (SecBoolNSErrorCallback) syncCallback {
if(!SecCKKSIsEnabled()) {
- ckksinfo("ckks", self, "Skipping handleKeychainEventDbConnection due to disabled CKKS");
+ ckksnotice("ckks", self, "Skipping handleKeychainEventDbConnection due to disabled CKKS");
return;
}
bool proceed = addedSync || deletedSync;
if(!proceed) {
- ckksinfo("ckks", self, "skipping sync of non-sync item");
+ ckksnotice("ckks", self, "skipping sync of non-sync item (%d, %d)", addedSync, deletedSync);
return;
}
if(! ([protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleWhenUnlocked] ||
[protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleAfterFirstUnlock] ||
[protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleAlways])) {
- ckksinfo("ckks", self, "skipping sync of device-bound item");
+ ckksnotice("ckks", self, "skipping sync of device-bound(%@) item", protection);
return;
}
// Our caller gave us a database connection. We must get on the local queue to ensure atomicity
// Note that we're at the mercy of the surrounding db transaction, so don't try to rollback here
[self dispatchSyncWithConnection: dbconn block: ^bool {
+
+ // Always record the callback, even if we can't encrypt the item right now. Maybe we'll get to it soon!
+ if(syncCallback) {
+ CFErrorRef cferror = NULL;
+ NSString* uuid = (__bridge_transfer NSString*) CFRetain(SecDbItemGetValue(added, &v10itemuuid, &cferror));
+ if(!cferror && uuid) {
+ self.pendingSyncCallbacks[uuid] = syncCallback;
+ }
+ CFReleaseNull(cferror);
+ }
+
if(![self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateReady]) {
ckksnotice("ckks", self, "Key state not ready for new items; skipping");
return true;
// If the problem is 'no UUID', launch a scan operation to find and fix it
// We don't want to fix it up here, in the closing moments of a transaction
- if([error.domain isEqualToString:@"securityd"] && error.code == CKKSNoUUIDOnItem) {
- ckksnotice("ckks", self, "Launching scan operation");
+ if([error.domain isEqualToString:CKKSErrorDomain] && error.code == CKKSNoUUIDOnItem) {
+ ckksnotice("ckks", self, "Launching scan operation to find UUID");
CKKSScanLocalItemsOperation* scanOperation = [[CKKSScanLocalItemsOperation alloc] initWithCKKSKeychainView: self ckoperationGroup:operationGroup];
[self scheduleOperation: scanOperation];
}
// If the problem is 'couldn't load key', tell the key hierarchy state machine to fix it
// Then, launch a scan operation to find this item and upload it
- if([error.domain isEqualToString:@"securityd"] && error.code == errSecItemNotFound) {
+ if([error.domain isEqualToString:CKKSErrorDomain] && error.code == errSecItemNotFound) {
[self _onqueueAdvanceKeyStateMachineToState: nil withError: nil];
ckksnotice("ckks", self, "Launching scan operation to refind %@", added);
return true;
}
+ if(!oqe) {
+ ckkserror("ckks", self, "Didn't create an outgoing queue entry, but no error given.");
+ return true;
+ }
+
if(rateLimiter) {
NSDate* limit = nil;
NSInteger value = [rateLimiter judge:oqe at:[NSDate date] limitTime:&limit];
if(error) {
ckkserror("ckks", self, "Couldn't save outgoing queue entry to database: %@", error);
return true;
+ } else {
+ ckksnotice("ckks", self, "Saved %@ to outgoing queue", oqe);
}
// This update supercedes all other local modifications to this item (_except_ those in-flight).
}
}
- if(syncCallback) {
- self.pendingSyncCallbacks[oqe.uuid] = syncCallback;
- }
-
// Schedule a "view changed" notification
[self.notifyViewChangedScheduler trigger];
complete:(void (^) (NSError* operror)) complete
{
if(accessGroup == nil || identifier == nil) {
- NSError* error = [NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"No access group or identifier given"}];
+ NSError* error = [NSError errorWithDomain:CKKSErrorDomain
+ code:errSecParam
+ description:@"No access group or identifier given"];
ckkserror("ckkscurrent", self, "Cancelling request: %@", error);
complete(error);
return;
if(!newItemComputedSHA1 || cferror ||
![newItemComputedSHA1 isEqual:newItemSHA1]) {
ckksnotice("ckkscurrent", self, "Hash mismatch for new item: %@ vs %@", newItemComputedSHA1, newItemSHA1);
- error = [NSError errorWithDomain:@"securityd" code:errSecItemInvalidValue userInfo:@{NSLocalizedDescriptionKey: @"New item has changed; hashes mismatch. Refetch and try again."}];
+ error = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSItemChanged
+ description:@"New item has changed; hashes mismatch. Refetch and try again."];
complete(error);
CFReleaseNull(cferror);
return false;
return false;
}
+ // We pass this into the change operation. If it's nil, that's an indicator that the old item doesn't exist in the keychain anymore
+ NSData* oldItemComputedSHA1 = nil;
if(oldItem) {
- NSData* oldItemComputedSHA1 = (NSData*) CFBridgingRelease(CFRetainSafe(SecDbItemGetSHA1(oldItem, &cferror)));
+ oldItemComputedSHA1 = (NSData*) CFBridgingRelease(CFRetainSafe(SecDbItemGetSHA1(oldItem, &cferror)));
if(!oldItemComputedSHA1 || cferror ||
![oldItemComputedSHA1 isEqual:oldItemSHA1]) {
ckksnotice("ckkscurrent", self, "Hash mismatch for old item: %@ vs %@", oldItemComputedSHA1, oldItemSHA1);
- error = [NSError errorWithDomain:@"securityd" code:errSecItemInvalidValue userInfo:@{NSLocalizedDescriptionKey: @"Old item has changed; hashes mismatch. Refetch and try again."}];
+ error = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSItemChanged
+ description:@"Old item has changed; hashes mismatch. Refetch and try again."];
complete(error);
CFReleaseNull(cferror);
return false;
// Not being in a CloudKit account is an automatic failure.
if(self.accountStatus != CKKSAccountStatusAvailable) {
ckksnotice("ckkscurrent", self, "Rejecting current item pointer set since we don't have an iCloud account.");
- error = [NSError errorWithDomain:@"securityd" code:errSecNotLoggedIn userInfo:@{NSLocalizedDescriptionKey: @"User is not signed into iCloud."}];
+ error = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSNotLoggedIn
+ description:@"User is not signed into iCloud."];
complete(error);
return false;
}
CKKSUpdateCurrentItemPointerOperation* ucipo = [[CKKSUpdateCurrentItemPointerOperation alloc] initWithCKKSKeychainView:self
currentPointer:(NSString*)currentIdentifier
oldItemUUID:(NSString*)oldItemUUID
+ oldItemHash:oldItemComputedSHA1
newItemUUID:(NSString*)newItemUUID
ckoperationGroup:[CKOperationGroup CKKSGroupWithName:@"currentitem-api"]];
CKKSResultOperation* returnCallback = [CKKSResultOperation operationWithBlock:^{
__strong __typeof(self) strongSelf = weakSelf;
if(ucipo.error) {
- ckkserror("ckkscurrent", strongSelf, "Failed setting a current item pointer with %@", ucipo.error);
+ ckkserror("ckkscurrent", strongSelf, "Failed setting a current item pointer for %@ with %@", currentIdentifier, ucipo.error);
} else {
- ckksnotice("ckkscurrent", strongSelf, "Finished setting a current item pointer");
+ ckksnotice("ckkscurrent", strongSelf, "Finished setting a current item pointer for %@", currentIdentifier);
}
complete(ucipo.error);
}];
complete:(void (^) (NSString* uuid, NSError* operror)) complete
{
if(accessGroup == nil || identifier == nil) {
- complete(NULL, [NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"No access group or identifier given"}]);
+ ckksnotice("ckkscurrent", self, "Rejecting current item pointer get since no access group(%@) or identifier(%@) given", accessGroup, identifier);
+ complete(NULL, [NSError errorWithDomain:CKKSErrorDomain
+ code:errSecParam
+ description:@"No access group or identifier given"]);
return;
}
// Not being in a CloudKit account is an automatic failure.
if(self.accountStatus != CKKSAccountStatusAvailable) {
ckksnotice("ckkscurrent", self, "Rejecting current item pointer get since we don't have an iCloud account.");
- complete(NULL, [NSError errorWithDomain:@"securityd" code:errSecNotLoggedIn userInfo:@{NSLocalizedDescriptionKey: @"User is not signed into iCloud."}]);
+ complete(NULL, [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSNotLoggedIn
+ description:@"User is not signed into iCloud."]);
return;
}
}
__weak __typeof(self) weakSelf = self;
- CKKSResultOperation* getCurrentItem = [CKKSResultOperation operationWithBlock:^{
+ CKKSResultOperation* getCurrentItem = [CKKSResultOperation named:@"get-current-item-pointer" withBlock:^{
if(fetchAndProcess.error) {
+ ckksnotice("ckkscurrent", self, "Rejecting current item pointer get since fetch failed: %@", fetchAndProcess.error);
complete(NULL, fetchAndProcess.error);
return;
}
if(!cip.currentItemUUID) {
ckkserror("ckkscurrent", strongSelf, "Current item pointer is empty %@", cip);
- complete(nil, [NSError errorWithDomain:@"securityd"
+ complete(nil, [NSError errorWithDomain:CKKSErrorDomain
code:errSecInternalError
- userInfo:@{NSLocalizedDescriptionKey: @"Current item pointer is empty"}]);
+ description:@"Current item pointer is empty"]);
return false;
}
+ ckksnotice("ckkscurrent", strongSelf, "Retrieved current item pointer: %@", cip);
complete(cip.currentItemUUID, NULL);
-
return true;
}];
}];
- getCurrentItem.name = @"get-current-item-pointer";
[getCurrentItem addNullableDependency:fetchAndProcess];
[self scheduleOperation: getCurrentItem];
[protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleAfterFirstUnlock]) {
class = SecCKKSKeyClassC;
} else {
- ckkserror("ckks", self, "can't pick key class for protection %@: %@", protection, item);
+ NSError* localError = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSInvalidKeyClass
+ description:[NSString stringWithFormat:@"can't pick key class for protection %@", protection]];
+ ckkserror("ckks", self, "can't pick key class: %@ %@", localError, item);
if(error) {
- *error =[NSError errorWithDomain:@"securityd"
- code:5
- userInfo:@{NSLocalizedDescriptionKey:
- [NSString stringWithFormat:@"can't pick key class for protection %@: %@", protection, item]}];
+ *error = localError;
}
return nil;
}
- CKKSKey* key = [CKKSKey currentKeyForClass: class zoneID:self.zoneID error:error];
+ NSError* currentKeyError = nil;
+ CKKSKey* key = [CKKSKey currentKeyForClass: class zoneID:self.zoneID error:¤tKeyError];
+ if(!key || currentKeyError) {
+ ckkserror("ckks", self, "Couldn't find current key for %@: %@", class, currentKeyError);
+
+ if(error) {
+ *error = currentKeyError;
+ }
+ return nil;
+ }
// and make sure it's unwrapped.
- if(![key ensureKeyLoaded:error]) {
+ NSError* loadedError = nil;
+ if(![key ensureKeyLoaded:&loadedError]) {
+ ckkserror("ckks", self, "Couldn't load key(%@): %@", key, loadedError);
+ if(error) {
+ *error = loadedError;
+ }
return nil;
}
(CKKSOutgoingQueueOperation*) [self findFirstPendingOperation:self.outgoingQueueOperations
ofClass:[CKKSOutgoingQueueOperation class]];
if(outgoingop) {
- ckksinfo("ckks", self, "Skipping processOutgoingQueue due to at least one pending instance");
if(after) {
[outgoingop addDependency: after];
}
if(!outgoingop.ckoperationGroup && ckoperationGroup) {
outgoingop.ckoperationGroup = ckoperationGroup;
} else if(ckoperationGroup) {
- ckkserror("ckks", self, "Throwing away CKOperationGroup(%@) in favor of %@", ckoperationGroup, outgoingop.ckoperationGroup);
+ ckkserror("ckks", self, "Throwing away CKOperationGroup(%@) in favor of (%@)", ckoperationGroup.name, outgoingop.ckoperationGroup.name);
}
+ // Will log any pending dependencies as well
+ ckksnotice("ckksoutgoing", self, "Returning existing %@", outgoingop);
return outgoingop;
}
}
[op addNullableDependency: self.initialScanOperation];
[self scheduleOperation: op];
+ ckksnotice("ckksoutgoing", self, "Scheduled %@", op);
return op;
}
if(incomingop) {
ckksinfo("ckks", self, "Skipping processIncomingQueue due to at least one pending instance");
if(after) {
- [incomingop addDependency: after];
+ [incomingop addNullableDependency: after];
}
// check (again) for race condition; if the op has started we need to add another (for the dependency)
if([incomingop isPending]) {
return op;
}
-- (CKKSUpdateDeviceStateOperation*)updateDeviceState:(bool)rateLimit ckoperationGroup:(CKOperationGroup*)ckoperationGroup {
+- (CKKSUpdateDeviceStateOperation*)updateDeviceState:(bool)rateLimit
+ waitForKeyHierarchyInitialization:(uint64_t)timeout
+ ckoperationGroup:(CKOperationGroup*)ckoperationGroup {
if(!SecCKKSIsEnabled()) {
ckksinfo("ckks", self, "Skipping updateDeviceState due to disabled CKKS");
return nil;
}
+ __weak __typeof(self) weakSelf = self;
+
+ // If securityd just started, the key state might be in some transient early state. Wait a bit.
+ CKKSResultOperation* waitForKeyReady = [CKKSResultOperation named:@"device-state-wait" withBlock:^{
+ __strong __typeof(self) strongSelf = weakSelf;
+ __block bool wait = timeout > 0;
+ if(!wait) {
+ return;
+ }
+
+ // Determine if we're in an initializing state. Otherwise, don't bother waiting.
+ [strongSelf dispatchSync:^bool {
+ wait = [strongSelf.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateInitializing] ||
+ [strongSelf.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateInitialized];
+ return false;
+ }];
+
+ if(wait) {
+ [strongSelf.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:timeout];
+ }
+ ckksnotice("ckksdevice", strongSelf, "Finished waiting for key hierarchy state, currently %@", strongSelf.keyHierarchyState);
+ }];
CKKSUpdateDeviceStateOperation* op = [[CKKSUpdateDeviceStateOperation alloc] initWithCKKSKeychainView:self rateLimit:rateLimit ckoperationGroup:ckoperationGroup];
op.name = @"device-state-operation";
+ [op addDependency: waitForKeyReady];
+
// op modifies the CloudKit zone, so it should insert itself into the list of OutgoingQueueOperations.
// Then, we won't have simultaneous zone-modifying operations and confuse ourselves.
// However, since we might have pending OQOs, it should try to insert itself at the beginning of the linearized list
[op linearDependenciesWithSelfFirst:self.outgoingQueueOperations];
// CKKSUpdateDeviceStateOperations are special: they should fire even if we don't believe we're in an iCloud account.
+ [self scheduleAccountStatusOperation:waitForKeyReady];
[self scheduleAccountStatusOperation:op];
return op;
}
}
- (CKKSResultOperation*)fetchAndProcessCKChanges:(CKKSFetchBecause*)because {
+ return [self fetchAndProcessCKChanges:because after:nil];
+}
+
+- (CKKSResultOperation*)fetchAndProcessCKChanges:(CKKSFetchBecause*)because after:(CKKSResultOperation*)after {
if(!SecCKKSIsEnabled()) {
ckksinfo("ckks", self, "Skipping fetchAndProcessCKChanges due to disabled CKKS");
return nil;
}
+ if(after) {
+ [self.zoneChangeFetcher holdFetchesUntil:after];
+ }
+
// We fetched some changes; try to process them!
return [self processIncomingQueue:false after:[self.zoneChangeFetcher requestSuccessfulFetch:because]];
}
return true;
}
}
+
+ // Check if this error was the CKKS server extension rejecting the write
+ for(CKRecordID* recordID in partialErrors.allKeys) {
+ NSError* error = partialErrors[recordID];
+
+ NSError* underlyingError = error.userInfo[NSUnderlyingErrorKey];
+ NSError* thirdLevelError = underlyingError.userInfo[NSUnderlyingErrorKey];
+ ckksnotice("ckks", self, "Examining 'write failed' error: %@ %@ %@", error, underlyingError, thirdLevelError);
+
+ if([error.domain isEqualToString:CKErrorDomain] && error.code == CKErrorServerRejectedRequest &&
+ underlyingError && [underlyingError.domain isEqualToString:CKInternalErrorDomain] && underlyingError.code == CKErrorInternalPluginError &&
+ thirdLevelError && [thirdLevelError.domain isEqualToString:@"CloudkitKeychainService"]) {
+
+ if(thirdLevelError.code == CKKSServerUnexpectedSyncKeyInChain) {
+ // The server thinks the classA/C synckeys don't wrap directly the to top TLK, but we don't (otherwise, we would have fixed it).
+ // Issue a key hierarchy fetch and see what's what.
+ ckkserror("ckks", self, "CKKS Server extension has told us about %@ for record %@; requesting refetch and reprocess of key hierarchy", thirdLevelError, recordID);
+ [self _onqueueKeyStateMachineRequestFetch];
+ } else {
+ ckkserror("ckks", self, "CKKS Server extension has told us about %@ for record %@, but we don't currently handle this error", thirdLevelError, recordID);
+ }
+ }
+ }
}
return false;
} else if([recordType isEqual: SecCKRecordIntermediateKeyType]) {
// TODO: handle in some interesting way
return true;
+ } else if([recordType isEqual: SecCKRecordTLKShareType]) {
+ NSError* error = nil;
+ ckksinfo("ckks", self, "CloudKit notification: deleted tlk share record(%@): %@", recordType, recordID);
+ CKKSTLKShare* share = [CKKSTLKShare tryFromDatabaseFromCKRecordID:recordID error:&error];
+ [share deleteFromDatabase:&error];
+
+ if(error) {
+ ckkserror("ckks", self, "CK notification: Couldn't delete deleted TLKShare: %@ %@", recordID, error);
+ }
+ return (error == nil);
+
} else if([recordType isEqual: SecCKRecordDeviceStateType]) {
NSError* error = nil;
ckksinfo("ckks", self, "CloudKit notification: deleted device state record(%@): %@", recordType, recordID);
// TODO: actually pass error back up
return error == nil;
}
+
else {
ckkserror("ckksfetch", self, "unknown record type: %@ %@", recordType, recordID);
return false;
} else if([[record recordType] isEqual: SecCKRecordIntermediateKeyType]) {
[self _onqueueCKRecordKeyChanged:record resync:resync];
return true;
+ } else if ([[record recordType] isEqual: SecCKRecordTLKShareType]) {
+ [self _onqueueCKRecordTLKShareChanged:record resync:resync];
+ return true;
} else if([[record recordType] isEqualToString: SecCKRecordCurrentKeyType]) {
[self _onqueueCKRecordCurrentKeyPointerChanged:record resync:resync];
return true;
[self _onqueueKeyStateMachineRequestProcess];
}
+- (void)_onqueueCKRecordTLKShareChanged:(CKRecord*)record resync:(bool)resync {
+ dispatch_assert_queue(self.queue);
+
+ NSError* error = nil;
+ if(resync) {
+ // TODO fill in
+ }
+
+ // CKKSTLKShares get saved with no modification
+ CKKSTLKShare* share = [[CKKSTLKShare alloc] initWithCKRecord:record];
+ [share saveToDatabase:&error];
+ if(error) {
+ ckkserror("ckksshare", self, "Couldn't save new TLK share to database: %@ %@", share, error);
+ }
+
+ [self _onqueueKeyStateMachineRequestProcess];
+}
+
- (void)_onqueueCKRecordCurrentKeyPointerChanged:(CKRecord*)record resync:(bool)resync {
dispatch_assert_queue(self.queue);
+ // Pull out the old CKP, if it exists
+ NSError* ckperror = nil;
+ CKKSCurrentKeyPointer* oldckp = [CKKSCurrentKeyPointer tryFromDatabase:((CKKSKeyClass*) record.recordID.recordName) zoneID:self.zoneID error:&ckperror];
+ if(ckperror) {
+ ckkserror("ckkskey", self, "error loading ckp: %@", ckperror);
+ }
+
if(resync) {
- NSError* ckperror = nil;
- CKKSCurrentKeyPointer* ckp = [CKKSCurrentKeyPointer tryFromDatabase:((CKKSKeyClass*) record.recordID.recordName) zoneID:self.zoneID error:&ckperror];
- if(ckperror) {
- ckkserror("ckksresync", self, "error loading ckp: %@", ckperror);
- }
- if(!ckp) {
+ if(!oldckp) {
ckkserror("ckksresync", self, "BUG: No current key pointer matching resynced CloudKit record: %@", record);
- } else if(![ckp matchesCKRecord:record]) {
- ckkserror("ckksresync", self, "BUG: Local current key pointer doesn't match resynced CloudKit record: %@ %@", ckp, record);
+ } else if(![oldckp matchesCKRecord:record]) {
+ ckkserror("ckksresync", self, "BUG: Local current key pointer doesn't match resynced CloudKit record: %@ %@", oldckp, record);
} else {
- ckksnotice("ckksresync", self, "Already know about this current key pointer, skipping update: %@", record);
- return;
+ ckksnotice("ckksresync", self, "Current key pointer has 'changed', but it matches our local copy: %@", record);
}
}
ckksinfo("ckkskey", self, "CKRecord was %@", record);
}
- // We've saved a new key in the database; trigger a rekey operation.
- [self _onqueueKeyStateMachineRequestProcess];
+ if([oldckp matchesCKRecord:record]) {
+ ckksnotice("ckkskey", self, "Current key pointer modification doesn't change anything interesting; skipping reprocess: %@", record);
+ } else {
+ // We've saved a new key in the database; trigger a rekey operation.
+ [self _onqueueKeyStateMachineRequestProcess];
+ }
}
- (void)_onqueueCKRecordCurrentItemPointerChanged:(CKRecord*)record resync:(bool)resync {
SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
if(callback) {
callback(true, nil);
+ self.pendingSyncCallbacks[oqe.uuid] = nil;
}
[oqe deleteFromDatabase: &localerror];
SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
if(callback) {
callback(false, itemError);
+ self.pendingSyncCallbacks[oqe.uuid] = nil;
}
NSError* localerror = nil;
}
}
-- (bool)checkTLK: (CKKSKey*) proposedTLK error: (NSError * __autoreleasing *) error {
- // Until we have Octagon Trust, accept this TLK iff we have its actual AES key in the keychain
+- (bool)_onqueueWithAccountKeysCheckTLK:(CKKSKey*)proposedTLK error:(NSError* __autoreleasing *)error {
+ dispatch_assert_queue(self.queue);
+ // First, if we have a local identity, check for any TLK shares
+ NSError* localerror = nil;
+
+ if(SecCKKSShareTLKs()) {
+ if(![proposedTLK wrapsSelf]) {
+ ckkserror("ckksshare", self, "Potential TLK %@ does not wrap self; skipping TLK share checking", proposedTLK);
+ } else {
+ if(!self.currentSelfPeers.currentSelf || self.currentSelfPeersError) {
+ ckkserror("ckksshare", self, "Couldn't fetch self peers: %@", self.currentSelfPeersError);
+ if(error) {
+ *error = self.currentSelfPeersError;
+ }
+ return false;
+ }
+
+ if(!self.currentTrustedPeers || self.currentTrustedPeersError) {
+ ckkserror("ckksshare", self, "Couldn't fetch trusted peers: %@", self.currentTrustedPeersError);
+ if(error) {
+ *error = self.currentTrustedPeersError;
+ }
+ return false;
+ }
+
+ NSArray<CKKSTLKShare*>* possibleShares = [CKKSTLKShare allFor:self.currentSelfPeers.currentSelf.peerID
+ keyUUID:proposedTLK.uuid
+ zoneID:self.zoneID
+ error:&localerror];
+ if(localerror) {
+ ckkserror("ckksshare", self, "Error fetching CKKSTLKShares: %@", localerror);
+ }
+
+ if(possibleShares.count == 0) {
+ ckksnotice("ckksshare", self, "No CKKSTLKShares for %@", proposedTLK);
+ }
+
+ for(CKKSTLKShare* possibleShare in possibleShares) {
+ NSError* possibleShareError = nil;
+ ckksnotice("ckksshare", self, "Checking possible TLK share %@ as %@",
+ possibleShare, self.currentSelfPeers.currentSelf);
+
+ CKKSKey* possibleKey = [possibleShare recoverTLK:self.currentSelfPeers.currentSelf
+ trustedPeers:self.currentTrustedPeers
+ error:&possibleShareError];
+
+ if(possibleShareError) {
+ ckkserror("ckksshare", self, "Unable to unwrap TLKShare(%@) as %@: %@",
+ possibleShare, self.currentSelfPeers.currentSelf, possibleShareError);
+ ckkserror("ckksshare", self, "Current trust set: %@", self.currentTrustedPeers);
+ // TODO: save error
+ continue;
+ }
+
+ bool result = [proposedTLK trySelfWrappedKeyCandidate:possibleKey.aessivkey error:&possibleShareError];
+ if(possibleShareError) {
+ ckkserror("ckksshare", self, "Unwrapped TLKShare(%@) does not unwrap proposed TLK(%@) as %@: %@",
+ possibleShare, proposedTLK, self.currentSelfPeers.currentSelf, possibleShareError);
+ // TODO save error
+ continue;
+ }
+
+ if(result) {
+ ckksnotice("ckksshare", self, "TLKShare(%@) unlocked TLK(%@) as %@",
+ possibleShare, proposedTLK, self.currentSelfPeers.currentSelf);
+
+ // The proposed TLK is trusted key material. Persist it as a "trusted" key.
+ [proposedTLK saveKeyMaterialToKeychain:true error: &possibleShareError];
+ if(possibleShareError) {
+ ckkserror("ckksshare", self, "Couldn't store the new TLK(%@) to the keychain: %@", proposedTLK, possibleShareError);
+ if(error) {
+ *error = possibleShareError;
+ }
+ return false;
+ }
+
+ return true;
+ }
+ }
+ }
+
+ } else {
+ ckksnotice("ckks", self, "No current self identity. Skipping TLK shares.")
+ }
if([proposedTLK loadKeyMaterialFromKeychain:error]) {
// Hurray!
}
}
-- (void)dispatchSyncWithAccountQueue:(bool (^)(void))block
+- (void)dispatchSyncWithAccountKeys:(bool (^)(void))block
{
- [SOSAccount performOnAccountQueue:^{
- [CKKSManifest performWithAccountInfo:^{
- [self dispatchSync:^bool{
- __block bool result = false;
- [SOSAccount performWhileHoldingAccountQueue:^{ // so any calls through SOS account will know they can perform their work without dispatching to the account queue, which we already hold
- result = block();
- }];
- return result;
+ [SOSAccount performOnAccountQueue: ^{
+ NSError* selfPeersError = nil;
+ CKKSSelves* currentSelfPeers = [self.currentPeerProvider fetchSelfPeers:&selfPeersError];
+
+ NSError* trustedPeersError = nil;
+ NSSet<id<CKKSPeer>>* currentTrustedPeers = [self.currentPeerProvider fetchTrustedPeers:&trustedPeersError];
+
+ [self dispatchSync:^bool{
+ self.currentSelfPeers = currentSelfPeers;
+ self.currentSelfPeersError = selfPeersError;
+
+ self.currentTrustedPeers = currentTrustedPeers;
+ self.currentTrustedPeersError = trustedPeersError;
+
+ __block bool result = false;
+ [SOSAccount performWhileHoldingAccountQueue:^{ // so any calls through SOS account will know they can perform their work without dispatching to the account queue, which we already hold
+ result = block();
}];
+
+ // Forget the peers; they might have class A key material
+ self.currentSelfPeers = nil;
+ self.currentSelfPeersError = [NSError errorWithDomain:CKKSErrorDomain code:CKKSNoPeersAvailable description:@"No current self peer available"];
+ self.currentTrustedPeers = nil;
+ self.currentTrustedPeersError = [NSError errorWithDomain:CKKSErrorDomain code:CKKSNoPeersAvailable description:@"No current trusted peers available"];
+
+ return result;
}];
}];
}
return false;
}
+#pragma mark CKKSPeerUpdateListener
+
+- (void)selfPeerChanged {
+ // Currently, we have no idea what to do with this. Kick off a key reprocess?
+ ckkserror("ckks", self, "Received update that our self identity has changed");
+ [self keyStateMachineRequestProcess];
+}
+
+- (void)trustedPeerSetChanged {
+ // We might need to share the TLK to some new people, or we might now trust the TLKs we have.
+ // The key state machine should handle that, so poke it.
+ ckkserror("ckks", self, "Received update that the trust set has changed");
+ [self keyStateMachineRequestProcess];
+}
+
#pragma mark - Test Support
- (bool) outgoingQueueEmpty: (NSError * __autoreleasing *) error {
[self.keyStateMachineOperation cancel];
[self.keyStateReadyDependency cancel];
[self.zoneChangeFetcher cancel];
+ [self.notifyViewChangedScheduler cancel];
+
+ for(NSOperation* op in self.outgoingQueueOperations) {
+ [op cancel];
+ }
+ [self.outgoingQueueOperations removeAllObjects];
+
+ for(NSOperation* op in self.incomingQueueOperations) {
+ [op cancel];
+ }
+ [self.incomingQueueOperations removeAllObjects];
+
+ // Don't send any more notifications, either
+ _notifierClass = nil;
[super cancelAllOperations];
[mutDeviceStates addObject: [obj description]];
}];
+ NSArray* tlkShares = [CKKSTLKShare allForUUID:uuidTLK zoneID:self.zoneID error:&error];
+ NSMutableArray<NSString*>* mutTLKShares = [[NSMutableArray alloc] init];
+ [tlkShares enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+ [mutTLKShares addObject: [obj description]];
+ }];
+
+
ret = @{
@"view": CKKSNilToNSNull(self.zoneName),
@"ckaccountstatus": self.accountStatus == CKAccountStatusCouldNotDetermine ? @"could not determine" :
@"iqe": CKKSNilToNSNull([CKKSIncomingQueueEntry countsByState:self.zoneID error:&error]),
@"ckmirror": CKKSNilToNSNull([CKKSMirrorEntry countsByParentKey:self.zoneID error:&error]),
@"devicestates": CKKSNilToNSNull(mutDeviceStates),
+ @"tlkshares": CKKSNilToNSNull(mutTLKShares),
@"keys": CKKSNilToNSNull([CKKSKey countsByClass:self.zoneID error:&error]),
@"currentTLK": CKKSNilToNSNull(uuidTLK),
@"currentClassA": CKKSNilToNSNull(uuidClassA),
@"currentClassC": CKKSNilToNSNull(uuidClassC),
@"currentManifestGen": CKKSNilToNSNull(manifestGeneration),
+
@"zoneSetupOperation": stringify(self.zoneSetupOperation),
@"viewSetupOperation": stringify(self.viewSetupOperation),
@"keyStateOperation": stringify(self.keyStateMachineOperation),
}
-(bool)isLockedError:(NSError *)error {
- return [error.domain isEqualToString:@"securityd"] && error.code == errSecInteractionNotAllowed;
+ return ([error.domain isEqualToString:@"securityd"] || [error.domain isEqualToString:(__bridge NSString*)kSecErrorDomain])
+ && error.code == errSecInteractionNotAllowed;
}
CFArrayForEach(peerInfos, ^(const void* peerInfoPtr) {
SOSPeerInfoRef peerInfo = (SOSPeerInfoRef)peerInfoPtr;
CFErrorRef blockError = NULL;
- SecKeyRef secPublicKey = SOSPeerInfoCopyOctagonPubKey(peerInfo, &blockError);
+ SecKeyRef secPublicKey = SOSPeerInfoCopyOctagonSigningPublicKey(peerInfo, &blockError);
if (!secPublicKey || error) {
CFReleaseNull(blockError);
return;
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#import "CKKSSQLDatabaseObject.h"
#import "CKKSItem.h"
#include <utilities/SecDb.h>
#ifndef CKKSMirrorEntry_h
#define CKKSMirrorEntry_h
-#if OCTAGON
-
#import <CloudKit/CloudKit.h>
@class CKKSWrappedAESSIVKey;
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#include <AssertMacros.h>
#import <Foundation/Foundation.h>
#include <securityd/SecDbItem.h>
#include <securityd/SecItemSchema.h>
-#if OCTAGON
#import <CloudKit/CloudKit.h>
#import "CKKSOutgoingQueueEntry.h"
#if OCTAGON
+#import "keychain/ckks/CKKSTLKShare.h"
+
@interface CKKSNewTLKOperation ()
@property NSBlockOperation* cloudkitModifyOperationFinished;
@property CKOperationGroup* ckoperationGroup;
}
// Synchronous, on some thread. Get back on the CKKS queue for SQL thread-safety.
- [ckks dispatchSync: ^bool{
+ [ckks dispatchSyncWithAccountKeys: ^bool{
if(self.cancelled) {
ckksnotice("ckkstlk", ckks, "CKKSNewTLKOperation cancelled, quitting");
return false;
}
// Save the proposed keys to the keychain. Note that we might reject this TLK later, but in that case, this TLK is just orphaned. No worries!
- ckksinfo("ckkstlk", ckks, "Saving new keys %@ to database %@", recordsToSave, ckks.database);
+ ckksnotice("ckkstlk", ckks, "Saving new keys %@ to database %@", recordsToSave, ckks.database);
[newTLK saveKeyMaterialToKeychain: &error];
[newClassAKey saveKeyMaterialToKeychain: &error];
return false;
}
+ // Generate the TLK sharing records for all trusted peers
+ NSMutableSet<CKKSTLKShare*>* tlkShares = [NSMutableSet set];
+ if(SecCKKSShareTLKs()) {
+ for(id<CKKSPeer> trustedPeer in ckks.currentTrustedPeers) {
+ ckksnotice("ckkstlk", ckks, "Generating TLK(%@) share for %@", newTLK, trustedPeer);
+ CKKSTLKShare* share = [CKKSTLKShare share:newTLK as:ckks.currentSelfPeers.currentSelf to:trustedPeer epoch:-1 poisoned:0 error:&error];
+
+ [tlkShares addObject:share];
+ [recordsToSave addObject: [share CKRecordWithZoneID: ckks.zoneID]];
+ }
+ }
+
// Use the spare operation trick to wait for the CKModifyRecordsOperation to complete
self.cloudkitModifyOperationFinished = [NSBlockOperation named:@"newtlk-cloudkit-modify-operation-finished" withBlock:^{}];
[self dependOnBeforeGroupFinished: self.cloudkitModifyOperationFinished];
return;
}
- [strongCKKS dispatchSync: ^bool{
+ [strongCKKS dispatchSyncWithAccountKeys: ^bool{
if(ckerror == nil) {
ckksnotice("ckkstlk", strongCKKS, "Completed TLK CloudKit operation");
} else if([currentClassCPointer matchesCKRecord: record]) {
currentClassCPointer.storedCKRecord = record;
}
+
+ for(CKKSTLKShare* share in tlkShares) {
+ if([share matchesCKRecord: record]) {
+ share.storedCKRecord = record;
+ }
+ }
}
[newTLK saveToDatabaseAsOnlyCurrentKeyForClassAndState: &localerror];
[wrappedOldTLK saveToDatabase: &localerror];
+ for(CKKSTLKShare* share in tlkShares) {
+ [share saveToDatabase:&localerror];
+ }
+
// TLKs are already saved in the local keychain; fire off a backup
CKKSNearFutureScheduler* tlkNotifier = strongCKKS.savedTLKNotifier;
ckksnotice("ckkstlk", strongCKKS, "triggering new TLK notification: %@", tlkNotifier);
#import "CKKSOutgoingQueueEntry.h"
#import "CKKSItemEncrypter.h"
#import "CKKSKey.h"
+#import "keychain/ckks/CloudKitCategories.h"
@implementation CKKSOutgoingQueueEntry
true) ? YES : NO;
}
-+ (instancetype)withItem: (SecDbItemRef) item action: (NSString*) action ckks:(CKKSKeychainView*) ckks error: (NSError * __autoreleasing *) error {
++ (instancetype)withItem:(SecDbItemRef)item action:(NSString*)action ckks:(CKKSKeychainView*)ckks error: (NSError * __autoreleasing *) error {
CFErrorRef cferror = NULL;
CKKSKey* key = nil;
NSString* uuid = nil;
NSInteger newGenerationCount = -1;
-
NSMutableDictionary* objd = nil;
- key = [ckks keyForItem: item error:error];
- if(!key) {
+ NSError* keyError = nil;
+ key = [ckks keyForItem:item error:&keyError];
+ if(!key || keyError) {
+ NSError* localerror = [NSError errorWithDomain:CKKSErrorDomain code:keyError.code description:@"No key for item" underlying:keyError];
+ ckkserror("ckksitem", ckks, "no key for item: %@ %@", localerror, item);
+ if(error) {
+ *error = localerror;
+ }
return nil;
}
objd = (__bridge_transfer NSMutableDictionary*) SecDbItemCopyPListWithMask(item, kSecDbSyncFlag, &cferror);
if(!objd) {
- SecTranslateError(error, cferror);
+ NSError* localerror = [NSError errorWithDomain:CKKSErrorDomain code:CFErrorGetCode(cferror) description:@"Couldn't create object plist" underlying:(__bridge_transfer NSError*)cferror];
+ ckkserror("ckksitem", ckks, "no plist: %@ %@", localerror, item);
+ if(error) {
+ *error = localerror;
+ }
return nil;
}
// Object classes aren't in the item plist, set them specifically
[objd setObject: (__bridge NSString*) item->class->name forKey: (__bridge NSString*) kSecClass];
- uuid = (__bridge_transfer NSString*) CFRetain(SecDbItemGetValue(item, &v10itemuuid, &cferror));
+ uuid = (__bridge_transfer NSString*) CFRetainSafe(SecDbItemGetValue(item, &v10itemuuid, &cferror));
if(!uuid || cferror) {
- SecTranslateError(error, cferror);
+ NSError* localerror = [NSError errorWithDomain:CKKSErrorDomain code:CKKSNoUUIDOnItem description:@"No UUID for item" underlying:(__bridge_transfer NSError*)cferror];
+ ckkserror("ckksitem", ckks, "No UUID for item: %@ %@", localerror, item);
+ if(error) {
+ *error = localerror;
+ }
return nil;
}
if([uuid isKindOfClass:[NSNull class]]) {
- NSError* localerror = [NSError errorWithDomain:@"securityd"
- code:CKKSNoUUIDOnItem
- userInfo:@{NSLocalizedDescriptionKey: @"UUID not found in object"}];
-
- secerror("ckksitem: couldn't fetch UUID: %@ %@", localerror, item);
+ NSError* localerror = [NSError errorWithDomain:CKKSErrorDomain code:CKKSNoUUIDOnItem description:@"UUID not found in object" underlying:nil];
+ ckkserror("ckksitem", ckks, "couldn't fetch UUID: %@ %@", localerror, item);
if(error) {
*error = localerror;
}
return nil;
}
- accessgroup = (__bridge_transfer NSString*) CFRetain(SecDbItemGetValue(item, &v6agrp, &cferror));
+ accessgroup = (__bridge_transfer NSString*) CFRetainSafe(SecDbItemGetValue(item, &v6agrp, &cferror));
if(!accessgroup || cferror) {
- SecTranslateError(error, cferror);
+ NSError* localerror = [NSError errorWithDomain:CKKSErrorDomain code:CFErrorGetCode(cferror) description:@"accessgroup not found in object" underlying:(__bridge_transfer NSError*)cferror];
+ ckkserror("ckksitem", ckks, "couldn't fetch access group from item: %@ %@", localerror, item);
+ if(error) {
+ *error = localerror;
+ }
return nil;
}
if([accessgroup isKindOfClass:[NSNull class]]) {
// That's okay; this is only used for rate limiting.
- secerror("ckksitem: couldn't fetch accessgroup: %@", item);
+ ckkserror("ckksitem", ckks, "couldn't fetch accessgroup: %@", item);
accessgroup = @"no-group";
}
-
CKKSMirrorEntry* ckme = [CKKSMirrorEntry tryFromDatabase:uuid zoneID:ckks.zoneID error:error];
// The action this change should be depends on any existing pending action, if any
if([existingOQE.action isEqual: SecCKKSActionDelete] && [action isEqual:SecCKKSActionAdd]) {
actualAction = SecCKKSActionModify;
}
-
}
newGenerationCount = ckme ? ckme.item.generationCount : (NSInteger) 0; // TODO: this is wrong
plaintextPCSPublicKey:pcsPublicKey
plaintextPCSPublicIdentity:pcsPublicIdentity];
+ if(!baseitem) {
+ NSError* localerror = [NSError errorWithDomain:CKKSErrorDomain code:CKKSItemCreationFailure description:@"Couldn't create an item" underlying:nil];
+ ckkserror("ckksitem", ckks, "couldn't create an item: %@ %@", localerror, item);
+ if(error) {
+ *error = localerror;
+ }
+ return nil;
+ }
+
+ NSError* encryptionError = nil;
CKKSItem* encryptedItem = [CKKSItemEncrypter encryptCKKSItem:baseitem
dataDictionary:objd
updatingCKKSItem:ckme.item
parentkey:key
- error:error];
+ error:&encryptionError];
- if(!encryptedItem) {
+ if(!encryptedItem || encryptionError) {
+ NSError* localerror = [NSError errorWithDomain:CKKSErrorDomain code:encryptionError.code description:@"Couldn't encrypt item" underlying:encryptionError];
+ ckkserror("ckksitem", ckks, "couldn't encrypt item: %@ %@", localerror, item);
+ if(error) {
+ *error = localerror;
+ }
return nil;
}
return;
}
- [ckks dispatchSyncWithAccountQueue: ^bool{
+ [ckks dispatchSyncWithAccountKeys: ^bool{
ckks.lastOutgoingQueueOperation = self;
if(self.cancelled) {
ckksnotice("ckksoutgoing", ckks, "CKKSOutgoingQueueOperation cancelled, quitting");
// The current key pointers have updated without our knowledge, so CloudKit failed this operation. Mark all records as 'needs reencryption' and kick that off.
[strongSelf _onqueueModifyAllRecordsAsReencrypt: failedRecords.allKeys];
- ckksnotice("ckksoutgoing", strongCKKS, "initiate key fetch and reencrypt");
- // Nudge the key state machine, so that it runs off to fetch the new keys
- [strongCKKS _onqueueKeyStateMachineRequestFetch];
+ // Note that _onqueueCKWriteFailed is responsible for kicking the key state machine, so we don't need to do it here.
// This will wait for the key hierarchy to become 'ready'
CKKSReencryptOutgoingItemsOperation* op = [[CKKSReencryptOutgoingItemsOperation alloc] initWithCKKSKeychainView:strongCKKS ckoperationGroup:strongSelf.ckoperationGroup];
[strongCKKS scheduleOperation: op];
} else {
// CKErrorServerRecordChanged on an item update means that we've been overwritten.
if([oqesModified containsObject:recordID]) {
- [self _onqueueModifyRecordAsError:recordID recordError:recordError];
+ [strongSelf _onqueueModifyRecordAsError:recordID recordError:recordError];
}
}
} else if(recordError.code == CKErrorBatchRequestFailed) {
// Some unknown error occurred on this record. If it's an OQE, move it to the error state.
ckkserror("ckksoutgoing", strongCKKS, "Unknown error on row: %@ %@", recordID, recordError);
if([oqesModified containsObject:recordID]) {
- [self _onqueueModifyRecordAsError:recordID recordError:recordError];
+ [strongSelf _onqueueModifyRecordAsError:recordID recordError:recordError];
}
}
}
[strongSelf.operationQueue addOperation: modifyComplete];
// Kick off another queue process. We expect it to exit instantly, but who knows!
- [strongCKKS processOutgoingQueue:self.ckoperationGroup];
+ [strongCKKS processOutgoingQueue:strongSelf.ckoperationGroup];
};
ckksinfo("ckksoutgoing", ckks, "Current keys to update: %@", currentKeysToSave);
if(!error) {
ckksnotice("ckksoutgoing", blockCKKS, "Record upload successful for %@", record.recordID.recordName);
} else {
- ckkserror("ckksoutgoing", blockCKKS, "error on row: %@ %@", record, error);
+ ckkserror("ckksoutgoing", blockCKKS, "error on row: %@ %@", error, record);
}
};
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import <Foundation/Foundation.h>
+#import <SecurityFoundation/SFKey.h>
+
+
+// ==== Peer protocols ====
+
+@protocol CKKSPeer <NSObject>
+@property (readonly) NSString* peerID;
+@property (readonly) SFECPublicKey* publicEncryptionKey;
+@property (readonly) SFECPublicKey* publicSigningKey;
+
+// Not exactly isEqual, since this only compares peerID
+- (bool)matchesPeer:(id<CKKSPeer>)peer;
+@end
+
+@protocol CKKSSelfPeer <CKKSPeer>
+@property (readonly) SFECKeyPair* encryptionKey;
+@property (readonly) SFECKeyPair* signingKey;
+@end
+
+// ==== Peer collection protocols ====
+
+@interface CKKSSelves : NSObject
+@property id<CKKSSelfPeer> currentSelf;
+@property NSSet<id<CKKSSelfPeer>>* allSelves;
+- (instancetype)initWithCurrent:(id<CKKSSelfPeer>)selfPeer allSelves:(NSSet<id<CKKSSelfPeer>>*)allSelves;
+@end
+
+// ==== Peer handler protocols ====
+
+@protocol CKKSPeerUpdateListener;
+
+@protocol CKKSPeerProvider <NSObject>
+- (CKKSSelves*)fetchSelfPeers:(NSError* __autoreleasing *)error;
+- (NSSet<id<CKKSPeer>>*)fetchTrustedPeers:(NSError* __autoreleasing *)error;
+// Trusted peers should include self peers
+
+- (void)registerForPeerChangeUpdates:(id<CKKSPeerUpdateListener>)listener;
+@end
+
+// A CKKSPeerUpdateListener wants to be notified when a CKKSPeerProvider has new information
+@protocol CKKSPeerUpdateListener <NSObject>
+- (void)selfPeerChanged;
+- (void)trustedPeerSetChanged;
+@end
+
+
+// These should be replaced by Octagon peers, when those exist
+@interface CKKSSOSPeer : NSObject <CKKSPeer>
+@property (readonly) NSString* peerID;
+@property (readonly) SFECPublicKey* publicEncryptionKey;
+@property (readonly) SFECPublicKey* publicSigningKey;
+
+- (instancetype)initWithSOSPeerID:(NSString*)syncingPeerID
+ encryptionPublicKey:(SFECPublicKey*)encryptionKey
+ signingPublicKey:(SFECPublicKey*)signingKey;
+@end
+
+@interface CKKSSOSSelfPeer : NSObject <CKKSPeer, CKKSSelfPeer>
+@property (readonly) NSString* peerID;
+@property (readonly) SFECPublicKey* publicEncryptionKey;
+@property (readonly) SFECPublicKey* publicSigningKey;
+
+@property (readonly) SFECKeyPair* encryptionKey;
+@property (readonly) SFECKeyPair* signingKey;
+
+- (instancetype)initWithSOSPeerID:(NSString*)syncingPeerID
+ encryptionKey:(SFECKeyPair*)encryptionKey
+ signingKey:(SFECKeyPair*)signingKey;
+@end
+
+#endif // OCTAGON
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import "keychain/ckks/CKKSPeer.h"
+
+@implementation CKKSSelves
+- (instancetype)initWithCurrent:(id<CKKSSelfPeer>)selfPeer
+ allSelves:(NSSet<id<CKKSSelfPeer>>*)allSelves {
+ if((self = [super init])) {
+ _currentSelf = selfPeer;
+
+ // Ensure allSelves contains selfPeer
+ _allSelves = allSelves ? [allSelves setByAddingObject:selfPeer] : [NSSet setWithObject:selfPeer];
+ }
+ return self;
+}
+
+- (NSString*)description {
+ NSMutableSet* pastSelves = [self.allSelves mutableCopy];
+ [pastSelves removeObject:self.currentSelf];
+ return [NSString stringWithFormat:@"<CKKSSelves: %@ %@>", self.currentSelf, pastSelves.count == 0u ? @"(no past selves)" : pastSelves ];
+}
+
+@end
+
+@interface CKKSSOSPeer ()
+@property NSString* spid;
+@end
+
+@implementation CKKSSOSPeer
+- (NSString*)description {
+ // Return the first 16 bytes of the public keys (for reading purposes)
+ return [NSString stringWithFormat:@"<CKKSSOSPeer(%@): pubEnc:%@ pubSign:%@>",
+ self.peerID,
+ [self.publicEncryptionKey.keyData subdataWithRange:NSMakeRange(0, MIN(16u,self.publicEncryptionKey.keyData.length))],
+ [self.publicSigningKey.keyData subdataWithRange:NSMakeRange(0, MIN(16u,self.publicSigningKey.keyData.length))]];
+}
+
+- (NSString*)prefix {
+ return @"spid-";
+}
+
+- (instancetype)initWithSOSPeerID:(NSString*)syncingPeerID
+ encryptionPublicKey:(SFECPublicKey*)encryptionKey
+ signingPublicKey:(SFECPublicKey*)signingKey
+{
+ if((self = [super init])) {
+
+ if([syncingPeerID hasPrefix:[self prefix]]) {
+ _spid = [syncingPeerID substringFromIndex:[self prefix].length];
+ } else {
+ _spid = syncingPeerID;
+ }
+ _publicEncryptionKey = encryptionKey;
+ _publicSigningKey = signingKey;
+ }
+ return self;
+}
+
+- (NSString*)peerID {
+ return [NSString stringWithFormat:@"%@%@", self.prefix, self.spid];
+}
+
+- (bool)matchesPeer:(id<CKKSPeer>)peer {
+ return (self.peerID == nil && peer.peerID == nil) ||
+ [self.peerID isEqualToString:peer.peerID];
+}
+@end
+
+@interface CKKSSOSSelfPeer ()
+@property NSString* spid;
+@end
+
+@implementation CKKSSOSSelfPeer
+- (NSString*)description {
+ return [NSString stringWithFormat:@"<CKKSSOSSelfPeer(%@): pubEnc:%@ pubSign:%@>",
+ self.peerID,
+ [self.publicEncryptionKey.keyData subdataWithRange:NSMakeRange(0, MIN(16u,self.publicEncryptionKey.keyData.length))],
+ [self.publicSigningKey.keyData subdataWithRange:NSMakeRange(0, MIN(16u,self.publicSigningKey.keyData.length))]];
+}
+
+- (instancetype)initWithSOSPeerID:(NSString*)syncingPeerID
+ encryptionKey:(SFECKeyPair*)encryptionKey
+ signingKey:(SFECKeyPair*)signingKey
+{
+ if((self = [super init])) {
+ _spid = syncingPeerID;
+ _encryptionKey = encryptionKey;
+ _signingKey = signingKey;
+ }
+ return self;
+}
+
+-(SFECPublicKey*)publicEncryptionKey {
+ return self.encryptionKey.publicKey;
+}
+-(SFECPublicKey*)publicSigningKey {
+ return self.signingKey.publicKey;
+}
+- (NSString*)peerID {
+ return [NSString stringWithFormat:@"spid-%@", self.spid];
+}
+
+- (bool)matchesPeer:(id<CKKSPeer>)peer {
+ return (self.peerID == nil && peer.peerID == nil) ||
+ [self.peerID isEqualToString:peer.peerID];
+}
+@end
+
+#endif // OCTAGON
return;
}
- [ckks dispatchSync: ^bool{
+ [ckks dispatchSyncWithAccountKeys: ^bool{
if(self.cancelled) {
ckksinfo("ckkskey", ckks, "CKKSProcessReceivedKeysOperation cancelled, quitting");
return false;
}
// This key is our proposed TLK. Check with the CKKS object.
- if(![ckks checkTLK: tlk error: &error]) {
+ if(![ckks _onqueueWithAccountKeysCheckTLK: tlk error: &error]) {
// Was this error "I've never seen that TLK before in my life"? If so, enter the "wait for TLK sync" state.
if(error && [error.domain isEqualToString: @"securityd"] && error.code == errSecItemNotFound) {
ckksnotice("ckkskey", ckks, "Received a TLK which we don't have in the local keychain(%@). Entering waitfortlk.", tlk);
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#include <utilities/SecDb.h>
#include <securityd/SecDbItem.h>
#ifndef CKKSRecordHolder_h
#define CKKSRecordHolder_h
-#if OCTAGON
-
#import "keychain/ckks/CKKSSQLDatabaseObject.h"
#import <CloudKit/CloudKit.h>
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#include <AssertMacros.h>
#import <Foundation/Foundation.h>
#include <securityd/SecDbItem.h>
#include <securityd/SecItemSchema.h>
-#if OCTAGON
-
#import <CloudKit/CloudKit.h>
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef CKKSResultOperation_h
+#define CKKSResultOperation_h
+
+#import <Foundation/Foundation.h>
+#import <dispatch/dispatch.h>
+#import "keychain/ckks/NSOperationCategories.h"
+
+@class CKKSCondition;
+
+#define CKKSResultErrorDomain @"CKKSResultOperationError"
+enum {
+ CKKSResultSubresultError = 1,
+ CKKSResultSubresultCancelled = 2,
+ CKKSResultTimedOut = 3,
+};
+
+@interface CKKSResultOperation : NSBlockOperation
+@property NSError* error;
+@property NSDate* finishDate;
+@property CKKSCondition* completionHandlerDidRunCondition;
+
+// Very similar to addDependency, but:
+// if the dependent operation has an error or is canceled, cancel this operation
+- (void)addSuccessDependency: (CKKSResultOperation*) operation;
+- (void)addNullableSuccessDependency:(CKKSResultOperation*)operation;
+
+// Call to check if you should run.
+// Note: all subclasses must call this if they'd like to comply with addSuccessDependency
+// Also sets your .error property to encapsulate the upstream error
+- (bool)allDependentsSuccessful;
+
+// Allows you to time out CKKSResultOperations: if they haven't started by now, they'll cancel themselves
+// and set their error to indicate the timeout
+- (instancetype)timeout:(dispatch_time_t)timeout;
+
+// Convenience constructor.
++(instancetype)operationWithBlock:(void (^)(void))block;
++(instancetype)named: (NSString*)name withBlock: (void(^)(void)) block;
+
+// Determine if all these operations were successful, and set this operation's result if not.
+- (bool)allSuccessful: (NSArray<CKKSResultOperation*>*) operations;
+
+// Call this to prevent the timeout on this operation from occuring.
+// Upon return, either this operation is cancelled, or the timeout will never fire.
+-(void)invalidateTimeout;
+@end
+
+#endif // CKKSResultOperation_h
+
--- /dev/null
+/*
+ * Copyright (c) 2016 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#import "keychain/ckks/CKKSResultOperation.h"
+#import "keychain/ckks/CKKSCondition.h"
+#include <utilities/debugging.h>
+
+@interface CKKSResultOperation()
+@property NSMutableArray<CKKSResultOperation*>* successDependencies;
+@property bool timeoutCanOccur;
+@property dispatch_queue_t timeoutQueue;
+@property void (^finishingBlock)(void);
+@end
+
+@implementation CKKSResultOperation
+- (instancetype)init {
+ if(self = [super init]) {
+ _error = nil;
+ _successDependencies = [[NSMutableArray alloc] init];
+ _timeoutCanOccur = true;
+ _timeoutQueue = dispatch_queue_create("result-operation-timeout", DISPATCH_QUEUE_SERIAL);
+ _completionHandlerDidRunCondition = [[CKKSCondition alloc] init];
+
+ __weak __typeof(self) weakSelf = self;
+ _finishingBlock = ^(void) {
+ weakSelf.finishDate = [NSDate dateWithTimeIntervalSinceNow:0];
+ };
+ self.completionBlock = ^{}; // our _finishing block gets added in the method override
+ }
+ return self;
+}
+
+- (NSString*)description {
+ NSString* state = ([self isFinished] ? [NSString stringWithFormat:@"finished %@", self.finishDate] :
+ [self isCancelled] ? @"cancelled" :
+ [self isExecuting] ? @"executing" :
+ [self isReady] ? @"ready" :
+ @"pending");
+
+ if(self.error) {
+ return [NSString stringWithFormat: @"<%@: %@ error:%@>", [self selfname], state, self.error];
+ } else {
+ return [NSString stringWithFormat: @"<%@: %@%@>", [self selfname], state, [self pendingDependenciesString:@" dep:"]];
+ }
+}
+
+- (NSString*)debugDescription {
+ return [self description];
+}
+
+- (void)setCompletionBlock:(void (^)(void))completionBlock
+{
+ __weak __typeof(self) weakSelf = self;
+ [super setCompletionBlock:^(void) {
+ __strong __typeof(self) strongSelf = weakSelf;
+ if (!strongSelf) {
+ secerror("ckksresultoperation: completion handler called on deallocated operation instance");
+ completionBlock(); // go ahead and still behave as things would if this method override were not here
+ return;
+ }
+
+ strongSelf.finishingBlock();
+ completionBlock();
+ [strongSelf.completionHandlerDidRunCondition fulfill];
+ }];
+}
+
+- (void)start {
+ if(![self allDependentsSuccessful]) {
+ secdebug("ckksresultoperation", "Not running due to some failed dependent: %@", self.error);
+ [self cancel];
+ } else {
+ [self invalidateTimeout];
+
+ }
+
+ [super start];
+}
+
+- (void)invalidateTimeout {
+ dispatch_sync(self.timeoutQueue, ^{
+ if(![self isCancelled]) {
+ self.timeoutCanOccur = false;
+ };
+ });
+}
+
+- (instancetype)timeout:(dispatch_time_t)timeout {
+ __weak __typeof(self) weakSelf = self;
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeout), self.timeoutQueue, ^{
+ __strong __typeof(self) strongSelf = weakSelf;
+ if(strongSelf.timeoutCanOccur) {
+ strongSelf.error = [NSError errorWithDomain:CKKSResultErrorDomain code: CKKSResultTimedOut userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Operation timed out waiting to start for [%@]", [self pendingDependenciesString:@""]]}];
+ strongSelf.timeoutCanOccur = false;
+ [strongSelf cancel];
+ }
+ });
+
+ return self;
+}
+
+- (void)addSuccessDependency:(CKKSResultOperation *)operation {
+ [self addNullableSuccessDependency:operation];
+}
+
+- (void)addNullableSuccessDependency:(CKKSResultOperation *)operation {
+ if(!operation) {
+ return;
+ }
+ @synchronized(self) {
+ [self.successDependencies addObject: operation];
+ [self addDependency: operation];
+ }
+}
+
+- (bool)allDependentsSuccessful {
+ return [self allSuccessful: self.successDependencies];
+}
+
+- (bool)allSuccessful: (NSArray<CKKSResultOperation*>*) operations {
+ @synchronized(self) {
+ bool result = false;
+
+ bool finished = true; // all dependents must be finished
+ bool cancelled = false; // no dependents can be cancelled
+ bool failed = false; // no dependents can have failed
+
+ for(CKKSResultOperation* op in operations) {
+ finished &= !!([op isFinished]);
+ cancelled |= !!([op isCancelled]);
+ failed |= (op.error != nil);
+
+ // TODO: combine suberrors
+ if(op.error != nil) {
+ if([op.error.domain isEqual: CKKSResultErrorDomain] && op.error.code == CKKSResultSubresultError) {
+ // Already a subresult, just copy it on in
+ self.error = op.error;
+ } else {
+ self.error = [NSError errorWithDomain:CKKSResultErrorDomain code: CKKSResultSubresultError userInfo:@{ NSUnderlyingErrorKey: op.error}];
+ }
+ }
+ }
+
+ result = finished && !( cancelled || failed );
+
+ if(!result && self.error == nil) {
+ self.error = [NSError errorWithDomain:CKKSResultErrorDomain code: CKKSResultSubresultCancelled userInfo:nil];
+ }
+ return result;
+ }
+}
+
++ (CKKSResultOperation*)operationWithBlock:(void (^)(void))block {
+ CKKSResultOperation* op = [[CKKSResultOperation alloc] init];
+ [op addExecutionBlock: block];
+ return op;
+}
+
++(instancetype)named:(NSString*)name withBlock:(void(^)(void)) block {
+ CKKSResultOperation* blockOp = [CKKSResultOperation operationWithBlock: block];
+ blockOp.name = name;
+ return blockOp;
+}
+@end
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#import <Foundation/Foundation.h>
// For AES-SIV 512.
- (NSData*)decryptData: (NSData*) ciphertext authenticatedData: (NSDictionary<NSString*, NSData*>*) ad error: (NSError * __autoreleasing *) error;
@end
+
+#endif // OCTAGON
* @APPLE_LICENSE_HEADER_END@
*/
+#if OCTAGON
+
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
#include <corecrypto/ccaes.h>
#include <corecrypto/ccmode_siv.h>
+#import "keychain/ckks/CloudKitCategories.h"
+
@implementation CKKSBaseAESSIVKey
- (instancetype)init {
if(self = [super init]) {
NSError* localerror = nil;
bool success = false;
+ if(!keyToWrap) {
+ localerror = [NSError errorWithDomain:@"securityd"
+ code:errSecParam
+ description:@"No key given"];
+ if(error) {
+ *error = localerror;
+ }
+ return nil;
+ }
+
CKKSWrappedAESSIVKey* wrappedKey = nil;
uint8_t buffer[CKKSWrappedKeySize] = {};
@end
+
+#endif // OCTAGON
NSString * sql = [[NSString alloc] initWithFormat: @"DELETE FROM %@%@;", table, whereClause];
SecDbPrepare(dbconn, (__bridge CFStringRef) sql, &cferror, ^void (sqlite3_stmt *stmt) {
+ __block int whereObjectsSkipped = 0;
[whereDict.allKeys enumerateObjectsUsingBlock:^(id _Nonnull key, NSUInteger i, BOOL * _Nonnull stop) {
if([whereDict[key] class] != [CKKSSQLWhereObject class]) {
- SecDbBindObject(stmt, (int)(i+1), (__bridge CFStringRef) whereDict[key], &cferror);
+ SecDbBindObject(stmt, (int)(i+1-whereObjectsSkipped), (__bridge CFStringRef) whereDict[key], &cferror);
+ } else {
+ whereObjectsSkipped += 1;
}
}];
NSString * sql = [[NSString alloc] initWithFormat: @"SELECT %@ FROM %@%@%@%@%@;", columns, table, whereClause, groupByClause, orderByClause, limitClause];
SecDbPrepare(dbconn, (__bridge CFStringRef) sql, &cferror, ^void (sqlite3_stmt *stmt) {
+ __block int whereObjectsSkipped = 0;
[whereDict.allKeys enumerateObjectsUsingBlock:^(id _Nonnull key, NSUInteger i, BOOL * _Nonnull stop) {
if([whereDict[key] class] != [CKKSSQLWhereObject class]) {
- SecDbBindObject(stmt, (int)(i+1), (__bridge CFStringRef) whereDict[key], &cferror);
+ SecDbBindObject(stmt, (int)(i+1-whereObjectsSkipped), (__bridge CFStringRef) whereDict[key], &cferror);
+ } else {
+ whereObjectsSkipped += 1;
}
}];
return;
}
- [ckks dispatchSyncWithAccountQueue: ^bool{
+ [ckks dispatchSyncWithAccountKeys: ^bool{
if(self.cancelled) {
ckksnotice("ckksscan", ckks, "CKKSScanLocalItemsOperation cancelled, quitting");
return false;
return;
}
- ckksnotice("ckksscan", ckks, "Syncing new item: %@ %@", oqe, itemToSave);
+ ckksnotice("ckksscan", ckks, "Syncing new item: %@", oqe);
CFReleaseNull(itemToSave);
[oqe saveToDatabase: &error];
if(error) {
- ckkserror("ckksscan", ckks, "Need to upload %@ %@, but can't save to database: %@", item, oqe, error);
+ ckkserror("ckksscan", ckks, "Need to upload %@, but can't save to database: %@", oqe, error);
self.error = error;
return;
}
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import <Foundation/Foundation.h>
+
+#import "keychain/ckks/CKKS.h"
+#import "keychain/ckks/CKKSItem.h"
+#import "keychain/ckks/CKKSKey.h"
+#import "keychain/ckks/CKKSPeer.h"
+
+#import <SecurityFoundation/SFKey.h>
+#import <SecurityFoundation/SFEncryptionOperation.h>
+
+typedef NS_ENUM(NSUInteger, SecCKKSTLKShareVersion) {
+ SecCKKSTLKShareVersion0 = 0, // Signature is over all fields except (signature) and (receiverPublicKey)
+ // Unknown fields in the CKRecord will be appended to the end, in sorted order based on column ID
+};
+
+#define SecCKKSTLKShareCurrentVersion SecCKKSTLKShareVersion0
+
+
+@interface CKKSTLKShare : CKKSCKRecordHolder
+@property SFEllipticCurve curve;
+@property SecCKKSTLKShareVersion version;
+
+@property NSString* tlkUUID;
+
+@property id<CKKSPeer> receiver;
+@property NSString* senderPeerID;
+
+@property NSInteger epoch;
+@property NSInteger poisoned;
+
+@property NSData* wrappedTLK;
+@property NSData* signature;
+
+-(instancetype)init NS_UNAVAILABLE;
+
+- (CKKSKey*)recoverTLK:(id<CKKSSelfPeer>)recoverer
+ trustedPeers:(NSSet<id<CKKSPeer>>*)peers
+ error:(NSError* __autoreleasing *)error;
+
++ (CKKSTLKShare*)share:(CKKSKey*)key
+ as:(id<CKKSSelfPeer>)sender
+ to:(id<CKKSPeer>)receiver
+ epoch:(NSInteger)epoch
+ poisoned:(NSInteger)poisoned
+ error:(NSError* __autoreleasing *)error;
+
+// Database loading
++ (instancetype)fromDatabase:(NSString*)uuid
+ receiverPeerID:(NSString*)receiverPeerID
+ senderPeerID:(NSString*)senderPeerID
+ zoneID:(CKRecordZoneID*)zoneID
+ error:(NSError * __autoreleasing *)error;
++ (instancetype)tryFromDatabase:(NSString*)uuid
+ receiverPeerID:(NSString*)receiverPeerID
+ senderPeerID:(NSString*)senderPeerID
+ zoneID:(CKRecordZoneID*)zoneID
+ error:(NSError * __autoreleasing *)error;
++ (NSArray<CKKSTLKShare*>*)allFor:(NSString*)receiverPeerID
+ keyUUID:(NSString*)uuid
+ zoneID:(CKRecordZoneID*)zoneID
+ error:(NSError * __autoreleasing *)error;
++ (NSArray<CKKSTLKShare*>*)allForUUID:(NSString*)uuid
+ zoneID:(CKRecordZoneID*)zoneID
+ error:(NSError * __autoreleasing *)error;
++ (NSArray<CKKSTLKShare*>*)allInZone:(CKRecordZoneID*)zoneID
+ error:(NSError * __autoreleasing *)error;
++ (instancetype)tryFromDatabaseFromCKRecordID:(CKRecordID*)recordID
+ error:(NSError * __autoreleasing *)error;
+
+// Returns a prefix that all every CKKSTLKShare CKRecord will have
++ (NSString*)ckrecordPrefix;
+
+// For tests
+- (CKKSKey*)unwrapUsing:(id<CKKSSelfPeer>)localPeer error:(NSError * __autoreleasing *)error;
+- (NSData*)signRecord:(SFECKeyPair*)signingKey error:(NSError* __autoreleasing *)error;
+- (bool)verifySignature:(NSData*)signature verifyingPeer:(id<CKKSPeer>)peer error:(NSError* __autoreleasing *)error;
+- (NSData*)dataForSigning;
+@end
+
+#endif // OCTAGON
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import "keychain/ckks/CKKSTLKShare.h"
+#import "keychain/ckks/CKKSPeer.h"
+#import "keychain/ckks/CloudKitCategories.h"
+
+#import <SecurityFoundation/SFKey.h>
+#import <SecurityFoundation/SFEncryptionOperation.h>
+#import <SecurityFoundation/SFSigningOperation.h>
+#import <SecurityFoundation/SFDigestOperation.h>
+
+@interface CKKSTLKShare ()
+@end
+
+@implementation CKKSTLKShare
+-(instancetype)init:(CKKSKey*)key
+ sender:(id<CKKSSelfPeer>)sender
+ receiver:(id<CKKSPeer>)receiver
+ curve:(SFEllipticCurve)curve
+ version:(SecCKKSTLKShareVersion)version
+ epoch:(NSInteger)epoch
+ poisoned:(NSInteger)poisoned
+ zoneID:(CKRecordZoneID*)zoneID
+ encodedCKRecord:(NSData*)encodedCKRecord
+{
+ if((self = [super initWithCKRecordType:SecCKRecordTLKShareType
+ encodedCKRecord:encodedCKRecord
+ zoneID:zoneID])) {
+ _curve = curve;
+ _version = version;
+ _tlkUUID = key.uuid;
+
+ _receiver = receiver;
+ _senderPeerID = sender.peerID;
+
+ _epoch = epoch;
+ _poisoned = poisoned;
+ }
+ return self;
+}
+
+- (instancetype)initForKey:(NSString*)tlkUUID
+ senderPeerID:(NSString*)senderPeerID
+ recieverPeerID:(NSString*)receiverPeerID
+ receiverEncPublicKey:(SFECPublicKey*)publicKey
+ curve:(SFEllipticCurve)curve
+ version:(SecCKKSTLKShareVersion)version
+ epoch:(NSInteger)epoch
+ poisoned:(NSInteger)poisoned
+ wrappedKey:(NSData*)wrappedKey
+ signature:(NSData*)signature
+ zoneID:(CKRecordZoneID*)zoneID
+ encodedCKRecord:(NSData*)encodedCKRecord
+{
+ if((self = [super initWithCKRecordType:SecCKRecordTLKShareType
+ encodedCKRecord:encodedCKRecord
+ zoneID:zoneID])) {
+ _tlkUUID = tlkUUID;
+ _senderPeerID = senderPeerID;
+
+ _receiver = [[CKKSSOSPeer alloc] initWithSOSPeerID:receiverPeerID encryptionPublicKey:publicKey signingPublicKey:nil];
+
+ _curve = curve;
+ _version = version;
+ _epoch = epoch;
+ _poisoned = poisoned;
+
+ _wrappedTLK = wrappedKey;
+ _signature = signature;
+ }
+ return self;
+}
+
+- (NSString*)description {
+ return [NSString stringWithFormat:@"<CKKSTLKShare(%@): recv:%@ send:%@>", self.tlkUUID, self.receiver.peerID, self.senderPeerID];
+}
+
+- (NSData*)wrap:(CKKSKey*)key publicKey:(SFECPublicKey*)receiverPublicKey error:(NSError* __autoreleasing *)error {
+ NSData* plaintext = [key serializeAsProtobuf:error];
+ if(!plaintext) {
+ return nil;
+ }
+
+ SFIESOperation* sfieso = [[SFIESOperation alloc] initWithCurve:self.curve];
+ SFIESCiphertext* ciphertext = [sfieso encrypt:plaintext withKey:receiverPublicKey error:error];
+
+ // Now use NSCoding to turn the ciphertext into something transportable
+ NSMutableData* data = [NSMutableData data];
+ NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
+ [ciphertext encodeWithCoder:archiver];
+ [archiver finishEncoding];
+
+ return data;
+}
+
+- (CKKSKey*)unwrapUsing:(id<CKKSSelfPeer>)localPeer error:(NSError * __autoreleasing *)error {
+ // Unwrap the ciphertext using NSSecureCoding
+ NSKeyedUnarchiver *coder = [[NSKeyedUnarchiver alloc] initForReadingWithData:self.wrappedTLK];
+ coder.requiresSecureCoding = YES;
+ SFIESCiphertext* ciphertext = [[SFIESCiphertext alloc] initWithCoder:coder];
+ [coder finishDecoding];
+
+ SFIESOperation* sfieso = [[SFIESOperation alloc] initWithCurve:self.curve];
+
+ NSError* localerror = nil;
+ NSData* plaintext = [sfieso decrypt:ciphertext withKey:localPeer.encryptionKey error:&localerror];
+ if(!plaintext || localerror) {
+ if(error) {
+ *error = localerror;
+ }
+ return nil;
+ }
+
+ return [CKKSKey loadFromProtobuf:plaintext error:error];
+}
+
+// Serialize this record in some format suitable for signing.
+// This record must serialize exactly the same on the other side for the signature to verify.
+- (NSData*)dataForSigning {
+ // Ideally, we'd put this as DER or some other structured, versioned format.
+ // For now, though, do the straightforward thing and concatenate the fields of interest.
+ NSMutableData* dataToSign = [[NSMutableData alloc] init];
+
+ uint64_t version = OSSwapHostToLittleConstInt64(self.version);
+ [dataToSign appendBytes:&version length:sizeof(version)];
+
+ // We only include the peer IDs in the signature; the receiver doesn't care if we signed the receiverPublicKey field;
+ // if it's wrong or doesn't match, the receiver will simply fail to decrypt the encrypted record.
+ [dataToSign appendData:[self.receiver.peerID dataUsingEncoding:NSUTF8StringEncoding]];
+ [dataToSign appendData:[self.senderPeerID dataUsingEncoding:NSUTF8StringEncoding]];
+
+ [dataToSign appendData:self.wrappedTLK];
+
+ uint64_t curve = OSSwapHostToLittleConstInt64(self.curve);
+ [dataToSign appendBytes:&curve length:sizeof(curve)];
+
+ uint64_t epoch = OSSwapHostToLittleConstInt64(self.epoch);
+ [dataToSign appendBytes:&epoch length:sizeof(epoch)];
+
+ uint64_t poisoned = OSSwapHostToLittleConstInt64(self.poisoned);
+ [dataToSign appendBytes:&poisoned length:sizeof(poisoned)];
+
+ // If we have a CKRecord saved here, add any unknown fields (that don't start with server_) to the signed data
+ // in sorted order by CKRecord key
+ CKRecord* record = self.storedCKRecord;
+ if(record) {
+ NSMutableDictionary<NSString*,id>* extraData = [NSMutableDictionary dictionary];
+
+ for(NSString* key in record.allKeys) {
+ if([key isEqualToString:SecCKRecordSenderPeerID] ||
+ [key isEqualToString:SecCKRecordReceiverPeerID] ||
+ [key isEqualToString:SecCKRecordReceiverPublicEncryptionKey] ||
+ [key isEqualToString:SecCKRecordCurve] ||
+ [key isEqualToString:SecCKRecordEpoch] ||
+ [key isEqualToString:SecCKRecordPoisoned] ||
+ [key isEqualToString:SecCKRecordSignature] ||
+ [key isEqualToString:SecCKRecordVersion] ||
+ [key isEqualToString:SecCKRecordParentKeyRefKey] ||
+ [key isEqualToString:SecCKRecordWrappedKeyKey]) {
+ // This version of CKKS knows about this data field. Ignore them with prejudice.
+ continue;
+ }
+
+ if([key hasPrefix:@"server_"]) {
+ // Ignore all fields prefixed by "server_"
+ continue;
+ }
+
+ extraData[key] = record[key];
+ }
+
+ NSArray* extraKeys = [[extraData allKeys] sortedArrayUsingSelector:@selector(compare:)];
+ for(NSString* extraKey in extraKeys) {
+ id obj = extraData[extraKey];
+
+ // Skip CKReferences, NSArray, CLLocation, and CKAsset.
+ if([obj isKindOfClass: [NSString class]]) {
+ [dataToSign appendData: [obj dataUsingEncoding: NSUTF8StringEncoding]];
+ } else if([obj isKindOfClass: [NSData class]]) {
+ [dataToSign appendData: obj];
+ } else if([obj isKindOfClass:[NSDate class]]) {
+ NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init];
+ NSString* str = [formatter stringForObjectValue: obj];
+ [dataToSign appendData: [str dataUsingEncoding: NSUTF8StringEncoding]];
+ } else if([obj isKindOfClass: [NSNumber class]]) {
+ // Add an NSNumber
+ uint64_t n64 = OSSwapHostToLittleConstInt64([obj unsignedLongLongValue]);
+ [dataToSign appendBytes:&n64 length:sizeof(n64)];
+ }
+ }
+ }
+
+ return dataToSign;
+}
+
+// Returns the signature, but not the signed data itself;
+- (NSData*)signRecord:(SFECKeyPair*)signingKey error:(NSError* __autoreleasing *)error {
+ // TODO: the digest operation can't be changed, as we don't have a good way of communicating it, like self.curve
+ SFEC_X962SigningOperation* xso = [[SFEC_X962SigningOperation alloc] initWithKeySpecifier:[[SFECKeySpecifier alloc] initWithCurve:self.curve]
+ digestOperation:[[SFSHA256DigestOperation alloc] init]];
+
+ NSData* data = [self dataForSigning];
+ SFSignedData* signedData = [xso sign:data withKey:signingKey error:error];
+
+ return signedData.signature;
+}
+
+- (bool)verifySignature:(NSData*)signature verifyingPeer:(id<CKKSPeer>)peer error:(NSError* __autoreleasing *)error {
+ // TODO: the digest operation can't be changed, as we don't have a good way of communicating it, like self.curve
+ SFEC_X962SigningOperation* xso = [[SFEC_X962SigningOperation alloc] initWithKeySpecifier:[[SFECKeySpecifier alloc] initWithCurve:self.curve]
+ digestOperation:[[SFSHA256DigestOperation alloc] init]];
+ SFSignedData* signedData = [[SFSignedData alloc] initWithData:[self dataForSigning] signature:signature];
+
+ bool ret = [xso verify:signedData withKey:peer.publicSigningKey error:error];
+ return ret;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ CKKSTLKShare* share = [[[self class] allocWithZone:zone] init];
+ share.curve = self.curve;
+ share.version = self.version;
+ share.tlkUUID = [self.tlkUUID copy];
+ share.senderPeerID = [self.senderPeerID copy];
+ share.epoch = self.epoch;
+ share.poisoned = self.poisoned;
+ share.wrappedTLK = [self.wrappedTLK copy];
+ share.signature = [self.signature copy];
+
+ share.receiver = self.receiver;
+ return share;
+}
+
+- (BOOL)isEqual:(id)object {
+ if(![object isKindOfClass:[CKKSTLKShare class]]) {
+ return NO;
+ }
+
+ CKKSTLKShare* obj = (CKKSTLKShare*) object;
+
+ // Note that for purposes of CKKSTLK equality, we only care about the receiver's peer ID and publicEncryptionKey
+ // <rdar://problem/34897551> SFKeys should support [isEqual:]
+ return ([self.tlkUUID isEqualToString:obj.tlkUUID] &&
+ [self.zoneID isEqual: obj.zoneID] &&
+ [self.senderPeerID isEqualToString:obj.senderPeerID] &&
+ ((self.receiver.peerID == nil && obj.receiver.peerID == nil) || [self.receiver.peerID isEqual: obj.receiver.peerID]) &&
+ ((self.receiver.publicEncryptionKey == nil && obj.receiver.publicEncryptionKey == nil)
+ || [self.receiver.publicEncryptionKey.keyData isEqual: obj.receiver.publicEncryptionKey.keyData]) &&
+ self.epoch == obj.epoch &&
+ self.curve == obj.curve &&
+ self.poisoned == obj.poisoned &&
+ ((self.wrappedTLK == nil && obj.wrappedTLK == nil) || [self.wrappedTLK isEqual: obj.wrappedTLK]) &&
+ ((self.signature == nil && obj.signature == nil) || [self.signature isEqual: obj.signature]) &&
+ true) ? YES : NO;
+}
+
++ (CKKSTLKShare*)share:(CKKSKey*)key
+ as:(id<CKKSSelfPeer>)sender
+ to:(id<CKKSPeer>)receiver
+ epoch:(NSInteger)epoch
+ poisoned:(NSInteger)poisoned
+ error:(NSError* __autoreleasing *)error
+{
+ NSError* localerror = nil;
+
+ // Load any existing TLK Share, so we can update it
+ CKKSTLKShare* oldShare = [CKKSTLKShare tryFromDatabase:key.uuid
+ receiverPeerID:receiver.peerID
+ senderPeerID:sender.peerID
+ zoneID:key.zoneID
+ error:&localerror];
+ if(localerror) {
+ secerror("ckksshare: couldn't load old share for %@: %@", key, localerror);
+ if(error) {
+ *error = localerror;
+ }
+ return nil;
+ }
+
+ CKKSTLKShare* share = [[CKKSTLKShare alloc] init:key
+ sender:sender
+ receiver:receiver
+ curve:SFEllipticCurveNistp384
+ version:SecCKKSTLKShareCurrentVersion
+ epoch:epoch
+ poisoned:poisoned
+ zoneID:key.zoneID
+ encodedCKRecord:oldShare.encodedCKRecord];
+
+ share.wrappedTLK = [share wrap:key publicKey:receiver.publicEncryptionKey error:&localerror];
+ if(localerror) {
+ secerror("ckksshare: couldn't share %@ (wrap failed): %@", key, localerror);
+ if(error) {
+ *error = localerror;
+ }
+ return nil;
+ }
+
+ share.signature = [share signRecord:sender.signingKey error:&localerror];
+ if(localerror) {
+ secerror("ckksshare: couldn't share %@ (signing failed): %@", key, localerror);
+ if(error) {
+ *error = localerror;
+ }
+ return nil;
+ }
+
+ return share;
+}
+
+- (CKKSKey*)recoverTLK:(id<CKKSSelfPeer>)recoverer
+ trustedPeers:(NSSet<id<CKKSPeer>>*)peers
+ error:(NSError* __autoreleasing *)error
+{
+ NSError* localerror = nil;
+
+ id<CKKSPeer> peer = nil;
+ for(id<CKKSPeer> p in peers) {
+ if([p.peerID isEqualToString: self.senderPeerID]) {
+ peer = p;
+ }
+ }
+
+ if(!peer) {
+ localerror = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSNoTrustedPeer
+ description:[NSString stringWithFormat:@"No trusted peer signed %@", self]];
+ if(error) {
+ *error = localerror;
+ }
+ return nil;
+ }
+
+ bool isSigned = [self verifySignature:self.signature verifyingPeer:peer error:error];
+ if(!isSigned) {
+ return nil;
+ }
+
+ CKKSKey* tlkTrial = [self unwrapUsing:recoverer error:error];
+ if(!tlkTrial) {
+ return nil;
+ }
+
+ if(![self.tlkUUID isEqualToString:tlkTrial.uuid]) {
+ localerror = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSDataMismatch
+ description:[NSString stringWithFormat:@"Signed UUID doesn't match unsigned UUID for %@", self]];
+ if(error) {
+ *error = localerror;
+ }
+ return nil;
+ }
+
+ return tlkTrial;
+}
+
+#pragma mark - Database Operations
+
++ (instancetype)fromDatabase:(NSString*)uuid
+ receiverPeerID:(NSString*)receiverPeerID
+ senderPeerID:(NSString*)senderPeerID
+ zoneID:(CKRecordZoneID*)zoneID
+ error:(NSError * __autoreleasing *)error {
+ return [self fromDatabaseWhere: @{@"uuid":CKKSNilToNSNull(uuid),
+ @"recvpeerid":CKKSNilToNSNull(receiverPeerID),
+ @"senderpeerid":CKKSNilToNSNull(senderPeerID),
+ @"ckzone": CKKSNilToNSNull(zoneID.zoneName)} error:error];
+}
+
++ (instancetype)tryFromDatabase:(NSString*)uuid
+ receiverPeerID:(NSString*)receiverPeerID
+ senderPeerID:(NSString*)senderPeerID
+ zoneID:(CKRecordZoneID*)zoneID
+ error:(NSError * __autoreleasing *)error {
+ return [self tryFromDatabaseWhere: @{@"uuid":CKKSNilToNSNull(uuid),
+ @"recvpeerid":CKKSNilToNSNull(receiverPeerID),
+ @"senderpeerid":CKKSNilToNSNull(senderPeerID),
+ @"ckzone": CKKSNilToNSNull(zoneID.zoneName)} error:error];
+}
+
++ (NSArray<CKKSTLKShare*>*)allFor:(NSString*)receiverPeerID
+ keyUUID:(NSString*)uuid
+ zoneID:(CKRecordZoneID*)zoneID
+ error:(NSError * __autoreleasing *)error {
+ return [self allWhere:@{@"recvpeerid":CKKSNilToNSNull(receiverPeerID),
+ @"uuid":uuid,
+ @"ckzone": CKKSNilToNSNull(zoneID.zoneName)} error:error];
+}
+
++ (NSArray<CKKSTLKShare*>*)allForUUID:(NSString*)uuid
+ zoneID:(CKRecordZoneID*)zoneID
+ error:(NSError * __autoreleasing *)error {
+ return [self allWhere:@{@"uuid":CKKSNilToNSNull(uuid),
+ @"ckzone":CKKSNilToNSNull(zoneID.zoneName)} error:error];
+}
+
++ (NSArray<CKKSTLKShare*>*)allInZone:(CKRecordZoneID*)zoneID
+ error:(NSError * __autoreleasing *)error {
+ return [self allWhere:@{@"ckzone": CKKSNilToNSNull(zoneID.zoneName)} error:error];
+}
+
++ (instancetype)tryFromDatabaseFromCKRecordID:(CKRecordID*)recordID
+ error:(NSError * __autoreleasing *)error {
+ // Welp. Try to parse!
+ NSError *localerror = NULL;
+ NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^tlkshare-(?<uuid>[0-9A-Fa-f-]*)::(?<receiver>.*)::(?<sender>.*)$"
+ options:NSRegularExpressionCaseInsensitive
+ error:&localerror];
+ if(localerror) {
+ if(error) {
+ *error = localerror;
+ }
+ return nil;
+ }
+
+ NSTextCheckingResult* regexmatch = [regex firstMatchInString:recordID.recordName options:0 range:NSMakeRange(0, recordID.recordName.length)];
+ if(!regexmatch) {
+ if(error) {
+ *error = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSNoSuchRecord
+ description:[NSString stringWithFormat:@"Couldn't parse '%@' as a TLKShare ID", recordID.recordName]];
+ }
+ return nil;
+ }
+
+ NSString* uuid = [recordID.recordName substringWithRange:[regexmatch rangeWithName:@"uuid"]];
+ NSString* receiver = [recordID.recordName substringWithRange:[regexmatch rangeWithName:@"receiver"]];
+ NSString* sender = [recordID.recordName substringWithRange:[regexmatch rangeWithName:@"sender"]];
+
+ return [self tryFromDatabaseWhere: @{@"uuid":CKKSNilToNSNull(uuid),
+ @"recvpeerid":CKKSNilToNSNull(receiver),
+ @"senderpeerid":CKKSNilToNSNull(sender),
+ @"ckzone": CKKSNilToNSNull(recordID.zoneID.zoneName)} error:error];
+}
+
+#pragma mark - CKKSCKRecordHolder methods
+
++ (NSString*)ckrecordPrefix {
+ return @"tlkshare";
+}
+
+- (NSString*)CKRecordName {
+ return [NSString stringWithFormat:@"tlkshare-%@::%@::%@", self.tlkUUID, self.receiver.peerID, self.senderPeerID];
+}
+
+- (CKRecord*)updateCKRecord:(CKRecord*)record zoneID:(CKRecordZoneID*)zoneID {
+ if(![record.recordID.recordName isEqualToString: [self CKRecordName]]) {
+ @throw [NSException
+ exceptionWithName:@"WrongCKRecordNameException"
+ reason:[NSString stringWithFormat: @"CKRecord name (%@) was not %@", record.recordID.recordName, [self CKRecordName]]
+ userInfo:nil];
+ }
+ if(![record.recordType isEqualToString: SecCKRecordTLKShareType]) {
+ @throw [NSException
+ exceptionWithName:@"WrongCKRecordTypeException"
+ reason:[NSString stringWithFormat: @"CKRecordType (%@) was not %@", record.recordType, SecCKRecordTLKShareType]
+ userInfo:nil];
+ }
+
+ record[SecCKRecordSenderPeerID] = self.senderPeerID;
+ record[SecCKRecordReceiverPeerID] = self.receiver.peerID;
+ record[SecCKRecordReceiverPublicEncryptionKey] = [self.receiver.publicEncryptionKey.keyData base64EncodedStringWithOptions:0];
+ record[SecCKRecordCurve] = [NSNumber numberWithUnsignedInteger:(NSUInteger)self.curve];
+ record[SecCKRecordVersion] = [NSNumber numberWithUnsignedInteger:(NSUInteger)self.version];
+ record[SecCKRecordEpoch] = [NSNumber numberWithLong:(long)self.epoch];
+ record[SecCKRecordPoisoned] = [NSNumber numberWithLong:(long)self.poisoned];
+
+ record[SecCKRecordParentKeyRefKey] = [[CKReference alloc] initWithRecordID: [[CKRecordID alloc] initWithRecordName: self.tlkUUID zoneID: zoneID]
+ action: CKReferenceActionValidate];
+
+ record[SecCKRecordWrappedKeyKey] = [self.wrappedTLK base64EncodedStringWithOptions:0];
+ record[SecCKRecordSignature] = [self.signature base64EncodedStringWithOptions:0];
+
+ return record;
+}
+
+- (bool)matchesCKRecord:(CKRecord*)record {
+ if(![record.recordType isEqualToString: SecCKRecordTLKShareType]) {
+ return false;
+ }
+
+ if(![record.recordID.recordName isEqualToString: [self CKRecordName]]) {
+ return false;
+ }
+
+ CKKSTLKShare* share = [[CKKSTLKShare alloc] initWithCKRecord:record];
+ return [self isEqual: share];
+}
+
+- (void)setFromCKRecord: (CKRecord*) record {
+ if(![record.recordType isEqualToString: SecCKRecordTLKShareType]) {
+ @throw [NSException
+ exceptionWithName:@"WrongCKRecordTypeException"
+ reason:[NSString stringWithFormat: @"CKRecordType (%@) was not %@", record.recordType, SecCKRecordDeviceStateType]
+ userInfo:nil];
+ }
+
+ [self setStoredCKRecord:record];
+
+ self.senderPeerID = record[SecCKRecordSenderPeerID];
+ self.curve = [record[SecCKRecordCurve] longValue]; // TODO: sanitize
+ self.version = [record[SecCKRecordVersion] longValue];
+
+ NSData* pubkeydata = CKKSUnbase64NullableString(record[SecCKRecordReceiverPublicEncryptionKey]);
+ NSError* error = nil;
+ SFECPublicKey* receiverPublicKey = pubkeydata ? [[SFECPublicKey alloc] initWithData:pubkeydata
+ specifier:[[SFECKeySpecifier alloc] initWithCurve:self.curve]
+ error:&error] : nil;
+
+ if(error) {
+ ckkserror("ckksshare", record.recordID.zoneID, "Couldn't make public key from data: %@", error);
+ receiverPublicKey = nil;
+ }
+
+ self.receiver = [[CKKSSOSPeer alloc] initWithSOSPeerID:record[SecCKRecordReceiverPeerID] encryptionPublicKey:receiverPublicKey signingPublicKey:nil];
+
+ self.epoch = [record[SecCKRecordEpoch] longValue];
+ self.poisoned = [record[SecCKRecordPoisoned] longValue];
+
+ self.tlkUUID = ((CKReference*)record[SecCKRecordParentKeyRefKey]).recordID.recordName;
+
+ self.wrappedTLK = CKKSUnbase64NullableString(record[SecCKRecordWrappedKeyKey]);
+ self.signature = CKKSUnbase64NullableString(record[SecCKRecordSignature]);
+}
+
+#pragma mark - CKKSSQLDatabaseObject methods
+
++ (NSString*)sqlTable {
+ return @"tlkshare";
+}
+
++ (NSArray<NSString*>*)sqlColumns {
+ return @[@"ckzone", @"uuid", @"senderpeerid", @"recvpeerid", @"recvpubenckey", @"poisoned", @"epoch", @"curve", @"version", @"wrappedkey", @"signature", @"ckrecord"];
+}
+
+- (NSDictionary<NSString*,NSString*>*)whereClauseToFindSelf {
+ return @{@"uuid":self.tlkUUID,
+ @"senderpeerid":self.senderPeerID,
+ @"recvpeerid":self.receiver.peerID,
+ @"ckzone":self.zoneID.zoneName,
+ };
+}
+
+- (NSDictionary<NSString*,NSString*>*)sqlValues {
+ return @{@"uuid": self.tlkUUID,
+ @"senderpeerid": self.senderPeerID,
+ @"recvpeerid": self.receiver.peerID,
+ @"recvpubenckey": CKKSNilToNSNull([self.receiver.publicEncryptionKey.keyData base64EncodedStringWithOptions:0]),
+ @"poisoned": [NSString stringWithFormat:@"%ld", (long)self.poisoned],
+ @"epoch": [NSString stringWithFormat:@"%ld", (long)self.epoch],
+ @"curve": [NSString stringWithFormat:@"%ld", (long)self.curve],
+ @"version": [NSString stringWithFormat:@"%ld", (long)self.version],
+ @"wrappedkey": CKKSNilToNSNull([self.wrappedTLK base64EncodedStringWithOptions:0]),
+ @"signature": CKKSNilToNSNull([self.signature base64EncodedStringWithOptions:0]),
+ @"ckzone": CKKSNilToNSNull(self.zoneID.zoneName),
+ @"ckrecord": CKKSNilToNSNull([self.encodedCKRecord base64EncodedStringWithOptions:0]),
+ };
+}
+
++ (instancetype)fromDatabaseRow:(NSDictionary<NSString*,NSString*>*)row {
+ CKRecordZoneID* zoneID = [[CKRecordZoneID alloc] initWithZoneName: row[@"ckzone"] ownerName:CKCurrentUserDefaultName];
+
+ SFEllipticCurve curve = (SFEllipticCurve)[row[@"curve"] integerValue]; // TODO: sanitize
+ SecCKKSTLKShareVersion version = (SecCKKSTLKShareVersion)[row[@"version"] integerValue]; // TODO: sanitize
+
+ NSData* keydata = CKKSUnbase64NullableString(row[@"recvpubenckey"]);
+ NSError* error = nil;
+ SFECPublicKey* receiverPublicKey = keydata ? [[SFECPublicKey alloc] initWithData:keydata
+ specifier:[[SFECKeySpecifier alloc] initWithCurve:curve]
+ error:&error] : nil;
+
+ if(error) {
+ ckkserror("ckksshare", zoneID, "Couldn't make public key from data: %@", error);
+ receiverPublicKey = nil;
+ }
+
+ return [[CKKSTLKShare alloc] initForKey:row[@"uuid"]
+ senderPeerID:row[@"senderpeerid"]
+ recieverPeerID:row[@"recvpeerid"]
+ receiverEncPublicKey:receiverPublicKey
+ curve:curve
+ version:version
+ epoch:[row[@"epoch"] integerValue]
+ poisoned:[row[@"poisoned"] integerValue]
+ wrappedKey:CKKSUnbase64NullableString(row[@"wrappedkey"])
+ signature:CKKSUnbase64NullableString(row[@"signature"])
+ zoneID:zoneID
+ encodedCKRecord:CKKSUnbase64NullableString(row[@"ckrecord"])
+ ];
+}
+
+@end
+
+#endif // OCTAGON
- (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*) ckks
currentPointer:(NSString*)identifier
oldItemUUID:(NSString*)oldItemUUID
+ oldItemHash:(NSData*)oldItemHash
newItemUUID:(NSString*)newItemUUID
ckoperationGroup:(CKOperationGroup*)ckoperationGroup;
@end
#import "keychain/ckks/CKKSCurrentItemPointer.h"
#import "keychain/ckks/CKKSUpdateCurrentItemPointerOperation.h"
#import "keychain/ckks/CKKSManifest.h"
+#import "keychain/ckks/CloudKitCategories.h"
#import <CloudKit/CloudKit.h>
@property NSString* currentPointerIdentifier;
@property NSString* oldCurrentItemUUID;
+@property NSData* oldCurrentItemHash;
@property NSString* currentItemUUID;
@end
- (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*) ckks
currentPointer:(NSString*)identifier
oldItemUUID:(NSString*)oldItemUUID
+ oldItemHash:(NSData*)oldItemHash
newItemUUID:(NSString*)newItemUUID
ckoperationGroup:(CKOperationGroup*)ckoperationGroup
{
_currentPointerIdentifier = identifier;
_oldCurrentItemUUID = oldItemUUID;
+ _oldCurrentItemHash = oldItemHash;
_currentItemUUID = newItemUUID;
_ckoperationGroup = ckoperationGroup;
}
CKKSKeychainView* ckks = self.ckks;
if(!ckks) {
ckkserror("ckkscurrent", ckks, "no CKKS object");
- self.error = [NSError errorWithDomain:@"securityd" code:errSecInternalError userInfo:@{NSLocalizedDescriptionKey: @"no CKKS object"}];
+ self.error = [NSError errorWithDomain:CKKSErrorDomain
+ code:errSecInternalError
+ description:@"no CKKS object"];
return;
}
__weak __typeof(self) weakSelf = self;
- [ckks dispatchSyncWithAccountQueue:^bool {
+ [ckks dispatchSyncWithAccountKeys:^bool {
if(self.cancelled) {
ckksnotice("ckksscan", ckks, "CKKSUpdateCurrentItemPointerOperation cancelled, quitting");
return false;
// Ensure that there's no pending pointer update
CKKSCurrentItemPointer* cipPending = [CKKSCurrentItemPointer tryFromDatabase:self.currentPointerIdentifier state:SecCKKSProcessedStateRemote zoneID:ckks.zoneID error:&error];
if(cipPending) {
- self.error = [NSError errorWithDomain:@"securityd" code:errSecItemInvalidValue
- userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Update to current item pointer is pending."]}];
+ self.error = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSRemoteItemChangePending
+ description:[NSString stringWithFormat:@"Update to current item pointer is pending."]];
ckkserror("ckkscurrent", ckks, "Attempt to set a new current item pointer when one exists: %@", self.error);
return false;
}
CKKSCurrentItemPointer* cip = [CKKSCurrentItemPointer tryFromDatabase:self.currentPointerIdentifier state:SecCKKSProcessedStateLocal zoneID:ckks.zoneID error:&error];
if(cip) {
- // Ensure that the itempointer matches the old item
- if(![cip.currentItemUUID isEqualToString: self.oldCurrentItemUUID]) {
- ckksnotice("ckkscurrent", ckks, "Caller's idea of the current item pointer for %@ doesn't match; rejecting change of current", cip);
- self.error = [NSError errorWithDomain:@"securityd" code:errSecItemInvalidValue
- userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Current pointer does not match given value of '%@', aborting", self.oldCurrentItemUUID]}];
+ // Ensure that the itempointer matches the old item (and the old item exists)
+ //
+ // We might be in the dangling-pointer case, where the 'fetch' API has returned the client a nil value because we
+ // have a CIP, but it points to a deleted keychain item.
+ // In that case, we shouldn't error out.
+ //
+ if(self.oldCurrentItemHash && ![cip.currentItemUUID isEqualToString: self.oldCurrentItemUUID]) {
+
+ ckksnotice("ckkscurrent", ckks, "Caller's idea of the current item pointer %@ doesn't match (%@); rejecting change of current", cip, self.oldCurrentItemUUID);
+ self.error = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSItemChanged
+ description:[NSString stringWithFormat:@"Current pointer(%@) does not match user-supplied %@, aborting", cip, self.oldCurrentItemUUID]];
return false;
}
// Cool. Since you know what you're updating, you're allowed to update!
} else if(self.oldCurrentItemUUID) {
// Error case: the client thinks there's a current pointer, but we don't have one
ckksnotice("ckkscurrent", ckks, "Requested to update a current item pointer but one doesn't exist at %@; rejecting change of current", self.currentPointerIdentifier);
- self.error = [NSError errorWithDomain:@"securityd" code:errSecItemInvalidValue
- userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Current pointer does not match given value of '%@', aborting", self.oldCurrentItemUUID]}];
+ self.error = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSItemChanged
+ description:[NSString stringWithFormat:@"Current pointer(%@) does not match given value of '%@', aborting", cip, self.oldCurrentItemUUID]];
return false;
} else {
// No current item pointer? How exciting! Let's make you a nice new one.
NSArray* oqes = [CKKSOutgoingQueueEntry allUUIDs:&error];
NSArray* iqes = [CKKSIncomingQueueEntry allUUIDs:&error];
if([oqes containsObject:self.currentItemUUID] || [iqes containsObject:self.currentItemUUID]) {
- error = [NSError errorWithDomain:@"securityd" code:errSecItemInvalidValue
- userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"New item(%@) is being synced; can't set current pointer.", self.currentItemUUID]}];
+ error = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSLocalItemChangePending
+ description:[NSString stringWithFormat:@"New item(%@) is being synced; can't set current pointer.", self.currentItemUUID]];
}
if([oqes containsObject: self.oldCurrentItemUUID] || [iqes containsObject:self.oldCurrentItemUUID]) {
- error = [NSError errorWithDomain:@"securityd" code:errSecItemInvalidValue
- userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Old item(%@) is being synced; can't set current pointer.", self.oldCurrentItemUUID]}];
+ error = [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSLocalItemChangePending
+ description:[NSString stringWithFormat:@"Old item(%@) is being synced; can't set current pointer.", self.oldCurrentItemUUID]];
}
if(error) {
CKKSMirrorEntry* ckme = [CKKSMirrorEntry fromDatabase:cip.currentItemUUID zoneID:ckks.zoneID error:&error];
if(!ckme || error) {
ckkserror("ckkscurrent", ckks, "Error attempting to set a current item pointer to an item that isn't synced: %@ %@", cip, ckme);
- // Why can't you put nulls in dictionary literals?
- if(error) {
- error = [NSError errorWithDomain:@"securityd"
- code:errSecItemNotFound
- userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"No synced item matching (%@); can't set current pointer.", cip.currentItemUUID],
- NSUnderlyingErrorKey:error,
- }];
- } else {
- error = [NSError errorWithDomain:@"securityd"
- code:errSecItemNotFound
- userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"No synced item matching (%@); can't set current pointer.", cip.currentItemUUID],
- }];
- }
+ error = [NSError errorWithDomain:CKKSErrorDomain
+ code:errSecItemNotFound
+ description:[NSString stringWithFormat:@"No synced item matching (%@); can't set current pointer.", cip.currentItemUUID]
+ underlying:error];
+
self.error = error;
return false;
}
__strong __typeof(strongSelf.ckks) strongCKKS = strongSelf.ckks;
if(!strongSelf || !strongCKKS) {
ckkserror("ckkscurrent", strongCKKS, "received callback for released object");
- strongSelf.error = [NSError errorWithDomain:@"securityd" code:errSecInternalError userInfo:@{NSLocalizedDescriptionKey: @"no CKKS object"}];
+ strongSelf.error = [NSError errorWithDomain:CKKSErrorDomain
+ code:errSecInternalError
+ description:@"no CKKS object"];
[strongCKKS scheduleOperation: modifyComplete];
return;
}
#if OCTAGON
+#include <utilities/SecInternalReleasePriv.h>
#import "keychain/ckks/CKKSKeychainView.h"
#import "keychain/ckks/CKKSUpdateDeviceStateOperation.h"
#import "keychain/ckks/CKKSCurrentKeyPointer.h"
return;
}
- [ckks dispatchSyncWithAccountQueue:^bool {
+ [ckks dispatchSyncWithAccountKeys:^bool {
NSError* error = nil;
CKKSDeviceStateEntry* cdse = [ckks _onqueueCurrentDeviceStateEntry:&error];
if(self.rateLimit) {
NSDate* lastUpdate = cdse.storedCKRecord.modificationDate;
- // Only upload this every 3 days
+ // Only upload this every 3 days (1 day for internal installs)
NSDate* now = [NSDate date];
NSDateComponents* offset = [[NSDateComponents alloc] init];
- [offset setHour:-3 * 24];
+ if(SecIsInternalRelease()) {
+ [offset setHour:-23];
+ } else {
+ [offset setHour:-3*24];
+ }
NSDate* deadline = [[NSCalendar currentCalendar] dateByAddingComponents:offset toDate:now options:0];
if(lastUpdate == nil || [lastUpdate compare: deadline] == NSOrderedAscending) {
#import "keychain/ckks/CKKSRateLimiter.h"
#import "keychain/ckks/CKKSNotifier.h"
#import "keychain/ckks/CKKSCondition.h"
+#import "keychain/ckks/CKKSPeer.h"
#endif
@class CKKSKeychainView, CKKSRateLimiter;
#if !OCTAGON
@interface CKKSViewManager : NSObject
#else
-@interface CKKSViewManager : NSObject <CKKSControlProtocol>
+@interface CKKSViewManager : NSObject <CKKSControlProtocol, CKKSPeerProvider>
@property CKContainer* container;
@property CKKSCKAccountStateTracker* accountTracker;
@property NSOperation* zoneStartupDependency;
- (instancetype)initCloudKitWithContainerName: (NSString*) containerName usePCS:(bool)usePCS;
-- (instancetype)initWithContainerName: (NSString*) containerNamee
+- (instancetype)initWithContainerName: (NSString*) containerName
usePCS: (bool)usePCS
fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
+ fetchRecordsOperationClass: (Class<CKKSFetchRecordsOperation>)fetchRecordsOperationClass
+ queryOperationClass:(Class<CKKSQueryOperation>)queryOperationClass
modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
- (CKKSKeychainView*)restartZone:(NSString*)viewName;
// Returns the viewList for a CKKSViewManager
-+(NSSet*)viewList;
+-(NSSet*)viewList;
// Notify sbd to re-backup.
-(void)notifyNewTLKsInKeychain;
-+(void)syncBackupAndNotifyAboutSync;
+-(void)syncBackupAndNotifyAboutSync;
+
+// Fetch peers from SOS
+- (CKKSSelves*)fetchSelfPeers:(NSError* __autoreleasing *)error;
+- (NSSet<id<CKKSPeer>>*)fetchTrustedPeers:(NSError* __autoreleasing *)error;
+
+- (void)sendSelfPeerChangedUpdate;
+- (void)sendTrustedPeerSetChangedUpdate;
#endif // OCTAGON
@end
#if OCTAGON
#import <CloudKit/CloudKit.h>
#import <CloudKit/CloudKit_Private.h>
+
+#import <SecurityFoundation/SFKey.h>
+#import <SecurityFoundation/SFKey_Private.h>
#endif
@interface CKKSViewManager () <NSXPCListenerDelegate>
// Once you set these, all CKKSKeychainViews created will use them
@property (readonly) Class<CKKSFetchRecordZoneChangesOperation> fetchRecordZoneChangesOperationClass;
+@property (readonly) Class<CKKSFetchRecordsOperation> fetchRecordsOperationClass;
+@property (readonly) Class<CKKSQueryOperation> queryOperationClass;
@property (readonly) Class<CKKSModifySubscriptionsOperation> modifySubscriptionsOperationClass;
@property (readonly) Class<CKKSModifyRecordZonesOperation> modifyRecordZonesOperationClass;
@property (readonly) Class<CKKSAPSConnection> apsConnectionClass;
@property NSMutableDictionary<NSString*, SecBoolNSErrorCallback>* pendingSyncCallbacks;
@property CKKSNearFutureScheduler* savedTLKNotifier;;
@property NSOperationQueue* operationQueue;
+
+@property NSMapTable<dispatch_queue_t, id<CKKSPeerUpdateListener>>* peerChangeListeners;
#endif
@end
return [self initWithContainerName:containerName
usePCS:usePCS
fetchRecordZoneChangesOperationClass:[CKFetchRecordZoneChangesOperation class]
+ fetchRecordsOperationClass:[CKFetchRecordsOperation class]
+ queryOperationClass:[CKQueryOperation class]
modifySubscriptionsOperationClass:[CKModifySubscriptionsOperation class]
modifyRecordZonesOperationClass:[CKModifyRecordZonesOperation class]
apsConnectionClass:[APSConnection class]
- (instancetype)initWithContainerName: (NSString*) containerName
usePCS:(bool)usePCS
fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
+ fetchRecordsOperationClass: (Class<CKKSFetchRecordsOperation>)fetchRecordsOperationClass
+ queryOperationClass:(Class<CKKSQueryOperation>)queryOperationClass
modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
setupHold: (NSOperation*) setupHold {
if(self = [super init]) {
_fetchRecordZoneChangesOperationClass = fetchRecordZoneChangesOperationClass;
+ _fetchRecordsOperationClass = fetchRecordsOperationClass;
+ _queryOperationClass = queryOperationClass;
_modifySubscriptionsOperationClass = modifySubscriptionsOperationClass;
_modifyRecordZonesOperationClass = modifyRecordZonesOperationClass;
_apsConnectionClass = apsConnectionClass;
_operationQueue = [[NSOperationQueue alloc] init];
+ // Backwards from how we'd like, but it's the best way to have weak pointers to CKKSPeerUpdateListener.
+ _peerChangeListeners = [NSMapTable strongToWeakObjectsMapTable];
+
_views = [[NSMutableDictionary alloc] init];
_pendingSyncCallbacks = [[NSMutableDictionary alloc] init];
_listener = [NSXPCListener anonymousListener];
_listener.delegate = self;
[_listener resume];
+
+ // If this is a live server, register with notify
+ if(!SecCKKSTestsEnabled()) {
+ int token = 0;
+ notify_register_dispatch(kSOSCCCircleOctagonKeysChangedNotification, &token, dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^(int t) {
+ // Since SOS doesn't change the self peer, we can reliably just send "trusted peers changed"; it'll be mostly right
+ secnotice("ckksshare", "Received a notification that the SOS Octagon peer set changed");
+ [weakSelf sendTrustedPeerSetChangedUpdate];
+ });
+ }
}
return self;
}
}
// Mostly exists to be mocked out.
-+(NSSet*)viewList {
+-(NSSet*)viewList {
return CFBridgingRelease(SOSViewCopyViewSet(kViewSetCKKS));
}
accountTracker: self.accountTracker
lockStateTracker: self.lockStateTracker
savedTLKNotifier: self.savedTLKNotifier
+ peerProvider:self
fetchRecordZoneChangesOperationClass: self.fetchRecordZoneChangesOperationClass
+ fetchRecordsOperationClass: self.fetchRecordsOperationClass
+ queryOperationClass:self.queryOperationClass
modifySubscriptionsOperationClass: self.modifySubscriptionsOperationClass
modifyRecordZonesOperationClass: self.modifyRecordZonesOperationClass
apsConnectionClass: self.apsConnectionClass
@synchronized(self.views) {
self.initializeNewZones = true;
- NSSet* viewSet = [CKKSViewManager viewList];
+ NSSet* viewSet = [self viewList];
for(NSString* s in viewSet) {
[self findOrCreateView:s]; // initializes any newly-created views
}
CKKSKeychainView* view = [self findView:viewHint];
if(!view) {
- secinfo("ckks", "No CKKS view for %@, skipping current request", viewHint);
- complete([NSError errorWithDomain:@"securityd"
- code:kSOSCCNoSuchView
- userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"No syncing view for view hint '%@'", viewHint]}]);
+ secnotice("ckks", "No CKKS view for %@, skipping current request", viewHint);
+ complete([NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSNoSuchView
+ description:[NSString stringWithFormat: @"No syncing view for view hint '%@'", viewHint]]);
return;
}
{
CKKSKeychainView* view = [self findView:viewHint];
if(!view) {
- secinfo("ckks", "No CKKS view for %@, skipping current fetch request", viewHint);
- complete(NULL, [NSError errorWithDomain:@"securityd"
- code:kSOSCCNoSuchView
- userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"No view for '%@'", viewHint]}]);
+ secnotice("ckks", "No CKKS view for %@, skipping current fetch request", viewHint);
+ complete(NULL, [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSNoSuchView
+ description:[NSString stringWithFormat: @"No view for '%@'", viewHint]]);
return;
}
-(void)notifyNewTLKsInKeychain {
// Why two functions here? Limitation of OCMock, unfortunately: can't stub and expect the same method
secnotice("ckksbackup", "New TLKs have arrived");
- [CKKSViewManager syncBackupAndNotifyAboutSync];
+ [self syncBackupAndNotifyAboutSync];
}
-+(void)syncBackupAndNotifyAboutSync {
+- (void)syncBackupAndNotifyAboutSync {
SOSAccount* account = (__bridge SOSAccount*)SOSKeychainAccountGetSharedAccount();
[account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
#pragma mark - RPCs to manage and report state
- (void)performanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *counter))reply {
- reply(@{ @"fake" : @(10) });
+ reply(@{});
}
- (void)rpcResetLocal:(NSString*)viewName reply: (void(^)(NSError* result)) reply {
} else {
secnotice("ckksreset", "Completed rpcResetLocal with error: %@", op.error);
}
- reply(op.error);
+ reply(CKXPCSuitableError(op.error));
}
- (void)rpcResetCloudKit:(NSString*)viewName reply: (void(^)(NSError* result)) reply {
secnotice("ckksreset", "Completed rpcResetCloudKit");
} else {
secnotice("ckksreset", "Completed rpcResetCloudKit with error: %@", op.error);
- }
- reply(op.error);
+ }
+ reply(CKXPCSuitableError(op.error));
}
- (void)rpcResync:(NSString*)viewName reply: (void(^)(NSError* result)) reply {
[op timeout:120*NSEC_PER_SEC];
[self.operationQueue addOperation:op];
[op waitUntilFinished];
- reply(op.error);
+ reply(CKXPCSuitableError(op.error));
}
- (void)rpcStatus: (NSString*)viewName reply: (void(^)(NSArray<NSDictionary*>* result, NSError* error)) reply {
NSMutableArray* a = [[NSMutableArray alloc] init];
+ // The first element is always the current global state (non-view-specific)
+ NSError* selfPeersError = nil;
+ CKKSSelves* selves = [self fetchSelfPeers:&selfPeersError];
+ NSError* trustedPeersError = nil;
+ NSSet<id<CKKSPeer>>* peers = [self fetchTrustedPeers:&trustedPeersError];
+
+
+ NSMutableArray<NSString*>* mutTrustedPeers = [[NSMutableArray alloc] init];
+ [peers enumerateObjectsUsingBlock:^(id<CKKSPeer> _Nonnull obj, BOOL * _Nonnull stop) {
+ [mutTrustedPeers addObject: [obj description]];
+ }];
+
+#define stringify(obj) CKKSNilToNSNull([obj description])
+ NSDictionary* global = @{
+ @"view": @"global",
+ @"selfPeers": stringify(selves),
+ @"selfPeersError": CKKSNilToNSNull(selfPeersError),
+ @"trustedPeers": CKKSNilToNSNull(mutTrustedPeers),
+ @"trustedPeersError": CKKSNilToNSNull(trustedPeersError),
+ };
+ [a addObject: global];
+
+ // Now, query the views about their status
NSArray* actualViews = nil;
if(viewName) {
secnotice("ckks", "Received a status RPC for zone %@", viewName);
[blockOp setCompletionBlock:^{
__strong __typeof(blockOp) strongBlockOp = weakBlockOp;
[strongBlockOp allDependentsSuccessful];
- reply(strongBlockOp.error);
+ reply(CKXPCSuitableError(strongBlockOp.error));
}];
for(CKKSKeychainView* view in actualViews) {
[blockOp setCompletionBlock:^{
__strong __typeof(blockOp) strongBlockOp = weakBlockOp;
[strongBlockOp allDependentsSuccessful];
- reply(strongBlockOp.error);
+ reply(CKXPCSuitableError(strongBlockOp.error));
}];
for(CKKSKeychainView* view in actualViews) {
{
NSError* error = nil;
NSString* sysdiagnose = [[CKKSAnalyticsLogger logger] getSysdiagnoseDumpWithError:&error];
- reply(sysdiagnose, error);
+ reply(sysdiagnose, CKXPCSuitableError(error));
}
- (void)rpcGetAnalyticsJSONWithReply:(void (^)(NSData* json, NSError* error))reply
{
NSError* error = nil;
- NSData* json = [[CKKSAnalyticsLogger logger] getLoggingJSONWithError:&error];
- reply(json, error);
+ NSData* json = [[CKKSAnalyticsLogger logger] getLoggingJSON:true error:&error];
+ reply(json, CKXPCSuitableError(error));
}
- (void)rpcForceUploadAnalyticsWithReply:(void (^)(BOOL success, NSError* error))reply
{
NSError* error = nil;
BOOL result = [[CKKSAnalyticsLogger logger] forceUploadWithError:&error];
- reply(result, error);
+ reply(result, CKXPCSuitableError(error));
}
-(void)xpc24HrNotification {
for(CKKSKeychainView* view in actualViews) {
ckksnotice("ckks", view, "Starting device state XPC update");
// Let the update know it should rate-limit itself
- [view updateDeviceState:true ckoperationGroup:group];
+ [view updateDeviceState:true waitForKeyHierarchyInitialization:30*NSEC_PER_SEC ckoperationGroup:group];
}
}
+#pragma mark - CKKSPeerProvider implementation
+
+- (CKKSSelves*)fetchSelfPeers:(NSError* __autoreleasing *)error {
+ __block SFECKeyPair* signingPrivateKey = nil;
+ __block SFECKeyPair* encryptionPrivateKey = nil;
+
+ __block NSError* localerror = nil;
+
+ // Wait for this to initialize, but don't worry if it isn't.
+ [self.accountTracker.accountCirclePeerIDInitialized wait:500*NSEC_PER_MSEC];
+ NSString* peerID = self.accountTracker.accountCirclePeerID;
+ if(!peerID || self.accountTracker.accountCirclePeerIDError) {
+ secerror("ckkspeer: Error fetching self peer : %@", self.accountTracker.accountCirclePeerIDError);
+ if(error) {
+ *error = self.accountTracker.accountCirclePeerIDError;
+ }
+ return nil;
+ }
+
+ SOSCCPerformWithOctagonSigningKey(^(SecKeyRef signingSecKey, CFErrorRef cferror) {
+ if(cferror) {
+ localerror = (__bridge NSError*)cferror;
+ return;
+ }
+ if (!cferror && signingSecKey) {
+ signingPrivateKey = [[SFECKeyPair alloc] initWithSecKey:signingSecKey];
+ }
+ });
+
+ SOSCCPerformWithOctagonEncryptionKey(^(SecKeyRef encryptionSecKey, CFErrorRef cferror) {
+ if(cferror) {
+ localerror = (__bridge NSError*)cferror;
+ return;
+ }
+ if (!cferror && encryptionSecKey) {
+ encryptionPrivateKey = [[SFECKeyPair alloc] initWithSecKey:encryptionSecKey];
+ }
+ });
+
+ if(localerror) {
+ secerror("ckkspeer: Error fetching self encryption keys: %@", localerror);
+ if(error) {
+ *error = localerror;
+ }
+ return nil;
+ }
+
+ CKKSSOSSelfPeer* selfPeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:peerID
+ encryptionKey:encryptionPrivateKey
+ signingKey:signingPrivateKey];
+ CKKSSelves* selves = [[CKKSSelves alloc] initWithCurrent:selfPeer allSelves:nil];
+ return selves;
+}
+
+- (NSSet<id<CKKSPeer>>*)fetchTrustedPeers:(NSError* __autoreleasing *)error {
+ __block NSMutableSet<id<CKKSPeer>>* peerSet = [NSMutableSet set];
+
+ SOSCCPerformWithTrustedPeers(^(CFSetRef sosPeerInfoRefs, CFErrorRef cfTrustedPeersError) {
+ if(cfTrustedPeersError) {
+ secerror("ckks: Error fetching trusted peers: %@", cfTrustedPeersError);
+ if(error) {
+ *error = (__bridge NSError*)cfTrustedPeersError;
+ }
+ }
+
+ CFSetForEach(sosPeerInfoRefs, ^(const void* voidPeer) {
+ CFErrorRef cfPeerError = NULL;
+ SOSPeerInfoRef sosPeerInfoRef = (SOSPeerInfoRef)voidPeer;
+
+ if(!sosPeerInfoRef) {
+ return;
+ }
+
+ CFStringRef cfpeerID = SOSPeerInfoGetPeerID(sosPeerInfoRef);
+ SecKeyRef cfOctagonSigningKey = SOSPeerInfoCopyOctagonSigningPublicKey(sosPeerInfoRef, &cfPeerError);
+ SecKeyRef cfOctagonEncryptionKey = SOSPeerInfoCopyOctagonEncryptionPublicKey(sosPeerInfoRef, &cfPeerError);
+
+ if(cfPeerError) {
+ secerror("ckkspeer: error fetching octagon keys for peer: %@ %@", sosPeerInfoRef, cfPeerError);
+ } else {
+ SFECPublicKey* signingPublicKey = cfOctagonSigningKey ? [[SFECPublicKey alloc] initWithSecKey:cfOctagonSigningKey] : nil;
+ SFECPublicKey* encryptionPublicKey = cfOctagonEncryptionKey ? [[SFECPublicKey alloc] initWithSecKey:cfOctagonEncryptionKey] : nil;
+
+ CKKSSOSPeer* peer = [[CKKSSOSPeer alloc] initWithSOSPeerID:(__bridge NSString*)cfpeerID
+ encryptionPublicKey:encryptionPublicKey
+ signingPublicKey:signingPublicKey];
+ [peerSet addObject:peer];
+ }
+
+ CFReleaseNull(cfOctagonSigningKey);
+ CFReleaseNull(cfOctagonEncryptionKey);
+ });
+ });
+
+ return peerSet;
+}
+
+- (void)registerForPeerChangeUpdates:(id<CKKSPeerUpdateListener>)listener {
+ bool alreadyRegisteredListener = false;
+ NSEnumerator *enumerator = [self.peerChangeListeners objectEnumerator];
+ id<CKKSPeerUpdateListener> value;
+
+ while ((value = [enumerator nextObject])) {
+ // do pointer comparison
+ alreadyRegisteredListener |= (value == listener);
+ }
+
+ if(listener && !alreadyRegisteredListener) {
+ NSString* queueName = [NSString stringWithFormat: @"ck-peer-change-%@", listener];
+
+ dispatch_queue_t objQueue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL);
+ [self.peerChangeListeners setObject: listener forKey: objQueue];
+ }
+}
+
+- (void)iteratePeerListenersOnTheirQueue:(void (^)(id<CKKSPeerUpdateListener>))block {
+ NSEnumerator *enumerator = [self.peerChangeListeners keyEnumerator];
+ dispatch_queue_t dq;
+
+ // Queue up the changes for each listener.
+ while ((dq = [enumerator nextObject])) {
+ id<CKKSPeerUpdateListener> listener = [self.peerChangeListeners objectForKey: dq];
+ __weak id<CKKSPeerUpdateListener> weakListener = listener;
+
+ if(listener) {
+ dispatch_async(dq, ^{
+ __strong id<CKKSPeerUpdateListener> strongListener = weakListener;
+ block(strongListener);
+ });
+ }
+ }
+}
+
+- (void)sendSelfPeerChangedUpdate {
+ [self iteratePeerListenersOnTheirQueue: ^(id<CKKSPeerUpdateListener> listener) {
+ [listener selfPeerChanged];
+ }];
+}
+
+- (void)sendTrustedPeerSetChangedUpdate {
+ [self iteratePeerListenersOnTheirQueue: ^(id<CKKSPeerUpdateListener> listener) {
+ [listener trustedPeerSetChanged];
+ }];
+}
#endif // OCTAGON
@end
// Dependencies (for injection)
@property (readonly) Class<CKKSFetchRecordZoneChangesOperation> fetchRecordZoneChangesOperationClass;
+@property (readonly) Class<CKKSFetchRecordsOperation> fetchRecordsOperationClass;
+@property (readonly) Class<CKKSQueryOperation> queryOperationClass;
@property (readonly) Class<CKKSModifySubscriptionsOperation> modifySubscriptionsOperationClass;
@property (readonly) Class<CKKSModifyRecordZonesOperation> modifyRecordZonesOperationClass;
@property (readonly) Class<CKKSAPSConnection> apsConnectionClass;
zoneName: (NSString*) zoneName
accountTracker:(CKKSCKAccountStateTracker*) tracker
fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
+ fetchRecordsOperationClass: (Class<CKKSFetchRecordsOperation>)fetchRecordsOperationClass
+ queryOperationClass:(Class<CKKSQueryOperation>)queryOperationClass
modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass;
zoneName: (NSString*) zoneName
accountTracker:(CKKSCKAccountStateTracker*) tracker
fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
+ fetchRecordsOperationClass: (Class<CKKSFetchRecordsOperation>)fetchRecordsOperationClass
+ queryOperationClass:(Class<CKKSQueryOperation>)queryOperationClass
modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
_accountOperations = [NSHashTable weakObjectsHashTable];
_fetchRecordZoneChangesOperationClass = fetchRecordZoneChangesOperationClass;
+ _fetchRecordsOperationClass = fetchRecordsOperationClass;
+ _queryOperationClass = queryOperationClass;
_modifySubscriptionsOperationClass = modifySubscriptionsOperationClass;
_modifyRecordZonesOperationClass = modifyRecordZonesOperationClass;
_apsConnectionClass = apsConnectionClass;
return;
}
+ bool fatalError = false;
+ if(operationError) {
+ // Okay, but if this error is either 'ZoneNotFound' or 'UserDeletedZone', that's fine by us: the zone is deleted.
+ NSDictionary* partialErrors = operationError.userInfo[CKPartialErrorsByItemIDKey];
+ if([operationError.domain isEqualToString:CKErrorDomain] && operationError.code == CKErrorPartialFailure && partialErrors) {
+ for(CKRecordZoneID* errorZoneID in partialErrors.allKeys) {
+ NSError* errorZone = partialErrors[errorZoneID];
+
+ if(errorZone && [errorZone.domain isEqualToString:CKErrorDomain] &&
+ (errorZone.code == CKErrorZoneNotFound || errorZone.code == CKErrorUserDeletedZone)) {
+ ckksnotice("ckkszone", strongSelf, "Attempted to delete zone %@, but it's already missing. This is okay: %@", errorZoneID, errorZone);
+ } else {
+ fatalError = true;
+ }
+ }
+
+ } else {
+ fatalError = true;
+ }
+ }
+
ckksinfo("ckkszone", strongSelf, "record zones deletion %@ completed with error: %@", deletedRecordZoneIDs, operationError);
[strongSelf resetSetup];
- doneOp.error = operationError;
+ if(operationError && fatalError) {
+ // If the error wasn't actually a problem, don't report it upward.
+ doneOp.error = operationError;
+ }
[strongSelf.operationQueue addOperation: doneOp];
};
- (CKKSResultOperation*)requestSuccessfulFetch:(CKKSFetchBecause*)why;
- (CKKSResultOperation*)requestSuccessfulResyncFetch:(CKKSFetchBecause*)why;
+// We don't particularly care what this does, as long as it finishes
+- (void)holdFetchesUntil:(CKKSResultOperation*)holdOperation;
+
-(void)cancel;
@end
@property CKKSResultOperation* successfulFetchDependency;
@property CKKSNearFutureScheduler* fetchScheduler;
+
+@property CKKSResultOperation* holdOperation;
@end
@implementation CKKSZoneChangeFetcher
CKOperationGroup* operationGroup = [CKOperationGroup CKKSGroupWithName: [[lastFetchReasons sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES]]] componentsJoinedByString:@","]];
CKKSFetchAllRecordZoneChangesOperation* fetchAllChanges = [[CKKSFetchAllRecordZoneChangesOperation alloc] initWithCKKSKeychainView: ckks ckoperationGroup:operationGroup];
+ [fetchAllChanges addNullableDependency: self.holdOperation];
fetchAllChanges.resync = self.newResyncRequests;
self.newResyncRequests = false;
self.successfulFetchDependency = dep;
}
+- (void)holdFetchesUntil:(CKKSResultOperation*)holdOperation {
+ self.holdOperation = holdOperation;
+}
+
-(void)cancel {
[self.fetchScheduler cancel];
}
#if OCTAGON
#import <CloudKit/CloudKit.h>
+#import "keychain/ckks/CKKSFixups.h"
/*
* This class hold the state for a particular zone: has the zone been created, have we subscribed to it,
@property NSData* encodedChangeToken;
@property NSDate* lastFetchTime;
+@property CKKSFixup lastFixup;
+
@property CKKSRateLimiter* rateLimiter;
@property NSData* encodedRateLimiter;
+ (instancetype) fromDatabase: (NSString*) ckzone error: (NSError * __autoreleasing *) error;
+ (instancetype) tryFromDatabase: (NSString*) ckzone error: (NSError * __autoreleasing *) error;
-- (instancetype) initWithCKZone: (NSString*) ckzone zoneCreated: (bool) ckzonecreated zoneSubscribed: (bool) ckzonesubscribed changeToken: (NSData*) changetoken lastFetch: (NSDate*) lastFetch encodedRateLimiter: (NSData*) encodedRateLimiter;
+- (instancetype)initWithCKZone:(NSString*)ckzone
+ zoneCreated:(bool)ckzonecreated
+ zoneSubscribed:(bool)ckzonesubscribed
+ changeToken:(NSData*)changetoken
+ lastFetch:(NSDate*)lastFetch
+ lastFixup:(CKKSFixup)lastFixup
+ encodedRateLimiter:(NSData*)encodedRateLimiter;
- (CKServerChangeToken*) getChangeToken;
- (void) setChangeToken: (CKServerChangeToken*) token;
#import <CloudKit/CloudKit.h>
#import "CKKSZoneStateEntry.h"
#import "keychain/ckks/CKKSRateLimiter.h"
+#import "keychain/ckks/CKKSFixups.h"
@implementation CKKSZoneStateEntry
-- (instancetype) initWithCKZone: (NSString*) ckzone zoneCreated: (bool) ckzonecreated zoneSubscribed: (bool) ckzonesubscribed changeToken: (NSData*) changetoken lastFetch: (NSDate*) lastFetch encodedRateLimiter: (NSData*) encodedRateLimiter {
+- (instancetype)initWithCKZone:(NSString*)ckzone
+ zoneCreated:(bool)ckzonecreated
+ zoneSubscribed:(bool)ckzonesubscribed
+ changeToken:(NSData*)changetoken
+ lastFetch:(NSDate*)lastFetch
+ lastFixup:(CKKSFixup)lastFixup
+ encodedRateLimiter:(NSData*)encodedRateLimiter
+{
if(self = [super init]) {
_ckzone = ckzone;
_ckzonecreated = ckzonecreated;
_ckzonesubscribed = ckzonesubscribed;
_encodedChangeToken = changetoken;
_lastFetchTime = lastFetch;
+ _lastFixup = lastFixup;
self.encodedRateLimiter = encodedRateLimiter;
}
self.ckzonesubscribed == obj.ckzonesubscribed &&
((self.encodedChangeToken == nil && obj.encodedChangeToken == nil) || [self.encodedChangeToken isEqual: obj.encodedChangeToken]) &&
((self.lastFetchTime == nil && obj.lastFetchTime == nil) || [self.lastFetchTime isEqualToDate: obj.lastFetchTime]) &&
- ((self.rateLimiter == nil && obj.rateLimiter == nil) || [self.rateLimiter isEqual: obj.rateLimiter])
- ) ? YES : NO;
+ ((self.rateLimiter == nil && obj.rateLimiter == nil) || [self.rateLimiter isEqual: obj.rateLimiter]) &&
+ self.lastFixup == obj.lastFixup &&
+ true) ? YES : NO;
}
+ (instancetype) state: (NSString*) ckzone {
}
if(!ret) {
- ret = [[CKKSZoneStateEntry alloc] initWithCKZone: ckzone zoneCreated: false zoneSubscribed: false changeToken: nil lastFetch:nil encodedRateLimiter: nil];
+ ret = [[CKKSZoneStateEntry alloc] initWithCKZone:ckzone
+ zoneCreated:false
+ zoneSubscribed:false
+ changeToken:nil
+ lastFetch:nil
+ lastFixup:CKKSCurrentFixupNumber
+ encodedRateLimiter:nil];
}
return ret;
}
}
+ (NSArray<NSString*>*) sqlColumns {
- return @[@"ckzone", @"ckzonecreated", @"ckzonesubscribed", @"changetoken", @"lastfetch", @"ratelimiter"];
+ return @[@"ckzone", @"ckzonecreated", @"ckzonesubscribed", @"changetoken", @"lastfetch", @"ratelimiter", @"lastFixup"];
}
- (NSDictionary<NSString*,NSString*>*) whereClauseToFindSelf {
@"ckzonesubscribed": [NSNumber numberWithBool:self.ckzonesubscribed],
@"changetoken": CKKSNilToNSNull([self.encodedChangeToken base64EncodedStringWithOptions:0]),
@"lastfetch": CKKSNilToNSNull(self.lastFetchTime ? [dateFormat stringFromDate: self.lastFetchTime] : nil),
- @"ratelimiter": CKKSNilToNSNull([self.encodedRateLimiter base64EncodedStringWithOptions:0])
+ @"ratelimiter": CKKSNilToNSNull([self.encodedRateLimiter base64EncodedStringWithOptions:0]),
+ @"lastFixup": [NSNumber numberWithLong:self.lastFixup],
};
}
[[NSData alloc] initWithBase64EncodedString: row[@"changetoken"] options:0] :
nil
lastFetch: [row[@"lastfetch"] isEqual: [NSNull null]] ? nil : [dateFormat dateFromString: row[@"lastfetch"]]
+ lastFixup:(CKKSFixup)[row[@"lastFixup"] integerValue]
encodedRateLimiter: [row[@"ratelimiter"] isEqual: [NSNull null]] ? nil : [[NSData alloc] initWithBase64EncodedString: row[@"ratelimiter"] options:0]
];
}
#import <Foundation/Foundation.h>
#import <CloudKit/CloudKit.h>
+#import <CloudKit/CloudKit_Private.h>
@interface CKOperationGroup (CKKS)
+(instancetype) CKKSGroupWithName:(NSString*)name;
@interface NSError (CKKS)
+// More useful constructor
++ (instancetype)errorWithDomain:(NSErrorDomain)domain code:(NSInteger)code description:(NSString*)description;
++ (instancetype)errorWithDomain:(NSErrorDomain)domain code:(NSInteger)code description:(NSString*)description underlying:(NSError*)underlying;
+
// Returns true if this is a CloudKit error where
// 1) An atomic write failed
// 2) Every single suberror is either CKErrorServerRecordChanged or CKErrorUnknownItem
-(bool) ckksIsCKErrorRecordChangedError;
@end
+// Ensure we don't print addresses
+@interface CKAccountInfo (CKKS)
+-(NSString*)description;
+@end
+
#endif // OCTAGON
@implementation NSError (CKKS)
++ (instancetype)errorWithDomain:(NSErrorDomain)domain code:(NSInteger)code description:(NSString*)description {
+ return [NSError errorWithDomain:domain code:code description:description underlying:nil];
+}
+
++ (instancetype)errorWithDomain:(NSErrorDomain)domain code:(NSInteger)code description:(NSString*)description underlying:(NSError*)underlying {
+ // Obj-C throws a fit if there's nulls in dictionaries, so we can't use a dictionary literal here.
+ // Use the null-assignment semantics of NSMutableDictionary to make a dictionary either with either, both, or neither key.
+ NSMutableDictionary* mut = [[NSMutableDictionary alloc] init];
+ mut[NSLocalizedDescriptionKey] = description;
+ mut[NSUnderlyingErrorKey] = underlying;
+
+ return [NSError errorWithDomain:domain code:code userInfo:mut];
+}
+
-(bool) ckksIsCKErrorRecordChangedError {
NSDictionary<CKRecordID*,NSError*>* partialErrors = self.userInfo[CKPartialErrorsByItemIDKey];
if([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorPartialFailure && partialErrors) {
@end
+@implementation CKAccountInfo (CKKS)
+// Ugly, and might break if CloudKit changes how they print objects. Sorry, CloudKit!
+- (NSString*)description {
+ NSString* ckprop = [self CKPropertiesDescription];
+ NSString* description = [NSString stringWithFormat: @"<CKAccountInfo: %@>", ckprop];
+ return description;
+}
+@end
+
#endif //OCTAGON
@interface CKFetchRecordZoneChangesOperation () <CKKSFetchRecordZoneChangesOperation>;
@end
+/* CKFetchRecordsOperation */
+@protocol CKKSFetchRecordsOperation <NSObject>
++ (instancetype)alloc;
+- (instancetype)init;
+- (instancetype)initWithRecordIDs:(NSArray<CKRecordID *> *)recordIDs;
+
+@property (nonatomic, copy, nullable) NSArray<CKRecordID *> *recordIDs;
+@property (nonatomic, copy, nullable) NSArray<NSString *> *desiredKeys;
+@property (nonatomic, copy, nullable) void (^perRecordProgressBlock)(CKRecordID *recordID, double progress);
+@property (nonatomic, copy, nullable) void (^perRecordCompletionBlock)(CKRecord * _Nullable record, CKRecordID * _Nullable recordID, NSError * _Nullable error);
+@property (nonatomic, copy, nullable) void (^fetchRecordsCompletionBlock)(NSDictionary<CKRecordID * , CKRecord *> * _Nullable recordsByRecordID, NSError * _Nullable operationError);
+@end
+
+@interface CKFetchRecordsOperation () <CKKSFetchRecordsOperation>
+@end
+
+/* CKQueryOperation */
+
+@protocol CKKSQueryOperation <NSObject>
++ (instancetype)alloc;
+- (instancetype)initWithQuery:(CKQuery *)query;
+//Not implemented: - (instancetype)initWithCursor:(CKQueryCursor *)cursor;
+
+@property (nonatomic, copy, nullable) CKQuery *query;
+@property (nonatomic, copy, nullable) CKQueryCursor *cursor;
+
+@property (nonatomic, copy, nullable) CKRecordZoneID *zoneID;
+@property (nonatomic, assign) NSUInteger resultsLimit;
+@property (nonatomic, copy, nullable) NSArray<NSString *> *desiredKeys;
+
+@property (nonatomic, copy, nullable) void (^recordFetchedBlock)(CKRecord *record);
+@property (nonatomic, copy, nullable) void (^queryCompletionBlock)(CKQueryCursor * _Nullable cursor, NSError * _Nullable operationError);
+@end
+
+@interface CKQueryOperation () <CKKSQueryOperation>
+@end
+
/* APSConnection */
@protocol CKKSAPSConnection <NSObject>
+ (instancetype)alloc;
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#import "keychain/ckks/NSOperationCategories.h"
+
+@interface NSOperation (CKKSUsefulPrintingOperation)
+- (NSString*)description;
+- (BOOL)isPending;
+
+// Use our .name field if it's there, otherwise, just a generic name
+- (NSString*)selfname;
+
+// If op is nonnull, op becomes a dependency of this operation
+- (void)addNullableDependency: (NSOperation*) op;
+
+// Add all operations in this collection as dependencies, then add yourself to the collection
+-(void)linearDependencies:(NSHashTable*)collection;
+
+// Insert yourself as high up the linearized list of dependencies as possible
+-(void)linearDependenciesWithSelfFirst: (NSHashTable*) collection;
+
+// Return a stringified representation of this operation's live dependencies.
+-(NSString*)pendingDependenciesString:(NSString*)prefix;
+@end
+
+@interface NSBlockOperation (CKKSUsefulConstructorOperation)
++(instancetype)named: (NSString*)name withBlock: (void(^)(void)) block;
+@end
+
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#import <Foundation/Foundation.h>
+#import "keychain/ckks/NSOperationCategories.h"
+
+@implementation NSOperation (CKKSUsefulPrintingOperation)
+- (NSString*)selfname {
+ if(self.name) {
+ return [NSString stringWithFormat: @"%@(%@)", NSStringFromClass([self class]), self.name];
+ } else {
+ return NSStringFromClass([self class]);
+ }
+}
+
+-(void)linearDependencies: (NSHashTable*) collection {
+ @synchronized(collection) {
+ for(NSOperation* existingop in collection) {
+ if(existingop == self) {
+ // don't depend on yourself
+ continue;
+ }
+ [self addDependency: existingop];
+ }
+ [collection addObject:self];
+ }
+}
+
+-(void)linearDependenciesWithSelfFirst: (NSHashTable*) collection {
+ @synchronized(collection) {
+ for(NSOperation* existingop in collection) {
+ if(existingop == self) {
+ // don't depend on yourself
+ continue;
+ }
+
+ if([existingop isPending]) {
+ [existingop addDependency: self];
+ if([existingop isPending]) {
+ // Good, we're ahead of this one.
+ } else {
+ // It started before we told it to wait on us. Reverse the dependency.
+ [existingop removeDependency: self];
+ [self addDependency:existingop];
+ }
+ } else {
+ // Not a pending op? We depend on it.
+ [self addDependency: existingop];
+ }
+ }
+ [collection addObject:self];
+ }
+}
+
+-(NSString*)pendingDependenciesString:(NSString*)prefix {
+ NSArray* dependencies = [self.dependencies copy];
+ dependencies = [dependencies objectsAtIndexes: [dependencies indexesOfObjectsPassingTest: ^BOOL (id obj,
+ NSUInteger idx,
+ BOOL* stop) {
+ return [obj isPending] ? YES : NO;
+ }]];
+
+ if(dependencies.count == 0u) {
+ return @"";
+ }
+
+ return [NSString stringWithFormat: @"%@%@", prefix, [dependencies componentsJoinedByString: @", "]];
+}
+
+- (NSString*)description {
+ NSString* state = ([self isFinished] ? @"finished" :
+ [self isCancelled] ? @"cancelled" :
+ [self isExecuting] ? @"executing" :
+ [self isReady] ? @"ready" :
+ @"pending");
+
+ return [NSString stringWithFormat: @"<%@: %@%@>", [self selfname], state, [self pendingDependenciesString: @" dep:"]];
+}
+- (NSString*)debugDescription {
+ NSString* state = ([self isFinished] ? @"finished" :
+ [self isCancelled] ? @"cancelled" :
+ [self isExecuting] ? @"executing" :
+ [self isReady] ? @"ready" :
+ @"pending");
+
+ return [NSString stringWithFormat: @"<%@ (%p): %@%@>", [self selfname], self, state, [self pendingDependenciesString: @" dep:"]];
+}
+
+- (BOOL)isPending {
+ return (!([self isExecuting] || [self isFinished] || [self isCancelled])) ? YES : NO;
+}
+
+- (void)addNullableDependency: (NSOperation*) op {
+ if(op) {
+ [self addDependency:op];
+ }
+}
+@end
+
+@implementation NSBlockOperation (CKKSUsefulConstructorOperation)
++(instancetype)named: (NSString*)name withBlock: (void(^)(void)) block {
+ // How many blocks could a block block if a block could block blocks?
+ NSBlockOperation* blockOp = [NSBlockOperation blockOperationWithBlock: block];
+ blockOp.name = name;
+ return blockOp;
+}
+@end
--- /dev/null
+syntax = "proto2";
+
+option objc_class_naming = "extended";
+option objc_class_visibility = "hidden";
+
+package CKKS;
+
+message SerializedKey {
+ required string uuid = 1;
+ required string zoneName = 2;
+ required string keyclass = 3;
+ required bytes key = 4;
+}
--- /dev/null
+// This file was automatically generated by protocompiler
+// DO NOT EDIT!
+// Compiled from CKKSSerializedKey.proto
+
+#import <Foundation/Foundation.h>
+#import <ProtocolBuffer/PBCodable.h>
+
+#ifdef __cplusplus
+#define CKKSSERIALIZEDKEY_FUNCTION extern "C" __attribute__((visibility("hidden")))
+#else
+#define CKKSSERIALIZEDKEY_FUNCTION extern __attribute__((visibility("hidden")))
+#endif
+
+__attribute__((visibility("hidden")))
+@interface CKKSSerializedKey : PBCodable <NSCopying>
+{
+ NSData *_key;
+ NSString *_keyclass;
+ NSString *_uuid;
+ NSString *_zoneName;
+}
+
+
+@property (nonatomic, retain) NSString *uuid;
+
+@property (nonatomic, retain) NSString *zoneName;
+
+@property (nonatomic, retain) NSString *keyclass;
+
+@property (nonatomic, retain) NSData *key;
+
+// Performs a shallow copy into other
+- (void)copyTo:(CKKSSerializedKey *)other;
+
+// Performs a deep merge from other into self
+// If set in other, singular values in self are replaced in self
+// Singular composite values are recursively merged
+// Repeated values from other are appended to repeated values in self
+- (void)mergeFrom:(CKKSSerializedKey *)other;
+
+CKKSSERIALIZEDKEY_FUNCTION BOOL CKKSSerializedKeyReadFrom(__unsafe_unretained CKKSSerializedKey *self, __unsafe_unretained PBDataReader *reader);
+
+@end
+
--- /dev/null
+// This file was automatically generated by protocompiler
+// DO NOT EDIT!
+// Compiled from CKKSSerializedKey.proto
+
+#import "CKKSSerializedKey.h"
+#import <ProtocolBuffer/PBConstants.h>
+#import <ProtocolBuffer/PBHashUtil.h>
+#import <ProtocolBuffer/PBDataReader.h>
+
+#if !__has_feature(objc_arc)
+# error This generated file depends on ARC but it is not enabled; turn on ARC, or use 'objc_use_arc' option to generate non-ARC code.
+#endif
+
+@implementation CKKSSerializedKey
+
+@synthesize uuid = _uuid;
+@synthesize zoneName = _zoneName;
+@synthesize keyclass = _keyclass;
+@synthesize key = _key;
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"%@ %@", [super description], [self dictionaryRepresentation]];
+}
+
+- (NSDictionary *)dictionaryRepresentation
+{
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+ if (self->_uuid)
+ {
+ [dict setObject:self->_uuid forKey:@"uuid"];
+ }
+ if (self->_zoneName)
+ {
+ [dict setObject:self->_zoneName forKey:@"zoneName"];
+ }
+ if (self->_keyclass)
+ {
+ [dict setObject:self->_keyclass forKey:@"keyclass"];
+ }
+ if (self->_key)
+ {
+ [dict setObject:self->_key forKey:@"key"];
+ }
+ return dict;
+}
+
+BOOL CKKSSerializedKeyReadFrom(__unsafe_unretained CKKSSerializedKey *self, __unsafe_unretained PBDataReader *reader) {
+ while (PBReaderHasMoreData(reader)) {
+ uint32_t tag = 0;
+ uint8_t aType = 0;
+
+ PBReaderReadTag32AndType(reader, &tag, &aType);
+
+ if (PBReaderHasError(reader))
+ break;
+
+ if (aType == TYPE_END_GROUP) {
+ break;
+ }
+
+ switch (tag) {
+
+ case 1 /* uuid */:
+ {
+ NSString *new_uuid = PBReaderReadString(reader);
+ self->_uuid = new_uuid;
+ }
+ break;
+ case 2 /* zoneName */:
+ {
+ NSString *new_zoneName = PBReaderReadString(reader);
+ self->_zoneName = new_zoneName;
+ }
+ break;
+ case 3 /* keyclass */:
+ {
+ NSString *new_keyclass = PBReaderReadString(reader);
+ self->_keyclass = new_keyclass;
+ }
+ break;
+ case 4 /* key */:
+ {
+ NSData *new_key = PBReaderReadData(reader);
+ self->_key = new_key;
+ }
+ break;
+ default:
+ if (!PBReaderSkipValueWithTag(reader, tag, aType))
+ return NO;
+ break;
+ }
+ }
+ return !PBReaderHasError(reader);
+}
+
+- (BOOL)readFrom:(PBDataReader *)reader
+{
+ return CKKSSerializedKeyReadFrom(self, reader);
+}
+- (void)writeTo:(PBDataWriter *)writer
+{
+ /* uuid */
+ {
+ assert(nil != self->_uuid);
+ PBDataWriterWriteStringField(writer, self->_uuid, 1);
+ }
+ /* zoneName */
+ {
+ assert(nil != self->_zoneName);
+ PBDataWriterWriteStringField(writer, self->_zoneName, 2);
+ }
+ /* keyclass */
+ {
+ assert(nil != self->_keyclass);
+ PBDataWriterWriteStringField(writer, self->_keyclass, 3);
+ }
+ /* key */
+ {
+ assert(nil != self->_key);
+ PBDataWriterWriteDataField(writer, self->_key, 4);
+ }
+}
+
+- (void)copyTo:(CKKSSerializedKey *)other
+{
+ other.uuid = _uuid;
+ other.zoneName = _zoneName;
+ other.keyclass = _keyclass;
+ other.key = _key;
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ CKKSSerializedKey *copy = [[[self class] allocWithZone:zone] init];
+ copy->_uuid = [_uuid copyWithZone:zone];
+ copy->_zoneName = [_zoneName copyWithZone:zone];
+ copy->_keyclass = [_keyclass copyWithZone:zone];
+ copy->_key = [_key copyWithZone:zone];
+ return copy;
+}
+
+- (BOOL)isEqual:(id)object
+{
+ CKKSSerializedKey *other = (CKKSSerializedKey *)object;
+ return [other isMemberOfClass:[self class]]
+ &&
+ ((!self->_uuid && !other->_uuid) || [self->_uuid isEqual:other->_uuid])
+ &&
+ ((!self->_zoneName && !other->_zoneName) || [self->_zoneName isEqual:other->_zoneName])
+ &&
+ ((!self->_keyclass && !other->_keyclass) || [self->_keyclass isEqual:other->_keyclass])
+ &&
+ ((!self->_key && !other->_key) || [self->_key isEqual:other->_key])
+ ;
+}
+
+- (NSUInteger)hash
+{
+ return 0
+ ^
+ [self->_uuid hash]
+ ^
+ [self->_zoneName hash]
+ ^
+ [self->_keyclass hash]
+ ^
+ [self->_key hash]
+ ;
+}
+
+- (void)mergeFrom:(CKKSSerializedKey *)other
+{
+ if (other->_uuid)
+ {
+ [self setUuid:other->_uuid];
+ }
+ if (other->_zoneName)
+ {
+ [self setZoneName:other->_zoneName];
+ }
+ if (other->_keyclass)
+ {
+ [self setKeyclass:other->_keyclass];
+ }
+ if (other->_key)
+ {
+ [self setKey:other->_key];
+ }
+}
+
+@end
+
--- /dev/null
+/*
+ * Copyright (c) 2016 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import <XCTest/XCTest.h>
+#import "CloudKitMockXCTest.h"
+
+#import "keychain/ckks/CKKS.h"
+#import "keychain/ckks/CKKSKey.h"
+#import "keychain/ckks/CKKSItem.h"
+#import "keychain/ckks/CKKSOutgoingQueueEntry.h"
+#import "keychain/ckks/CKKSIncomingQueueEntry.h"
+#import "keychain/ckks/CKKSItemEncrypter.h"
+
+#include <securityd/SecItemServer.h>
+#include <Security/SecItemPriv.h>
+
+@interface CloudKitKeychainAESSIVEncryptionTests : CloudKitMockXCTest
+@end
+
+@implementation CloudKitKeychainAESSIVEncryptionTests
+
++ (void)setUp {
+ // We don't really want to spin up the whole machinery for the encryption tests
+ SecCKKSDisable();
+
+ [super setUp];
+}
+
+- (void)setUp {
+ [super setUp];
+}
+
+- (void)tearDown {
+ [super tearDown];
+}
+
++ (void)tearDown {
+ [super tearDown];
+ SecCKKSResetSyncing();
+}
+
+- (void)testKeyGeneration {
+ CKKSAESSIVKey* key1 = [CKKSAESSIVKey randomKey];
+ CKKSAESSIVKey* key2 = [CKKSAESSIVKey randomKey];
+ CKKSAESSIVKey* fixedkey1 = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
+ XCTAssertNotNil(fixedkey1, "fixedkey1 generated from base64");
+ CKKSAESSIVKey* fixedkey2 = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
+ XCTAssertNotNil(fixedkey2, "fixedkey2 generated from base64");
+
+ XCTAssertEqualObjects(fixedkey1, fixedkey2, "matching fixed keys match");
+ XCTAssertNotEqualObjects(fixedkey1, key1, "fixed key and random key do not match");
+ XCTAssertNotEqualObjects(key1, key2, "two random keys do not match");
+
+ XCTAssertNil([[CKKSAESSIVKey alloc] initWithBase64: @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA------AAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], "Invalid base64 does not generate a key");
+}
+
+- (void)testBasicAESSIVEncryption {
+ NSString* plaintext = @"plaintext is plain";
+ NSData* plaintextData = [plaintext dataUsingEncoding: NSUTF8StringEncoding];
+
+ NSError* error = nil;
+
+ CKKSKey* key = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="]
+ uuid:@"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"
+ keyclass:SecCKKSKeyClassC
+ state: SecCKKSProcessedStateLocal
+ zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
+ encodedCKRecord: nil
+ currentkey: true];
+
+ NSData* ciphertext = [key encryptData: plaintextData authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error encrypting plaintext");
+ XCTAssertNotNil(ciphertext, "Received a ciphertext");
+ NSData* roundtrip = [key decryptData: ciphertext authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error decrypting roundtrip");
+ XCTAssertNotNil(roundtrip, "Received a plaintext");
+ XCTAssertEqualObjects(plaintextData, roundtrip, "roundtripped data matches input");
+
+ NSData* shortDecrypt = [key decryptData: [@"asdf" dataUsingEncoding:NSUTF8StringEncoding] authenticatedData:nil error:&error];
+ XCTAssertNotNil(error, "Decrypting a short plaintext returned an error");
+ XCTAssertNil(shortDecrypt, "Decrypting a short plaintext returned nil");
+ error = nil;
+
+ // Check that we're adding enough entropy
+ NSData* ciphertextAgain = [key encryptData: plaintextData authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error encrypting plaintext");
+ XCTAssertNotNil(ciphertextAgain, "Received a ciphertext");
+ NSData* roundtripAgain = [key decryptData: ciphertextAgain authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error decrypting roundtrip");
+ XCTAssertNotNil(roundtripAgain, "Received a plaintext");
+ XCTAssertEqualObjects(plaintextData, roundtripAgain, "roundtripped data matches input");
+
+ XCTAssertNotEqualObjects(ciphertext, ciphertextAgain, "two encryptions of same input produce different outputs");
+
+ // Do it all again
+ CKKSKey* key2 = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="]
+ uuid:@"f5e7f20f-0885-48f9-b75d-9f0cfd2171b6"
+ keyclass:SecCKKSKeyClassC
+ state: SecCKKSProcessedStateLocal
+ zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
+ encodedCKRecord: nil
+ currentkey: true];
+
+ NSData* ciphertext2 = [key2 encryptData: plaintextData authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error encrypting plaintext");
+ XCTAssertNotNil(ciphertext2, "Received a ciphertext");
+ NSData* roundtrip2 = [key decryptData: ciphertext authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error decrypting roundtrip");
+ XCTAssertNotNil(roundtrip2, "Received a plaintext");
+ XCTAssertEqualObjects(plaintextData, roundtrip2, "roundtripped data matches input");
+
+ XCTAssertNotEqualObjects(ciphertext, ciphertext2, "ciphertexts with distinct keys are distinct");
+}
+
+- (void)testAuthEncryption {
+ NSString* plaintext = @"plaintext is plain";
+ NSData* plaintextData = [plaintext dataUsingEncoding: NSUTF8StringEncoding];
+
+ NSError* error = nil;
+
+ CKKSKey* key = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="]
+ uuid:@"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"
+ keyclass:SecCKKSKeyClassC
+ state:SecCKKSProcessedStateLocal
+ zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
+ encodedCKRecord:nil
+ currentkey:true];
+ NSDictionary<NSString*, NSData*>* ad = @{ @"test": [@"data" dataUsingEncoding: NSUTF8StringEncoding] };
+
+ NSData* ciphertext = [key encryptData: plaintextData authenticatedData: ad error: &error];
+ XCTAssertNil(error, "No error encrypting plaintext");
+ XCTAssertNotNil(ciphertext, "Received a ciphertext");
+ NSData* roundtrip = [key decryptData: ciphertext authenticatedData: ad error: &error];
+ XCTAssertNil(error, "No error decrypting roundtrip");
+ XCTAssertNotNil(roundtrip, "Received a plaintext");
+ XCTAssertEqualObjects(plaintextData, roundtrip, "roundtripped data matches input");
+
+ // Without AD, decryption should fail
+ roundtrip = [key decryptData: ciphertext authenticatedData: nil error: &error];
+ XCTAssertNotNil(error, "Not passing in the authenticated data causes break");
+ XCTAssertNil(roundtrip, "on error, don't receive plaintext");
+ error = nil;
+
+ roundtrip = [key decryptData: ciphertext authenticatedData: @{ @"test": [@"wrongdata" dataUsingEncoding: NSUTF8StringEncoding] } error: &error];
+ XCTAssertNotNil(error, "Wrong authenticated data causes break");
+ XCTAssertNil(roundtrip, "on error, don't receive plaintext");
+ error = nil;
+}
+
+- (void)testDictionaryEncryption {
+ NSDictionary<NSString*, NSData*>* plaintext = @{ @"test": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
+ @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
+ NSDictionary<NSString*, NSData*>* roundtrip;
+
+ NSError* error = nil;
+
+ CKKSKey* key = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="]
+ uuid:@"f5e7f20f-0885-48f9-b75d-9f0cfd2171b6"
+ keyclass:SecCKKSKeyClassC
+ state: SecCKKSProcessedStateLocal
+ zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
+ encodedCKRecord: nil
+ currentkey: true];
+
+ NSData* ciphertext = [CKKSItemEncrypter encryptDictionary: plaintext key: key.aessivkey authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error encrypting plaintext");
+ XCTAssertNotNil(ciphertext, "Received a ciphertext");
+ roundtrip = [CKKSItemEncrypter decryptDictionary: ciphertext key: key.aessivkey authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error decrypting roundtrip");
+ XCTAssertNotNil(roundtrip, "Received a plaintext");
+ XCTAssertEqualObjects(plaintext, roundtrip, "roundtripped dictionary matches input");
+
+ NSDictionary* authenticatedData = @{@"data": [@"auth" dataUsingEncoding: NSUTF8StringEncoding], @"moredata": [@"unauth" dataUsingEncoding: NSUTF8StringEncoding]};
+ NSDictionary* unauthenticatedData = @{@"data": [@"notequal" dataUsingEncoding: NSUTF8StringEncoding], @"moredata": [@"unauth" dataUsingEncoding: NSUTF8StringEncoding]};
+
+ NSData* authciphertext = [CKKSItemEncrypter encryptDictionary: plaintext key: key.aessivkey authenticatedData: authenticatedData error: &error];
+ XCTAssertNil(error, "No error encrypting plaintext with authenticated data");
+ XCTAssertNotNil(authciphertext, "Received a ciphertext");
+ roundtrip = [CKKSItemEncrypter decryptDictionary: authciphertext key: key.aessivkey authenticatedData: authenticatedData error: &error];
+ XCTAssertNil(error, "No error decrypting roundtrip with authenticated data");
+ XCTAssertNotNil(roundtrip, "Received a plaintext");
+ XCTAssertEqualObjects(plaintext, roundtrip, "roundtripped dictionary matches input");
+
+ roundtrip = [CKKSItemEncrypter decryptDictionary: authciphertext key: key.aessivkey authenticatedData: unauthenticatedData error: &error];
+ XCTAssertNotNil(error, "Error decrypting roundtrip with bad authenticated data");
+ XCTAssertNil(roundtrip, "Did not receive a plaintext when authenticated data is wrong");
+}
+
+- (void)testKeyWrapping {
+ CKKSAESSIVKey* key = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
+
+ CKKSAESSIVKey* keyToWrap = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
+
+ NSError* error = nil;
+
+ CKKSWrappedAESSIVKey* wrappedKey = [key wrapAESKey: keyToWrap error:&error];
+ XCTAssertNil(error, "no error wrapping key");
+ XCTAssertNotNil(wrappedKey, "wrapped key was returned");
+
+ XCTAssert(0 != memcmp(keyToWrap->key, (wrappedKey->key)+(CKKSWrappedKeySize - CKKSKeySize), CKKSKeySize), "wrapped key is different from original key");
+
+ CKKSAESSIVKey* unwrappedKey = [key unwrapAESKey: wrappedKey error:&error];
+ XCTAssertNil(error, "no error unwrapping key");
+ XCTAssertNotNil(unwrappedKey, "unwrapped key was returned");
+
+ XCTAssert(0 == memcmp(keyToWrap->key, unwrappedKey->key, CKKSKeySize), "unwrapped key matches original key");
+ XCTAssertEqualObjects(keyToWrap, unwrappedKey, "unwrapped key matches original key");
+}
+
+- (void)testKeyWrappingFailure {
+ CKKSAESSIVKey* key = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
+
+ CKKSAESSIVKey* keyToWrap = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
+
+ NSError* error = nil;
+
+ CKKSWrappedAESSIVKey* wrappedKey = [key wrapAESKey: keyToWrap error:&error];
+ XCTAssertNil(error, "no error wrapping key");
+ XCTAssertNotNil(wrappedKey, "wrapped key was returned");
+
+ XCTAssert(0 != memcmp(keyToWrap->key, (wrappedKey->key)+(CKKSWrappedKeySize - CKKSKeySize), CKKSKeySize), "wrapped key is different from original key");
+ wrappedKey->key[0] ^= 0x1;
+
+ CKKSAESSIVKey* unwrappedKey = [key unwrapAESKey: wrappedKey error:&error];
+ XCTAssertNotNil(error, "error unwrapping key");
+ XCTAssertNil(unwrappedKey, "unwrapped key was not returned in error case");
+}
+
+- (void)testKeyHierarchy {
+ NSError* error = nil;
+ NSData* testCKRecord = [@"nonsense" dataUsingEncoding:NSUTF8StringEncoding];
+ CKKSKey* tlk = [self fakeTLK:self.testZoneID];
+
+ [tlk saveToDatabase:&error];
+ [tlk saveKeyMaterialToKeychain:&error];
+ XCTAssertNil(error, "tlk saved to database without error");
+
+ CKKSKey* level1 = [CKKSKey randomKeyWrappedByParent: tlk keyclass:SecCKKSKeyClassA error:&error];
+ level1.encodedCKRecord = testCKRecord;
+ XCTAssertNotNil(level1, "level 1 key created");
+ XCTAssertNil(error, "level 1 key created");
+
+ [level1 saveToDatabase:&error];
+ XCTAssertNil(error, "level 1 key saved to database without error");
+
+ CKKSKey* level2 = [CKKSKey randomKeyWrappedByParent: level1 error:&error];
+ level2.encodedCKRecord = testCKRecord;
+ XCTAssertNotNil(level2, "level 2 key created");
+ XCTAssertNil(error, "no error creating level 2 key");
+ [level2 saveToDatabase:&error];
+ XCTAssertNil(error, "level 2 key saved to database without error");
+
+ NSString* level2UUID = level2.uuid;
+
+ // Fetch the level2 key from the database.
+ CKKSKey* extractedkey = [CKKSKey fromDatabase:level2UUID zoneID:self.testZoneID error:&error];
+ [extractedkey unwrapViaKeyHierarchy: &error];
+ XCTAssertNotNil(extractedkey, "could fetch key again");
+ XCTAssertNil(error, "no error fetching key from database");
+
+ CKKSAESSIVKey* extracedaeskey = [extractedkey ensureKeyLoaded:&error];
+ XCTAssertNotNil(extractedkey, "fetched key could unwrap");
+ XCTAssertNil(error, "no error forcing unwrap on fetched key");
+
+ XCTAssertEqualObjects(level2.aessivkey, extracedaeskey, @"fetched aes key is equal to saved key");
+}
+
+- (void)ensureKeychainSaveLoad: (CKKSKey*) key {
+ NSError* error = nil;
+ [key saveToDatabase:&error];
+ XCTAssertNil(error, "no error saving to database");
+ [key saveKeyMaterialToKeychain:&error];
+ XCTAssertNil(error, "no error saving to keychain");
+
+ CKKSKey* loadedKey = [CKKSKey fromDatabase:key.uuid zoneID:self.testZoneID error:&error];
+ XCTAssertNil(error, "no error loading from database");
+ XCTAssertNotNil(loadedKey, "Received an item back from the database");
+
+ XCTAssert([loadedKey loadKeyMaterialFromKeychain:&error], "could load key material back from keychain");
+ XCTAssertNil(error, "no error loading key from keychain");
+
+ XCTAssertEqualObjects(loadedKey.aessivkey, key.aessivkey, "Loaded key is identical after save/load");
+}
+
+- (void)testKeychainSave {
+ NSError* error = nil;
+ NSData* testCKRecord = [@"nonsense" dataUsingEncoding:NSUTF8StringEncoding];
+ CKKSKey* tlk = [self fakeTLK:self.testZoneID];
+ [self ensureKeychainSaveLoad: tlk];
+
+ // Ensure that Class A and Class C can do the same thing
+ CKKSKey* classA = [CKKSKey randomKeyWrappedByParent: tlk keyclass:SecCKKSKeyClassA error:&error];
+ classA.encodedCKRecord = testCKRecord;
+ XCTAssertNil(error, "No error creating random class A key");
+ [self ensureKeychainSaveLoad: classA];
+ CKKSKey* classC = [CKKSKey randomKeyWrappedByParent: tlk keyclass:SecCKKSKeyClassC error:&error];
+ classC.encodedCKRecord = testCKRecord;
+ XCTAssertNil(error, "No error creating random class C key");
+ [self ensureKeychainSaveLoad: classC];
+}
+
+- (void)testCKKSKeyProtobuf {
+ NSError* error = nil;
+ CKKSKey* tlk = [self fakeTLK:self.testZoneID];
+
+ NSData* tlkPersisted = [tlk serializeAsProtobuf:&error];
+ XCTAssertNil(error, "Shouldn't have been an error serializing to protobuf");
+ XCTAssertNotNil(tlkPersisted, "Should have gotten some protobuf data back");
+
+ CKKSKey* otherKey = [CKKSKey loadFromProtobuf:tlkPersisted error:&error];
+ XCTAssertNil(error, "Shouldn't have been an error serializing from protobuf");
+ XCTAssertNotNil(otherKey, "Should have gotten some protobuf data back");
+
+ XCTAssertEqualObjects(tlk.uuid, otherKey.uuid, "Should have gotten the same UUID");
+ XCTAssertEqualObjects(tlk.keyclass, otherKey.keyclass, "Should have gotten the same key class");
+ XCTAssertEqualObjects(tlk.zoneID, otherKey.zoneID, "Should have gotten the same zoneID");
+ XCTAssertEqualObjects(tlk.aessivkey, otherKey.aessivkey, "Should have gotten the same underlying key back");
+ XCTAssertEqualObjects(tlk, otherKey, "Should have gotten the same key");
+}
+
+- (BOOL)tryDecryptWithProperAuthData:(CKKSItem*)ciphertext plaintext:(NSDictionary<NSString*, NSData*>*)plaintext {
+ NSDictionary<NSString*, NSData*>* roundtrip;
+ NSError *error = nil;
+ roundtrip = [CKKSItemEncrypter decryptItemToDictionary: (CKKSItem*) ciphertext error: &error];
+ XCTAssertNil(error, "No error decrypting roundtrip");
+ XCTAssertNotNil(roundtrip, "Received a plaintext");
+ XCTAssertEqualObjects(plaintext, roundtrip, "roundtripped dictionary matches input");
+ return error == nil && roundtrip != nil && [plaintext isEqualToDictionary:roundtrip];
+}
+
+- (BOOL)tryDecryptWithBrokenAuthData:(CKKSItem *)ciphertext {
+ NSDictionary<NSString*, NSData*>* brokenAuthentication;
+ NSError *error = nil;
+ brokenAuthentication = [CKKSItemEncrypter decryptItemToDictionary: (CKKSItem*) ciphertext error: &error];
+ XCTAssertNotNil(error, "Error exists decrypting ciphertext with bad authenticated data: %@", error);
+ XCTAssertNil(brokenAuthentication, "Did not receive a plaintext if authenticated data was mucked with");
+ return error != nil && brokenAuthentication == nil;
+}
+
+- (void)testItemDictionaryEncryption {
+ NSDictionary<NSString*, NSData*>* plaintext = @{ @"test": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
+ @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
+ NSError* error = nil;
+ NSString *uuid = @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7";
+
+ CKKSKey* key = [self fakeTLK:self.testZoneID];
+ [key saveToDatabase: &error];
+ [key saveKeyMaterialToKeychain:&error];
+ XCTAssertNil(error, @"could save the fake TLK to the database");
+
+ CKKSItem* ciphertext = [CKKSItemEncrypter encryptCKKSItem: [[CKKSItem alloc] initWithUUID:uuid
+ parentKeyUUID:key.uuid
+ zoneID:self.testZoneID]
+ dataDictionary:plaintext
+ updatingCKKSItem:nil
+ parentkey:key
+ error:&error];
+ XCTAssertNil(error, "No error encrypting plaintext");
+ XCTAssertNotNil(ciphertext, "Received a ciphertext");
+ XCTAssertEqual(ciphertext.encver, currentCKKSItemEncryptionVersion, "Encryption sets the current protocol version");
+
+ [self tryDecryptWithProperAuthData:ciphertext plaintext:plaintext];
+
+ // Make sure these fields are authenticated and that authentication works.
+ // Messing with them should make the item not decrypt.
+ ciphertext.generationCount = 100;
+ XCTAssertTrue([self tryDecryptWithBrokenAuthData:ciphertext], "Decryption with broken authentication data fails");
+ ciphertext.generationCount = 0;
+ XCTAssertTrue([self tryDecryptWithProperAuthData:ciphertext plaintext:plaintext], "Decryption with authentication data succeeds");
+
+ ciphertext.encver += 1;
+ XCTAssertTrue([self tryDecryptWithBrokenAuthData:ciphertext], "Decryption with broken authentication data fails");
+ ciphertext.encver -= 1;
+ XCTAssertTrue([self tryDecryptWithProperAuthData:ciphertext plaintext:plaintext], "Decryption with authentication data succeeds");
+
+ ciphertext.uuid = @"x";
+ XCTAssertTrue([self tryDecryptWithBrokenAuthData:ciphertext], "Decryption with broken authentication data fails");
+ ciphertext.uuid = uuid;
+ XCTAssertTrue([self tryDecryptWithProperAuthData:ciphertext plaintext:plaintext], "Decryption with authentication data succeeds");
+}
+
+- (void)testEncryptionVersions {
+ NSDictionary<NSString*, NSData*>* plaintext = @{ @"test": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
+ @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
+ NSDictionary<NSString*, NSData*>* output;
+ NSError *error = nil;
+ NSData* data = [NSPropertyListSerialization dataWithPropertyList:plaintext
+ format:NSPropertyListBinaryFormat_v1_0
+ options:0
+ error:&error];
+ XCTAssertNil(error);
+ CKKSKey* key = [self fakeTLK:self.testZoneID];
+ [key saveToDatabase: &error];
+ [key saveKeyMaterialToKeychain:&error];
+ XCTAssertNil(error, @"could save the fake TLK to the database");
+
+ CKKSAESSIVKey* keyToWrap = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
+ CKKSWrappedAESSIVKey* wrappedKey = [key wrapAESKey: keyToWrap error:&error];
+ XCTAssertNil(error, "no error wrapping key");
+ XCTAssertNotNil(wrappedKey, "wrapped key was returned");
+ CKKSItem* baseitem = [[CKKSItem alloc] initWithUUID:@"abc"
+ parentKeyUUID:key.uuid
+ zoneID:self.testZoneID
+ encItem:data
+ wrappedkey:wrappedKey
+ generationCount:0
+ encver:CKKSItemEncryptionVersionNone];
+ XCTAssertNotNil(baseitem, "Constructed CKKSItem");
+
+ // First try versionNone. Should fail, we don't support unencrypted data
+ output = [CKKSItemEncrypter decryptItemToDictionary:baseitem error:&error];
+ XCTAssert(error, "Did not failed to decrypt v0 item");
+ XCTAssertNil(output, "Did not failed to decrypt v0 item");
+ error = nil;
+ output = nil;
+
+ // Then try version1. Should take actual decryption path and fail because there's no properly encrypted data.
+ baseitem.encver = CKKSItemEncryptionVersion1;
+ output = [CKKSItemEncrypter decryptItemToDictionary:baseitem error:&error];
+ XCTAssertNotNil(error, "Taking v1 codepath without encrypted item fails");
+ XCTAssertEqualObjects(error.localizedDescription, @"could not ccsiv_crypt", "Error specifically failure to ccsiv_crypt");
+ XCTAssertNil(output, "Did not receive output from failed decryption call");
+ error = nil;
+ output = nil;
+
+ // Finally, some unknown version should fail immediately
+ baseitem.encver = 100;
+ output = [CKKSItemEncrypter decryptItemToDictionary:baseitem error:&error];
+ XCTAssertNotNil(error);
+ NSString *errstr = [NSString stringWithFormat:@"%@", error.localizedDescription];
+ NSString *expected = @"Unrecognized encryption version: 100";
+ XCTAssertEqualObjects(expected, errstr, "Error is specific to unrecognized version failure");
+ XCTAssertNil(output);
+}
+
+- (void)testKeychainPersistence {
+
+ NSString* plaintext = @"plaintext is plain";
+ NSData* plaintextData = [plaintext dataUsingEncoding: NSUTF8StringEncoding];
+
+ NSError* error = nil;
+
+ NSString* uuid = @"f5e7f20f-0885-48f9-b75d-9f0cfd2171b6";
+
+ CKKSKey* key = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="]
+ uuid:uuid
+ keyclass:SecCKKSKeyClassA
+ state:SecCKKSProcessedStateLocal
+ zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
+ encodedCKRecord: nil
+ currentkey: true];
+
+ NSData* ciphertext = [key encryptData: plaintextData authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error encrypting plaintext");
+ XCTAssertNotNil(ciphertext, "Received a ciphertext");
+ NSData* roundtrip = [key decryptData: ciphertext authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error decrypting roundtrip");
+ XCTAssertNotNil(roundtrip, "Received a plaintext");
+ XCTAssertEqualObjects(plaintextData, roundtrip, "roundtripped data matches input");
+
+ // Check that there is no key material in the keychain
+ CKKSKey* reloadedKey = [CKKSKey keyFromKeychain:uuid
+ parentKeyUUID:uuid
+ keyclass:SecCKKSKeyClassA
+ state:SecCKKSProcessedStateLocal
+ zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
+ encodedCKRecord:nil
+ currentkey:true
+ error:&error];
+
+ XCTAssertNotNil(error, "error exists when there's nothing in the keychain");
+ XCTAssertNil(reloadedKey, "no key object when there's nothing in the keychain");
+ error = nil;
+
+ [key saveKeyMaterialToKeychain:&error];
+ XCTAssertNil(error, "Could save key material to keychain");
+
+ // Reload the key material and check that it works
+ reloadedKey = [CKKSKey keyFromKeychain:uuid
+ parentKeyUUID:uuid
+ keyclass:SecCKKSKeyClassA
+ state:SecCKKSProcessedStateLocal
+ zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
+ encodedCKRecord:nil
+ currentkey:true
+ error:&error];
+
+ XCTAssertNil(error, "No error loading key from keychain");
+ XCTAssertNotNil(reloadedKey, "Could load key from keychain");
+
+ NSData* ciphertext2 = [reloadedKey encryptData: plaintextData authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error encrypting plaintext");
+ XCTAssertNotNil(ciphertext2, "Received a ciphertext");
+ NSData* roundtrip2 = [reloadedKey decryptData: ciphertext2 authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error decrypting roundtrip");
+ XCTAssertNotNil(roundtrip2, "Received a plaintext");
+ XCTAssertEqualObjects(plaintextData, roundtrip2, "roundtripped data matches input");
+
+ XCTAssertEqualObjects(key.aessivkey, reloadedKey.aessivkey, "reloaded AES key is equal to generated key");
+
+ [key deleteKeyMaterialFromKeychain: &error];
+ XCTAssertNil(error, "could delete key material from keychain");
+
+ // Check that there is no key material in the keychain
+ // Note that TLKs will be stashed (and deleteKeyMaterial won't delete the stash), and so this test would fail for a TLK
+
+ reloadedKey = [CKKSKey keyFromKeychain:uuid
+ parentKeyUUID:uuid
+ keyclass:SecCKKSKeyClassA
+ state:SecCKKSProcessedStateLocal
+ zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
+ encodedCKRecord:nil
+ currentkey:true
+ error:&error];
+
+ XCTAssertNotNil(error, "error exists when there's nothing in the keychain");
+ XCTAssertNil(reloadedKey, "no key object when there's nothing in the keychain");
+ error = nil;
+}
+
+- (void)testCKKSKeyTrialSelfWrapped {
+ NSError* error = nil;
+ CKKSKey* tlk = [self fakeTLK:self.testZoneID];
+ XCTAssertTrue([tlk wrapsSelf], "TLKs should wrap themselves");
+
+ CKRecord* record = [tlk CKRecordWithZoneID:self.testZoneID];
+ XCTAssertNotNil(record, "TLKs should know how to turn themselves into CKRecords");
+ CKKSKey* receivedTLK = [[CKKSKey alloc] initWithCKRecord:record];
+ XCTAssertNotNil(receivedTLK, "Keys should know how to recover themselves from CKRecords");
+
+ XCTAssertTrue([receivedTLK wrapsSelf], "TLKs should wrap themselves, even when received from CloudKit");
+
+ XCTAssertFalse([receivedTLK ensureKeyLoaded:&error], "Received keys can't load themselves when there's no key data");
+ XCTAssertNotNil(error, "Error should exist when a key fails to load itself");
+ error = nil;
+
+ XCTAssertTrue([receivedTLK trySelfWrappedKeyCandidate:tlk.aessivkey error:&error], "Shouldn't be an error when we give a CKKSKey its key");
+ XCTAssertNil(error, "Shouldn't be an error giving a CKKSKey its key material");
+
+ XCTAssertTrue([receivedTLK ensureKeyLoaded:&error], "Once a CKKSKey has its key material, it doesn't need to load it again");
+ XCTAssertNil(error, "Shouldn't be an error loading a loaded CKKSKey");
+}
+
+- (void)testCKKSKeyTrialSelfWrappedFailure {
+ NSError* error = nil;
+ CKKSKey* tlk = [self fakeTLK:self.testZoneID];
+ XCTAssertTrue([tlk wrapsSelf], "TLKs should wrap themselves");
+
+ CKRecord* record = [tlk CKRecordWithZoneID:self.testZoneID];
+ XCTAssertNotNil(record, "TLKs should know how to turn themselves into CKRecords");
+ CKKSKey* receivedTLK = [[CKKSKey alloc] initWithCKRecord:record];
+ XCTAssertNotNil(receivedTLK, "Keys should know how to recover themselves from CKRecords");
+
+ XCTAssertTrue([receivedTLK wrapsSelf], "TLKs should wrap themselves, even when received from CloudKit");
+
+ XCTAssertFalse([receivedTLK ensureKeyLoaded:&error], "Received keys can't load themselves when there's no key data");
+ XCTAssertNotNil(error, "Error should exist when a key fails to load itself");
+ error = nil;
+
+ XCTAssertFalse([receivedTLK trySelfWrappedKeyCandidate:[[CKKSAESSIVKey alloc] initWithBase64: @"aaaaaZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="] error:&error], "Should be an error when we give a CKKSKey the wrong key");
+ XCTAssertNotNil(error, "Should be an error giving a CKKSKey the wrong key material");
+
+ XCTAssertFalse([receivedTLK ensureKeyLoaded:&error], "Received keys can't load themselves when there's no key data");
+ XCTAssertNotNil(error, "Error should exist when a key fails to load itself");
+ error = nil;
+}
+
+- (void)testCKKSKeyTrialNotSelfWrappedFailure {
+ NSError* error = nil;
+ CKKSKey* tlk = [self fakeTLK:self.testZoneID];
+ XCTAssertTrue([tlk wrapsSelf], "TLKs should wrap themselves");
+
+ CKKSKey* classC = [CKKSKey randomKeyWrappedByParent: tlk keyclass:SecCKKSKeyClassC error:&error];
+ XCTAssertFalse([classC wrapsSelf], "Wrapped keys should not wrap themselves");
+
+ XCTAssertTrue([classC ensureKeyLoaded:&error], "Once a CKKSKey has its key material, it doesn't need to load it again");
+ XCTAssertNil(error, "Shouldn't be an error loading a loaded CKKSKey");
+
+ XCTAssertFalse([classC trySelfWrappedKeyCandidate:classC.aessivkey error:&error], "Should be an error when we attempt to trial a key on a non-self-wrapped key");
+ XCTAssertNotNil(error, "Should be an error giving a CKKSKey the wrong key material");
+ XCTAssertEqual(error.code, CKKSKeyNotSelfWrapped, "Should have gotten CKKSKeyNotSelfWrapped as an error");
+ error = nil;
+
+ // But, since we didn't throw away its key, it's still loaded
+ XCTAssertTrue([classC ensureKeyLoaded:&error], "Once a CKKSKey has its key material, it doesn't need to load it again");
+ XCTAssertNil(error, "Shouldn't be an error loading a loaded CKKSKey");
+}
+
+- (BOOL)padAndUnpadDataWithLength:(NSUInteger)dataLength blockSize:(NSUInteger)blockSize extra:(BOOL)extra {
+ // Test it works
+ NSMutableData *data = [NSMutableData dataWithLength:dataLength];
+ memset((unsigned char *)[data mutableBytes], 0x55, dataLength);
+ NSMutableData *orig = [data mutableCopy];
+ NSData *padded = [CKKSItemEncrypter padData:data blockSize:blockSize additionalBlock:extra];
+ XCTAssertNotNil(padded, "Padding never returns nil");
+ XCTAssertEqualObjects(data, orig, "Input object unmodified");
+ XCTAssertTrue(padded.length % blockSize == 0, "Padded data aligns on %lu-byte blocksize", (unsigned long)blockSize);
+ XCTAssertTrue(padded.length > data.length, "At least one byte of padding has been added");
+ NSData *unpadded = [CKKSItemEncrypter removePaddingFromData:padded];
+ XCTAssertNotNil(unpadded, "Successfully removed padding again");
+
+ // Test it fails by poking some byte in the padding
+ NSMutableData *glitch = [NSMutableData dataWithData:padded];
+ NSUInteger offsetFromTop = glitch.length - arc4random_uniform((unsigned)(glitch.length - data.length)) - 1;
+ uint8_t poke = ((uint8_t)arc4random_uniform(0xFF) & 0x7E) + 1; // This gets most of the values while excluding 0 and 0x80
+ unsigned char *bytes = [glitch mutableBytes];
+ bytes[offsetFromTop] = poke;
+ XCTAssertNil([CKKSItemEncrypter removePaddingFromData:glitch], "Cannot remove broken padding (len %lu, dlen %lu, plen %lu glitchidx %lu, glitchval 0x%x)", (unsigned long)glitch.length, (unsigned long)data.length, (unsigned long)glitch.length - data.length, (unsigned long)offsetFromTop, poke);
+
+ return padded && unpadded && [unpadded isEqual:data];
+}
+
+- (void)testPadding {
+ [self runPaddingTest:NO];
+ [self runPaddingTest:YES];
+
+ NSData *data = nil;
+ XCTAssertNil([CKKSItemEncrypter removePaddingFromData:[NSData data]], "zero data valid ?");
+
+ data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x80" length:1]];
+ XCTAssert(data && data.length == 0, "data wrong size");
+
+ data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x80\x00" length:2]];
+ XCTAssert(data && data.length == 0, "data wrong size");
+ data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x80\x00\x00" length:3]];
+ XCTAssert(data && data.length == 0, "data wrong size");
+ data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x80\x80\x80" length:3]];
+ XCTAssert(data && data.length == 2, "data wrong size");
+ data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x80\x80\x00" length:3]];
+ XCTAssert(data && data.length == 1, "data wrong size");
+ data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x00\x80\x00" length:3]];
+ XCTAssert(data && data.length == 1, "data wrong size");
+
+}
+
+- (void)runPaddingTest:(BOOL)extra {
+
+ // Aligned, arbitrary lengths
+ for (int idx = 1; idx <= 128; ++idx) {
+ XCTAssertTrue([self padAndUnpadDataWithLength:idx blockSize:idx extra:extra], "Padding aligned data succeeds");
+ }
+
+ // Off-by-one, arbitrary lengths
+ for (int idx = 1; idx <= 128; ++idx) {
+ XCTAssertTrue([self padAndUnpadDataWithLength:idx - 1 blockSize:idx extra:extra], "Padding aligned data succeeds");
+ XCTAssertTrue([self padAndUnpadDataWithLength:idx + 1 blockSize:idx extra:extra], "Padding aligned data succeeds");
+ }
+
+ // Misaligned, arbitrary lengths
+ for (int idx = 1; idx <= 1000; ++idx) {
+ NSUInteger dataSize = arc4random_uniform(128) + 1;
+ NSUInteger blockSize = arc4random_uniform(128) + 1;
+ XCTAssertTrue([self padAndUnpadDataWithLength:dataSize blockSize:blockSize extra:extra], "Padding data lenght %lu to blockSize %lu succeeds", (unsigned long)dataSize, (unsigned long)blockSize);
+ }
+
+ // Special case: blocksize 0 results in 1 byte of padding always
+ NSMutableData *data = [NSMutableData dataWithLength:23];
+ memset((unsigned char *)[data mutableBytes], 0x55, 23);
+ NSData *padded = [CKKSItemEncrypter padData:data blockSize:0 additionalBlock:extra];
+ XCTAssertNotNil(padded, "Padding never returns nil");
+ XCTAssertTrue(padded.length == data.length + extra ? 2 : 1, "One byte of padding has been added, 2 if extra padding");
+ NSData *unpadded = [CKKSItemEncrypter removePaddingFromData:padded];
+ XCTAssertNotNil(unpadded, "Successfully removed padding again");
+ XCTAssertEqualObjects(data, unpadded, "Data effectively unmodified through padding-unpadding trip");
+
+ // Nonpadded data
+ unpadded = [CKKSItemEncrypter removePaddingFromData:data];
+ XCTAssertNil(unpadded, "Cannot remove padding where none exists");
+
+ // Feeding nil
+ padded = [CKKSItemEncrypter padData:nil blockSize:0 additionalBlock:extra];
+ XCTAssertNotNil(padded, "padData always returns a data object");
+ XCTAssertEqual(padded.length, extra ? 2ul : 1ul, "Length of padded nil object is padding byte only--two if extra");
+ unpadded = [CKKSItemEncrypter removePaddingFromData:nil];
+ XCTAssertNil(unpadded, "Removing padding from nil is senseless");
+}
+
+- (BOOL)encryptAndDecryptDictionary:(NSDictionary<NSString*, NSData*>*)data key:(CKKSKey *)key {
+ NSDictionary<NSString*, NSData*>* roundtrip;
+ NSError *error = nil;
+ NSData* ciphertext = [CKKSItemEncrypter encryptDictionary: data key: key.aessivkey authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error encrypting plaintext");
+ XCTAssertNotNil(ciphertext, "Received a ciphertext");
+ // AES-SIV adds 32 bytes, need to subtract them
+ XCTAssertTrue((ciphertext.length - 32) % SecCKKSItemPaddingBlockSize == 0, "Ciphertext aligned on %lu-byte boundary", (unsigned long)SecCKKSItemPaddingBlockSize);
+ roundtrip = [CKKSItemEncrypter decryptDictionary: ciphertext key: key.aessivkey authenticatedData: nil error: &error];
+ XCTAssertNil(error, "No error decrypting roundtrip");
+ XCTAssertNotNil(roundtrip, "Received a plaintext");
+ XCTAssertEqualObjects(data, roundtrip, "roundtripped dictionary matches input");
+ return (ciphertext.length - 32) % SecCKKSItemPaddingBlockSize == 0 && roundtrip && error == nil && [data isEqualToDictionary:roundtrip];
+}
+
+- (void)testDictionaryPadding {
+ // Pad a bunch of bytes to nearest boundary
+ NSDictionary<NSString*, NSData*>* unaligned_74 = @{ @"test": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
+ @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
+ // Pad precisely one byte
+ NSDictionary<NSString*, NSData*>* unaligned_79 = @{ @"test12345": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
+ @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
+ // Already on boundary, pad until next boundary
+ NSDictionary<NSString*, NSData*>* aligned_80 = @{ @"test123456": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
+ @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
+ CKKSKey* key = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="]
+ uuid:@"f5e7f20f-0885-48f9-b75d-9f0cfd2171b6"
+ keyclass:SecCKKSKeyClassC
+ state:SecCKKSProcessedStateLocal
+ zoneID:nil
+ encodedCKRecord:nil
+ currentkey:true];
+
+ XCTAssertTrue([self encryptAndDecryptDictionary:unaligned_74 key:key], "Roundtrip with unaligned data succeeds");
+ XCTAssertTrue([self encryptAndDecryptDictionary:unaligned_79 key:key], "Roundtrip with unaligned data succeeds");
+ XCTAssertTrue([self encryptAndDecryptDictionary:aligned_80 key:key], "Roundtrip with aligned data succeeds");
+}
+
+@end
+
+#endif // OCTAGON
CKKSViewManager* manager = [[CKKSViewManager alloc] initWithContainerName:containerName
usePCS:SecCKKSContainerUsePCS
fetchRecordZoneChangesOperationClass:[CKFetchRecordZoneChangesOperation class]
+ fetchRecordsOperationClass:[CKFetchRecordsOperation class]
+ queryOperationClass:[CKQueryOperation class]
modifySubscriptionsOperationClass:[CKModifySubscriptionsOperation class]
modifyRecordZonesOperationClass:[CKModifyRecordZonesOperation class]
apsConnectionClass:[APSConnection class]
+++ /dev/null
-/*
- * Copyright (c) 2016 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#if OCTAGON
-
-#import <XCTest/XCTest.h>
-#import "CloudKitMockXCTest.h"
-
-#import "keychain/ckks/CKKS.h"
-#import "keychain/ckks/CKKSKey.h"
-#import "keychain/ckks/CKKSItem.h"
-#import "keychain/ckks/CKKSOutgoingQueueEntry.h"
-#import "keychain/ckks/CKKSIncomingQueueEntry.h"
-#import "keychain/ckks/CKKSItemEncrypter.h"
-
-#include <securityd/SecItemServer.h>
-#include <Security/SecItemPriv.h>
-
-@interface CloudKitKeychainEncryptionTests : CloudKitMockXCTest
-@end
-
-@implementation CloudKitKeychainEncryptionTests
-
-+ (void)setUp {
- // We don't really want to spin up the whole machinery for the encryption tests
- SecCKKSDisable();
-
- [super setUp];
-}
-
-- (void)setUp {
- [super setUp];
-}
-
-- (void)tearDown {
- [super tearDown];
-}
-
-+ (void)tearDown {
- [super tearDown];
- SecCKKSResetSyncing();
-}
-
-- (void)testKeyGeneration {
- CKKSAESSIVKey* key1 = [CKKSAESSIVKey randomKey];
- CKKSAESSIVKey* key2 = [CKKSAESSIVKey randomKey];
- CKKSAESSIVKey* fixedkey1 = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
- XCTAssertNotNil(fixedkey1, "fixedkey1 generated from base64");
- CKKSAESSIVKey* fixedkey2 = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
- XCTAssertNotNil(fixedkey2, "fixedkey2 generated from base64");
-
- XCTAssertEqualObjects(fixedkey1, fixedkey2, "matching fixed keys match");
- XCTAssertNotEqualObjects(fixedkey1, key1, "fixed key and random key do not match");
- XCTAssertNotEqualObjects(key1, key2, "two random keys do not match");
-
- XCTAssertNil([[CKKSAESSIVKey alloc] initWithBase64: @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA------AAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="], "Invalid base64 does not generate a key");
-}
-
-- (void)testBasicEncryption {
- NSString* plaintext = @"plaintext is plain";
- NSData* plaintextData = [plaintext dataUsingEncoding: NSUTF8StringEncoding];
-
- NSError* error = nil;
-
- CKKSKey* key = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="]
- uuid:@"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"
- keyclass:SecCKKSKeyClassC
- state: SecCKKSProcessedStateLocal
- zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
- encodedCKRecord: nil
- currentkey: true];
-
- NSData* ciphertext = [key encryptData: plaintextData authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error encrypting plaintext");
- XCTAssertNotNil(ciphertext, "Received a ciphertext");
- NSData* roundtrip = [key decryptData: ciphertext authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error decrypting roundtrip");
- XCTAssertNotNil(roundtrip, "Received a plaintext");
- XCTAssertEqualObjects(plaintextData, roundtrip, "roundtripped data matches input");
-
- NSData* shortDecrypt = [key decryptData: [@"asdf" dataUsingEncoding:NSUTF8StringEncoding] authenticatedData:nil error:&error];
- XCTAssertNotNil(error, "Decrypting a short plaintext returned an error");
- XCTAssertNil(shortDecrypt, "Decrypting a short plaintext returned nil");
- error = nil;
-
- // Check that we're adding enough entropy
- NSData* ciphertextAgain = [key encryptData: plaintextData authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error encrypting plaintext");
- XCTAssertNotNil(ciphertextAgain, "Received a ciphertext");
- NSData* roundtripAgain = [key decryptData: ciphertextAgain authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error decrypting roundtrip");
- XCTAssertNotNil(roundtripAgain, "Received a plaintext");
- XCTAssertEqualObjects(plaintextData, roundtripAgain, "roundtripped data matches input");
-
- XCTAssertNotEqualObjects(ciphertext, ciphertextAgain, "two encryptions of same input produce different outputs");
-
- // Do it all again
- CKKSKey* key2 = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="]
- uuid:@"f5e7f20f-0885-48f9-b75d-9f0cfd2171b6"
- keyclass:SecCKKSKeyClassC
- state: SecCKKSProcessedStateLocal
- zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
- encodedCKRecord: nil
- currentkey: true];
-
- NSData* ciphertext2 = [key2 encryptData: plaintextData authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error encrypting plaintext");
- XCTAssertNotNil(ciphertext2, "Received a ciphertext");
- NSData* roundtrip2 = [key decryptData: ciphertext authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error decrypting roundtrip");
- XCTAssertNotNil(roundtrip2, "Received a plaintext");
- XCTAssertEqualObjects(plaintextData, roundtrip2, "roundtripped data matches input");
-
- XCTAssertNotEqualObjects(ciphertext, ciphertext2, "ciphertexts with distinct keys are distinct");
-}
-
-- (void)testAuthEncryption {
- NSString* plaintext = @"plaintext is plain";
- NSData* plaintextData = [plaintext dataUsingEncoding: NSUTF8StringEncoding];
-
- NSError* error = nil;
-
- CKKSKey* key = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="]
- uuid:@"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"
- keyclass:SecCKKSKeyClassC
- state:SecCKKSProcessedStateLocal
- zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
- encodedCKRecord:nil
- currentkey:true];
- NSDictionary<NSString*, NSData*>* ad = @{ @"test": [@"data" dataUsingEncoding: NSUTF8StringEncoding] };
-
- NSData* ciphertext = [key encryptData: plaintextData authenticatedData: ad error: &error];
- XCTAssertNil(error, "No error encrypting plaintext");
- XCTAssertNotNil(ciphertext, "Received a ciphertext");
- NSData* roundtrip = [key decryptData: ciphertext authenticatedData: ad error: &error];
- XCTAssertNil(error, "No error decrypting roundtrip");
- XCTAssertNotNil(roundtrip, "Received a plaintext");
- XCTAssertEqualObjects(plaintextData, roundtrip, "roundtripped data matches input");
-
- // Without AD, decryption should fail
- roundtrip = [key decryptData: ciphertext authenticatedData: nil error: &error];
- XCTAssertNotNil(error, "Not passing in the authenticated data causes break");
- XCTAssertNil(roundtrip, "on error, don't receive plaintext");
- error = nil;
-
- roundtrip = [key decryptData: ciphertext authenticatedData: @{ @"test": [@"wrongdata" dataUsingEncoding: NSUTF8StringEncoding] } error: &error];
- XCTAssertNotNil(error, "Wrong authenticated data causes break");
- XCTAssertNil(roundtrip, "on error, don't receive plaintext");
- error = nil;
-}
-
-- (void)testDictionaryEncryption {
- NSDictionary<NSString*, NSData*>* plaintext = @{ @"test": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
- @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
- NSDictionary<NSString*, NSData*>* roundtrip;
-
- NSError* error = nil;
-
- CKKSKey* key = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="]
- uuid:@"f5e7f20f-0885-48f9-b75d-9f0cfd2171b6"
- keyclass:SecCKKSKeyClassC
- state: SecCKKSProcessedStateLocal
- zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
- encodedCKRecord: nil
- currentkey: true];
-
- NSData* ciphertext = [CKKSItemEncrypter encryptDictionary: plaintext key: key.aessivkey authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error encrypting plaintext");
- XCTAssertNotNil(ciphertext, "Received a ciphertext");
- roundtrip = [CKKSItemEncrypter decryptDictionary: ciphertext key: key.aessivkey authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error decrypting roundtrip");
- XCTAssertNotNil(roundtrip, "Received a plaintext");
- XCTAssertEqualObjects(plaintext, roundtrip, "roundtripped dictionary matches input");
-
- NSDictionary* authenticatedData = @{@"data": [@"auth" dataUsingEncoding: NSUTF8StringEncoding], @"moredata": [@"unauth" dataUsingEncoding: NSUTF8StringEncoding]};
- NSDictionary* unauthenticatedData = @{@"data": [@"notequal" dataUsingEncoding: NSUTF8StringEncoding], @"moredata": [@"unauth" dataUsingEncoding: NSUTF8StringEncoding]};
-
- NSData* authciphertext = [CKKSItemEncrypter encryptDictionary: plaintext key: key.aessivkey authenticatedData: authenticatedData error: &error];
- XCTAssertNil(error, "No error encrypting plaintext with authenticated data");
- XCTAssertNotNil(authciphertext, "Received a ciphertext");
- roundtrip = [CKKSItemEncrypter decryptDictionary: authciphertext key: key.aessivkey authenticatedData: authenticatedData error: &error];
- XCTAssertNil(error, "No error decrypting roundtrip with authenticated data");
- XCTAssertNotNil(roundtrip, "Received a plaintext");
- XCTAssertEqualObjects(plaintext, roundtrip, "roundtripped dictionary matches input");
-
- roundtrip = [CKKSItemEncrypter decryptDictionary: authciphertext key: key.aessivkey authenticatedData: unauthenticatedData error: &error];
- XCTAssertNotNil(error, "Error decrypting roundtrip with bad authenticated data");
- XCTAssertNil(roundtrip, "Did not receive a plaintext when authenticated data is wrong");
-}
-
-- (void)testKeyWrapping {
- CKKSAESSIVKey* key = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
-
- CKKSAESSIVKey* keyToWrap = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
-
- NSError* error = nil;
-
- CKKSWrappedAESSIVKey* wrappedKey = [key wrapAESKey: keyToWrap error:&error];
- XCTAssertNil(error, "no error wrapping key");
- XCTAssertNotNil(wrappedKey, "wrapped key was returned");
-
- XCTAssert(0 != memcmp(keyToWrap->key, (wrappedKey->key)+(CKKSWrappedKeySize - CKKSKeySize), CKKSKeySize), "wrapped key is different from original key");
-
- CKKSAESSIVKey* unwrappedKey = [key unwrapAESKey: wrappedKey error:&error];
- XCTAssertNil(error, "no error unwrapping key");
- XCTAssertNotNil(unwrappedKey, "unwrapped key was returned");
-
- XCTAssert(0 == memcmp(keyToWrap->key, unwrappedKey->key, CKKSKeySize), "unwrapped key matches original key");
- XCTAssertEqualObjects(keyToWrap, unwrappedKey, "unwrapped key matches original key");
-}
-
-- (void)testKeyWrappingFailure {
- CKKSAESSIVKey* key = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
-
- CKKSAESSIVKey* keyToWrap = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
-
- NSError* error = nil;
-
- CKKSWrappedAESSIVKey* wrappedKey = [key wrapAESKey: keyToWrap error:&error];
- XCTAssertNil(error, "no error wrapping key");
- XCTAssertNotNil(wrappedKey, "wrapped key was returned");
-
- XCTAssert(0 != memcmp(keyToWrap->key, (wrappedKey->key)+(CKKSWrappedKeySize - CKKSKeySize), CKKSKeySize), "wrapped key is different from original key");
- wrappedKey->key[0] ^= 0x1;
-
- CKKSAESSIVKey* unwrappedKey = [key unwrapAESKey: wrappedKey error:&error];
- XCTAssertNotNil(error, "error unwrapping key");
- XCTAssertNil(unwrappedKey, "unwrapped key was not returned in error case");
-}
-
-- (void)testKeyHierarchy {
- NSError* error = nil;
- NSData* testCKRecord = [@"nonsense" dataUsingEncoding:NSUTF8StringEncoding];
- CKKSKey* tlk = [self fakeTLK:self.testZoneID];
-
- [tlk saveToDatabase:&error];
- [tlk saveKeyMaterialToKeychain:&error];
- XCTAssertNil(error, "tlk saved to database without error");
-
- CKKSKey* level1 = [CKKSKey randomKeyWrappedByParent: tlk keyclass:SecCKKSKeyClassA error:&error];
- level1.encodedCKRecord = testCKRecord;
- XCTAssertNotNil(level1, "level 1 key created");
- XCTAssertNil(error, "level 1 key created");
-
- [level1 saveToDatabase:&error];
- XCTAssertNil(error, "level 1 key saved to database without error");
-
- CKKSKey* level2 = [CKKSKey randomKeyWrappedByParent: level1 error:&error];
- level2.encodedCKRecord = testCKRecord;
- XCTAssertNotNil(level2, "level 2 key created");
- XCTAssertNil(error, "no error creating level 2 key");
- [level2 saveToDatabase:&error];
- XCTAssertNil(error, "level 2 key saved to database without error");
-
- NSString* level2UUID = level2.uuid;
-
- // Fetch the level2 key from the database.
- CKKSKey* extractedkey = [CKKSKey fromDatabase:level2UUID zoneID:self.testZoneID error:&error];
- [extractedkey unwrapViaKeyHierarchy: &error];
- XCTAssertNotNil(extractedkey, "could fetch key again");
- XCTAssertNil(error, "no error fetching key from database");
-
- CKKSAESSIVKey* extracedaeskey = [extractedkey ensureKeyLoaded:&error];
- XCTAssertNotNil(extractedkey, "fetched key could unwrap");
- XCTAssertNil(error, "no error forcing unwrap on fetched key");
-
- XCTAssertEqualObjects(level2.aessivkey, extracedaeskey, @"fetched aes key is equal to saved key");
-}
-
-- (void)ensureKeychainSaveLoad: (CKKSKey*) key {
- NSError* error = nil;
- [key saveToDatabase:&error];
- XCTAssertNil(error, "no error saving to database");
- [key saveKeyMaterialToKeychain:&error];
- XCTAssertNil(error, "no error saving to keychain");
-
- CKKSKey* loadedKey = [CKKSKey fromDatabase:key.uuid zoneID:self.testZoneID error:&error];
- XCTAssertNil(error, "no error loading from database");
- XCTAssertNotNil(loadedKey, "Received an item back from the database");
-
- XCTAssert([loadedKey loadKeyMaterialFromKeychain:&error], "could load key material back from keychain");
- XCTAssertNil(error, "no error loading key from keychain");
-
- XCTAssertEqualObjects(loadedKey.aessivkey, key.aessivkey, "Loaded key is identical after save/load");
-}
-
-- (void)testKeychainSave {
- NSError* error = nil;
- NSData* testCKRecord = [@"nonsense" dataUsingEncoding:NSUTF8StringEncoding];
- CKKSKey* tlk = [self fakeTLK:self.testZoneID];
- [self ensureKeychainSaveLoad: tlk];
-
- // Ensure that Class A and Class C can do the same thing
- CKKSKey* classA = [CKKSKey randomKeyWrappedByParent: tlk keyclass:SecCKKSKeyClassA error:&error];
- classA.encodedCKRecord = testCKRecord;
- XCTAssertNil(error, "No error creating random class A key");
- [self ensureKeychainSaveLoad: classA];
- CKKSKey* classC = [CKKSKey randomKeyWrappedByParent: tlk keyclass:SecCKKSKeyClassC error:&error];
- classC.encodedCKRecord = testCKRecord;
- XCTAssertNil(error, "No error creating random class C key");
- [self ensureKeychainSaveLoad: classC];
-}
-
-- (BOOL)tryDecryptWithProperAuthData:(CKKSItem*)ciphertext plaintext:(NSDictionary<NSString*, NSData*>*)plaintext {
- NSDictionary<NSString*, NSData*>* roundtrip;
- NSError *error = nil;
- roundtrip = [CKKSItemEncrypter decryptItemToDictionary: (CKKSItem*) ciphertext error: &error];
- XCTAssertNil(error, "No error decrypting roundtrip");
- XCTAssertNotNil(roundtrip, "Received a plaintext");
- XCTAssertEqualObjects(plaintext, roundtrip, "roundtripped dictionary matches input");
- return error == nil && roundtrip != nil && [plaintext isEqualToDictionary:roundtrip];
-}
-
-- (BOOL)tryDecryptWithBrokenAuthData:(CKKSItem *)ciphertext {
- NSDictionary<NSString*, NSData*>* brokenAuthentication;
- NSError *error = nil;
- brokenAuthentication = [CKKSItemEncrypter decryptItemToDictionary: (CKKSItem*) ciphertext error: &error];
- XCTAssertNotNil(error, "Error exists decrypting ciphertext with bad authenticated data: %@", error);
- XCTAssertNil(brokenAuthentication, "Did not receive a plaintext if authenticated data was mucked with");
- return error != nil && brokenAuthentication == nil;
-}
-
-- (void)testItemDictionaryEncryption {
- NSDictionary<NSString*, NSData*>* plaintext = @{ @"test": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
- @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
- NSError* error = nil;
- NSString *uuid = @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7";
-
- CKKSKey* key = [self fakeTLK:self.testZoneID];
- [key saveToDatabase: &error];
- [key saveKeyMaterialToKeychain:&error];
- XCTAssertNil(error, @"could save the fake TLK to the database");
-
- CKKSItem* ciphertext = [CKKSItemEncrypter encryptCKKSItem: [[CKKSItem alloc] initWithUUID:uuid
- parentKeyUUID:key.uuid
- zoneID:self.testZoneID]
- dataDictionary:plaintext
- updatingCKKSItem:nil
- parentkey:key
- error:&error];
- XCTAssertNil(error, "No error encrypting plaintext");
- XCTAssertNotNil(ciphertext, "Received a ciphertext");
- XCTAssertEqual(ciphertext.encver, currentCKKSItemEncryptionVersion, "Encryption sets the current protocol version");
-
- [self tryDecryptWithProperAuthData:ciphertext plaintext:plaintext];
-
- // Make sure these fields are authenticated and that authentication works.
- // Messing with them should make the item not decrypt.
- ciphertext.generationCount = 100;
- XCTAssertTrue([self tryDecryptWithBrokenAuthData:ciphertext], "Decryption with broken authentication data fails");
- ciphertext.generationCount = 0;
- XCTAssertTrue([self tryDecryptWithProperAuthData:ciphertext plaintext:plaintext], "Decryption with authentication data succeeds");
-
- ciphertext.encver += 1;
- XCTAssertTrue([self tryDecryptWithBrokenAuthData:ciphertext], "Decryption with broken authentication data fails");
- ciphertext.encver -= 1;
- XCTAssertTrue([self tryDecryptWithProperAuthData:ciphertext plaintext:plaintext], "Decryption with authentication data succeeds");
-
- ciphertext.uuid = @"x";
- XCTAssertTrue([self tryDecryptWithBrokenAuthData:ciphertext], "Decryption with broken authentication data fails");
- ciphertext.uuid = uuid;
- XCTAssertTrue([self tryDecryptWithProperAuthData:ciphertext plaintext:plaintext], "Decryption with authentication data succeeds");
-}
-
-- (void)testEncryptionVersions {
- NSDictionary<NSString*, NSData*>* plaintext = @{ @"test": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
- @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
- NSDictionary<NSString*, NSData*>* output;
- NSError *error = nil;
- NSData* data = [NSPropertyListSerialization dataWithPropertyList:plaintext
- format:NSPropertyListBinaryFormat_v1_0
- options:0
- error:&error];
- XCTAssertNil(error);
- CKKSKey* key = [self fakeTLK:self.testZoneID];
- [key saveToDatabase: &error];
- [key saveKeyMaterialToKeychain:&error];
- XCTAssertNil(error, @"could save the fake TLK to the database");
-
- CKKSAESSIVKey* keyToWrap = [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="];
- CKKSWrappedAESSIVKey* wrappedKey = [key wrapAESKey: keyToWrap error:&error];
- XCTAssertNil(error, "no error wrapping key");
- XCTAssertNotNil(wrappedKey, "wrapped key was returned");
- CKKSItem* baseitem = [[CKKSItem alloc] initWithUUID:@"abc"
- parentKeyUUID:key.uuid
- zoneID:self.testZoneID
- encItem:data
- wrappedkey:wrappedKey
- generationCount:0
- encver:CKKSItemEncryptionVersionNone];
- XCTAssertNotNil(baseitem, "Constructed CKKSItem");
-
- // First try versionNone. Should fail, we don't support unencrypted data
- output = [CKKSItemEncrypter decryptItemToDictionary:baseitem error:&error];
- XCTAssert(error, "Did not failed to decrypt v0 item");
- XCTAssertNil(output, "Did not failed to decrypt v0 item");
- error = nil;
- output = nil;
-
- // Then try version1. Should take actual decryption path and fail because there's no properly encrypted data.
- baseitem.encver = CKKSItemEncryptionVersion1;
- output = [CKKSItemEncrypter decryptItemToDictionary:baseitem error:&error];
- XCTAssertNotNil(error, "Taking v1 codepath without encrypted item fails");
- XCTAssertEqualObjects(error.localizedDescription, @"could not ccsiv_crypt", "Error specifically failure to ccsiv_crypt");
- XCTAssertNil(output, "Did not receive output from failed decryption call");
- error = nil;
- output = nil;
-
- // Finally, some unknown version should fail immediately
- baseitem.encver = 100;
- output = [CKKSItemEncrypter decryptItemToDictionary:baseitem error:&error];
- XCTAssertNotNil(error);
- NSString *errstr = [NSString stringWithFormat:@"%@", error.localizedDescription];
- NSString *expected = @"Unrecognized encryption version: 100";
- XCTAssertEqualObjects(expected, errstr, "Error is specific to unrecognized version failure");
- XCTAssertNil(output);
-}
-
-- (void)testKeychainPersistence {
-
- NSString* plaintext = @"plaintext is plain";
- NSData* plaintextData = [plaintext dataUsingEncoding: NSUTF8StringEncoding];
-
- NSError* error = nil;
-
- NSString* uuid = @"f5e7f20f-0885-48f9-b75d-9f0cfd2171b6";
-
- CKKSKey* key = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="]
- uuid:uuid
- keyclass:SecCKKSKeyClassA
- state:SecCKKSProcessedStateLocal
- zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
- encodedCKRecord: nil
- currentkey: true];
-
- NSData* ciphertext = [key encryptData: plaintextData authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error encrypting plaintext");
- XCTAssertNotNil(ciphertext, "Received a ciphertext");
- NSData* roundtrip = [key decryptData: ciphertext authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error decrypting roundtrip");
- XCTAssertNotNil(roundtrip, "Received a plaintext");
- XCTAssertEqualObjects(plaintextData, roundtrip, "roundtripped data matches input");
-
- // Check that there is no key material in the keychain
- CKKSKey* reloadedKey = [CKKSKey keyFromKeychain:uuid
- parentKeyUUID:uuid
- keyclass:SecCKKSKeyClassA
- state:SecCKKSProcessedStateLocal
- zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
- encodedCKRecord:nil
- currentkey:true
- error:&error];
-
- XCTAssertNotNil(error, "error exists when there's nothing in the keychain");
- XCTAssertNil(reloadedKey, "no key object when there's nothing in the keychain");
- error = nil;
-
- [key saveKeyMaterialToKeychain:&error];
- XCTAssertNil(error, "Could save key material to keychain");
-
- // Reload the key material and check that it works
- reloadedKey = [CKKSKey keyFromKeychain:uuid
- parentKeyUUID:uuid
- keyclass:SecCKKSKeyClassA
- state:SecCKKSProcessedStateLocal
- zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
- encodedCKRecord:nil
- currentkey:true
- error:&error];
-
- XCTAssertNil(error, "No error loading key from keychain");
- XCTAssertNotNil(reloadedKey, "Could load key from keychain");
-
- NSData* ciphertext2 = [reloadedKey encryptData: plaintextData authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error encrypting plaintext");
- XCTAssertNotNil(ciphertext2, "Received a ciphertext");
- NSData* roundtrip2 = [reloadedKey decryptData: ciphertext2 authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error decrypting roundtrip");
- XCTAssertNotNil(roundtrip2, "Received a plaintext");
- XCTAssertEqualObjects(plaintextData, roundtrip2, "roundtripped data matches input");
-
- XCTAssertEqualObjects(key.aessivkey, reloadedKey.aessivkey, "reloaded AES key is equal to generated key");
-
- [key deleteKeyMaterialFromKeychain: &error];
- XCTAssertNil(error, "could delete key material from keychain");
-
- // Check that there is no key material in the keychain
- // Note that TLKs will be stashed (and deleteKeyMaterial won't delete the stash), and so this test would fail for a TLK
-
- reloadedKey = [CKKSKey keyFromKeychain:uuid
- parentKeyUUID:uuid
- keyclass:SecCKKSKeyClassA
- state:SecCKKSProcessedStateLocal
- zoneID:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName]
- encodedCKRecord:nil
- currentkey:true
- error:&error];
-
- XCTAssertNotNil(error, "error exists when there's nothing in the keychain");
- XCTAssertNil(reloadedKey, "no key object when there's nothing in the keychain");
- error = nil;
-}
-
-- (BOOL)padAndUnpadDataWithLength:(NSUInteger)dataLength blockSize:(NSUInteger)blockSize extra:(BOOL)extra {
- // Test it works
- NSMutableData *data = [NSMutableData dataWithLength:dataLength];
- memset((unsigned char *)[data mutableBytes], 0x55, dataLength);
- NSMutableData *orig = [data mutableCopy];
- NSData *padded = [CKKSItemEncrypter padData:data blockSize:blockSize additionalBlock:extra];
- XCTAssertNotNil(padded, "Padding never returns nil");
- XCTAssertEqualObjects(data, orig, "Input object unmodified");
- XCTAssertTrue(padded.length % blockSize == 0, "Padded data aligns on %lu-byte blocksize", (unsigned long)blockSize);
- XCTAssertTrue(padded.length > data.length, "At least one byte of padding has been added");
- NSData *unpadded = [CKKSItemEncrypter removePaddingFromData:padded];
- XCTAssertNotNil(unpadded, "Successfully removed padding again");
-
- // Test it fails by poking some byte in the padding
- NSMutableData *glitch = [NSMutableData dataWithData:padded];
- NSUInteger offsetFromTop = glitch.length - arc4random_uniform((unsigned)(glitch.length - data.length)) - 1;
- uint8_t poke = ((uint8_t)arc4random_uniform(0xFF) & 0x7E) + 1; // This gets most of the values while excluding 0 and 0x80
- unsigned char *bytes = [glitch mutableBytes];
- bytes[offsetFromTop] = poke;
- XCTAssertNil([CKKSItemEncrypter removePaddingFromData:glitch], "Cannot remove broken padding (len %lu, dlen %lu, plen %lu glitchidx %lu, glitchval 0x%x)", (unsigned long)glitch.length, (unsigned long)data.length, (unsigned long)glitch.length - data.length, (unsigned long)offsetFromTop, poke);
-
- return padded && unpadded && [unpadded isEqual:data];
-}
-
-- (void)testPadding {
- [self runPaddingTest:NO];
- [self runPaddingTest:YES];
-
- NSData *data = nil;
- XCTAssertNil([CKKSItemEncrypter removePaddingFromData:[NSData data]], "zero data valid ?");
-
- data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x80" length:1]];
- XCTAssert(data && data.length == 0, "data wrong size");
-
- data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x80\x00" length:2]];
- XCTAssert(data && data.length == 0, "data wrong size");
- data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x80\x00\x00" length:3]];
- XCTAssert(data && data.length == 0, "data wrong size");
- data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x80\x80\x80" length:3]];
- XCTAssert(data && data.length == 2, "data wrong size");
- data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x80\x80\x00" length:3]];
- XCTAssert(data && data.length == 1, "data wrong size");
- data = [CKKSItemEncrypter removePaddingFromData:[NSData dataWithBytes:"\x00\x80\x00" length:3]];
- XCTAssert(data && data.length == 1, "data wrong size");
-
-}
-
-- (void)runPaddingTest:(BOOL)extra {
-
- // Aligned, arbitrary lengths
- for (int idx = 1; idx <= 128; ++idx) {
- XCTAssertTrue([self padAndUnpadDataWithLength:idx blockSize:idx extra:extra], "Padding aligned data succeeds");
- }
-
- // Off-by-one, arbitrary lengths
- for (int idx = 1; idx <= 128; ++idx) {
- XCTAssertTrue([self padAndUnpadDataWithLength:idx - 1 blockSize:idx extra:extra], "Padding aligned data succeeds");
- XCTAssertTrue([self padAndUnpadDataWithLength:idx + 1 blockSize:idx extra:extra], "Padding aligned data succeeds");
- }
-
- // Misaligned, arbitrary lengths
- for (int idx = 1; idx <= 1000; ++idx) {
- NSUInteger dataSize = arc4random_uniform(128) + 1;
- NSUInteger blockSize = arc4random_uniform(128) + 1;
- XCTAssertTrue([self padAndUnpadDataWithLength:dataSize blockSize:blockSize extra:extra], "Padding data lenght %lu to blockSize %lu succeeds", (unsigned long)dataSize, (unsigned long)blockSize);
- }
-
- // Special case: blocksize 0 results in 1 byte of padding always
- NSMutableData *data = [NSMutableData dataWithLength:23];
- memset((unsigned char *)[data mutableBytes], 0x55, 23);
- NSData *padded = [CKKSItemEncrypter padData:data blockSize:0 additionalBlock:extra];
- XCTAssertNotNil(padded, "Padding never returns nil");
- XCTAssertTrue(padded.length == data.length + extra ? 2 : 1, "One byte of padding has been added, 2 if extra padding");
- NSData *unpadded = [CKKSItemEncrypter removePaddingFromData:padded];
- XCTAssertNotNil(unpadded, "Successfully removed padding again");
- XCTAssertEqualObjects(data, unpadded, "Data effectively unmodified through padding-unpadding trip");
-
- // Nonpadded data
- unpadded = [CKKSItemEncrypter removePaddingFromData:data];
- XCTAssertNil(unpadded, "Cannot remove padding where none exists");
-
- // Feeding nil
- padded = [CKKSItemEncrypter padData:nil blockSize:0 additionalBlock:extra];
- XCTAssertNotNil(padded, "padData always returns a data object");
- XCTAssertEqual(padded.length, extra ? 2ul : 1ul, "Length of padded nil object is padding byte only--two if extra");
- unpadded = [CKKSItemEncrypter removePaddingFromData:nil];
- XCTAssertNil(unpadded, "Removing padding from nil is senseless");
-}
-
-- (BOOL)encryptAndDecryptDictionary:(NSDictionary<NSString*, NSData*>*)data key:(CKKSKey *)key {
- NSDictionary<NSString*, NSData*>* roundtrip;
- NSError *error = nil;
- NSData* ciphertext = [CKKSItemEncrypter encryptDictionary: data key: key.aessivkey authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error encrypting plaintext");
- XCTAssertNotNil(ciphertext, "Received a ciphertext");
- // AES-SIV adds 32 bytes, need to subtract them
- XCTAssertTrue((ciphertext.length - 32) % SecCKKSItemPaddingBlockSize == 0, "Ciphertext aligned on %lu-byte boundary", (unsigned long)SecCKKSItemPaddingBlockSize);
- roundtrip = [CKKSItemEncrypter decryptDictionary: ciphertext key: key.aessivkey authenticatedData: nil error: &error];
- XCTAssertNil(error, "No error decrypting roundtrip");
- XCTAssertNotNil(roundtrip, "Received a plaintext");
- XCTAssertEqualObjects(data, roundtrip, "roundtripped dictionary matches input");
- return (ciphertext.length - 32) % SecCKKSItemPaddingBlockSize == 0 && roundtrip && error == nil && [data isEqualToDictionary:roundtrip];
-}
-
-- (void)testDictionaryPadding {
- // Pad a bunch of bytes to nearest boundary
- NSDictionary<NSString*, NSData*>* unaligned_74 = @{ @"test": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
- @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
- // Pad precisely one byte
- NSDictionary<NSString*, NSData*>* unaligned_79 = @{ @"test12345": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
- @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
- // Already on boundary, pad until next boundary
- NSDictionary<NSString*, NSData*>* aligned_80 = @{ @"test123456": [@"data" dataUsingEncoding: NSUTF8StringEncoding],
- @"more": [@"testdata" dataUsingEncoding: NSUTF8StringEncoding] };
- CKKSKey* key = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"uImdbZ7Zg+6WJXScTnRBfNmoU1UiMkSYxWc+d1Vuq3IFn2RmTRkTdWTe3HmeWo1pAomqy+upK8KHg2PGiRGhqg=="]
- uuid:@"f5e7f20f-0885-48f9-b75d-9f0cfd2171b6"
- keyclass:SecCKKSKeyClassC
- state:SecCKKSProcessedStateLocal
- zoneID:nil
- encodedCKRecord:nil
- currentkey:true];
-
- XCTAssertTrue([self encryptAndDecryptDictionary:unaligned_74 key:key], "Roundtrip with unaligned data succeeds");
- XCTAssertTrue([self encryptAndDecryptDictionary:unaligned_79 key:key], "Roundtrip with unaligned data succeeds");
- XCTAssertTrue([self encryptAndDecryptDictionary:aligned_80 key:key], "Roundtrip with aligned data succeeds");
-}
-
-@end
-
-#endif // OCTAGON
{
NSString* schema = @"CREATE table test (test_column INTEGER);";
SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
- XCTAssertTrue([sqlTable openWithError:nil]);
+ NSError* error = nil;
+ XCTAssertTrue([sqlTable openWithError:&error], @"failed to open sql database");
+ XCTAssertNil(error, "encountered error opening database: %@", error);
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:tablePath]);
[sqlTable close];
}
{
NSString* schema = @"CREATE table test (test_column INTEGER);";
SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
- XCTAssertTrue([sqlTable openWithError:nil]);
+ NSError* error = nil;
+ XCTAssertTrue([sqlTable openWithError:&error], @"failed to open sql database");
+ XCTAssertNil(error, "encountered error opening database: %@", error);
[sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(1)}];
[sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(2)}];
XCTAssertTrue([[sqlTable selectAllFrom:@"test" where:nil bindings:nil] count] == 0);
}
+- (void)testDontCrashWhenThereAreNoWritePermissions
+{
+ NSString* schema = @"CREATE table test (test_column INTEGER);";
+ SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
+
+ NSError* error = nil;
+ XCTAssertNoThrow([sqlTable openWithError:&error], @"opening database threw an exception");
+ XCTAssertNil(error, "encountered error opening database: %@", error);
+ XCTAssertNoThrow([sqlTable close], @"closing database threw an exception");
+
+ NSDictionary* originalAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:tablePath error:&error];
+ XCTAssertNil(error, @"encountered error getting database file attributes: %@", error);
+
+ [[NSFileManager defaultManager] setAttributes:@{NSFilePosixPermissions : @(400), NSFileImmutable : @(YES)} ofItemAtPath:tablePath error:&error];
+ XCTAssertNil(error, @"encountered error setting database file attributes: %@", error);
+ XCTAssertNoThrow([sqlTable openWithError:&error]);
+ XCTAssertNotNil(error, @"failed to generate error when opening file without permissions");
+ error = nil;
+
+ [[NSFileManager defaultManager] setAttributes:originalAttributes ofItemAtPath:tablePath error:&error];
+ XCTAssertNil(error, @"encountered error setting database file attributes back to original attributes: %@", error);
+}
+
+- (void)testDontCrashFromInternalErrors
+{
+ NSString* schema = @"CREATE table test (test_column INTEGER);";
+ SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
+
+ NSError* error = nil;
+ XCTAssertTrue([sqlTable openWithError:&error], @"failed to open database");
+ XCTAssertNil(error, "encountered error opening database: %@", error);
+
+ // delete the database to create havoc
+ [[NSFileManager defaultManager] removeItemAtPath:tablePath error:nil];
+
+ XCTAssertNoThrow([sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(1)}], @"inserting into deleted table threw an exception");
+}
+
@end
@interface CKKSAnalyticsLoggerTests : CloudKitKeychainSyncingTestsBase
OCMVerifyAllWithDelay(self.mockDatabase, 8);
NSError* error = nil;
- NSData* json = [[CKKSAnalyticsLogger logger] getLoggingJSONWithError:&error];
+ NSData* json = [[CKKSAnalyticsLogger logger] getLoggingJSON:false error:&error];
XCTAssertNotNil(json, @"failed to generate logging json");
XCTAssertNil(error, @"encourntered error getting logging json: %@", error);
XCTAssertNotNil(dictionary, @"failed to generate dictionary from json data");
XCTAssertNil(error, @"encountered error deserializing json: %@", error);
XCTAssertTrue([dictionary isKindOfClass:[NSDictionary class]], @"did not get the class we expected from json deserialization");
+
+ XCTAssertNotNil(dictionary[@"postTime"], @"Failed to get posttime");
+
+ NSArray *events = dictionary[@"events"];
+ XCTAssertNotNil(events, @"Failed to get events");
+ XCTAssert([events isKindOfClass:[NSArray class]], @"did not get the class we expected for events");
+
+
+ for (NSDictionary *event in events) {
+ XCTAssert([event isKindOfClass:[NSDictionary class]], @"did not get the class we expected for events");
+ XCTAssertNotNil(event[@"build"], @"Failed to get build in event");
+ XCTAssertNotNil(event[@"product"], @"Failed to get product in event");
+ XCTAssertNotNil(event[@"topic"], @"Failed to get topic in event");
+
+ NSString *eventtype = event[@"eventType"];
+ XCTAssertNotNil(eventtype, @"Failed to get eventType in eventtype");
+ XCTAssert([eventtype isKindOfClass:[NSString class]], @"did not get the class we expected for events");
+ if ([eventtype isEqualToString:@"ckksHealthSummary"]) {
+ XCTAssertNotNil(event[@"ckdeviceID"], @"Failed to get deviceID in event");
+ }
+ }
}
- (void)testSplunkDefaultTopicNameExists
[[[self.keychainView waitForFetchAndIncomingQueueProcessing] completionHandlerDidRunCondition] wait:4 * NSEC_PER_SEC];
NSDictionary* extraValues = [[CKKSAnalyticsLogger logger] extraValuesToUploadToServer];
+ XCTAssertTrue([extraValues[@"inCircle"] boolValue]);
XCTAssertTrue([extraValues[@"keychain-TLKs"] boolValue]);
XCTAssertTrue([extraValues[@"keychain-inSyncA"] boolValue]);
XCTAssertTrue([extraValues[@"keychain-inSyncC"] boolValue]);
NSData* json = nil;
NSError* error = nil;
- XCTAssertNoThrow(json = [logger getLoggingJSONWithError:&error]);
+ XCTAssertNoThrow(json = [logger getLoggingJSON:false error:&error]);
XCTAssertNotNil(json, @"Failed to get JSON after logging nil event");
XCTAssertNil(error, @"Got error when grabbing JSON after logging nil event: %@", error);
}
[super setUp];
+ // Should be removed when manifests is turned back on
+ SFECKeyPair* keyPair = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
+ [CKKSManifestInjectionPointHelper registerEgoPeerID:@"MeMyselfAndI" keyPair:keyPair];
+
// Always sync manifests, and never enforce them
SecCKKSSetSyncManifests(true);
SecCKKSSetEnforceManifests(false);
// Test starts with keys in CloudKit (so we can create items later)
[self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
[self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ // If TLK sharing is enabled, CKKS will save a share for itself
+ if(SecCKKSShareTLKs()) {
+ [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ }
[self addGenericPassword:@"data" account:@"first"];
[self addGenericPassword:@"data" account:@"second"];
// Wait for uploads to happen
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[self waitForCKModifications];
- XCTAssertEqual(self.keychainZone.currentDatabase.count, SYSTEM_DB_RECORD_COUNT + passwordCount, "Have 6+passwordCount objects in cloudkit");
+ int tlkshares = 1;
+ XCTAssertEqual(self.keychainZone.currentDatabase.count, SYSTEM_DB_RECORD_COUNT + passwordCount + tlkshares, "Have 6+passwordCount objects in cloudkit");
NSArray* items = [self mirrorItemsForExistingItems];
_egoManifest = [CKKSEgoManifest newManifestForZone:self.keychainZoneID.zoneName withItems:items peerManifestIDs:@[] currentItems:@{} error:&error];
return query;
}
-- (void)testReceiveManifest
+// <rdar://problem/35102286> Fix primary keys in ckmanifest sql table
+// The ckmanifest table only has a single primary key column, so each new manifest written appears to overwrite older manifests
+// This is causing this test to fail, since the local peer overwrites the manifest created by FakeSigner-1.
+// Disable this test until manifests come back.
+- (void)disable35102286testReceiveManifest
{
if (![CKKSManifest shouldSyncManifests]) {
return;
#import <XCTest/XCTest.h>
#import "keychain/ckks/CKKSGroupOperation.h"
+#import "keychain/ckks/CKKSCondition.h"
// Helper Operations
@interface CKKSResultCancelOperation : CKKSResultOperation
[super tearDown];
}
+- (void)testIsPending {
+ NSBlockOperation* run = [NSBlockOperation blockOperationWithBlock:^{}];
+ NSBlockOperation* cancel = [NSBlockOperation blockOperationWithBlock:^{}];
+ NSBlockOperation* pending = [NSBlockOperation blockOperationWithBlock:^{}];
+
+ [self.queue addOperation: run];
+ [cancel cancel];
+ [self.queue waitUntilAllOperationsAreFinished];
+
+ XCTAssertTrue( [pending isPending], @"Pending operation should be pending");
+ XCTAssertFalse([run isPending], @"run operation should not be pending");
+ XCTAssertFalse([cancel isPending], @"Cancelled operation should not be pending");
+}
+
- (void)testResultOperation {
CKKSResultOperation* op = [[CKKSResultOperation alloc] init];
__weak __typeof(op) weakOp = op;
XCTAssertNil(group.error, "Group operation: no error");
}
-- (void)testGroupOperationCancel {
+- (void)testGroupOperationSubOperationCancel {
CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
XCTAssertNil(op1.error, "First operation: no error");
XCTAssertNil(op2.error, "Second operation: no error");
- XCTAssertNotNil(group.error, "Group operation: no error");
+ XCTAssertNotNil(group.error, "Group operation: has an error");
+ XCTAssertEqual(group.error.code, CKKSResultSubresultCancelled, "Error code is CKKSResultSubresultCancelled");
+}
+
+- (void)testGroupOperationCancel {
+ CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
+
+ CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
+ [group runBeforeGroupFinished: op1];
+
+ [group cancel];
+ [self.queue addOperation: group];
+
+ [self.queue waitUntilAllOperationsAreFinished];
+
+ XCTAssertEqual(op1.finished, YES, "First operation finished");
+ XCTAssertEqual(group.finished, YES, "Group operation finished");
+
+ XCTAssertEqual(op1.cancelled, YES, "First operation cancelled");
+ XCTAssertEqual(group.cancelled, YES, "Group operation cancelled");
+
+ XCTAssertNil(op1.error, "First operation: no error");
+ XCTAssertNil(group.error, "Group operation: no error");
+}
+
+- (void)testGroupOperationCancelAfterAdd {
+ CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
+
+ CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
+ [group runBeforeGroupFinished: op1];
+
+ CKKSResultOperation* never = [[CKKSResultOperation alloc] init];
+ [group addDependency: never];
+
+ [self.queue addOperation: group];
+
+ [group cancel];
+
+ // Both of these should finish. Wait for that.
+ [op1 waitUntilFinished];
+ [group waitUntilFinished];
+
+ XCTAssertEqual(op1.finished, YES, "First operation finished");
+ XCTAssertEqual(group.finished, YES, "Group operation finished");
+
+ XCTAssertEqual(op1.cancelled, YES, "First operation cancelled");
+ XCTAssertEqual(group.cancelled, YES, "Group operation cancelled");
+
+ XCTAssertNil(op1.error, "First operation: no error");
+ XCTAssertNil(group.error, "Group operation: no error");
+}
+
+- (void)testGroupOperationCancelWhileRunning {
+ CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
+ group.name = @"operation-under-test";
+
+ XCTestExpectation* groupStarted = [self expectationWithDescription: @"group started"];
+ XCTestExpectation* cancelOccurs = [self expectationWithDescription: @"cancel occurs"];
+
+ CKKSCondition* everythingFinished = [[CKKSCondition alloc] init];
+ CKKSResultOperation* op1 = [CKKSResultOperation named:@"op1" withBlock:^{
+ [groupStarted fulfill];
+
+ [self waitForExpectations:@[cancelOccurs] timeout:8.0];
+
+ // 'do some work'. Will wait 200msec.
+ [everythingFinished wait:200*NSEC_PER_MSEC];
+ }];
+ [group runBeforeGroupFinished: op1];
+ [self.queue addOperation: group];
+
+ [self waitForExpectations:@[groupStarted] timeout:8.0];
+ [group cancel];
+ [cancelOccurs fulfill];
+
+ [group waitUntilFinished];
+
+ XCTAssertEqual(op1.finished, YES, "First operation finished");
+ XCTAssertEqual(group.finished, YES, "Group operation finished");
+
+ [everythingFinished fulfill];
+
+ XCTAssertEqual(op1.cancelled, YES, "First operation cancelled");
+ XCTAssertEqual(group.cancelled, YES, "Group operation cancelled");
+
+ XCTAssertNil(op1.error, "First operation: no error");
+ XCTAssertNotNil(group.error, "Group operation: has an error");
XCTAssertEqual(group.error.code, CKKSResultSubresultCancelled, "Error code is CKKSResultSubresultCancelled");
}
+- (void)testGroupOperationWithDependsOn {
+ CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
+
+ CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
+ [group dependOnBeforeGroupFinished:op1];
+
+ [group cancel];
+ [self.queue addOperation: group];
+
+ [self.queue waitUntilAllOperationsAreFinished];
+
+ XCTAssertEqual(op1.finished, NO, "First operation not finished");
+ XCTAssertEqual(group.finished, YES, "Group operation finished");
+
+ XCTAssertEqual(op1.cancelled, NO, "First operation not cancelled");
+ XCTAssertEqual(group.cancelled, YES, "Group operation cancelled");
+
+ XCTAssertNil(op1.error, "First operation: no error");
+ XCTAssertNil(group.error, "Group operation: no error");
+}
+
- (void)testGroupOperationTimeout {
CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
CKKSResultOperation* never = [[CKKSResultOperation alloc] init];
[group addDependency: never];
- [group timeout:50*NSEC_PER_MSEC];
+ [group timeout:10*NSEC_PER_MSEC];
[self.queue addOperation: group];
[self.queue waitUntilAllOperationsAreFinished];
XCTAssertNil(op2.error, "Second operation: no error");
XCTAssertNotNil(group.error, "Group operation: error");
XCTAssertEqual(group.error.code, CKKSResultTimedOut, "Error code is CKKSResultTimedOut");
+
+ // Try a few more times, just in case
+ for(int i = 0; i < 100; i++) {
+ CKKSGroupOperation* g = [[CKKSGroupOperation alloc] init];
+ [g addDependency: never];
+ [g timeout:((i%20)+1)*NSEC_PER_MSEC];
+
+ [self.queue addOperation: g];
+ [self.queue waitUntilAllOperationsAreFinished];
+ }
}
- (void)testGroupOperationError {
XCTAssertNil(group.error, "Group operation: no error");
}
+- (void)testGroupOperationPendingAfterCancel {
+ CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
+
+ CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
+ [group runBeforeGroupFinished: op1];
+
+ CKKSResultOperation* op2 = [[CKKSResultOperation alloc] init];
+ [group addDependency: op2];
+
+ [group cancel];
+
+ XCTAssertFalse([group isPending], "group operation isn't pending, as it's cancelled");
+}
+
+
@end
#import "keychain/ckks/tests/MockCloudKit.h"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include <Security/SecureObjectSync/SOSCloudCircle.h>
-#include <Security/SecureObjectSync/SOSAccountPriv.h>
#include <Security/SecureObjectSync/SOSAccount.h>
#include <Security/SecureObjectSync/SOSInternal.h>
#include <Security/SecureObjectSync/SOSFullPeerInfo.h>
#include <Security/SecKey.h>
#include <Security/SecKeyPriv.h>
+#pragma clang diagnostic pop
+
@interface CloudKitKeychainSyncingSOSIntegrationTests : CloudKitKeychainSyncingMockXCTest
@property CKRecordZoneID* engramZoneID;
@property FakeCKZone* healthZone;
@property (readonly) ZoneKeys* healthZoneKeys;
+@property CKRecordZoneID* applepayZoneID;
+@property CKKSKeychainView* applepayView;
+@property FakeCKZone* applepayZone;
+@property (readonly) ZoneKeys* applepayZoneKeys;
@end
self.healthZone = [[FakeCKZone alloc] initZone: self.healthZoneID];
self.zones[self.healthZoneID] = self.healthZone;
self.healthView = [[CKKSViewManager manager] findView:@"Health"];
- XCTAssertNotNil(self.autoUnlockView, "CKKSViewManager created the Health view");
+ XCTAssertNotNil(self.healthView, "CKKSViewManager created the Health view");
+
+ self.applepayZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"ApplePay" ownerName:CKCurrentUserDefaultName];
+ self.applepayZone = [[FakeCKZone alloc] initZone: self.healthZoneID];
+ self.zones[self.applepayZoneID] = self.applepayZone;
+ self.applepayView = [[CKKSViewManager manager] findView:@"ApplePay"];
+ XCTAssertNotNil(self.applepayView, "CKKSViewManager created the ApplePay view");
}
+ (void)tearDown {
}
- (void)tearDown {
+ // If the test didn't already do this, allow each zone to spin up
+ self.accountStatus = CKAccountStatusNoAccount;
+ [self startCKKSSubsystem];
+
[self.engramView cancelAllOperations];
[self.engramView waitUntilAllOperationsAreFinished];
+ self.engramView = nil;
[self.manateeView cancelAllOperations];
[self.manateeView waitUntilAllOperationsAreFinished];
+ self.manateeView = nil;
[self.autoUnlockView cancelAllOperations];
[self.autoUnlockView waitUntilAllOperationsAreFinished];
+ self.autoUnlockView = nil;
[self.healthView cancelAllOperations];
[self.healthView waitUntilAllOperationsAreFinished];
+ self.healthView = nil;
+
+ [self.applepayView cancelAllOperations];
+ [self.applepayView waitUntilAllOperationsAreFinished];
+ self.applepayView = nil;
[super tearDown];
}
[self createAndSaveFakeKeyHierarchy: self.manateeZoneID];
[self createAndSaveFakeKeyHierarchy: self.autoUnlockZoneID];
[self createAndSaveFakeKeyHierarchy: self.healthZoneID];
+ [self createAndSaveFakeKeyHierarchy: self.applepayZoneID];
}
-(void)testAddEngramManateeItems {
[self startCKKSSubsystem];
XCTestExpectation* healthChanged = [self expectChangeForView:self.healthZoneID.zoneName];
- // AutoUnlock is NOT is PCS view, so it should not send the fake 'PCS' view notification
+ // Health is NOT is PCS view, so it should not send the fake 'PCS' view notification
XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
pcsChanged.inverted = YES;
[self waitForExpectations:@[pcsChanged] timeout:0.2];
}
+-(void)testAddApplePayItems {
+ [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
+
+ [self startCKKSSubsystem];
+
+ XCTestExpectation* applepayChanged = [self expectChangeForView:self.applepayZoneID.zoneName];
+ // ApplePay is NOT is PCS view, so it should not send the fake 'PCS' view notification
+ XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
+ pcsChanged.inverted = YES;
+
+ // We expect a single record to be uploaded to the ApplePay view.
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.applepayZoneID];
+ [self addGenericPassword: @"data" account: @"account-delete-me-autounlock" viewHint:(NSString*) kSecAttrViewHintApplePay];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+ [self waitForExpectations:@[applepayChanged] timeout:1];
+ [self waitForExpectations:@[pcsChanged] timeout:0.2];
+}
+
+
-(void)testAddOtherViewHintItem {
[self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
[self.healthZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
XCTAssertNil(error, "No error loading Health tlk from piggy contents");
+
+ [self.applepayZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
+ XCTAssertNil(error, "No error loading ApplePay tlk from piggy contents");
}
-(NSString*)fileForStorage
NSArray<NSData *>* icloudidentities = piggydata[@"idents"];
NSArray<NSDictionary *>* tlks = piggydata[@"tlk"];
- XCTAssertEqual([tlks count], [[CKKSViewManager viewList] count], "TLKs not same as views");
+ XCTAssertEqual([tlks count], [[self.injectedManager viewList] count], "TLKs not same as views");
XCTAssertNotNil(tlks, "tlks not set");
XCTAssertNotEqual([tlks count], (NSUInteger)0, "0 tlks");
[self putFakeKeyHierarchyInCloudKit: self.manateeZoneID];
[self putFakeKeyHierarchyInCloudKit: self.autoUnlockZoneID];
[self putFakeKeyHierarchyInCloudKit: self.healthZoneID];
+ [self putFakeKeyHierarchyInCloudKit: self.applepayZoneID];
}
-(void)saveTLKsToKeychain{
[self saveTLKMaterialToKeychain:self.engramZoneID];
[self saveTLKMaterialToKeychain:self.manateeZoneID];
[self saveTLKMaterialToKeychain:self.autoUnlockZoneID];
[self saveTLKMaterialToKeychain:self.healthZoneID];
+ [self saveTLKMaterialToKeychain:self.applepayZoneID];
}
-(void)deleteTLKMaterialsFromKeychain{
[self deleteTLKMaterialFromKeychain: self.engramZoneID];
[self deleteTLKMaterialFromKeychain: self.manateeZoneID];
[self deleteTLKMaterialFromKeychain: self.autoUnlockZoneID];
[self deleteTLKMaterialFromKeychain: self.healthZoneID];
+ [self deleteTLKMaterialFromKeychain: self.applepayZoneID];
}
-(void)waitForKeyHierarchyReadinesses {
[self.engramView waitForKeyHierarchyReadiness];
[self.autoUnlockView waitForKeyHierarchyReadiness];
[self.healthView waitForKeyHierarchyReadiness];
+ [self.applepayView waitForKeyHierarchyReadiness];
}
-(void)testAcceptExistingAndUsePiggyKeyHierarchy {
}
-(void)testCKKSZoneStateEntrySQL {
- CKKSZoneStateEntry* zse = [[CKKSZoneStateEntry alloc] initWithCKZone: @"sqltest"
- zoneCreated: true
- zoneSubscribed: true
- changeToken: [@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
- lastFetch: [NSDate date]
+ CKKSZoneStateEntry* zse = [[CKKSZoneStateEntry alloc] initWithCKZone:@"sqltest"
+ zoneCreated:true
+ zoneSubscribed:true
+ changeToken:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
+ lastFetch:[NSDate date]
+ lastFixup:CKKSCurrentFixupNumber
encodedRateLimiter:nil];
zse.rateLimiter = [[CKKSRateLimiter alloc] init];
- CKKSZoneStateEntry* zseClone = [[CKKSZoneStateEntry alloc] initWithCKZone: @"sqltest"
- zoneCreated: true
- zoneSubscribed: true
- changeToken: [@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
- lastFetch: zse.lastFetchTime
+ CKKSZoneStateEntry* zseClone = [[CKKSZoneStateEntry alloc] initWithCKZone:@"sqltest"
+ zoneCreated:true
+ zoneSubscribed:true
+ changeToken:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
+ lastFetch:zse.lastFetchTime
+ lastFixup:CKKSCurrentFixupNumber
encodedRateLimiter:zse.encodedRateLimiter];
- CKKSZoneStateEntry* zseDifferent = [[CKKSZoneStateEntry alloc] initWithCKZone: @"sqltest"
- zoneCreated: true
- zoneSubscribed: true
- changeToken: [@"allnonsense" dataUsingEncoding:NSUTF8StringEncoding]
- lastFetch: zse.lastFetchTime
+ CKKSZoneStateEntry* zseDifferent = [[CKKSZoneStateEntry alloc] initWithCKZone:@"sqltest"
+ zoneCreated:true
+ zoneSubscribed:true
+ changeToken:[@"allnonsense" dataUsingEncoding:NSUTF8StringEncoding]
+ lastFetch:zse.lastFetchTime
+ lastFixup:CKKSCurrentFixupNumber
encodedRateLimiter:zse.encodedRateLimiter];
XCTAssertEqualObjects(zse, zseClone, "CKKSZoneStateEntry isEqual of equal objects seems sane");
XCTAssertNotEqualObjects(zse, zseDifferent, "CKKSZoneStateEntry isEqual of nonequal objects seems sane");
XCTAssertNotNil(selfWrapped, "Returned some array from allWhere");
XCTAssertNil(error, "no error back from allWhere");
XCTAssertEqual([selfWrapped count], 1ul, "Received one row (and expected one row)");
+
+ // Try using CKKSSQLWhereObject alongside normal binds
+ NSArray<CKKSKey*>* selfWrapped2 = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereObject op:@"=" string:@"uuid"],
+ @"uuid": @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"}
+ error: &error];
+ XCTAssertNotNil(selfWrapped2, "Returned some array from allWhere");
+ XCTAssertNil(error, "no error back from allWhere");
+ XCTAssertEqual([selfWrapped2 count], 1ul, "Received one row (and expected one row)");
}
- (void)testGroupBy {
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import <CloudKit/CloudKit.h>
+#import <XCTest/XCTest.h>
+#import <OCMock/OCMock.h>
+
+#import "keychain/ckks/tests/CloudKitMockXCTest.h"
+#import "keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.h"
+#import "keychain/ckks/CKKS.h"
+#import "keychain/ckks/CKKSCurrentKeyPointer.h"
+#import "keychain/ckks/CKKSKey.h"
+#import "keychain/ckks/CKKSOutgoingQueueEntry.h"
+#import "keychain/ckks/CKKSIncomingQueueEntry.h"
+
+#import "keychain/ckks/tests/MockCloudKit.h"
+#import "keychain/ckks/tests/CKKSTests.h"
+
+@interface CloudKitKeychainSyncingServerValidationRecoveryTests : CloudKitKeychainSyncingTestsBase
+@end
+
+@implementation CloudKitKeychainSyncingServerValidationRecoveryTests
+
+/* Tests for CKKSServerUnexpectedSyncKeyInChain */
+
+- (void)testRecoverFromWrongClassACurrentKeyPointersOnStartup {
+ // The current key pointers in cloudkit should always point directly under the top TLK.
+
+ // Test starts with a broken key hierarchy in our fake CloudKit, and the TLK already arrived.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ CKReference* oldClassAKey = self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassAPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey];
+ [self rollFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+ CKReference* newClassAKey = self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassAPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey];
+
+ // Break the reference
+ self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassAPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey] = oldClassAKey;
+ self.keychainZoneKeys.currentClassAPointer.currentKeyUUID = oldClassAKey.recordID.recordName;
+
+ // CKKS should then fix the pointers, but not update any keys
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords: 1 tlkShareRecords: 0 zoneID:self.keychainZoneID];
+
+ // And then upload the record as normal
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassABlock:self.keychainZoneID message:@"Object was encrypted under class A key in hierarchy"]];
+ [self addGenericPassword:@"asdf"
+ account:@"account-class-A"
+ viewHint:nil
+ access:(id)kSecAttrAccessibleWhenUnlocked
+ expecting:errSecSuccess
+ message:@"Adding class A item"];
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self waitForCKModifications];
+ XCTAssertEqualObjects(newClassAKey, self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassAPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey], "CKKS should have fixed up the broken class A key reference");
+}
+
+- (void)testRecoverFromWrongClassCCurrentKeyPointersOnStartup {
+ // The current key pointers in cloudkit should always point directly under the top TLK.
+
+ // Test starts with a broken key hierarchy in our fake CloudKit, and the TLK already arrived.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ CKReference* oldClassCKey = self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassCPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey];
+ [self rollFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+ CKReference* newClassCKey = self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassCPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey];
+
+ // Break the reference
+ self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassCPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey] = oldClassCKey;
+ self.keychainZoneKeys.currentClassCPointer.currentKeyUUID = oldClassCKey.recordID.recordName;
+
+ // CKKS should then fix the pointers, but not update any keys
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords: 1 tlkShareRecords: 0 zoneID:self.keychainZoneID];
+
+ // And then upload the record as normal
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self waitForCKModifications];
+ XCTAssertEqualObjects(newClassCKey, self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassCPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey], "CKKS should have fixed up the broken class C key reference");
+}
+
+- (void)testRecoverFromWrongClassCCurrentKeyPointersOnNotification {
+ // The current key pointers in cloudkit should always point directly under the top TLK.
+
+ // Test starts with a good key hierarchy in our fake CloudKit, and the TLK already arrived.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ CKReference* oldClassCKey = self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassCPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey];
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ // Uploading works
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // Break the key hierarchy
+ [self rollFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ CKReference* newClassCKey = self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassCPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey];
+ self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassCPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey] = oldClassCKey;
+ self.keychainZoneKeys.currentClassCPointer.currentKeyUUID = oldClassCKey.recordID.recordName;
+
+ [self.keychainView notifyZoneChange:nil];
+
+ // CKKS should then fix the pointers, but not update any keys
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords: 1 tlkShareRecords: 0 zoneID:self.keychainZoneID];
+ [self.keychainView notifyZoneChange:nil];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // And then upload the item as usual
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me-2"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self waitForCKModifications];
+ XCTAssertEqualObjects(newClassCKey, self.keychainZone.currentDatabase[self.keychainZoneKeys.currentClassCPointer.storedCKRecord.recordID][SecCKRecordParentKeyRefKey], "CKKS should have fixed up the broken class C key reference");
+}
+
+- (void)testRecoverFromWrongClassCCurrentKeyPointersOnNotificationFixRejected {
+ // The current key pointers in cloudkit should always point directly under the top TLK.
+
+ // Test starts with a good key hierarchy in our fake CloudKit, and the TLK already arrived.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ CKRecordID* classCPointerRecordID = self.keychainZoneKeys.currentClassCPointer.storedCKRecord.recordID;
+
+ CKReference* oldClassCKey = self.keychainZone.currentDatabase[classCPointerRecordID][SecCKRecordParentKeyRefKey];
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ // Uploading works
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // Break the key hierarchy
+ [self rollFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ CKReference* newClassCKey = self.keychainZone.currentDatabase[classCPointerRecordID][SecCKRecordParentKeyRefKey];
+ CKRecord* classCPointer = self.keychainZone.currentDatabase[classCPointerRecordID];
+
+ CKRecord* brokenClassCPointer = [classCPointer copy];
+ brokenClassCPointer[SecCKRecordParentKeyRefKey] = oldClassCKey;
+ self.keychainZoneKeys.currentClassCPointer.currentKeyUUID = oldClassCKey.recordID.recordName;
+ [self.keychainZone addCKRecordToZone:brokenClassCPointer];
+
+ self.silentFetchesAllowed = false;
+ [self expectCKFetchAndRunBeforeFinished: ^{
+ // Directly after CKKS fetches, we should fix up the pointers to be right again
+ self.keychainZoneKeys.currentClassCPointer.currentKeyUUID = newClassCKey.recordID.recordName;
+ [self.keychainZone addToZone: classCPointer];
+ XCTAssertEqualObjects(newClassCKey, self.keychainZone.currentDatabase[classCPointerRecordID][SecCKRecordParentKeyRefKey], "CKKS should have fixed up the broken class C key reference");
+ self.silentFetchesAllowed = true;
+ }];
+
+ // CKKS should try to fix the pointers, but be rejected (since someone else has already fixed them)
+ // It should not try again, because someone already fixed them
+ [self expectCKAtomicModifyItemRecordsUpdateFailure:self.keychainZoneID];
+ [self.keychainView notifyZoneChange:nil];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // And then use the 'new' key as it should
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me-2"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self waitForCKModifications];
+ XCTAssertEqualObjects(newClassCKey, self.keychainZone.currentDatabase[classCPointerRecordID][SecCKRecordParentKeyRefKey], "CKKS should have fixed up the broken class C key reference");
+}
+
+- (void)testRecoverFromWrongClassCCurrentKeyPointersOnRecordWrite {
+ // The current key pointers in cloudkit should always point directly under the top TLK.
+
+ // Test starts with a good but rolled key hierarchy in our fake CloudKit, and the TLK already arrived.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ CKRecordID* classCPointerRecordID = self.keychainZoneKeys.currentClassCPointer.storedCKRecord.recordID;
+ CKReference* oldClassCKey = self.keychainZone.currentDatabase[classCPointerRecordID][SecCKRecordParentKeyRefKey];
+
+ [self rollFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ // Uploading works
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // Now, break the class C pointer, but don't tell CKKS
+ CKReference* newClassCKey = self.keychainZone.currentDatabase[classCPointerRecordID][SecCKRecordParentKeyRefKey];
+ CKRecord* classCPointer = self.keychainZone.currentDatabase[classCPointerRecordID];
+
+ CKRecord* brokenClassCPointer = [classCPointer copy];
+ brokenClassCPointer[SecCKRecordParentKeyRefKey] = oldClassCKey;
+ self.keychainZoneKeys.currentClassCPointer.currentKeyUUID = oldClassCKey.recordID.recordName;
+ [self.keychainZone addCKRecordToZone:brokenClassCPointer];
+
+ // CKKS should receive a key hierarchy error, since it's wrong in CloudKit
+ // It should then fix the pointers and retry the upload
+ [self expectCKReceiveSyncKeyHierarchyError:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords: 1 tlkShareRecords: 0 zoneID:self.keychainZoneID];
+
+ // And then use the 'new' key as it should
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me-2"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self waitForCKModifications];
+ XCTAssertEqualObjects(newClassCKey, self.keychainZone.currentDatabase[classCPointerRecordID][SecCKRecordParentKeyRefKey], "CKKS should have fixed up the broken class C key reference");
+}
+
+@end
+
+#endif // OCTAGON
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import <XCTest/XCTest.h>
+#import "keychain/ckks/CKKS.h"
+#import "keychain/ckks/CKKSKey.h"
+#import "keychain/ckks/CKKSTLKShare.h"
+#import "keychain/ckks/CKKSPeer.h"
+#import "keychain/ckks/tests/CloudKitMockXCTest.h"
+
+#import <SecurityFoundation/SFSigningOperation.h>
+#import <SecurityFoundation/SFKey.h>
+#import <SecurityFoundation/SFKey_Private.h>
+#import <SecurityFoundation/SFDigestOperation.h>
+
+@interface CloudKitKeychainTLKSharingEncryptionTests : CloudKitMockXCTest
+@property CKKSKey* tlk;
+@property CKKSSOSSelfPeer* localPeer;
+@property CKKSSOSSelfPeer* remotePeer;
+@property CKKSSOSSelfPeer* remotePeer2;
+@end
+
+@implementation CloudKitKeychainTLKSharingEncryptionTests
+
+- (void)setUp {
+ // We don't really want to spin up the whole machinery for the encryption tests
+ SecCKKSDisable();
+
+ NSError* error = nil;
+ self.tlk = [CKKSKey randomKeyWrappedBySelf:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName] error:&error];
+ XCTAssertNil(error, "Shouldn't be an error creating a new TLK");
+
+ self.localPeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"local"
+ encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
+ signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
+ XCTAssertNotNil(self.localPeer, "Should be able to make a new local peer");
+
+ self.remotePeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"remote"
+ encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
+ signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
+ XCTAssertNotNil(self.remotePeer, "Should be able to make a new remote peer");
+
+ self.remotePeer2 = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"remote"
+ encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
+ signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
+ XCTAssertNotNil(self.remotePeer2, "Should be able to make a new remote peer");
+
+ [super setUp];
+}
+
+- (void)tearDown {
+ [super tearDown];
+}
+
+- (void)testKeyWrapAndUnwrap {
+ NSError* error = nil;
+ CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
+ as:self.localPeer
+ to:self.remotePeer
+ epoch:-1
+ poisoned:0
+ error:&error];
+ XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
+
+ XCTAssertEqual(self.tlk.uuid, share.tlkUUID, "TLK shares should know which key they hold");
+
+ CKKSKey* unwrappedKey = [share unwrapUsing:self.remotePeer error:&error];
+ XCTAssertNil(error, "Should have been no error unwrapping a CKKSKey");
+ XCTAssertEqualObjects(self.tlk, unwrappedKey, "CKKSKeys should be identical after wrapping/unwrapping through a TLK Share record");
+}
+
+- (void)testTLKShareSignAndVerify {
+ NSError* error = nil;
+ CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
+ as:self.localPeer
+ to:self.remotePeer
+ epoch:-1
+ poisoned:0
+ error:&error];
+ XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
+
+ NSData* signature = [share signRecord:self.localPeer.signingKey error:&error];
+ XCTAssertNil(error, "Should have been no error signing a CKKSTLKShare");
+ XCTAssertNotNil(signature, "Should have received a signature blob");
+
+ XCTAssertTrue([share verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
+}
+
+- (void)testTLKShareSignAndFailVerify {
+ NSError* error = nil;
+ CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
+ as:self.localPeer
+ to:self.remotePeer
+ epoch:-1
+ poisoned:0
+ error:&error];
+ XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
+
+ NSData* signature = [share signRecord:self.localPeer.signingKey error:&error];
+ XCTAssertNil(error, "Should have been no error signing a CKKSTLKShare");
+ XCTAssertNotNil(signature, "Should have received a signature blob");
+
+ CKKSTLKShare* shareEpoch = [share copy];
+ XCTAssertTrue([shareEpoch verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
+ error = nil;
+ shareEpoch.epoch = 1;
+ XCTAssertFalse([shareEpoch verifySignature:signature verifyingPeer:self.localPeer error:&error], "After epoch change, signature should no longer verify");
+ XCTAssertNotNil(error, "Signature verification after epoch change should have produced an error");
+ error = nil;
+
+ CKKSTLKShare* sharePoisoned = [share copy];
+ XCTAssertTrue([sharePoisoned verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
+ error = nil;
+ sharePoisoned.poisoned = 1;
+ XCTAssertFalse([sharePoisoned verifySignature:signature verifyingPeer:self.localPeer error:&error], "After poison change, signature should no longer verify");
+ XCTAssertNotNil(error, "Signature verification after poison change should have produced an error");
+ error = nil;
+
+ CKKSTLKShare* shareData = [share copy];
+ XCTAssertTrue([shareData verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
+ error = nil;
+ shareData.wrappedTLK = [NSMutableData dataWithLength:shareData.wrappedTLK.length];
+ XCTAssertFalse([shareData verifySignature:signature verifyingPeer:self.localPeer error:&error], "After data change, signature should no longer verify");
+ XCTAssertNotNil(error, "Signature verification due to data change should have produced an error");
+ error = nil;
+}
+
+- (void)testKeyShareAndRecover {
+ NSError* error = nil;
+ CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
+ as:self.localPeer
+ to:self.remotePeer
+ epoch:-1
+ poisoned:0
+ error:&error];
+ XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
+
+ NSSet* peers = [NSSet setWithObject:self.localPeer];
+ CKKSKey* key = [share recoverTLK:self.remotePeer trustedPeers:peers error:&error];
+ XCTAssertNil(error, "Should have been no error unwrapping a CKKSKey");
+ XCTAssertEqualObjects(self.tlk, key, "CKKSKeys should be identical after wrapping/unwrapping through a TLK Share record");
+}
+
+- (void)testKeyShareAndFailRecovery {
+ NSError* error = nil;
+ CKKSKey* key = nil;
+ CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
+ as:self.localPeer
+ to:self.remotePeer
+ epoch:-1
+ poisoned:0
+ error:&error];
+ XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
+
+ NSSet* peers = [NSSet setWithObject:self.localPeer];
+
+ key = [share recoverTLK:self.remotePeer trustedPeers:[NSSet set] error:&error];
+ XCTAssertNil(key, "No key should have been extracted when no trusted peers exist");
+ XCTAssertNotNil(error, "Should have produced an error when failing to extract a key");
+ error = nil;
+
+ key = [share recoverTLK:self.remotePeer2 trustedPeers:peers error:&error];
+ XCTAssertNil(key, "No key should have been extracted when using the wrong key");
+ XCTAssertNotNil(error, "Should have produced an error when failing to extract a key");
+ error = nil;
+
+ CKKSTLKShare* shareSignature = [share copy];
+ shareSignature.signature = [NSMutableData dataWithLength:shareSignature.signature.length];
+ key = [shareSignature recoverTLK:self.remotePeer trustedPeers:peers error:&error];
+ XCTAssertNil(key, "No key should have been extracted when signature fails to verify");
+ XCTAssertNotNil(error, "Should have produced an error when failing to extract a key");
+ error = nil;
+
+ CKKSTLKShare* shareUUID = [share copy];
+ shareUUID.tlkUUID = [[NSUUID UUID] UUIDString];
+ key = [shareUUID recoverTLK:self.remotePeer trustedPeers:peers error:&error];
+ XCTAssertNil(key, "No key should have been extracted when uuid has changed");
+ XCTAssertNotNil(error, "Should have produced an error when failing to extract a key");
+ error = nil;
+}
+
+- (void)testKeyShareSaveAndLoad {
+ NSError* error = nil;
+ CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
+ as:self.localPeer
+ to:self.remotePeer
+ epoch:-1
+ poisoned:0
+ error:&error];
+ XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
+
+ [share saveToDatabase:&error];
+ XCTAssertNil(error, "Shouldn't be an error saving a TLKShare record to the database");
+
+ CKKSTLKShare* loadedShare = [CKKSTLKShare fromDatabase:self.tlk.uuid
+ receiverPeerID:self.remotePeer.peerID
+ senderPeerID:self.localPeer.peerID
+ zoneID:self.tlk.zoneID
+ error:&error];
+ XCTAssertNil(error, "Shouldn't get an error loading the share from the db");
+ XCTAssertNotNil(loadedShare, "Should've gotten a TLK share object back from the database");
+
+ XCTAssertEqualObjects(share, loadedShare, "Re-loaded TLK share object should be equivalent to the original");
+
+ CKRecord* record = [share CKRecordWithZoneID:self.tlk.zoneID];
+ XCTAssertNotNil(record, "Should be able to turn a share into a CKRecord");
+ XCTAssertTrue([share matchesCKRecord: record], "Should be able to compare a CKRecord with a TLKShare");
+
+ CKKSTLKShare* fromCKRecord = [[CKKSTLKShare alloc] initWithCKRecord:record];
+ XCTAssertNotNil(fromCKRecord, "Should be able to turn a CKRecord into a TLK share");
+
+ XCTAssertEqualObjects(share, fromCKRecord, "TLK shares sent through CloudKit should be identical");
+}
+
+- (void)testKeyShareSignExtraFieldsInCKRecord {
+ NSError* error = nil;
+ CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
+ as:self.localPeer
+ to:self.remotePeer
+ epoch:-1
+ poisoned:0
+ error:&error];
+ XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
+
+ CKRecord* record = [share CKRecordWithZoneID:self.tlk.zoneID];
+ XCTAssertNotNil(record, "Should be able to turn a share into a CKRecord");
+ XCTAssertTrue([share matchesCKRecord: record], "Should be able to compare a CKRecord with a TLKShare");
+
+ // Add another few fields to the record
+ record[@"extra_field"] = @"asdf";
+ record[@"another_field"] = [NSNumber numberWithInt:5];
+ record[@"data"] = [@"asdfdata" dataUsingEncoding:NSUTF8StringEncoding];
+ CKKSTLKShare* share2 = [share copy];
+ share2.storedCKRecord = record;
+
+ XCTAssertNotNil([share dataForSigning], "TLKShares should be able to produce some data to sign");
+ XCTAssertNotNil([share2 dataForSigning], "TLKShares should be able to produce some data to sign (that includes extra fields)");
+ XCTAssertNotEqualObjects([share dataForSigning], [share2 dataForSigning], "TLKShares should prepare to sign extra unknown data");
+
+ share2.signature = [share2 signRecord:self.localPeer.signingKey error:&error];
+ XCTAssertNil(error, "Shouldn't be an error signing a record with extra fields");
+ XCTAssertNotEqualObjects(share.signature, share2.signature, "Signatures should be different for different data");
+
+ XCTAssert([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data should verify");
+
+ // Now, change some of the extra data and see how that works
+ CKRecord* changedDataRecord = [record copy];
+ changedDataRecord[@"extra_field"] = @"no signature here";
+ share2.storedCKRecord = changedDataRecord;
+ XCTAssertFalse([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data shouldn't verify if the data changes");
+
+ CKRecord* addedDataRecord = [record copy];
+ addedDataRecord[@"anotherfieldaltogether"] = @"asdfasdf";
+ share2.storedCKRecord = addedDataRecord;
+ XCTAssertFalse([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data shouldn't verify if extra data is added");
+
+ // And verify that saving to disk and reloading is successful
+ share2.storedCKRecord = record;
+ XCTAssert([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data should verify");
+ [share2 saveToDatabase:&error];
+ XCTAssertNil(error, "No error saving share2 to database");
+
+ /*
+ * DISABLE FOR NOW:
+ * a bad rebase made the implementation of this function not be avilable
+ * yet
+ CKKSTLKShare* loadedShare2 = [CKKSTLKShare tryFromDatabaseFromCKRecordID:record.recordID error:&error];
+ XCTAssertNil(error, "No error loading loadedShare2 from database");
+ XCTAssertNotNil(loadedShare2, "Should have received a CKKSTLKShare from the database");
+
+ XCTAssert([loadedShare2 verifySignature:loadedShare2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data should verify after save/load");
+ */
+}
+
+@end
+
+#endif // OCTAGON
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import <CloudKit/CloudKit.h>
+#import <XCTest/XCTest.h>
+#import <OCMock/OCMock.h>
+
+#import "keychain/ckks/tests/CloudKitMockXCTest.h"
+#import "keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.h"
+#import "keychain/ckks/CKKS.h"
+#import "keychain/ckks/CKKSKey.h"
+#import "keychain/ckks/CKKSPeer.h"
+#import "keychain/ckks/CKKSTLKShare.h"
+#import "keychain/ckks/CKKSViewManager.h"
+
+#import "keychain/ckks/tests/MockCloudKit.h"
+#import "keychain/ckks/tests/CKKSTests.h"
+
+@interface CloudKitKeychainSyncingTLKSharingTests : CloudKitKeychainSyncingTestsBase
+@property CKKSSOSSelfPeer* remotePeer1;
+@property CKKSSOSPeer* remotePeer2;
+
+
+@property CKKSSOSSelfPeer* untrustedPeer;
+@end
+
+@implementation CloudKitKeychainSyncingTLKSharingTests
+
+- (void)setUp {
+ [super setUp];
+ SecCKKSSetShareTLKs(true);
+
+ self.remotePeer1 = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"remote-peer1"
+ encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
+ signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
+
+ self.remotePeer2 = [[CKKSSOSPeer alloc] initWithSOSPeerID:@"remote-peer2"
+ encryptionPublicKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]].publicKey
+ signingPublicKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]].publicKey];
+
+ // Local SOS trusts these peers
+ [self.currentPeers addObject:self.remotePeer1];
+ [self.currentPeers addObject:self.remotePeer2];
+
+ self.untrustedPeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"untrusted-peer"
+ encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
+ signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
+}
+
+- (void)tearDown {
+ self.remotePeer1 = nil;
+ self.remotePeer2 = nil;
+ self.untrustedPeer = nil;
+
+ [super tearDown];
+
+ SecCKKSSetShareTLKs(false);
+}
+
+- (void)testAcceptExistingTLKSharedKeyHierarchy {
+ // Test starts with no keys in CKKS database, but one in our fake CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Test also starts with the TLK shared to all trusted peers from peer1
+ [self putTLKSharesInCloudKit:self.keychainZoneKeys.tlk from:self.remotePeer1 zoneID:self.keychainZoneID];
+
+ // The CKKS subsystem should accept the keys, and share the TLK back to itself
+ [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:10*NSEC_PER_SEC], "Key state should become ready");
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // Verify that there are three local keys, and three local current key records
+ __weak __typeof(self) weakSelf = self;
+ [self.keychainView dispatchSync: ^bool{
+ __strong __typeof(weakSelf) strongSelf = weakSelf;
+ XCTAssertNotNil(strongSelf, "self exists");
+
+ NSError* error = nil;
+
+ NSArray<CKKSKey*>* keys = [CKKSKey localKeys:strongSelf.keychainZoneID error:&error];
+ XCTAssertNil(error, "no error fetching keys");
+ XCTAssertEqual(keys.count, 3u, "Three keys in local database");
+
+ NSArray<CKKSCurrentKeyPointer*>* currentkeys = [CKKSCurrentKeyPointer all:&error];
+ XCTAssertNil(error, "no error fetching current keys");
+ XCTAssertEqual(currentkeys.count, 3u, "Three current key pointers in local database");
+
+ return false;
+ }];
+}
+
+- (void)testAcceptExistingTLKSharedKeyHierarchyAndUse {
+ // Test starts with nothing in database, but one in our fake CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Test also starts with the TLK shared to all trusted peers from peer1
+ [self putTLKSharesInCloudKit:self.keychainZoneKeys.tlk from:self.remotePeer1 zoneID:self.keychainZoneID];
+
+ // The CKKS subsystem should accept the keys, and share the TLK back to itself
+ [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "Key state should become ready");
+
+ // We expect a single record to be uploaded for each key class
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassABlock:self.keychainZoneID message:@"Object was encrypted under class A key in hierarchy"]];
+ [self addGenericPassword:@"asdf"
+ account:@"account-class-A"
+ viewHint:nil
+ access:(id)kSecAttrAccessibleWhenUnlocked
+ expecting:errSecSuccess
+ message:@"Adding class A item"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
+- (void)testNewTLKSharesHaveChangeTags {
+ // Since there's currently no flow for CKKS to ever update a TLK share when things are working properly, do some hackery
+
+ // Test starts with no keys in CKKS database, but one in our fake CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Test also starts with the TLK shared to all trusted peers from peer1
+ [self putTLKSharesInCloudKit:self.keychainZoneKeys.tlk from:self.remotePeer1 zoneID:self.keychainZoneID];
+ [self saveTLKSharesInLocalDatabase:self.keychainZoneID];
+
+ // The CKKS subsystem should accept the keys, and share the TLK back to itself
+ [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:10*NSEC_PER_SEC], "Key state should become ready");
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+ [self waitForCKModifications];
+
+ // Verify that making a new share will have the old share's change tag
+ __weak __typeof(self) weakSelf = self;
+ [self.keychainView dispatchSyncWithAccountKeys: ^bool{
+ __strong __typeof(weakSelf) strongSelf = weakSelf;
+ XCTAssertNotNil(strongSelf, "self exists");
+
+ NSError* error = nil;
+ CKKSTLKShare* share = [CKKSTLKShare share:strongSelf.keychainZoneKeys.tlk
+ as:strongSelf.currentSelfPeer
+ to:strongSelf.currentSelfPeer
+ epoch:-1
+ poisoned:0
+ error:&error];
+ XCTAssertNil(error, "Shouldn't be an error creating a share");
+ XCTAssertNotNil(share, "Should be able to create share");
+
+ CKRecord* newRecord = [share CKRecordWithZoneID:strongSelf.keychainZoneID];
+ XCTAssertNotNil(newRecord, "Should be able to create a CKRecord");
+
+ CKRecord* cloudKitRecord = strongSelf.keychainZone.currentDatabase[newRecord.recordID];
+ XCTAssertNotNil(cloudKitRecord, "Should have found existing CKRecord in cloudkit");
+ XCTAssertNotNil(cloudKitRecord.recordChangeTag, "Existing record should have a change tag");
+
+ XCTAssertEqualObjects(cloudKitRecord.recordChangeTag, newRecord.recordChangeTag, "Change tags on existing and new records should match");
+
+ return false;
+ }];
+}
+
+- (void)testReceiveTLKShareRecordsAndDeletes {
+ // Test starts with no keys in CKKS database, but one in our fake CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Test also starts with the TLK shared to all trusted peers from peer1
+ [self putTLKSharesInCloudKit:self.keychainZoneKeys.tlk from:self.remotePeer1 zoneID:self.keychainZoneID];
+
+ // The CKKS subsystem should accept the keys, and share the TLK back to itself
+ [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "Key state should become ready");
+
+ // The CKKS subsystem should not try to write anything to the CloudKit database while it's accepting the keys
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:10*NSEC_PER_SEC], "Key state should become ready");
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+
+ // Make another share, but from an untrusted peer to some other peer. local shouldn't necessarily care.
+ NSError* error = nil;
+ CKKSTLKShare* share = [CKKSTLKShare share:self.keychainZoneKeys.tlk
+ as:self.untrustedPeer
+ to:self.remotePeer1
+ epoch:-1
+ poisoned:0
+ error:&error];
+ XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
+ XCTAssertNotNil(share, "Should be able to create a share");
+
+ CKRecord* shareCKRecord = [share CKRecordWithZoneID: self.keychainZoneID];
+ XCTAssertNotNil(shareCKRecord, "Should have been able to create a CKRecord");
+ [self.keychainZone addToZone:shareCKRecord];
+ [self.keychainView notifyZoneChange:nil];
+ [self.keychainView waitForFetchAndIncomingQueueProcessing];
+
+ [self.keychainView dispatchSync:^bool {
+ NSError* blockerror = nil;
+ CKKSTLKShare* localshare = [CKKSTLKShare tryFromDatabaseFromCKRecordID:shareCKRecord.recordID error:&blockerror];
+ XCTAssertNil(blockerror, "Shouldn't error finding TLKShare record in database");
+ XCTAssertNotNil(localshare, "Should be able to find a TLKShare record in database");
+ return true;
+ }];
+
+ // Delete the record in CloudKit...
+ [self.keychainZone deleteCKRecordIDFromZone:shareCKRecord.recordID];
+ [self.keychainView notifyZoneChange:nil];
+ [self.keychainView waitForFetchAndIncomingQueueProcessing];
+
+ // Should be gone now.
+ [self.keychainView dispatchSync:^bool {
+ NSError* blockerror = nil;
+ CKKSTLKShare* localshare = [CKKSTLKShare tryFromDatabaseFromCKRecordID:shareCKRecord.recordID error:&blockerror];
+
+ XCTAssertNil(blockerror, "Shouldn't error trying to find non-existent TLKShare record in database");
+ XCTAssertNil(localshare, "Shouldn't be able to find a TLKShare record in database");
+
+ return true;
+ }];
+}
+
+- (void)testReceiveSharedTLKWhileInWaitForTLK {
+ // Test starts with nothing in database, but one in our fake CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ // The CKKS subsystem should not try to write anything to the CloudKit database, but it should enter waitfortlk
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:20*NSEC_PER_SEC], "Key state should become waitfortlk");
+
+ // peer1 arrives to save the day
+ // The CKKS subsystem should accept the keys, and share the TLK back to itself
+ [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
+
+ [self putTLKSharesInCloudKit:self.keychainZoneKeys.tlk from:self.remotePeer1 zoneID:self.keychainZoneID];
+ [self.keychainView notifyZoneChange:nil];
+ [self.keychainView waitForFetchAndIncomingQueueProcessing];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "Key state should become ready");
+
+ // We expect a single record to be uploaded for each key class
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassABlock:self.keychainZoneID message:@"Object was encrypted under class A key in hierarchy"]];
+ [self addGenericPassword:@"asdf"
+ account:@"account-class-A"
+ viewHint:nil
+ access:(id)kSecAttrAccessibleWhenUnlocked
+ expecting:errSecSuccess
+ message:@"Adding class A item"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
+- (void)testReceiveTLKShareWhileLocked {
+ // Test starts with no keys in CKKS database, but one in our fake CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Test also starts with the TLK shared to all trusted peers from peer1
+ [self putTLKSharesInCloudKit:self.keychainZoneKeys.tlk from:self.remotePeer1 zoneID:self.keychainZoneID];
+
+ // Because 33710924 didn't make it backwards in time, this test is fragile on Chipmunk/Cinar.
+ self.aksLockState = true;
+ [self.lockStateTracker recheck];
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ // The CKKS subsystem should not try to write anything to the CloudKit database, but it should enter waitforunlock
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForUnlock] wait:10*NSEC_PER_SEC], "Key state should become waitforunlock");
+
+ // Now unlock things. We expect a TLKShare upload.
+ [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
+
+ self.aksLockState = false;
+ [self.lockStateTracker recheck];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:10*NSEC_PER_SEC], "Key state should become ready");
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
+- (void)testUploadTLKSharesForExistingHierarchy {
+ // Test starts with key material locally and in CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords:0 tlkShareRecords:3 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
+- (void)testUploadTLKSharesForExistingHierarchyOnRestart {
+ // Turn off TLK sharing, and get situated
+ SecCKKSSetShareTLKs(false);
+
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:10*NSEC_PER_SEC], "Key state should become ready");
+
+ // Turn TLK sharing back on, and restart. We expect an upload of 3 TLK shares.
+ SecCKKSSetShareTLKs(true);
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords:0 tlkShareRecords:3 zoneID:self.keychainZoneID];
+ self.keychainView = [self.injectedManager restartZone: self.keychainZoneID.zoneName];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:10*NSEC_PER_SEC], "Key state should become ready");
+}
+
+- (void)testHandleExternalSharedTLKRoll {
+ // Test starts with key material locally and in CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords:0 tlkShareRecords:3 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // Now the external peer rolls the TLK and updates the shares
+ [self rollFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self putTLKSharesInCloudKit:self.keychainZoneKeys.tlk from:self.remotePeer1 zoneID:self.keychainZoneID];
+
+ // CKKS will share the TLK back to itself
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
+
+ // Trigger a notification
+ [self.keychainView notifyZoneChange:nil];
+ [self.keychainView waitForFetchAndIncomingQueueProcessing];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:10*NSEC_PER_SEC], "Key state should become ready");
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+ [self waitForCKModifications];
+
+ // We expect a single record to be uploaded.
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me-rolled-key"];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
+- (void)testUploadTLKSharesForExternalTLKRollWithoutShares {
+ // Test starts with key material locally and in CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords:0 tlkShareRecords:3 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // Now, an old (Tigris) peer rolls the TLK, but doesn't share it
+ // CKKS should get excited and throw 3 new share records up
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords:0 tlkShareRecords:3 zoneID:self.keychainZoneID];
+
+ // Wait for that modification to finish before changing CK data
+ [self waitForCKModifications];
+
+ [self rollFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ // Trigger a notification
+ [self.keychainView notifyZoneChange:nil];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:10*NSEC_PER_SEC], "Key state should become ready");
+
+ // We expect a single record to be uploaded.
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me-rolled-key"];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
+- (void)testRecoverFromTLKShareUploadFailure {
+ // Test starts with key material locally and in CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ __weak __typeof(self) weakSelf = self;
+ [self failNextCKAtomicModifyItemRecordsUpdateFailure:self.keychainZoneID blockAfterReject:^{
+ __strong __typeof(self) strongSelf = weakSelf;
+ [strongSelf expectCKModifyKeyRecords: 0 currentKeyPointerRecords:0 tlkShareRecords:3 zoneID:self.keychainZoneID];
+ }];
+ [self startCKKSSubsystem];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:10*NSEC_PER_SEC], "Key state should become ready");
+}
+
+- (void)testFillInMissingPeerShares {
+ // Test starts with nothing in database, but one in our fake CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Test also starts with the TLK shared to just the local peer from peer1
+ // We expect the local peer to send it to peer2
+ [self putTLKSharesInCloudKit:self.keychainZoneKeys.tlk from:self.remotePeer1 zoneID:self.keychainZoneID];
+
+ // The CKKS subsystem should accept the keys, and share the TLK back to itself
+ [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "Key state should become ready");
+
+ // We expect a single record to be uploaded for each key class
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassABlock:self.keychainZoneID message:@"Object was encrypted under class A key in hierarchy"]];
+ [self addGenericPassword:@"asdf"
+ account:@"account-class-A"
+ viewHint:nil
+ access:(id)kSecAttrAccessibleWhenUnlocked
+ expecting:errSecSuccess
+ message:@"Adding class A item"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
+- (void)testDontAcceptTLKFromUntrustedPeer {
+ // Test starts with nothing in database, but key hierarchy in our fake CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Test also starts with the key hierarchy shared from a non-trusted peer
+ [self putTLKSharesInCloudKit:self.keychainZoneKeys.tlk from:self.untrustedPeer zoneID:self.keychainZoneID];
+
+ // The CKKS subsystem should go into waitfortlk, since it doesn't trust this peer
+ [self startCKKSSubsystem];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:20*NSEC_PER_SEC], "Key state should become ready");
+}
+
+- (void)testAcceptSharedTLKOnTrustSetAdditionOfSharer {
+ // Test starts with nothing in database, but key hierarchy in our fake CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Test also starts with the key hierarchy shared from a non-trusted peer
+ // note that it would share it itself too
+ [self putTLKSharesInCloudKit:self.keychainZoneKeys.tlk from:self.untrustedPeer zoneID:self.keychainZoneID];
+ [self putTLKShareInCloudKit:self.keychainZoneKeys.tlk from:self.untrustedPeer to:self.untrustedPeer zoneID:self.keychainZoneID];
+
+ // The CKKS subsystem should go into waitfortlk, since it doesn't trust this peer
+ [self startCKKSSubsystem];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:20*NSEC_PER_SEC], "Key state should become waitfortlk");
+
+ // Wait to be sure we really get into that state
+ [self.keychainView waitForOperationsOfClass:[CKKSProcessReceivedKeysOperation class]];
+
+ // Now, trust the previously-untrusted peer
+ [self.currentPeers addObject: self.untrustedPeer];
+ [self.injectedManager sendTrustedPeerSetChangedUpdate];
+
+ // The CKKS subsystem should now accept the key, and share the TLK back to itself
+ [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:500*NSEC_PER_SEC], "Key state should become ready");
+
+ // And use it as well
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
+- (void)testSendNewTLKSharesOnTrustSetAddition {
+ // step 1: add a new peer; we should share the TLK with them
+ // start with no trusted peers
+ [self.currentPeers removeAllObjects];
+
+ [self expectCKModifyKeyRecords:3 currentKeyPointerRecords:3 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // Cool! New peer arrives!
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ [self.currentPeers addObject:self.remotePeer1];
+ [self.injectedManager sendTrustedPeerSetChangedUpdate];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+ [self waitForCKModifications];
+
+ // step 2: add a new peer who already has a share; no share should be created
+ [self putTLKShareInCloudKit:self.keychainZoneKeys.tlk from:self.remotePeer1 to:self.remotePeer2 zoneID:self.keychainZoneID];
+ [self.keychainView notifyZoneChange:nil];
+ [self.keychainView waitForFetchAndIncomingQueueProcessing];
+
+ // CKKS should not upload a tlk share for this peer
+ [self.currentPeers addObject:self.remotePeer2];
+ [self.injectedManager sendTrustedPeerSetChangedUpdate];
+
+ [self.keychainView waitUntilAllOperationsAreFinished];
+}
+
+- (void)testSendNewTLKSharesOnTrustSetRemoval {
+ // Not implemented. Trust set removal demands a key roll, but let's not get ahead of ourselves...
+}
+
+
+@end
+
+#endif // OCTAGON
#import "keychain/ckks/CKKSViewManager.h"
#import "keychain/ckks/CKKSZoneStateEntry.h"
-#import "keychain/ckks/tests/MockCloudKit.h"
+#import "keychain/ckks/CKKSControl.h"
+#import "keychain/ckks/tests/MockCloudKit.h"
#import "keychain/ckks/tests/CKKSTests.h"
#import "keychain/ckks/tests/CKKSTests+API.h"
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
+- (void)testAddAndNotifyOnSyncBeforeKeyHierarchyReady {
+ // Test starts with a key hierarchy in cloudkit and the TLK having arrived
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+
+ // But block CloudKit fetches (so the key hierarchy won't be ready when we add this new item)
+ [self holdCloudKitFetches];
+
+ [self startCKKSSubsystem];
+ [self.keychainView.viewSetupOperation waitUntilFinished];
+
+ NSMutableDictionary* query = [@{
+ (id)kSecClass : (id)kSecClassGenericPassword,
+ (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
+ (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
+ (id)kSecAttrAccount : @"testaccount",
+ (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+ (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
+ (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
+ } mutableCopy];
+
+ XCTestExpectation* blockExpectation = [self expectationWithDescription: @"callback occurs"];
+
+ XCTAssertEqual(errSecSuccess, _SecItemAddAndNotifyOnSync((__bridge CFDictionaryRef) query, NULL, ^(bool didSync, CFErrorRef error) {
+ XCTAssertTrue(didSync, "Item synced");
+ XCTAssertNil((__bridge NSError*)error, "Shouldn't have received an error syncing item");
+
+ [blockExpectation fulfill];
+ }), @"_SecItemAddAndNotifyOnSync succeeded");
+
+ // We should be in the 'initialized' state, but no further
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateInitialized] wait:100*NSEC_PER_MSEC], @"Should have reached key state 'initialized', but no further");
+
+ // When we release the fetch, the callback should still fire and the item should upload
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID];
+ [self releaseCloudKitFetchHold];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:5*NSEC_PER_SEC], @"Should have reached key state 'ready'");
+
+ // Verify that the item was written to CloudKit
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self waitForExpectationsWithTimeout:5.0 handler:nil];
+}
+
- (BOOL (^) (CKRecord*)) checkPCSFieldsBlock: (CKRecordZoneID*) zoneID
PCSServiceIdentifier:(NSNumber*)servIdentifier
PCSPublicKey:(NSData*)publicKey
OCMVerifyAllWithDelay(self.mockDatabase, 8);
// We expect a key hierarchy upload, and then the class C item upload
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
dispatch_semaphore_t resetSemaphore = dispatch_semaphore_create(0);
OCMVerifyAllWithDelay(self.mockDatabase, 8);
}
+- (void)testResetCloudKitZoneDuringWaitForTLK {
+ // Test starts with nothing in database, but one in our fake CloudKit.
+ // No TLK, though!
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ // No records should be uploaded
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:8*NSEC_PER_SEC], "CKKS should have entered waitfortlk");
+
+ // Restart CKKS to really get in the spirit of waitfortlk (and get a pending processOutgoingQueue operation going)
+ self.keychainView = [[CKKSViewManager manager] restartZone: self.keychainZoneID.zoneName];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:8*NSEC_PER_SEC], "CKKS entered waitfortlk");
+
+ CKKSOutgoingQueueOperation* outgoingOp = [self.keychainView processOutgoingQueue:nil];
+ XCTAssertTrue([outgoingOp isPending], "outgoing queue processing should be on hold");
+
+ // Now, reset everything. The outgoingOp should get cancelled.
+ // We expect a key hierarchy upload, and then the class C item upload
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords:3 zoneID:self.keychainZoneID];
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+
+ XCTestExpectation* resetExpectation = [self expectationWithDescription: @"reset callback occurs"];
+ [self.injectedManager rpcResetCloudKit:nil reply:^(NSError* result) {
+ XCTAssertNil(result, "no error resetting cloudkit");
+ [resetExpectation fulfill];
+ }];
+ [self waitForExpectations:@[resetExpectation] timeout:8.0];
+
+ XCTAssertTrue([outgoingOp isCancelled], "old stuck ProcessOutgoingQueue should be cancelled");
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // And adding another item works too
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID checkItem: [self checkClassABlock:self.keychainZoneID message:@"Object was encrypted under class A key in hierarchy"]];
+ [self addGenericPassword:@"asdf"
+ account:@"account-class-A"
+ viewHint:nil
+ access:(id)kSecAttrAccessibleWhenUnlocked
+ expecting:errSecSuccess
+ message:@"Adding class A item"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
+/*
+ * This test doesn't work, since the resetLocal fails. CKKS gets back into waitfortlk
+ * but that isn't considered a successful resetLocal.
+ *
+- (void)testResetLocalDuringWaitForTLK {
+ // Test starts with nothing in database, but one in our fake CloudKit.
+ // No TLK, though!
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ // No records should be uploaded
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:8*NSEC_PER_SEC], "CKKS should have entered waitfortlk");
+
+ // Restart CKKS to really get in the spirit of waitfortlk (and get a pending processOutgoingQueue operation going)
+ self.keychainView = [[CKKSViewManager manager] restartZone: self.keychainZoneID.zoneName];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:8*NSEC_PER_SEC], "CKKS entered waitfortlk");
+
+ CKKSOutgoingQueueOperation* outgoingOp = [self.keychainView processOutgoingQueue:nil];
+ XCTAssertTrue([outgoingOp isPending], "outgoing queue processing should be on hold");
+
+ // Now, reset everything. The outgoingOp should get cancelled.
+ // We expect a key hierarchy upload, and then the class C item upload
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords:3 zoneID:self.keychainZoneID];
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+
+ XCTestExpectation* resetExpectation = [self expectationWithDescription: @"reset callback occurs"];
+ [self.injectedManager rpcResetLocal:nil reply:^(NSError* result) {
+ XCTAssertNil(result, "no error resetting local");
+ [resetExpectation fulfill];
+ }];
+ [self waitForExpectations:@[resetExpectation] timeout:8.0];
+
+ XCTAssertTrue([outgoingOp isCancelled], "old stuck ProcessOutgoingQueue should be cancelled");
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // And adding another item works too
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID checkItem: [self checkClassABlock:self.keychainZoneID message:@"Object was encrypted under class A key in hierarchy"]];
+ [self addGenericPassword:@"asdf"
+ account:@"account-class-A"
+ viewHint:nil
+ access:(id)kSecAttrAccessibleWhenUnlocked
+ expecting:errSecSuccess
+ message:@"Adding class A item"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}*/
+
-(void)testResetCloudKitZoneWhileLoggedOut {
// We're "logged in to" cloudkit but not in circle.
self.circleStatus = kSOSCCNotInCircle;
// Spin up CKKS subsystem.
[self startCKKSSubsystem];
+ [self.keychainView.viewSetupOperation waitUntilFinished];
+ // Reset setup, since that's the most likely state to be in (33866282)
+ [self.keychainView resetSetup];
+
CKRecord* ckr = [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-F9C5-481E-98AC-5A507ACB2D85"];
[self.keychainZone addToZone: ckr];
self.circleStatus = kSOSCCInCircle;
[self.accountStateTracker notifyCircleStatusChangeAndWaitForSignal];
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID checkItem:[self checkClassABlock:self.keychainZoneID message:@"Object was encrypted under class A key in hierarchy"]];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
}
+- (void)testRPCTLKMissingWhenMissing {
+ // Bring CKKS up in waitfortlk
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:8*NSEC_PER_SEC], "CKKS entered waitfortlk");
+
+ XCTestExpectation* callbackOccurs = [self expectationWithDescription:@"callback-occurs"];
+
+ [self.ckksControl rpcTLKMissing:@"keychain" reply:^(bool missing) {
+ XCTAssertTrue(missing, "TLKs should be missing");
+ [callbackOccurs fulfill];
+ }];
+
+ [self waitForExpectations:@[callbackOccurs] timeout:5.0];
+}
+
+- (void)testRPCTLKMissingWhenFound {
+ // Bring CKKS up in waitfortlk
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:8*NSEC_PER_SEC], "CKKS entered 'ready''");
+
+ XCTestExpectation* callbackOccurs = [self expectationWithDescription:@"callback-occurs"];
+
+ [self.ckksControl rpcTLKMissing:@"keychain" reply:^(bool missing) {
+ XCTAssertFalse(missing, "TLKs should not be missing");
+ [callbackOccurs fulfill];
+ }];
+
+ [self waitForExpectations:@[callbackOccurs] timeout:5.0];
+}
+
@end
#endif // OCTAGON
[self waitForExpectations:@[keychainChanged] timeout:1];
- // Check that the record is where we expect it in CloudKit
+ // And a second item
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkPCSFieldsBlock:self.keychainZoneID
+ PCSServiceIdentifier:(NSNumber *)servIdentifier
+ PCSPublicKey:publicKey
+ PCSPublicIdentity:publicIdentity]];
+ result = [self pcsAddItem:@"testaccount2"
+ data:[@"asdf" dataUsingEncoding:NSUTF8StringEncoding]
+ serviceIdentifier:(NSNumber*)servIdentifier
+ publicKey:(NSData*)publicKey
+ publicIdentity:(NSData*)publicIdentity
+ expectingSync:true];
+ XCTAssertNotNil(result, "Received result from adding item");
+ NSData* persistentRef2 = result[(id)kSecValuePersistentRef];
+
+ // Check that the records are where we expect them in CloudKit
[self waitForCKModifications];
CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
+ CKRecordID* pcsItemRecordID2 = [[CKRecordID alloc] initWithRecordName: @"3AB8E78D-75AF-CFEF-F833-FA3E3E90978A" zoneID:self.keychainZoneID];
+ CKRecord* record2 = self.keychainZone.currentDatabase[pcsItemRecordID2];
+ XCTAssertNotNil(record2, "Found 2nd record in CloudKit at expected UUID");
+
// Still no current pointer.
[self fetchCurrentPointerExpectingError:false];
[self.keychainView waitForFetchAndIncomingQueueProcessing];
[self waitForExpectations:@[keychainChanged] timeout:1];
-
[self fetchCurrentPointer:false persistentRef:persistentRef];
+ // And again!
+ CKKSCurrentItemPointer* cip2 = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice"
+ currentItemUUID:pcsItemRecordID2.recordName
+ state:SecCKKSProcessedStateRemote
+ zoneID:self.keychainZoneID
+ encodedCKRecord:nil];
+ [self.keychainZone addToZone: [cip2 CKRecordWithZoneID:self.keychainZoneID]];
+ CKRecordID* currentPointerRecordID2 = [[CKRecordID alloc] initWithRecordName: @"com.apple.security.ckks-pcsservice" zoneID:self.keychainZoneID];
+ CKRecord* currentPointerRecord2 = self.keychainZone.currentDatabase[currentPointerRecordID2];
+ XCTAssertNotNil(currentPointerRecord2, "Found record in CloudKit at expected UUID");
+
+ keychainChanged = [self expectChangeForView:self.keychainZoneID.zoneName];
+
+ [self.keychainView notifyZoneChange:nil];
+ [self.keychainView waitForFetchAndIncomingQueueProcessing];
+
+ [self waitForExpectations:@[keychainChanged] timeout:1];
+ [self fetchCurrentPointer:false persistentRef:persistentRef2];
+
SecResetLocalSecuritydXPCFakeEntitlements();
}
SecResetLocalSecuritydXPCFakeEntitlements();
}
+- (void)testPCSCurrentRecoverFromDanglingPointer {
+ SecResetLocalSecuritydXPCFakeEntitlements();
+ SecAddLocalSecuritydXPCFakeEntitlement(kSecEntitlementPrivateCKKSPlaintextFields, kCFBooleanTrue);
+ SecAddLocalSecuritydXPCFakeEntitlement(kSecEntitlementPrivateCKKSWriteCurrentItemPointers, kCFBooleanTrue);
+ SecAddLocalSecuritydXPCFakeEntitlement(kSecEntitlementPrivateCKKSReadCurrentItemPointers, kCFBooleanTrue);
+
+ NSNumber* servIdentifier = @3;
+ NSData* publicKey = [@"asdfasdf" dataUsingEncoding:NSUTF8StringEncoding];
+ NSData* publicIdentity = [@"somedata" dataUsingEncoding:NSUTF8StringEncoding];
+
+ [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
+ [self startCKKSSubsystem];
+
+ // Let things shake themselves out.
+ [self.keychainView waitForKeyHierarchyReadiness];
+
+ // Ensure there's no current pointer
+ [self fetchCurrentPointerExpectingError:false];
+
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkPCSFieldsBlock:self.keychainZoneID
+ PCSServiceIdentifier:(NSNumber *)servIdentifier
+ PCSPublicKey:publicKey
+ PCSPublicIdentity:publicIdentity]];
+
+ NSDictionary* result = [self pcsAddItem:@"testaccount"
+ data:[@"asdf" dataUsingEncoding:NSUTF8StringEncoding]
+ serviceIdentifier:(NSNumber*)servIdentifier
+ publicKey:(NSData*)publicKey
+ publicIdentity:(NSData*)publicIdentity
+ expectingSync:true];
+ XCTAssertNotNil(result, "Received result from adding item");
+
+ // Check that the record is where we expect it in CloudKit
+ [self waitForCKModifications];
+ CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+ CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
+ XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
+
+ NSData* persistentRef = result[(id)kSecValuePersistentRef];
+ NSData* sha1 = result[(id)kSecAttrSHA1];
+
+ [self expectCKModifyRecords:@{SecCKRecordCurrentItemType: [NSNumber numberWithUnsignedInteger: 1]}
+ deletedRecordTypeCounts:nil
+ zoneID:self.keychainZoneID
+ checkModifiedRecord:nil
+ runAfterModification:nil];
+
+ // Set the 'current' pointer.
+ XCTestExpectation* setCurrentExpectation = [self expectationWithDescription: @"callback occurs"];
+
+ // Ensure that setting the current pointer sends a notification
+ SecItemSetCurrentItemAcrossAllDevices((__bridge CFStringRef)@"com.apple.security.ckks",
+ (__bridge CFStringRef)@"pcsservice",
+ (__bridge CFStringRef)@"keychain",
+ (__bridge CFDataRef)persistentRef,
+ (__bridge CFDataRef)sha1, NULL, NULL, ^ (CFErrorRef cferror) {
+ NSError* error = (__bridge NSError*)cferror;
+ XCTAssertNil(error, "No error setting current item");
+ [setCurrentExpectation fulfill];
+ });
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+ [self waitForCKModifications];
+
+ [self waitForExpectationsWithTimeout:8.0 handler:nil];
+
+ // Delete the keychain item
+ [self expectCKDeleteItemRecords:1 zoneID:self.keychainZoneID];
+ XCTAssertEqual(errSecSuccess, SecItemDelete((__bridge CFDictionaryRef)@{
+ (id)kSecClass : (id)kSecClassGenericPassword,
+ (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
+ (id)kSecAttrAccount:@"testaccount",
+ (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+ }), "Should receive no error deleting item");
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // Now, fetch the current pointer: we should get an error
+ [self fetchCurrentPointerExpectingError:false];
+
+ // Setting the current item pointer again, using a NULL old value, should work.
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
+ checkItem: [self checkPCSFieldsBlock:self.keychainZoneID
+ PCSServiceIdentifier:(NSNumber *)servIdentifier
+ PCSPublicKey:publicKey
+ PCSPublicIdentity:publicIdentity]];
+
+ result = [self pcsAddItem:@"testaccount2"
+ data:[@"asdf" dataUsingEncoding:NSUTF8StringEncoding]
+ serviceIdentifier:(NSNumber*)servIdentifier
+ publicKey:(NSData*)publicKey
+ publicIdentity:(NSData*)publicIdentity
+ expectingSync:true];
+ XCTAssertNotNil(result, "Should have result from adding item2");
+
+ persistentRef = result[(id)kSecValuePersistentRef];
+ sha1 = result[(id)kSecAttrSHA1];
+
+ [self expectCKModifyRecords:@{SecCKRecordCurrentItemType: [NSNumber numberWithUnsignedInteger: 1]}
+ deletedRecordTypeCounts:nil
+ zoneID:self.keychainZoneID
+ checkModifiedRecord:nil
+ runAfterModification:nil];
+
+ // Set the 'current' pointer.
+ setCurrentExpectation = [self expectationWithDescription: @"callback occurs"];
+
+ // Ensure that setting the current pointer sends a notification
+ SecItemSetCurrentItemAcrossAllDevices((__bridge CFStringRef)@"com.apple.security.ckks",
+ (__bridge CFStringRef)@"pcsservice",
+ (__bridge CFStringRef)@"keychain",
+ (__bridge CFDataRef)persistentRef,
+ (__bridge CFDataRef)sha1, NULL, NULL, ^ (CFErrorRef cferror) {
+ NSError* error = (__bridge NSError*)cferror;
+ XCTAssertNil(error, "No error setting current item");
+ [setCurrentExpectation fulfill];
+ });
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+ [self waitForCKModifications];
+ [self waitForExpectationsWithTimeout:8.0 handler:nil];
+
+ SecResetLocalSecuritydXPCFakeEntitlements();
+}
+
@end
#endif // OCTAGON
self.keychainZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"keychain" ownerName:CKCurrentUserDefaultName];
self.keychainZone = [[FakeCKZone alloc] initZone: self.keychainZoneID];
- SFECKeyPair* keyPair = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
- [CKKSManifestInjectionPointHelper registerEgoPeerID:@"MeMyselfAndI" keyPair:keyPair];
-
// Wait for the ViewManager to be brought up
XCTAssertEqual(0, [self.injectedManager.completedSecCKKSInitialize wait:4*NSEC_PER_SEC], "No timeout waiting for SecCKKSInitialize");
NSDictionary* status = [self.keychainView status];
(void)status;
+ [self.keychainView cancelAllOperations];
+ [self.keychainView waitUntilAllOperationsAreFinished];
+
self.keychainView = nil;
self.keychainZoneID = nil;
- (void)testUploadInitialKeyHierarchy {
// Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
// Spin up CKKS subsystem.
[self startCKKSSubsystem];
}
// After unlock, the key hierarchy should be created.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
self.aksLockState = false;
[self.lockStateTracker recheck];
- (void)testUploadAndUseKeyHierarchy {
// Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self startCKKSSubsystem];
- (void)testUploadInitialKeyHierarchyTriggersBackup {
// Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
// We also expect the view manager's notifyNewTLKsInKeychain call to fire (after some delay)
- id mockVM = OCMPartialMock(self.injectedManager);
- OCMExpect([mockVM notifyNewTLKsInKeychain]);
+ OCMExpect([self.mockCKKSViewManager notifyNewTLKsInKeychain]);
// Spin up CKKS subsystem.
[self startCKKSSubsystem];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
- OCMVerifyAllWithDelay(mockVM, 10);
-
- [mockVM stopMocking];
+ OCMVerifyAllWithDelay(self.mockCKKSViewManager, 10);
}
- (void)testAcceptExistingKeyHierarchy {
- (void)testAcceptExistingKeyHierarchyDespiteLocked {
// Test starts with no keys in CKKS database, but one in our fake CloudKit.
// Test also begins with the TLK having arrived in the local keychain (via SOS)
- // However, the CKKSKeychainView's "checkTLK" method should return a keychain error the first time through, indicating that the keybag is locked
+ // However, the CKKSKeychainView's "_onqueueWithAccountKeysCheckTLK" method should return a keychain error the first time through, indicating that the keybag is locked
[self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
[self saveTLKMaterialToKeychain:self.keychainZoneID];
[self.lockStateTracker recheck];
id partialKVMock = OCMPartialMock(self.keychainView);
- OCMExpect([partialKVMock checkTLK: [OCMArg any] error: [OCMArg setTo:[[NSError alloc] initWithDomain:@"securityd" code:errSecInteractionNotAllowed userInfo:nil]]]).andReturn(false);
+ OCMExpect([partialKVMock _onqueueWithAccountKeysCheckTLK: [OCMArg any] error: [OCMArg setTo:[[NSError alloc] initWithDomain:@"securityd" code:errSecInteractionNotAllowed userInfo:nil]]]).andReturn(false);
// Spin up CKKS subsystem.
[self startCKKSSubsystem];
XCTAssertNotNil(self.keychainZoneKeys.classA, "Have class A key for zone");
XCTAssertNotNil(self.keychainZoneKeys.classC, "Have class C key for zone");
+ [self.keychainView dispatchSync: ^bool {
+ [self.keychainView _onqueueKeyStateMachineRequestProcess];
+ return true;
+ }];
+ // And ensure we end up back in 'ready': we have the keys, we're just locked now
+ [self.keychainView waitForOperationsOfClass:[CKKSProcessReceivedKeysOperation class]];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:5*NSEC_PER_SEC], "Key state should have returned to ready");
+
[self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-F9C5-481E-98AC-5A507ACB2D85" withAccount:@"classCItem" key:self.keychainZoneKeys.classC]];
[self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-FFFF-FFFF-FFFF-5A507ACB2D85" withAccount:@"classAItem" key:self.keychainZoneKeys.classA]];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
}
+- (void)testRecoverFromRequestKeyRefetchWithoutRolling {
+ // Simply requesting a key state refetch shouldn't roll the key hierarchy.
+
+ [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ // Items should upload.
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self waitForCKModifications];
+
+
+ // CKKS should not roll the keys while progressing back to 'ready', but it will fetch once
+ self.silentFetchesAllowed = false;
+ [self expectCKFetch];
+
+ [self.keychainView dispatchSync: ^bool {
+ [self.keychainView _onqueueKeyStateMachineRequestFetch];
+ return true;
+ }];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:5*NSEC_PER_SEC], "Key state should have returned to ready");
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
+- (void)testRecoverFromIncrementedCurrentKeyPointerEtag {
+ // CloudKit sometimes reports the current key pointers have changed (etag mismatch), but their content hasn't.
+ // In this case, CKKS shouldn't roll the TLK.
+
+ [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ // Items should upload.
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self waitForCKModifications];
+
+ // Bump the etag on the class C current key record, but don't change any data
+ CKRecordID* currentClassCID = [[CKRecordID alloc] initWithRecordName: @"classC" zoneID: self.keychainZoneID];
+ CKRecord* currentClassC = self.keychainZone.currentDatabase[currentClassCID];
+ XCTAssertNotNil(currentClassC, "Should have the class C current key pointer record");
+
+ [self.keychainZone addCKRecordToZone:[currentClassC copy]];
+ XCTAssertNotEqualObjects(currentClassC.etag, self.keychainZone.currentDatabase[currentClassCID].etag, "Etag should have changed");
+
+ // Add another item. This write should fail, then CKKS should recover without rolling the key hierarchy or issuing a fetch.
+ self.silentFetchesAllowed = false;
+ [self expectCKAtomicModifyItemRecordsUpdateFailure:self.keychainZoneID];
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID];
+ [self addGenericPassword: @"data" account: @"account-delete-me-2"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
- (void)testOnboardOldItems {
// In this test, we'll check if the CKKS subsystem will pick up a keychain item which existed before the key hierarchy, both with and without a UUID attached at item creation
[self addGenericPassword: @"data" account: @"account-delete-me-with-UUID" expecting:errSecSuccess message: @"Add item (w/ UUID) to keychain"];
// We expect an upload of the key hierarchy
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
// We then expect an upload of the added items
[self expectCKModifyItemRecords: 2 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID];
- (void)testMultipleZoneAdd {
// Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
// Bring up a new zone: we expect a key hierarchy upload.
[self.injectedManager findOrCreateView:(id)kSecAttrViewHintAppleTV];
CKRecordZoneID* appleTVZoneID = [[CKRecordZoneID alloc] initWithZoneName:(__bridge NSString*) kSecAttrViewHintAppleTV ownerName:CKCurrentUserDefaultName];
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:appleTVZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:appleTVZoneID];
// We also expect the view manager's notifyNewTLKsInKeychain call to fire once (after some delay)
- id mockVM = OCMPartialMock(self.injectedManager);
- OCMExpect([mockVM notifyNewTLKsInKeychain]);
+ OCMExpect([self.mockCKKSViewManager notifyNewTLKsInKeychain]);
// Let the horses loose
[self startCKKSSubsystem];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
- OCMVerifyAllWithDelay(mockVM, 10);
- [mockVM stopMocking];
+ OCMVerifyAllWithDelay(self.mockCKKSViewManager, 10);
}
- (void)testMultipleZoneDelete {
// Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self startCKKSSubsystem];
// Bring up a new zone: we expect a key hierarchy and an item.
[self.injectedManager findOrCreateView:(id)kSecAttrViewHintAppleTV];
CKRecordZoneID* appleTVZoneID = [[CKRecordZoneID alloc] initWithZoneName:(__bridge NSString*) kSecAttrViewHintAppleTV ownerName:CKCurrentUserDefaultName];
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:appleTVZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:appleTVZoneID];
[self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:appleTVZoneID];
[self addGenericPassword: @"atv"
// Restarting the CKKS operation should check that it's been 15 minutes since the last fetch before it fetches again. Simulate this.
// Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self startCKKSSubsystem];
[self.keychainView waitForKeyHierarchyReadiness];
[self startCKKSSubsystem];
// The CKKS subsystem should figure out the issue, and fix it.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self.keychainView waitForKeyHierarchyReadiness];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[self startCKKSSubsystem];
// The CKKS subsystem should figure out the issue, and fix it.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self.keychainView waitForKeyHierarchyReadiness];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[self startCKKSSubsystem];
// The CKKS subsystem should figure out the issue, and fix it.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self.keychainView waitForKeyHierarchyReadiness];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[self startCKKSSubsystem];
// The CKKS subsystem should figure out the issue, and fix it.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self.keychainView waitForKeyHierarchyReadiness];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[self startCKKSSubsystem];
// The CKKS subsystem should figure out the issue, and fix it.
- [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords: 3 tlkShareRecords: 0 zoneID:self.keychainZoneID];
[self.keychainView waitForKeyHierarchyReadiness];
[self startCKKSSubsystem];
// The CKKS subsystem should figure out the issue, and fix it.
- [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords: 3 tlkShareRecords: 0 zoneID:self.keychainZoneID];
[self.keychainView waitForKeyHierarchyReadiness];
- (void)testRecoverFromDeletedKeysNewItem {
// Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self startCKKSSubsystem];
[self.keychainView waitForKeyHierarchyReadiness];
- (void)testRecoverFromDeletedKeysReceive {
// Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self startCKKSSubsystem];
[self.keychainView waitForKeyHierarchyReadiness];
// If the TLK disappears halfway through, well, that's no good. But we should make it into waitfortlk.
// Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self startCKKSSubsystem];
[self.keychainView waitForKeyHierarchyReadiness];
[self.keychainView waitForFetchAndIncomingQueueProcessing];
[self.keychainView waitForOperationsOfClass:[CKKSHealKeyHierarchyOperation class]];
- XCTAssertEqual(self.keychainView.keyHierarchyState, SecCKKSZoneKeyStateWaitForTLK, "CKKS re-entered waitfortlk");
+ XCTAssertEqualObjects(self.keychainView.keyHierarchyState, SecCKKSZoneKeyStateWaitForTLK, "CKKS re-entered waitfortlk");
}
- (void)testRecoverFromBadCurrentKeyPointer {
[self startCKKSSubsystem];
// The CKKS subsystem should figure out the issue, and fix it.
- [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 0 currentKeyPointerRecords: 3 tlkShareRecords: 0 zoneID:self.keychainZoneID];
[self.keychainView waitForKeyHierarchyReadiness];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
}
+
- (void)testRecoverFromCloudKitFetchFail {
// Test starts with nothing in database, but one in our fake CloudKit.
[self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
[self.keychainZone failNextFetchWith:[[NSError alloc] initWithDomain:CKErrorDomain code:CKErrorUserDeletedZone userInfo:@{}]];
// We expect a key hierarchy upload, and then the class C item upload
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
[self.keychainView notifyZoneChange:nil];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
}
-- (void)testRecoverFromCloudKitZoneNotFound {
+- (void)testRecoverFromCloudKitZoneNotFoundZoneDeletionSuccess {
// Test starts with nothing in database, but one in our fake CloudKit.
[self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
[self addGenericPassword: @"data" account: @"account-delete-me"];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
+ [self waitForCKModifications];
+ self.zones[self.keychainZoneID] = nil; // delete the autocreated zone
+
// The next CKRecordZoneChanges should fail with a 'zone not found' error.
+ // BUT: when it goes to delete the zone as part of the reset, that should succeed. So, we won't delete the zone here.
NSError* zoneNotFoundError = [[CKPrettyError alloc] initWithDomain:CKErrorDomain
code:CKErrorZoneNotFound
userInfo:@{}];
userInfo:@{CKPartialErrorsByItemIDKey: @{self.keychainZoneID:zoneNotFoundError}}];
[self.keychainZone failNextFetchWith:error];
- // We expect a key hierarchy upload, and then the class C item upload
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ // We expect CKKS to reset itself and recover, then a key hierarchy upload, and then the class C item upload
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
[self.keychainView notifyZoneChange:nil];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
}
+- (void)testRecoverFromCloudKitZoneNotFoundZoneDeletionFail {
+ // Test starts with nothing in database, but one in our fake CloudKit.
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+
+ // Spin up CKKS subsystem.
+ [self startCKKSSubsystem];
+
+ // Now, save the TLK to the keychain (to simulate it coming in later via SOS).
+ [self saveTLKMaterialToKeychainSimulatingSOS:self.keychainZoneID];
+
+ // We expect a single record to be uploaded
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self addGenericPassword: @"data" account: @"account-delete-me"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self waitForCKModifications];
+ self.zones[self.keychainZoneID] = nil; // delete the autocreated zone
+
+ // We expect CKKS to reset itself and recover, then a key hierarchy upload, and then the class C item upload
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+
+ [self.keychainView notifyZoneChange:nil];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // And check that a new upload occurs.
+ [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID checkItem: [self checkClassABlock:self.keychainZoneID message:@"Object was encrypted under class A key in hierarchy"]];
+
+ [self addGenericPassword:@"asdf"
+ account:@"account-class-A"
+ viewHint:nil
+ access:(id)kSecAttrAccessibleWhenUnlocked
+ expecting:errSecSuccess
+ message:@"Adding class A item"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
- (void)testNoCloudKitAccount {
// Test starts with nothing in database and the user logged out of CloudKit. We expect no CKKS operations.
self.accountStatus = CKAccountStatusNoAccount;
OCMVerifyAllWithDelay(self.mockDatabase, 8);
// We expect some sort of TLK/key hierarchy upload once we are notified of entering the circle.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
self.circleStatus = kSOSCCInCircle;
[self.accountStateTracker notifyCircleStatusChangeAndWaitForSignal];
- (void)testCloudKitLogoutLogin {
// Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
[self startCKKSSubsystem];
// Now that we're here (and handleCKLogout hasn't been called), bring the account up
// We expect some sort of TLK/key hierarchy upload once we are notified of entering the circle.
- [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 zoneID:self.keychainZoneID];
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords: 1 zoneID:self.keychainZoneID];
// We expect a single class C record to be uploaded.
[self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
}
runAfterModification:nil];
- [self.keychainView updateDeviceState:false ckoperationGroup:nil];
+ [self.keychainView updateDeviceState:false waitForKeyHierarchyInitialization:2*NSEC_PER_SEC ckoperationGroup:nil];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
}
}
runAfterModification:nil];
- CKKSUpdateDeviceStateOperation* op = [self.keychainView updateDeviceState:true ckoperationGroup:nil];
+ CKKSUpdateDeviceStateOperation* op = [self.keychainView updateDeviceState:true waitForKeyHierarchyInitialization:2*NSEC_PER_SEC ckoperationGroup:nil];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[op waitUntilFinished];
// Check that an immediate rate-limited retry doesn't upload anything
- op = [self.keychainView updateDeviceState:true ckoperationGroup:nil];
+ op = [self.keychainView updateDeviceState:true waitForKeyHierarchyInitialization:2*NSEC_PER_SEC ckoperationGroup:nil];
[op waitUntilFinished];
// But not rate-limiting works just fine!
zoneID:self.keychainZoneID
checkModifiedRecord:nil
runAfterModification:nil];
- op = [self.keychainView updateDeviceState:false ckoperationGroup:nil];
+ op = [self.keychainView updateDeviceState:false waitForKeyHierarchyInitialization:2*NSEC_PER_SEC ckoperationGroup:nil];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[op waitUntilFinished];
zoneID:self.keychainZoneID
checkModifiedRecord:nil
runAfterModification:nil];
- op = [self.keychainView updateDeviceState:true ckoperationGroup:nil];
+ op = [self.keychainView updateDeviceState:true waitForKeyHierarchyInitialization:2*NSEC_PER_SEC ckoperationGroup:nil];
OCMVerifyAllWithDelay(self.mockDatabase, 12);
[op waitUntilFinished];
}
OCMVerifyAllWithDelay(self.mockDatabase, 8);
// Check that an immediate rate-limited retry doesn't upload anything
- CKKSUpdateDeviceStateOperation* op = [self.keychainView updateDeviceState:true ckoperationGroup:nil];
+ CKKSUpdateDeviceStateOperation* op = [self.keychainView updateDeviceState:true waitForKeyHierarchyInitialization:2*NSEC_PER_SEC ckoperationGroup:nil];
[op waitUntilFinished];
}
+- (void)testDeviceStateUploadWaitsForKeyHierarchy {
+ [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
+
+ __weak __typeof(self) weakSelf = self;
+ // Expect a ready upload
+ [self expectCKModifyRecords: @{SecCKRecordDeviceStateType: [NSNumber numberWithInt:1]}
+ deletedRecordTypeCounts:nil
+ zoneID:self.keychainZoneID
+ checkModifiedRecord: ^BOOL (CKRecord* record){
+ if([record.recordType isEqualToString: SecCKRecordDeviceStateType]) {
+ __strong __typeof(weakSelf) strongSelf = weakSelf;
+ XCTAssertNotNil(strongSelf, "self exists");
+
+ ZoneKeys* zoneKeys = strongSelf.keys[strongSelf.keychainZoneID];
+ XCTAssertNotNil(zoneKeys, "Have zone keys for %@", strongSelf.keychainZoneID);
+
+ XCTAssertEqualObjects(record[SecCKRecordCirclePeerID], @"fake-circle-id", "peer ID matches what we gave it");
+ XCTAssertEqualObjects(record[SecCKRecordCircleStatus], [NSNumber numberWithInt:kSOSCCInCircle], "device is in circle");
+ XCTAssertEqualObjects(record[SecCKRecordKeyState], CKKSZoneKeyToNumber(SecCKKSZoneKeyStateReady), "Device is in ready");
+
+ XCTAssertEqualObjects([record[SecCKRecordCurrentTLK] recordID].recordName, zoneKeys.tlk.uuid, "Correct TLK uuid");
+ XCTAssertEqualObjects([record[SecCKRecordCurrentClassA] recordID].recordName, zoneKeys.classA.uuid, "Correct class A uuid");
+ XCTAssertEqualObjects([record[SecCKRecordCurrentClassC] recordID].recordName, zoneKeys.classC.uuid, "Correct class C uuid");
+ return YES;
+ } else {
+ return NO;
+ }
+ }
+ runAfterModification:nil];
+
+ // Ensure we'll wait for quite a while if we don't become ready
+ [self.keychainView updateDeviceState:false waitForKeyHierarchyInitialization:20*NSEC_PER_SEC ckoperationGroup:nil];
+
+ // But don't allow the key state to progress until now
+ [self startCKKSSubsystem];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+}
+
- (void)testDeviceStateReceive {
[self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
[self startCKKSSubsystem];
XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:8*NSEC_PER_SEC], "CKKS entered waitfortlk");
- XCTAssertEqual(self.keychainView.keyHierarchyState, SecCKKSZoneKeyStateWaitForTLK, "CKKS entered waitfortlk");
+ XCTAssertEqualObjects(self.keychainView.keyHierarchyState, SecCKKSZoneKeyStateWaitForTLK, "CKKS entered waitfortlk");
__weak __typeof(self) weakSelf = self;
[self expectCKModifyRecords: @{SecCKRecordDeviceStateType: [NSNumber numberWithInt:1]}
}
runAfterModification:nil];
- [self.keychainView updateDeviceState:false ckoperationGroup:nil];
+ [self.keychainView updateDeviceState:false waitForKeyHierarchyInitialization:500*NSEC_PER_MSEC ckoperationGroup:nil];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
}
[self startCKKSSubsystem];
XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:8*NSEC_PER_SEC], "CKKS entered waitfortlk");
- XCTAssertEqual(self.keychainView.keyHierarchyState, SecCKKSZoneKeyStateWaitForTLK, "CKKS entered waitfortlk");
+ XCTAssertEqualObjects(self.keychainView.keyHierarchyState, SecCKKSZoneKeyStateWaitForTLK, "CKKS entered waitfortlk");
// And restart CKKS...
self.keychainView = [[CKKSViewManager manager] restartZone: self.keychainZoneID.zoneName];
XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:8*NSEC_PER_SEC], "CKKS entered waitfortlk");
- XCTAssertEqual(self.keychainView.keyHierarchyState, SecCKKSZoneKeyStateWaitForTLK, "CKKS entered waitfortlk");
+ XCTAssertEqualObjects(self.keychainView.keyHierarchyState, SecCKKSZoneKeyStateWaitForTLK, "CKKS entered waitfortlk");
__weak __typeof(self) weakSelf = self;
[self expectCKModifyRecords: @{SecCKRecordDeviceStateType: [NSNumber numberWithInt:1]}
}
runAfterModification:nil];
- [self.keychainView updateDeviceState:false ckoperationGroup:nil];
+ [self.keychainView updateDeviceState:false waitForKeyHierarchyInitialization:500*NSEC_PER_MSEC ckoperationGroup:nil];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
}
// Since CKKS should start up enough to get back into the error state and then back into initializing state, wait for that to happen.
XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateError] wait:8*NSEC_PER_SEC], "CKKS entered error");
XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateInitializing] wait:8*NSEC_PER_SEC], "CKKS entered initializing");
- XCTAssertEqual(self.keychainView.keyHierarchyState, SecCKKSZoneKeyStateInitializing, "CKKS entered intializing");
+ XCTAssertEqualObjects(self.keychainView.keyHierarchyState, SecCKKSZoneKeyStateInitializing, "CKKS entered intializing");
__weak __typeof(self) weakSelf = self;
[self expectCKModifyRecords: @{SecCKRecordDeviceStateType: [NSNumber numberWithInt:1]}
}
runAfterModification:nil];
- CKKSUpdateDeviceStateOperation* op = [self.keychainView updateDeviceState:false ckoperationGroup:nil];
+ CKKSUpdateDeviceStateOperation* op = [self.keychainView updateDeviceState:false waitForKeyHierarchyInitialization:500*NSEC_PER_MSEC ckoperationGroup:nil];
OCMVerifyAllWithDelay(self.mockDatabase, 8);
[op waitUntilFinished];
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import <CloudKit/CloudKit.h>
+#import <XCTest/XCTest.h>
+#import <OCMock/OCMock.h>
+
+#import "keychain/ckks/tests/CloudKitMockXCTest.h"
+#import "keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.h"
+#import "keychain/ckks/CKKS.h"
+#import "keychain/ckks/CKKSFixups.h"
+#import "keychain/ckks/CKKSZoneStateEntry.h"
+#import "keychain/ckks/CKKSViewManager.h"
+#import "keychain/ckks/CKKSCurrentItemPointer.h"
+#import "keychain/ckks/CKKSIncomingQueueOperation.h"
+
+#import "keychain/ckks/tests/MockCloudKit.h"
+#import "keychain/ckks/tests/CKKSTests.h"
+#import "keychain/ckks/tests/CKKSTests+API.h"
+
+
+@interface CloudKitKeychainSyncingFixupTests : CloudKitKeychainSyncingTestsBase
+@end
+
+@implementation CloudKitKeychainSyncingFixupTests
+
+- (void)testNoFixupOnInitialStart {
+ id mockFixups = OCMClassMock([CKKSFixups class]);
+ OCMReject([[[mockFixups stub] ignoringNonObjectArgs] fixup:0 for:[OCMArg any]]);
+
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ [self.keychainView waitForKeyHierarchyReadiness];
+ [self waitForCKModifications];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [mockFixups verify];
+ [mockFixups stopMocking];
+}
+
+- (void)testImmediateRestartUsesLatestFixup {
+ id mockFixups = OCMClassMock([CKKSFixups class]);
+ OCMExpect([mockFixups fixup:CKKSCurrentFixupNumber for:[OCMArg any]]);
+
+ // Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
+ [self expectCKModifyKeyRecords: 3 currentKeyPointerRecords: 3 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ [self.keychainView waitForKeyHierarchyReadiness];
+ [self waitForCKModifications];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // Tear down the CKKS object
+ [self.keychainView cancelAllOperations];
+
+ self.keychainView = [[CKKSViewManager manager] restartZone:self.keychainZoneID.zoneName];
+ [self.keychainView waitForKeyHierarchyReadiness];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [mockFixups verify];
+ [mockFixups stopMocking];
+}
+
+- (void)testFixupRefetchAllCurrentItemPointers {
+ // Due to <rdar://problem/34916549> CKKS: current item pointer CKRecord resurrection,
+ // CKKS needs to refetch all current item pointers if it restarts and hasn't yet.
+
+ // Test starts with no keys in database. We expect some sort of TLK/key hierarchy upload.
+ [self expectCKModifyKeyRecords:3 currentKeyPointerRecords:3 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ [self.keychainView waitForKeyHierarchyReadiness];
+ [self waitForCKModifications];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // Add some current item pointers. They don't necessarily need to point to anything...
+
+ CKKSCurrentItemPointer* cip = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice"
+ currentItemUUID:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3"
+ state:SecCKKSProcessedStateRemote
+ zoneID:self.keychainZoneID
+ encodedCKRecord:nil];
+ [self.keychainZone addToZone: [cip CKRecordWithZoneID:self.keychainZoneID]];
+ CKRecordID* currentPointerRecordID = [[CKRecordID alloc] initWithRecordName: @"com.apple.security.ckks-pcsservice" zoneID:self.keychainZoneID];
+ CKRecord* currentPointerRecord = self.keychainZone.currentDatabase[currentPointerRecordID];
+ XCTAssertNotNil(currentPointerRecord, "Found record in CloudKit at expected UUID");
+
+ CKKSCurrentItemPointer* cip2 = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice2"
+ currentItemUUID:@"3AB8E78D-75AF-CFEF-F833-FA3E3E90978A"
+ state:SecCKKSProcessedStateRemote
+ zoneID:self.keychainZoneID
+ encodedCKRecord:nil];
+ [self.keychainZone addToZone: [cip2 CKRecordWithZoneID:self.keychainZoneID]];
+ CKRecordID* currentPointerRecordID2 = [[CKRecordID alloc] initWithRecordName: @"com.apple.security.ckks-pcsservice2" zoneID:self.keychainZoneID];
+ CKRecord* currentPointerRecord2 = self.keychainZone.currentDatabase[currentPointerRecordID2];
+ XCTAssertNotNil(currentPointerRecord2, "Found record in CloudKit at expected UUID");
+
+ [self.keychainView notifyZoneChange:nil];
+ [self.keychainView waitForFetchAndIncomingQueueProcessing];
+
+ // Tear down the CKKS object
+ [self.keychainView cancelAllOperations];
+
+ [self.keychainView dispatchSync: ^bool {
+ // Edit the zone state entry to have no fixups
+ NSError* error = nil;
+ CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry fromDatabase:self.keychainZoneID.zoneName error:&error];
+
+ XCTAssertNil(error, "no error pulling ckse from database");
+ XCTAssertNotNil(ckse, "received a ckse");
+
+ ckse.lastFixup = CKKSFixupNever;
+ [ckse saveToDatabase: &error];
+ XCTAssertNil(error, "no error saving to database");
+
+ // And add a garbage CIP
+ CKKSCurrentItemPointer* cip3 = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"garbage"
+ currentItemUUID:@"3AB8E78D-75AF-CFEF-F833-FA3E3E90978A"
+ state:SecCKKSProcessedStateLocal
+ zoneID:self.keychainZoneID
+ encodedCKRecord:nil];
+ cip3.storedCKRecord = [cip3 CKRecordWithZoneID:self.keychainZoneID];
+ XCTAssertEqual(cip3.identifier, @"garbage", "Identifier is what we thought it was");
+ [cip3 saveToDatabase:&error];
+ XCTAssertNil(error, "no error saving to database");
+ return true;
+ }];
+
+ self.silentFetchesAllowed = false;
+ [self expectCKFetchByRecordID];
+ if(SecCKKSShareTLKs()) {
+ [self expectCKFetchByQuery]; // and one for the TLKShare fixup
+ }
+
+ // Change one of the CIPs while CKKS is offline
+ cip2.currentItemUUID = @"changed-by-cloudkit";
+ [self.keychainZone addToZone: [cip2 CKRecordWithZoneID:self.keychainZoneID]];
+
+ // Bring CKKS back up
+ self.keychainView = [[CKKSViewManager manager] restartZone:self.keychainZoneID.zoneName];
+ [self.keychainView waitForKeyHierarchyReadiness];
+
+ [self.keychainView waitForOperationsOfClass:[CKKSIncomingQueueOperation class]];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self.keychainView dispatchSync: ^bool {
+ // The zone state entry should be up the most recent fixup level
+ NSError* error = nil;
+ CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry fromDatabase:self.keychainZoneID.zoneName error:&error];
+ XCTAssertNil(error, "no error pulling ckse from database");
+ XCTAssertNotNil(ckse, "received a ckse");
+ XCTAssertEqual(ckse.lastFixup, CKKSCurrentFixupNumber, "CKSE should have the current fixup number stored");
+
+ // The garbage CIP should be gone, and CKKS should have caught up to the CIP change
+ NSArray<CKKSCurrentItemPointer*>* allCIPs = [CKKSCurrentItemPointer allInZone:self.keychainZoneID error:&error];
+ XCTAssertNil(error, "no error loading all CIPs from database");
+
+ XCTestExpectation* foundCIP2 = [self expectationWithDescription: @"found CIP2"];
+ for(CKKSCurrentItemPointer* loaded in allCIPs) {
+ if([loaded.identifier isEqualToString: cip2.identifier]) {
+ [foundCIP2 fulfill];
+ XCTAssertEqualObjects(loaded.currentItemUUID, @"changed-by-cloudkit", "Fixup should have fixed UUID to new value, not pre-shutdown value");
+ }
+ XCTAssertNotEqualObjects(loaded.identifier, @"garbage", "Garbage CIP shouldn't exist anymore");
+ }
+
+ [self waitForExpectations:@[foundCIP2] timeout:0.1];
+ return true;
+ }];
+}
+
+- (void)setFixupNumber:(CKKSFixup)newFixup ckks:(CKKSKeychainView*)ckks {
+ [ckks dispatchSync: ^bool {
+ // Edit the zone state entry to have no fixups
+ NSError* error = nil;
+ CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry fromDatabase:ckks.zoneID.zoneName error:&error];
+
+ XCTAssertNil(error, "no error pulling ckse from database");
+ XCTAssertNotNil(ckse, "received a ckse");
+
+ ckse.lastFixup = newFixup;
+ [ckse saveToDatabase: &error];
+ XCTAssertNil(error, "no error saving to database");
+ return true;
+ }];
+}
+
+- (void)testFixupFetchAllTLKShareRecords {
+ SecCKKSSetShareTLKs(true);
+ // In <rdar://problem/34901306> CKKSTLK: TLKShare CloudKit upload/download on TLK change, trust set addition,
+ // we added the TLKShare CKRecord type. Upgrading devices must fetch all such records when they come online for the first time.
+
+ // Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
+ // Note that this already does TLK sharing, and so technically doesn't need to do the fixup, but we'll fix that later.
+ [self expectCKModifyKeyRecords:3 currentKeyPointerRecords:3 tlkShareRecords:1 zoneID:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ [self.keychainView waitForKeyHierarchyReadiness];
+ [self waitForCKModifications];
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ // Tear down the CKKS object
+ [self.keychainView cancelAllOperations];
+ [self setFixupNumber:CKKSFixupRefetchCurrentItemPointers ckks:self.keychainView];
+
+ // Also, create a TLK share record that CKKS should find
+ // Make another share, but from an untrusted peer to some other peer. local shouldn't necessarily care.
+ NSError* error = nil;
+
+ CKKSSOSSelfPeer* remotePeer1 = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"remote-peer1"
+ encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
+ signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
+
+ CKKSTLKShare* share = [CKKSTLKShare share:self.keychainZoneKeys.tlk
+ as:remotePeer1
+ to:self.currentSelfPeer
+ epoch:-1
+ poisoned:0
+ error:&error];
+ XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
+ XCTAssertNotNil(share, "Should be able to create a share");
+
+ CKRecord* shareCKRecord = [share CKRecordWithZoneID: self.keychainZoneID];
+ XCTAssertNotNil(shareCKRecord, "Should have been able to create a CKRecord");
+ [self.keychainZone addToZone:shareCKRecord];
+
+ // Now, restart CKKS
+ self.silentFetchesAllowed = false;
+ [self expectCKFetchByQuery];
+
+ // We want to ensure that the key hierarchy state machine doesn't progress past fixup until we let this go
+ [self holdCloudKitFetches];
+
+ self.keychainView = [[CKKSViewManager manager] restartZone:self.keychainZoneID.zoneName];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForFixupOperation] wait:500*NSEC_PER_SEC], "Key state should become waitforfixup");
+ [self releaseCloudKitFetchHold];
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:500*NSEC_PER_SEC], "Key state should become ready");
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 8);
+
+ [self.keychainView.lastFixupOperation waitUntilFinished];
+ XCTAssertNil(self.keychainView.lastFixupOperation.error, "Shouldn't have been any error performing fixup");
+
+ // and check that the share made it
+ [self.keychainView dispatchSync:^bool {
+ NSError* blockerror = nil;
+ CKKSTLKShare* localshare = [CKKSTLKShare tryFromDatabaseFromCKRecordID:shareCKRecord.recordID error:&blockerror];
+ XCTAssertNil(blockerror, "Shouldn't error finding new TLKShare record in database");
+ XCTAssertNotNil(localshare, "Should be able to find a new TLKShare record in database");
+ return true;
+ }];
+
+ SecCKKSSetShareTLKs(false);
+}
+
+@end
+
+#endif // OCTAGON
*/
#import "CloudKitMockXCTest.h"
#import "keychain/ckks/CKKS.h"
+#import "keychain/ckks/CKKSControl.h"
#import "keychain/ckks/CKKSCurrentKeyPointer.h"
@class CKKSKey;
@interface ZoneKeys : CKKSCurrentKeySet
@property CKKSKey* rolledTLK;
+
+- (instancetype)initLoadingRecordsFromZone:(FakeCKZone*)zone;
@end
/*
@interface CloudKitKeychainSyncingMockXCTest : CloudKitMockXCTest
+@property CKKSControl* ckksControl;
+
@property id mockCKKSKey;
+@property id<CKKSSelfPeer> currentSelfPeer;
+@property NSMutableSet<id<CKKSPeer>>* currentPeers;
+
@property NSMutableDictionary<CKRecordZoneID*, ZoneKeys*>* keys;
// Pass in an oldTLK to wrap it to the new TLK; otherwise, pass nil
- (NSMutableDictionary*)SOSPiggyBackCopyFromKeychain;
- (NSMutableArray<NSData *>*) SOSPiggyICloudIdentities;
+- (void)putTLKShareInCloudKit:(CKKSKey*)key
+ from:(CKKSSOSSelfPeer*)sharingPeer
+ to:(id<CKKSPeer>)receivingPeer
+ zoneID:(CKRecordZoneID*)zoneID;
+- (void)putTLKSharesInCloudKit:(CKKSKey*)key
+ from:(CKKSSOSSelfPeer*)sharingPeer
+ zoneID:(CKRecordZoneID*)zoneID;
+- (void)putSelfTLKSharesInCloudKit:(CKRecordZoneID*)zoneID;
+- (void)saveTLKSharesInLocalDatabase:(CKRecordZoneID*)zoneID;
+
- (void)saveClassKeyMaterialToKeychain: (CKRecordZoneID*)zoneID;
// Call this to fake out your test: all keys are created, saved in cloudkit, and saved locally (as if the key state machine had processed them)
// Returns an expectation that someone will send an NSNotification that this view changed
-(XCTestExpectation*)expectChangeForView:(NSString*)view;
+
+// Establish an assertion that CKKS will cause a server extension error soon.
+- (void)expectCKReceiveSyncKeyHierarchyError:(CKRecordZoneID*)zoneID;
@end
#import "keychain/ckks/CKKSViewManager.h"
#import "keychain/ckks/CKKSZoneStateEntry.h"
#import "keychain/ckks/CKKSManifest.h"
+#import "keychain/ckks/CKKSPeer.h"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#import "Security/SecureObjectSync/SOSAccount.h"
+#pragma clang diagnostic pop
+
@implementation ZoneKeys
+- (instancetype)initLoadingRecordsFromZone:(FakeCKZone*)zone {
+ if((self = [super init])) {
+ CKRecordID* currentTLKPointerID = [[CKRecordID alloc] initWithRecordName:SecCKKSKeyClassTLK zoneID:zone.zoneID];
+ CKRecordID* currentClassAPointerID = [[CKRecordID alloc] initWithRecordName:SecCKKSKeyClassA zoneID:zone.zoneID];
+ CKRecordID* currentClassCPointerID = [[CKRecordID alloc] initWithRecordName:SecCKKSKeyClassC zoneID:zone.zoneID];
+
+ CKRecord* currentTLKPointerRecord = zone.currentDatabase[currentTLKPointerID];
+ CKRecord* currentClassAPointerRecord = zone.currentDatabase[currentClassAPointerID];
+ CKRecord* currentClassCPointerRecord = zone.currentDatabase[currentClassCPointerID];
+
+ self.currentTLKPointer = currentTLKPointerRecord ? [[CKKSCurrentKeyPointer alloc] initWithCKRecord: currentTLKPointerRecord] : nil;
+ self.currentClassAPointer = currentClassAPointerRecord ? [[CKKSCurrentKeyPointer alloc] initWithCKRecord: currentClassAPointerRecord] : nil;
+ self.currentClassCPointer = currentClassCPointerRecord ? [[CKKSCurrentKeyPointer alloc] initWithCKRecord: currentClassCPointerRecord] : nil;
+
+ CKRecordID* currentTLKID = self.currentTLKPointer.currentKeyUUID ? [[CKRecordID alloc] initWithRecordName:self.currentTLKPointer.currentKeyUUID zoneID:zone.zoneID] : nil;
+ CKRecordID* currentClassAID = self.currentClassAPointer.currentKeyUUID ? [[CKRecordID alloc] initWithRecordName:self.currentClassAPointer.currentKeyUUID zoneID:zone.zoneID] : nil;
+ CKRecordID* currentClassCID = self.currentClassCPointer.currentKeyUUID ? [[CKRecordID alloc] initWithRecordName:self.currentClassCPointer.currentKeyUUID zoneID:zone.zoneID] : nil;
+
+ CKRecord* currentTLKRecord = currentTLKID ? zone.currentDatabase[currentTLKID] : nil;
+ CKRecord* currentClassARecord = currentClassAID ? zone.currentDatabase[currentClassAID] : nil;
+ CKRecord* currentClassCRecord = currentClassCID ? zone.currentDatabase[currentClassCID] : nil;
+
+ self.tlk = currentTLKRecord ? [[CKKSKey alloc] initWithCKRecord: currentTLKRecord] : nil;
+ self.classA = currentClassARecord ? [[CKKSKey alloc] initWithCKRecord: currentClassARecord] : nil;
+ self.classC = currentClassCRecord ? [[CKKSKey alloc] initWithCKRecord: currentClassCRecord] : nil;
+ }
+ return self;
+}
+
@end
// No tests here, just helper functions
@implementation CloudKitKeychainSyncingMockXCTest
- (void)setUp {
- // Need to convince your tests to set these, no matter what the on-disk pist says? Uncomment.
+ // Need to convince your tests to set these, no matter what the on-disk plist says? Uncomment.
(void)[CKKSManifest shouldSyncManifests]; // perfrom initialization
SecCKKSSetSyncManifests(false);
SecCKKSSetEnforceManifests(false);
stashTLK:[OCMArg anyObjectRef]
error:[OCMArg anyObjectRef]]
).andCall(self, @selector(handleLockSaveKeyMaterialToKeychain:stashTLK:error:));
+
+ // Fake out SOS peers
+ self.currentSelfPeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"local-peer"
+ encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
+ signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
+
+ // No trusted non-self peers by default. Your test can change this if it wants.
+ self.currentPeers = [NSMutableSet set];
+
+ OCMStub([self.mockCKKSViewManager fetchSelfPeers:[OCMArg anyObjectRef]]).andCall(self, @selector(fetchSelfPeers:));
+ OCMStub([self.mockCKKSViewManager fetchTrustedPeers:[OCMArg anyObjectRef]]).andCall(self, @selector(fetchTrustedPeers:));
+
+ // Bring up a fake CKKSControl object
+ id mockConnection = OCMPartialMock([[NSXPCConnection alloc] init]);
+ OCMStub([mockConnection remoteObjectProxyWithErrorHandler:[OCMArg any]]).andCall(self, @selector(injectedManager));
+ self.ckksControl = [[CKKSControl alloc] initWithConnection:mockConnection];
+ XCTAssertNotNil(self.ckksControl, "Should have received control object");
}
- (void)tearDown {
[super tearDown];
self.keys = nil;
+
+ self.currentSelfPeer = nil;
+ self.currentPeers = nil;
+}
+
+- (CKKSSelves*)fetchSelfPeers:(NSError* __autoreleasing *)error {
+ //
+ if(self.aksLockState) {
+ if(error) {
+ *error = [NSError errorWithDomain:(__bridge NSString*)kSecErrorDomain code:errSecInteractionNotAllowed userInfo:nil];
+ }
+ return nil;
+ } else {
+ // Only supports a single self peer for now
+ return [[CKKSSelves alloc] initWithCurrent:self.currentSelfPeer allSelves:nil];
+ }
+}
+
+- (NSSet<id<CKKSPeer>>*)fetchTrustedPeers:(NSError* __autoreleasing *)error {
+ // Trusted Peers include ourselves, but as a CKKSSOSPeer object instead of a self peer
+ CKKSSOSPeer* s = [[CKKSSOSPeer alloc] initWithSOSPeerID:self.currentSelfPeer.peerID
+ encryptionPublicKey:self.currentSelfPeer.publicEncryptionKey
+ signingPublicKey:self.currentSelfPeer.publicSigningKey];
+
+ return [self.currentPeers setByAddingObject: s];
}
- (void)createClassCItemAndWaitForUpload:(CKRecordZoneID*)zoneID account:(NSString*)account {
self.keys[zoneID] = nil;
CKKSKey* oldTLK = zonekeys.tlk;
+ NSError* error = nil;
+ [oldTLK ensureKeyLoaded:&error];
+ XCTAssertNil(error, "shouldn't error ensuring that the oldTLK has its key material");
[self createFakeKeyHierarchy: zoneID oldTLK:oldTLK];
[self putFakeKeyHierarchyInCloudKit: zoneID];
XCTAssertNil( (__bridge NSError*)cferror, @"no error with transaction");
CFReleaseNull(cferror);
}
-static SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(CFStringRef name, SecKeyRef* outSigningKey, SecKeyRef* outOctagonSigningKey, CFErrorRef *error)
+static SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(CFStringRef name,
+ SecKeyRef* outSigningKey,
+ SecKeyRef* outOctagonSigningKey,
+ SecKeyRef* outOctagonEncryptionKey,
+ CFErrorRef *error)
{
SOSFullPeerInfoRef result = NULL;
SecKeyRef publicKey = NULL;
*outSigningKey = GeneratePermanentFullECKey(256, name, error);
*outOctagonSigningKey = GeneratePermanentFullECKey(384, name, error);
-
+ *outOctagonEncryptionKey = GeneratePermanentFullECKey(384, name, error);
+
gestalt = SOSCreatePeerGestaltFromName(name);
- result = SOSFullPeerInfoCreate(NULL, gestalt, NULL, *outSigningKey, *outOctagonSigningKey, error);
+ result = SOSFullPeerInfoCreate(NULL, gestalt, NULL, *outSigningKey,
+ *outOctagonSigningKey, *outOctagonEncryptionKey,
+ error);
CFReleaseNull(gestalt);
CFReleaseNull(publicKey);
{
SecKeyRef signingKey = NULL;
SecKeyRef octagonSigningKey = NULL;
+ SecKeyRef octagonEncryptionKey = NULL;
NSMutableArray<NSData *>* icloudidentities = [NSMutableArray array];
- SOSFullPeerInfoRef fpi = SOSCreateFullPeerInfoFromName(CFSTR("Test Peer"), &signingKey, &octagonSigningKey, NULL);
+ SOSFullPeerInfoRef fpi = SOSCreateFullPeerInfoFromName(CFSTR("Test Peer"), &signingKey, &octagonSigningKey, &octagonEncryptionKey, NULL);
NSData *data = CFBridgingRelease(SOSPeerInfoCopyData(SOSFullPeerInfoGetPeerInfo(fpi), NULL));
if (data)
CFReleaseNull(signingKey);
CFReleaseNull(octagonSigningKey);
-
+ CFReleaseNull(octagonEncryptionKey);
+
return icloudidentities;
}
static CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name)
XCTAssertNil(error, @"Saved Class C material to keychain");
}
+
+- (void)putTLKShareInCloudKit:(CKKSKey*)key
+ from:(CKKSSOSSelfPeer*)sharingPeer
+ to:(id<CKKSPeer>)receivingPeer
+ zoneID:(CKRecordZoneID*)zoneID
+{
+ NSError* error = nil;
+ CKKSTLKShare* share = [CKKSTLKShare share:key
+ as:sharingPeer
+ to:receivingPeer
+ epoch:-1
+ poisoned:0
+ error:&error];
+ XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
+ XCTAssertNotNil(share, "Should be able to create a share");
+
+ CKRecord* shareRecord = [share CKRecordWithZoneID: zoneID];
+ XCTAssertNotNil(shareRecord, "Should have been able to create a CKRecord");
+
+ FakeCKZone* zone = self.zones[zoneID];
+ XCTAssertNotNil(zone, "Should have a zone to put a TLKShare in");
+ [zone addToZone:shareRecord];
+
+ ZoneKeys* keys = self.keys[zoneID];
+ XCTAssertNotNil(keys, "Have a zonekeys object for this zone");
+ keys.tlkShares = keys.tlkShares ? [keys.tlkShares arrayByAddingObject:share] : @[share];
+}
+
+- (void)putTLKSharesInCloudKit:(CKKSKey*)key
+ from:(CKKSSOSSelfPeer*)sharingPeer
+ zoneID:(CKRecordZoneID*)zoneID
+{
+ NSSet* peers = [self.currentPeers setByAddingObject:self.currentSelfPeer];
+
+ for(id<CKKSPeer> peer in peers) {
+ [self putTLKShareInCloudKit:key from:sharingPeer to:peer zoneID:zoneID];
+ }
+}
+
+- (void)putSelfTLKSharesInCloudKit:(CKRecordZoneID*)zoneID {
+ CKKSKey* tlk = self.keys[zoneID].tlk;
+ XCTAssertNotNil(tlk, "Should have a TLK for zone %@", zoneID);
+ [self putTLKSharesInCloudKit:tlk from:self.currentSelfPeer zoneID:zoneID];
+}
+
+- (void)saveTLKSharesInLocalDatabase:(CKRecordZoneID*)zoneID {
+ ZoneKeys* keys = self.keys[zoneID];
+ XCTAssertNotNil(keys, "Have a zonekeys object for this zone");
+
+ for(CKKSTLKShare* share in keys.tlkShares) {
+ NSError* error = nil;
+ [share saveToDatabase:&error];
+ XCTAssertNil(error, "Shouldn't have been an error saving a TLKShare to the database");
+ }
+}
+
- (void)createAndSaveFakeKeyHierarchy: (CKRecordZoneID*)zoneID {
[self saveFakeKeyHierarchyToLocalDatabase: zoneID];
[self putFakeKeyHierarchyInCloudKit: zoneID];
[self saveTLKMaterialToKeychain: zoneID];
[self saveClassKeyMaterialToKeychain: zoneID];
+
+ if(SecCKKSShareTLKs()) {
+ [self putSelfTLKSharesInCloudKit:zoneID];
+ [self saveTLKSharesInLocalDatabase:zoneID];
+ }
}
// Override our base class here:
-- (void)expectCKModifyKeyRecords: (NSUInteger) expectedNumberOfRecords currentKeyPointerRecords: (NSUInteger) expectedCurrentKeyRecords zoneID: (CKRecordZoneID*) zoneID {
+- (void)expectCKModifyKeyRecords:(NSUInteger)expectedNumberOfRecords
+ currentKeyPointerRecords:(NSUInteger)expectedCurrentKeyRecords
+ tlkShareRecords:(NSUInteger)expectedTLKShareRecords
+ zoneID:(CKRecordZoneID*) zoneID {
__weak __typeof(self) weakSelf = self;
[self expectCKModifyRecords: @{
SecCKRecordIntermediateKeyType: [NSNumber numberWithUnsignedInteger: expectedNumberOfRecords],
- SecCKRecordCurrentKeyType: [NSNumber numberWithUnsignedInteger: expectedCurrentKeyRecords]}
+ SecCKRecordCurrentKeyType: [NSNumber numberWithUnsignedInteger: expectedCurrentKeyRecords],
+ SecCKRecordTLKShareType: [NSNumber numberWithUnsignedInteger:(SecCKKSShareTLKs() ? expectedTLKShareRecords : 0)],
+ }
deletedRecordTypeCounts:nil
zoneID:zoneID
checkModifiedRecord:nil
XCTAssertNotNil(zonekeys.tlk, "Have the current TLK");
XCTAssertNotNil(zonekeys.classA, "Have the current Class A key");
XCTAssertNotNil(zonekeys.classC, "Have the current Class C key");
+
+ NSMutableArray<CKKSTLKShare*>* shares = [NSMutableArray array];
+ for(CKRecordID* recordID in strongSelf.zones[zoneID].currentDatabase.allKeys) {
+ if([recordID.recordName hasPrefix: [CKKSTLKShare ckrecordPrefix]]) {
+ CKKSTLKShare* share = [[CKKSTLKShare alloc] initWithCKRecord:strongSelf.zones[zoneID].currentDatabase[recordID]];
+ XCTAssertNotNil(share, "Should be able to parse a CKKSTLKShare CKRecord into a CKKSTLKShare");
+ [shares addObject:share];
+ }
+ }
+ zonekeys.tlkShares = shares;
}];
}
+- (void)expectCKReceiveSyncKeyHierarchyError:(CKRecordZoneID*)zoneID {
+
+ __weak __typeof(self) weakSelf = self;
+ [[self.mockDatabase expect] addOperation:[OCMArg checkWithBlock:^BOOL(id obj) {
+ __strong __typeof(weakSelf) strongSelf = weakSelf;
+ XCTAssertNotNil(strongSelf, "self exists");
+
+ __block bool rejected = false;
+ if ([obj isKindOfClass:[CKModifyRecordsOperation class]]) {
+ CKModifyRecordsOperation *op = (CKModifyRecordsOperation *)obj;
+
+ if(!op.atomic) {
+ // We only care about atomic operations
+ return NO;
+ }
+
+ // We want to only match zone updates pertaining to this zone
+ for(CKRecord* record in op.recordsToSave) {
+ if(![record.recordID.zoneID isEqual: zoneID]) {
+ return NO;
+ }
+ }
+
+ // we oly want to match updates that are updating a class C or class A CKP
+ bool updatingClassACKP = false;
+ bool updatingClassCCKP = false;
+ for(CKRecord* record in op.recordsToSave) {
+ if([record.recordID.recordName isEqualToString:SecCKKSKeyClassA]) {
+ updatingClassACKP = true;
+ }
+ if([record.recordID.recordName isEqualToString:SecCKKSKeyClassC]) {
+ updatingClassCCKP = true;
+ }
+ }
+
+ if(!updatingClassACKP && !updatingClassCCKP) {
+ return NO;
+ }
+
+ FakeCKZone* zone = strongSelf.zones[zoneID];
+ XCTAssertNotNil(zone, "Should have a zone for these records");
+
+ // We only want to match if the synckeys aren't pointing correctly
+
+ ZoneKeys* zonekeys = [[ZoneKeys alloc] initLoadingRecordsFromZone:zone];
+
+ XCTAssertNotNil(zonekeys.currentTLKPointer, "Have a currentTLKPointer");
+ XCTAssertNotNil(zonekeys.currentClassAPointer, "Have a currentClassAPointer");
+ XCTAssertNotNil(zonekeys.currentClassCPointer, "Have a currentClassCPointer");
+
+ XCTAssertNotNil(zonekeys.tlk, "Have the current TLK");
+ XCTAssertNotNil(zonekeys.classA, "Have the current Class A key");
+ XCTAssertNotNil(zonekeys.classC, "Have the current Class C key");
+
+ // Ensure that either the Class A synckey or the class C synckey do not immediately wrap to the current TLK
+ bool classALinkBroken = ![zonekeys.classA.parentKeyUUID isEqualToString:zonekeys.tlk.uuid];
+ bool classCLinkBroken = ![zonekeys.classC.parentKeyUUID isEqualToString:zonekeys.tlk.uuid];
+
+ // Neither synckey link is broken. Don't match this operation.
+ if(!classALinkBroken && !classCLinkBroken) {
+ return NO;
+ }
+
+ NSMutableDictionary<CKRecordID*, NSError*>* failedRecords = [[NSMutableDictionary alloc] init];
+
+ @synchronized(zone.currentDatabase) {
+ for(CKRecord* record in op.recordsToSave) {
+ if(classALinkBroken && [record.recordID.recordName isEqualToString:SecCKKSKeyClassA]) {
+ failedRecords[record.recordID] = [strongSelf ckInternalServerExtensionError:CKKSServerUnexpectedSyncKeyInChain description:@"synckey record: current classA synckey does not point to current tlk synckey"];
+ rejected = true;
+ }
+ if(classCLinkBroken && [record.recordID.recordName isEqualToString:SecCKKSKeyClassC]) {
+ failedRecords[record.recordID] = [strongSelf ckInternalServerExtensionError:CKKSServerUnexpectedSyncKeyInChain description:@"synckey record: current classC synckey does not point to current tlk synckey"];
+ rejected = true;
+ }
+ }
+ }
+
+ if(rejected) {
+ [strongSelf rejectWrite: op failedRecords:failedRecords];
+ }
+ }
+ return rejected ? YES : NO;
+ }]];
+}
+
- (void)checkNoCKKSData: (CKKSKeychainView*) view {
// Test that there are no items in the database
[view dispatchSync:^bool{
@property id mockFakeCKModifyRecordZonesOperation;
@property id mockFakeCKModifySubscriptionsOperation;
@property id mockFakeCKFetchRecordZoneChangesOperation;
+@property id mockFakeCKFetchRecordsOperation;
+@property id mockFakeCKQueryOperation;
@property id mockAccountStateTracker;
@property NSBlockOperation* ckaccountHoldOperation;
@property NSBlockOperation* ckModifyHoldOperation;
+@property NSBlockOperation* ckFetchHoldOperation;
@property bool silentFetchesAllowed;
- (void)expectCKDeleteItemRecords: (NSUInteger) expectedNumberOfRecords zoneID: (CKRecordZoneID*) zoneID;
-- (void)expectCKModifyKeyRecords: (NSUInteger) expectedNumberOfRecords
- currentKeyPointerRecords: (NSUInteger) expectedCurrentKeyRecords
- zoneID: (CKRecordZoneID*) zoneID;
+- (void)expectCKModifyKeyRecords:(NSUInteger)expectedNumberOfRecords
+ currentKeyPointerRecords:(NSUInteger)expectedCurrentKeyRecords
+ tlkShareRecords:(NSUInteger)expectedTLKShareRecords
+ zoneID:(CKRecordZoneID*)zoneID;
- (void)expectCKModifyRecords:(NSDictionary<NSString*, NSNumber*>*) expectedRecordTypeCounts
deletedRecordTypeCounts:(NSDictionary<NSString*, NSNumber*>*) expectedDeletedRecordTypeCounts
// Use this to assert that a fetch occurs (especially if silentFetchesAllowed = false)
- (void)expectCKFetch;
+// Use this to 1) assert that a fetch occurs and 2) cause a block to run _after_ all changes have been delivered but _before_ the fetch 'completes'.
+// This way, you can modify the CK zone to cause later collisions.
+- (void)expectCKFetchAndRunBeforeFinished: (void (^)())blockAfterFetch;
+
+// Use this to assert that a FakeCKFetchRecordsOperation occurs.
+-(void)expectCKFetchByRecordID;
+
+// Use this to assert that a FakeCKQueryOperation occurs.
+- (void)expectCKFetchByQuery;
+
// Wait until all scheduled cloudkit operations are reflected in the currentDatabase
- (void)waitForCKModifications;
// Unblocks the hold you've added with holdCloudKitModifications; CloudKit modifications will finish
-(void)releaseCloudKitModificationHold;
+// Blocks the CloudKit fetches from beginning (similar to network latency)
+-(void)holdCloudKitFetches;
+// Unblocks the hold you've added with holdCloudKitFetches; CloudKit fetches will finish
+-(void)releaseCloudKitFetchHold;
+
+// Make a CK internal server extension error with a given code and description.
+- (NSError*)ckInternalServerExtensionError:(NSInteger)code description:(NSString*)desc;
+
+// Schedule an operation for execution (and failure), with some existing record errors.
+// Other records in the operation but not in failedRecords will have CKErrorBatchRequestFailed errors created.
+-(void)rejectWrite:(CKModifyRecordsOperation*)op failedRecords:(NSMutableDictionary<CKRecordID*, NSError*>*)failedRecords;
+
#endif // OCTAGON
@end
self.mockFakeCKFetchRecordZoneChangesOperation = OCMClassMock([FakeCKFetchRecordZoneChangesOperation class]);
OCMStub([self.mockFakeCKFetchRecordZoneChangesOperation ckdb]).andReturn(self.zones);
+ self.mockFakeCKFetchRecordsOperation = OCMClassMock([FakeCKFetchRecordsOperation class]);
+ OCMStub([self.mockFakeCKFetchRecordsOperation ckdb]).andReturn(self.zones);
+
+ self.mockFakeCKQueryOperation = OCMClassMock([FakeCKQueryOperation class]);
+ OCMStub([self.mockFakeCKQueryOperation ckdb]).andReturn(self.zones);
+
+
OCMStub([self.mockDatabase addOperation: [OCMArg checkWithBlock:^BOOL(id obj) {
__strong __typeof(self) strongSelf = weakSelf;
BOOL matches = NO;
matches = YES;
FakeCKFetchRecordZoneChangesOperation *frzco = (FakeCKFetchRecordZoneChangesOperation *)obj;
+ [frzco addNullableDependency:strongSelf.ckFetchHoldOperation];
[strongSelf.operationQueue addOperation: frzco];
}
}
return matches;
}]]);
+ OCMStub([self.mockDatabase addOperation: [OCMArg checkWithBlock:^BOOL(id obj) {
+ __strong __typeof(self) strongSelf = weakSelf;
+ BOOL matches = NO;
+ if ([obj isKindOfClass: [FakeCKFetchRecordsOperation class]]) {
+ if(strongSelf.silentFetchesAllowed) {
+ matches = YES;
+
+ FakeCKFetchRecordsOperation *ffro = (FakeCKFetchRecordsOperation *)obj;
+ [ffro addNullableDependency:strongSelf.ckFetchHoldOperation];
+ [strongSelf.operationQueue addOperation: ffro];
+ }
+ }
+ return matches;
+ }]]);
+
+ OCMStub([self.mockDatabase addOperation: [OCMArg checkWithBlock:^BOOL(id obj) {
+ __strong __typeof(self) strongSelf = weakSelf;
+ BOOL matches = NO;
+ if ([obj isKindOfClass: [FakeCKQueryOperation class]]) {
+ if(strongSelf.silentFetchesAllowed) {
+ matches = YES;
+
+ FakeCKQueryOperation *fqo = (FakeCKQueryOperation *)obj;
+ [fqo addNullableDependency:strongSelf.ckFetchHoldOperation];
+ [strongSelf.operationQueue addOperation: fqo];
+ }
+ }
+ return matches;
+ }]]);
+
self.testZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName];
}];
self.ckksHoldOperation.name = @"ckks-hold";
- self.mockCKKSViewManager = OCMClassMock([CKKSViewManager class]);
+ //self.mockCKKSViewManagerClass = OCMClassMock([CKKSViewManager class]);
+
+ // We don't want to use class mocks here, because they don't play well with partial mocks
+ self.mockCKKSViewManager = OCMPartialMock(
+ [[CKKSViewManager alloc] initWithContainerName:SecCKKSContainerName
+ usePCS:SecCKKSContainerUsePCS
+ fetchRecordZoneChangesOperationClass:[FakeCKFetchRecordZoneChangesOperation class]
+ fetchRecordsOperationClass:[FakeCKFetchRecordsOperation class]
+ queryOperationClass:[FakeCKQueryOperation class]
+ modifySubscriptionsOperationClass:[FakeCKModifySubscriptionsOperation class]
+ modifyRecordZonesOperationClass:[FakeCKModifyRecordZonesOperation class]
+ apsConnectionClass:[FakeAPSConnection class]
+ nsnotificationCenterClass:[FakeNSNotificationCenter class]
+ notifierClass:[FakeCKKSNotifier class]
+ setupHold:self.ckksHoldOperation]);
+
OCMStub([self.mockCKKSViewManager viewList]).andCall(self, @selector(managedViewList));
OCMStub([self.mockCKKSViewManager syncBackupAndNotifyAboutSync]);
- self.injectedManager = [[CKKSViewManager alloc] initWithContainerName:SecCKKSContainerName
- usePCS:SecCKKSContainerUsePCS
- fetchRecordZoneChangesOperationClass:[FakeCKFetchRecordZoneChangesOperation class]
- modifySubscriptionsOperationClass:[FakeCKModifySubscriptionsOperation class]
- modifyRecordZonesOperationClass:[FakeCKModifyRecordZonesOperation class]
- apsConnectionClass:[FakeAPSConnection class]
- nsnotificationCenterClass:[FakeNSNotificationCenter class]
- notifierClass:[FakeCKKSNotifier class]
- setupHold:self.ckksHoldOperation];
+ self.injectedManager = self.mockCKKSViewManager;
[CKKSViewManager resetManager:false setTo:self.injectedManager];
}
-(void)expectCKFetch {
+ [self expectCKFetchAndRunBeforeFinished: nil];
+}
+
+-(void)expectCKFetchAndRunBeforeFinished: (void (^)())blockAfterFetch {
// Create an object for the block to retain and modify
BoolHolder* runAlready = [[BoolHolder alloc] init];
runAlready.state = true;
FakeCKFetchRecordZoneChangesOperation *frzco = (FakeCKFetchRecordZoneChangesOperation *)obj;
+ frzco.blockAfterFetch = blockAfterFetch;
+ [frzco addNullableDependency: strongSelf.ckFetchHoldOperation];
[strongSelf.operationQueue addOperation: frzco];
}
return matches;
}]];
}
+-(void)expectCKFetchByRecordID {
+ // Create an object for the block to retain and modify
+ BoolHolder* runAlready = [[BoolHolder alloc] init];
+
+ __weak __typeof(self) weakSelf = self;
+ [[self.mockDatabase expect] addOperation: [OCMArg checkWithBlock:^BOOL(id obj) {
+ __strong __typeof(self) strongSelf = weakSelf;
+ if(runAlready.state) {
+ return NO;
+ }
+ BOOL matches = NO;
+ if ([obj isKindOfClass: [FakeCKFetchRecordsOperation class]]) {
+ matches = YES;
+ runAlready.state = true;
+
+ FakeCKFetchRecordsOperation *ffro = (FakeCKFetchRecordsOperation *)obj;
+ [ffro addNullableDependency: strongSelf.ckFetchHoldOperation];
+ [strongSelf.operationQueue addOperation: ffro];
+ }
+ return matches;
+ }]];
+}
+
+
+-(void)expectCKFetchByQuery {
+ // Create an object for the block to retain and modify
+ BoolHolder* runAlready = [[BoolHolder alloc] init];
+
+ __weak __typeof(self) weakSelf = self;
+ [[self.mockDatabase expect] addOperation: [OCMArg checkWithBlock:^BOOL(id obj) {
+ __strong __typeof(self) strongSelf = weakSelf;
+ if(runAlready.state) {
+ return NO;
+ }
+ BOOL matches = NO;
+ if ([obj isKindOfClass: [FakeCKQueryOperation class]]) {
+ matches = YES;
+ runAlready.state = true;
+
+ FakeCKQueryOperation *fqo = (FakeCKQueryOperation *)obj;
+ [fqo addNullableDependency: strongSelf.ckFetchHoldOperation];
+ [strongSelf.operationQueue addOperation: fqo];
+ }
+ return matches;
+ }]];
+}
+
- (void)startCKKSSubsystem {
[self startCKAccountStatusMock];
[self startCKKSSubsystemOnly];
}
-(void)holdCloudKitModifications {
+ XCTAssertFalse([self.ckModifyHoldOperation isPending], "Shouldn't already be a pending cloudkit modify hold operation");
self.ckModifyHoldOperation = [NSBlockOperation blockOperationWithBlock:^{
secnotice("ckks", "Released CloudKit modification hold.");
}];
}
}
+-(void)holdCloudKitFetches {
+ XCTAssertFalse([self.ckFetchHoldOperation isPending], "Shouldn't already be a pending cloudkit fetch hold operation");
+ self.ckFetchHoldOperation = [NSBlockOperation blockOperationWithBlock:^{
+ secnotice("ckks", "Released CloudKit fetch hold.");
+ }];
+}
+-(void)releaseCloudKitFetchHold {
+ if([self.ckFetchHoldOperation isPending]) {
+ [self.operationQueue addOperation: self.ckFetchHoldOperation];
+ }
+}
+
- (void)expectCKModifyItemRecords: (NSUInteger) expectedNumberOfRecords currentKeyPointerRecords: (NSUInteger) expectedCurrentKeyRecords zoneID: (CKRecordZoneID*) zoneID {
[self expectCKModifyItemRecords:expectedNumberOfRecords
currentKeyPointerRecords:expectedCurrentKeyRecords
-- (void)expectCKModifyKeyRecords: (NSUInteger) expectedNumberOfRecords currentKeyPointerRecords: (NSUInteger) expectedCurrentKeyRecords zoneID: (CKRecordZoneID*) zoneID {
+- (void)expectCKModifyKeyRecords:(NSUInteger)expectedNumberOfRecords
+ currentKeyPointerRecords:(NSUInteger)expectedCurrentKeyRecords
+ tlkShareRecords:(NSUInteger)expectedTLKShareRecords
+ zoneID:(CKRecordZoneID*)zoneID {
NSNumber* nkeys = [NSNumber numberWithUnsignedInteger: expectedNumberOfRecords];
NSNumber* ncurrentkeys = [NSNumber numberWithUnsignedInteger: expectedCurrentKeyRecords];
+ NSNumber* ntlkshares = [NSNumber numberWithUnsignedInteger: expectedTLKShareRecords];
- [self expectCKModifyRecords:@{SecCKRecordIntermediateKeyType: nkeys, SecCKRecordCurrentKeyType: ncurrentkeys}
+ [self expectCKModifyRecords:@{SecCKRecordIntermediateKeyType: nkeys,
+ SecCKRecordCurrentKeyType: ncurrentkeys,
+ SecCKRecordTLKShareType: ntlkshares,
+ }
deletedRecordTypeCounts:nil
zoneID:zoneID
checkModifiedRecord:nil
if(SecCKKSIsEnabled()) {
// Ensure we don't have any blocking operations
+ self.accountStatus = CKAccountStatusNoAccount;
[self startCKKSSubsystem];
[self waitForCKModifications];
[self.injectedManager cancelPendingOperations];
[CKKSViewManager resetManager:true setTo:nil];
self.injectedManager = nil;
+ [self.mockCKKSViewManager stopMocking];
+ self.mockCKKSViewManager = nil;
[self.mockAccountStateTracker stopMocking];
self.mockAccountStateTracker = nil;
[self.mockLockStateTracker stopMocking];
self.mockLockStateTracker = nil;
- [self.mockCKKSViewManager stopMocking];
- self.mockCKKSViewManager = nil;
-
[self.mockFakeCKModifyRecordZonesOperation stopMocking];
self.mockFakeCKModifyRecordZonesOperation = nil;
[self.mockFakeCKFetchRecordZoneChangesOperation stopMocking];
self.mockFakeCKFetchRecordZoneChangesOperation = nil;
+ [self.mockFakeCKFetchRecordsOperation stopMocking];
+ self.mockFakeCKFetchRecordsOperation = nil;
+
+ [self.mockFakeCKQueryOperation stopMocking];
+ self.mockFakeCKQueryOperation = nil;
+
[self.mockDatabase stopMocking];
self.mockDatabase = nil;
return key;
}
+- (NSError*)ckInternalServerExtensionError:(NSInteger)code description:(NSString*)desc {
+ NSError* extensionError = [[CKPrettyError alloc] initWithDomain:@"CloudkitKeychainService"
+ code:code
+ userInfo:@{
+ CKErrorServerDescriptionKey: desc,
+ NSLocalizedDescriptionKey: desc,
+ }];
+ NSError* internalError = [[CKPrettyError alloc] initWithDomain:CKInternalErrorDomain
+ code:CKErrorInternalPluginError
+ userInfo:@{CKErrorServerDescriptionKey: desc,
+ NSLocalizedDescriptionKey: desc,
+ NSUnderlyingErrorKey: extensionError,
+ }];
+ NSError* error = [[CKPrettyError alloc] initWithDomain:CKErrorDomain
+ code:CKErrorServerRejectedRequest
+ userInfo:@{NSUnderlyingErrorKey: internalError,
+ CKErrorServerDescriptionKey: desc,
+ NSLocalizedDescriptionKey: desc,
+ CKContainerIDKey: SecCKKSContainerName,
+ }];
+ return error;
+}
@end
+(FakeCKDatabase*) ckdb;
@end
-
@interface FakeCKModifySubscriptionsOperation : NSBlockOperation <CKKSModifySubscriptionsOperation>
@property (nullable) NSError* subscriptionError;
@property (nonatomic, nullable) NSMutableArray<CKSubscription *> *subscriptionsSaved;
@interface FakeCKFetchRecordZoneChangesOperation : NSOperation <CKKSFetchRecordZoneChangesOperation>
+(FakeCKDatabase*) ckdb;
+@property (nullable) void (^blockAfterFetch)();
+@end
+
+@interface FakeCKFetchRecordsOperation : NSBlockOperation <CKKSFetchRecordsOperation>
++ (FakeCKDatabase*)ckdb;
+@end
+
+@interface FakeCKQueryOperation : NSBlockOperation <CKKSQueryOperation>
++ (FakeCKDatabase*)ckdb;
@end
}
for(CKRecordZoneID* zoneID in self.recordZoneIDsToDelete) {
- ckdb[zoneID] = nil;
+ FakeCKZone* zone = ckdb[zoneID];
+
+ if(zone) {
+ // The zone exists. Its deletion will succeed.
+ ckdb[zoneID] = nil;
+
+ if(!self.recordZoneIDsDeleted) {
+ self.recordZoneIDsDeleted = [[NSMutableArray alloc] init];
+ }
+ [self.recordZoneIDsDeleted addObject:zoneID];
+ } else {
+ // The zone does not exist! CloudKit will tell us that the deletion failed.
+ if(!self.creationError) {
+ self.creationError = [[CKPrettyError alloc] initWithDomain:CKErrorDomain code:CKErrorPartialFailure userInfo:nil];
+ }
- if(!self.recordZoneIDsDeleted) {
- self.recordZoneIDsDeleted = [[NSMutableArray alloc] init];
+ // There really should be a better way to do this...
+ NSMutableDictionary* newDictionary = [self.creationError.userInfo mutableCopy] ?: [NSMutableDictionary dictionary];
+ NSMutableDictionary* newPartials = newDictionary[CKPartialErrorsByItemIDKey] ?: [NSMutableDictionary dictionary];
+ newPartials[zoneID] = [[CKPrettyError alloc] initWithDomain:CKErrorDomain code:CKErrorZoneNotFound
+ userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Mock CloudKit: zone '%@' not found", zoneID.zoneName]}];
+ newDictionary[CKPartialErrorsByItemIDKey] = newPartials;
+
+ self.creationError = [[CKPrettyError alloc] initWithDomain:self.creationError.domain code:self.creationError.code userInfo:newDictionary];
}
- [self.recordZoneIDsDeleted addObject:zoneID];
}
}
self.recordZoneChangeTokensUpdatedBlock(zoneID, zone.currentChangeToken, nil);
self.recordZoneFetchCompletionBlock(zoneID, zone.currentChangeToken, nil, NO, nil);
+
+ if(self.blockAfterFetch) {
+ self.blockAfterFetch();
+ }
+
self.fetchRecordZoneChangesCompletionBlock(nil);
}
}
}
@end
+@implementation FakeCKFetchRecordsOperation
+@synthesize recordIDs = _recordIDs;
+@synthesize desiredKeys = _desiredKeys;
+
+@synthesize perRecordProgressBlock = _perRecordProgressBlock;
+@synthesize perRecordCompletionBlock = _perRecordCompletionBlock;
+
+@synthesize fetchRecordsCompletionBlock = _fetchRecordsCompletionBlock;
+
+- (instancetype)init {
+ if((self = [super init])) {
+
+ }
+ return self;
+}
+- (instancetype)initWithRecordIDs:(NSArray<CKRecordID *> *)recordIDs {
+ if((self = [super init])) {
+ _recordIDs = recordIDs;
+ }
+ return self;
+}
+
+
+- (void)main {
+ FakeCKDatabase* ckdb = [FakeCKFetchRecordsOperation ckdb];
+
+ // Doesn't call the per-record progress block
+ NSMutableDictionary<CKRecordID*, CKRecord*>* records = [NSMutableDictionary dictionary];
+ NSError* operror = nil;
+
+ for(CKRecordID* recordID in self.recordIDs) {
+ CKRecordZoneID* zoneID = recordID.zoneID;
+ FakeCKZone* zone = ckdb[zoneID];
+
+ if(!zone) {
+ ckksnotice("fakeck", zoneID, "Fetched for a missing zone %@", zoneID);
+ NSError* zoneNotFoundError = [[CKPrettyError alloc] initWithDomain:CKErrorDomain
+ code:CKErrorZoneNotFound
+ userInfo:@{}];
+ NSError* error = [[CKPrettyError alloc] initWithDomain:CKErrorDomain
+ code:CKErrorPartialFailure
+ userInfo:@{CKPartialErrorsByItemIDKey: @{zoneID:zoneNotFoundError}}];
+
+ // Not strictly right, but good enough for now
+ self.fetchRecordsCompletionBlock(nil, error);
+ return;
+ }
+
+ CKRecord* record = zone.currentDatabase[recordID];
+ if(record) {
+ if(self.perRecordCompletionBlock) {
+ self.perRecordCompletionBlock(record, recordID, nil);
+ }
+ records[recordID] = record;
+ } else {
+ secerror("fakeck: Should be an error fetching %@", recordID);
+
+ if(!operror) {
+ operror = [[CKPrettyError alloc] initWithDomain:CKErrorDomain code:CKErrorPartialFailure userInfo:nil];
+ }
+
+ // There really should be a better way to do this...
+ NSMutableDictionary* newDictionary = [operror.userInfo mutableCopy] ?: [NSMutableDictionary dictionary];
+ NSMutableDictionary* newPartials = newDictionary[CKPartialErrorsByItemIDKey] ?: [NSMutableDictionary dictionary];
+ newPartials[recordID] = [[CKPrettyError alloc] initWithDomain:operror.domain code:CKErrorUnknownItem
+ userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Mock CloudKit: no record of %@", recordID]}];
+ newDictionary[CKPartialErrorsByItemIDKey] = newPartials;
+
+ operror = [[CKPrettyError alloc] initWithDomain:operror.domain code:operror.code userInfo:newDictionary];
+
+ /// TODO: do this better
+ if(self.perRecordCompletionBlock) {
+ self.perRecordCompletionBlock(nil, recordID, newPartials[zoneID]);
+ }
+ }
+ }
+
+ if(self.fetchRecordsCompletionBlock) {
+ self.fetchRecordsCompletionBlock(records, operror);
+ }
+}
+
++(FakeCKDatabase*) ckdb {
+ // Shouldn't ever be called: must be mocked out.
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException
+ reason:[NSString stringWithFormat:@"+ckdb[] must be mocked out for use"]
+ userInfo:nil];
+}
+@end
+
+
+@implementation FakeCKQueryOperation
+@synthesize query = _query;
+@synthesize cursor = _cursor;
+@synthesize zoneID = _zoneID;
+@synthesize resultsLimit = _resultsLimit;
+@synthesize desiredKeys = _desiredKeys;
+@synthesize recordFetchedBlock = _recordFetchedBlock;
+@synthesize queryCompletionBlock = _queryCompletionBlock;
+
+- (instancetype)initWithQuery:(CKQuery *)query {
+ if((self = [super init])) {
+ _query = query;
+ }
+ return self;
+}
+
+- (void)main {
+ FakeCKDatabase* ckdb = [FakeCKFetchRecordsOperation ckdb];
+
+ FakeCKZone* zone = ckdb[self.zoneID];
+ if(!zone) {
+ ckksnotice("fakeck", self.zoneID, "Queried a missing zone %@", self.zoneID);
+
+ // I'm really not sure if this is right, but...
+ NSError* zoneNotFoundError = [[CKPrettyError alloc] initWithDomain:CKErrorDomain
+ code:CKErrorZoneNotFound
+ userInfo:@{}];
+ self.queryCompletionBlock(nil, zoneNotFoundError);
+ return;
+ }
+
+ NSMutableArray<CKRecord*>* matches = [NSMutableArray array];
+ for(CKRecordID* recordID in zone.currentDatabase.keyEnumerator) {
+ CKRecord* record = zone.currentDatabase[recordID];
+
+ if([self.query.recordType isEqualToString: record.recordType] &&
+ [self.query.predicate evaluateWithObject:record]) {
+
+ [matches addObject:record];
+ self.recordFetchedBlock(record);
+ }
+ }
+
+ if(self.queryCompletionBlock) {
+ // The query cursor will be non-null if there are more than self.resultsLimit classes. Don't implement this.
+ self.queryCompletionBlock(nil, nil);
+ }
+}
+
+
++(FakeCKDatabase*) ckdb {
+ // Shouldn't ever be called: must be mocked out.
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException
+ reason:[NSString stringWithFormat:@"+ckdb[] must be mocked out for use"]
+ userInfo:nil];
+}
+@end
+
+
// Do literally nothing
@implementation FakeAPSConnection
[self rollChangeToken];
record.etag = [self.currentChangeToken description];
- ckksnotice("fakeck", self.zoneID, "change tag: %@", record.recordChangeTag);
+ ckksnotice("fakeck", self.zoneID, "change tag: %@ %@", record.recordChangeTag, record.recordID);
record.modificationDate = [NSDate date];
self.currentDatabase[record.recordID] = record;
}
+++ /dev/null
-//
-// Security
-//
-
-#import <Foundation/Foundation.h>
-
-@interface CKKSControl : NSObject
-- (NSDictionary<NSString *, id> *)printPerformanceCounters;
-- (NSDictionary<NSString *, id> *)status: (NSString*) view;
-- (void)status_custom: (NSString*) view;
-- (void)resync: (NSString*) view;
-
-- (void)resetLocal: (NSString*)view;
-- (void)resetCloudKit: (NSString*) view;
-
-- (void)getAnalyticsJSON;
-- (void)forceAnalyticsUpload;
-@end
#import <err.h>
#import "keychain/ckks/CKKS.h"
-#import "keychain/ckks/CKKSControlProtocol.h"
-#import "ckksctl.h"
+#import "keychain/ckks/CKKSControl.h"
#include "lib/SecArgParse.h"
}
}
-@interface CKKSControl ()
-@property NSXPCConnection *connection;
+@interface CKKSControlCLI : NSObject
+@property CKKSControl* control;
@end
-@implementation CKKSControl
-
-- (instancetype) initWithEndpoint:(xpc_endpoint_t)endpoint
-{
- if ((self = [super init]) == NULL)
- return NULL;
-
- NSXPCInterface *interface = CKKSSetupControlProtocol([NSXPCInterface interfaceWithProtocol:@protocol(CKKSControlProtocol)]);
- NSXPCListenerEndpoint *listenerEndpoint = [[NSXPCListenerEndpoint alloc] init];
-
- [listenerEndpoint _setEndpoint:endpoint];
-
- self.connection = [[NSXPCConnection alloc] initWithListenerEndpoint:listenerEndpoint];
- if (self.connection == NULL)
- return NULL;
-
- self.connection.remoteObjectInterface = interface;
-
- [self.connection resume];
+@implementation CKKSControlCLI
+- (instancetype) initWithCKKSControl:(CKKSControl*)control {
+ if ((self = [super init])) {
+ _control = control;
+ }
return self;
}
-- (NSDictionary<NSString *, id> *)printPerformanceCounters
+- (NSDictionary<NSString *, id> *)fetchPerformanceCounters
{
NSMutableDictionary *perfDict = [[NSMutableDictionary alloc] init];
#if OCTAGON
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
- perfDict[@"error"] = [error description];
- dispatch_semaphore_signal(sema);
+ [self.control rpcPerformanceCounters:^(NSDictionary<NSString *,NSNumber *> * counters, NSError * error) {
+ if(error) {
+ perfDict[@"error"] = [error description];
+ }
- }] performanceCounters:^(NSDictionary <NSString *, NSNumber *> *counters){
[counters enumerateKeysAndObjectsUsingBlock:^(NSString * key, NSNumber * obj, BOOL *stop) {
perfDict[key] = obj;
}];
+
dispatch_semaphore_signal(sema);
}];
printf("Beginning local reset for %s...\n", view ? [[view description] UTF8String] : "all zones");
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
- printf("\n\nError talking with daemon: %s\n", [[error description] UTF8String]);
- dispatch_semaphore_signal(sema);
-
- }] rpcResetLocal:view reply:^(NSError* result){
- if(result == NULL) {
- printf("reset complete.\n");
- } else {
- printf("reset error: %s\n", [[result description] UTF8String]);
- }
- dispatch_semaphore_signal(sema);
- }];
+ [self.control rpcResetLocal:view
+ reply:^(NSError *error) {
+ if(error == NULL) {
+ printf("reset complete.\n");
+ } else {
+ nsprintf(@"reset error: %@\n", error);
+ }
+ dispatch_semaphore_signal(sema);
+ }];
if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
printf("\n\nError: timed out waiting for response\n");
printf("Beginning CloudKit reset for %s...\n", view ? [[view description] UTF8String] : "all zones");
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
- printf("\n\nError talking with daemon: %s\n", [[error description] UTF8String]);
- dispatch_semaphore_signal(sema);
-
- }] rpcResetCloudKit:view reply:^(NSError* result){
- if(result == NULL) {
+ [self.control rpcResetCloudKit:view reply:^(NSError* error){
+ if(error == NULL) {
printf("CloudKit Reset complete.\n");
} else {
- printf("Reset error: %s\n", [[result description] UTF8String]);
+ nsprintf(@"Reset error: %@\n", error);
}
dispatch_semaphore_signal(sema);
}];
printf("Beginning resync for %s...\n", view ? [[view description] UTF8String] : "all zones");
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
- printf("\n\nError talking with daemon: %s\n", [[error description] UTF8String]);
- dispatch_semaphore_signal(sema);
-
- }] rpcResync:view reply:^(NSError* result){
- if(result == NULL) {
+ [self.control rpcResync:view reply:^(NSError* error){
+ if(error == NULL) {
printf("resync success.\n");
} else {
- printf("resync errored: %s\n", [[result description] UTF8String]);
+ nsprintf(@"resync errored: %@\n", error);
}
dispatch_semaphore_signal(sema);
}];
printf("Getting analytics sysdiagnose....\n");
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
- printf("\n\nError talking with daemon: %s\n", [[error description] UTF8String]);
- dispatch_semaphore_signal(sema);
- }] rpcGetAnalyticsSysdiagnoseWithReply:^(NSString* sysdiagnose, NSError* error) {
+ [self.control rpcGetAnalyticsSysdiagnoseWithReply:^(NSString* sysdiagnose, NSError* error) {
if (sysdiagnose && !error) {
nsprintf(@"Analytics sysdiagnose:\n\n%@", sysdiagnose);
}
printf("Getting analytics json....\n");
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
- printf("\n\nError talking with daemon: %s\n", [[error description] UTF8String]);
- dispatch_semaphore_signal(sema);
- }] rpcGetAnalyticsJSONWithReply:^(NSData* json, NSError* error) {
+ [self.control rpcGetAnalyticsJSONWithReply:^(NSData* json, NSError* error) {
if (json && !error) {
nsprintf(@"Analytics JSON:\n\n%@", [[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding]);
}
printf("Uploading....\n");
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [[self.connection remoteObjectProxyWithErrorHandler:^(NSError* error) {
- printf("\n\nError talking with daemon: %s\n", [[error description] UTF8String]);
- dispatch_semaphore_signal(sema);
- }] rpcForceUploadAnalyticsWithReply:^(BOOL success, NSError* error) {
+ [self.control rpcForceUploadAnalyticsWithReply:^(BOOL success, NSError* error) {
if (success) {
nsprintf(@"successfully uploaded analytics data");
}
}
}
-- (NSDictionary<NSString *, id> *)status: (NSString*) view {
+- (NSDictionary<NSString *, id> *)fetchStatus: (NSString*) view {
NSMutableDictionary *status = [[NSMutableDictionary alloc] init];
#if OCTAGON
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
- status[@"error"] = [error description];
- dispatch_semaphore_signal(sema);
-
- }] rpcStatus: view reply: ^(NSArray<NSDictionary*>* result, NSError* error) {
+ [self.control rpcStatus: view reply: ^(NSArray<NSDictionary*>* result, NSError* error) {
if(error) {
status[@"error"] = [error description];
}
- if(result.count == 0u) {
+ if(result.count <= 1u) {
printf("No CKKS views are active.\n");
}
return status;
}
-- (void)status_custom: (NSString*) view {
+- (void)printHumanReadableStatus: (NSString*) view {
#if OCTAGON
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
- printf("\n\nError talking with daemon: %s\n", [[error description] UTF8String]);
- dispatch_semaphore_signal(sema);
-
- }] rpcStatus: view reply: ^(NSArray<NSDictionary*>* result, NSError* error) {
+ [self.control rpcStatus: view reply: ^(NSArray<NSDictionary*>* result, NSError* error) {
if(error) {
printf("ERROR FETCHING STATUS: %s\n", [[error description] UTF8String]);
}
- if(result.count == 0u) {
+#define pop(d, key) ({ id x = d[key]; d[key] = nil; x; })
+
+ // First result is always global state
+ // Ideally, this would come in another parameter, but we can't change the protocol until
+ // <rdar://problem/33583242> CKKS: remove PCS's use of CKKSControlProtocol
+ NSMutableDictionary* global = [result[0] mutableCopy];
+ if(global) {
+ NSString* selfPeers = pop(global, @"selfPeers");
+ NSString* selfPeersError = pop(global, @"selfPeersError");
+ NSArray* trustedPeers = pop(global, @"trustedPeers");
+ NSString* trustedPeersError = pop(global, @"trustedPeersError");
+
+ printf("================================================================================\n\n");
+ printf("Global state:\n\n");
+ printf("Current self: %s\n", [[selfPeers description] UTF8String]);
+ if(![selfPeersError isEqual: [NSNull null]]) {
+ printf("Self Peers Error: %s\n", [[selfPeersError description] UTF8String]);
+ }
+ printf("Trusted peers: %s\n", [[trustedPeers description] UTF8String]);
+ if(![trustedPeersError isEqual: [NSNull null]]) {
+ printf("Trusted Peers Error: %s\n", [[trustedPeersError description] UTF8String]);
+ }
+ printf("\n");
+ }
+
+ NSArray* remainingViews = result.count > 1 ? [result subarrayWithRange:NSMakeRange(1, result.count-1)] : @[];
+
+ if(remainingViews.count == 0u) {
printf("No CKKS views are active.\n");
}
- for(NSDictionary* viewStatus in result) {
+ for(NSDictionary* viewStatus in remainingViews) {
NSMutableDictionary* status = [viewStatus mutableCopy];
- #define pop(d, key) ({ id x = d[key]; d[key] = nil; x; })
-
NSString* viewName = pop(status,@"view");
NSString* accountStatus = pop(status,@"ckaccountstatus");
NSString* lockStateTracker = pop(status,@"lockstatetracker");
NSDictionary* keys = pop(status,@"keys");
NSDictionary* ckmirror = pop(status,@"ckmirror");
NSArray* devicestates = pop(status, @"devicestates");
+ NSArray* tlkshares = pop(status, @"tlkshares");
+
NSString* zoneSetupOperation = pop(status,@"zoneSetupOperation");
NSString* viewSetupOperation = pop(status,@"viewSetupOperation");
printf("Current ClassA: %s\n", [currentClassA isEqual: [NSNull null]] ? "null" : [currentClassA UTF8String]);
printf("Current ClassC: %s\n", [currentClassC isEqual: [NSNull null]] ? "null" : [currentClassC UTF8String]);
+ printf("TLK shares: %s\n", [[tlkshares description] UTF8String]);
+
printf("Outgoing Queue counts: %s\n", [[oqe description] UTF8String]);
printf("Incoming Queue counts: %s\n", [[iqe description] UTF8String]);
printf("Key counts: %s\n", [[keys description] UTF8String]);
#if OCTAGON
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
- printf("\n\nError talking with daemon: %s\n", [[error description] UTF8String]);
- dispatch_semaphore_signal(sema);
-
- }] rpcFetchAndProcessChanges:view reply:^(NSError* error) {
+ [self.control rpcFetchAndProcessChanges:view reply:^(NSError* error) {
if(error) {
printf("Error fetching: %s\n", [[error description] UTF8String]);
} else {
#if OCTAGON
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
- printf("\n\nError talking with daemon: %s\n", [[error description] UTF8String]);
- dispatch_semaphore_signal(sema);
-
- }] rpcPushOutgoingChanges:view reply:^(NSError* error) {
+ [self.control rpcPushOutgoingChanges:view reply:^(NSError* error) {
if(error) {
printf("Error pushing: %s\n", [[error description] UTF8String]);
} else {
}
@autoreleasepool {
- xpc_endpoint_t endpoint = NULL;
-
- CFErrorRef cferror = NULL;
- endpoint = _SecSecuritydCopyCKKSEndpoint(&cferror);
- if (endpoint == NULL) {
- CFStringRef errorstr = NULL;
+ NSError* error = nil;
- if(cferror) {
- errorstr = CFErrorCopyDescription(cferror);
- }
-
- errx(1, "no CKKSControl endpoint available: %s", errorstr ? CFStringGetCStringPtr(errorstr, kCFStringEncodingUTF8) : "unknown error");
+ CKKSControl* rpc = [CKKSControl controlObject:&error];
+ if(error || !rpc) {
+ errx(1, "no CKKSControl failed: %s", [[error description] UTF8String]);
}
- CKKSControl *ctl = [[CKKSControl alloc] initWithEndpoint:endpoint];
- if (ctl == NULL) {
- errx(1, "failed to create CKKSControl object");
- }
+ CKKSControlCLI* ctl = [[CKKSControlCLI alloc] initWithCKKSControl:rpc];
NSString* view = viewArg ? [NSString stringWithCString: viewArg encoding: NSUTF8StringEncoding] : nil;
- if(status || perfCounters) {
+ if(status) {
+ // Complicated logic, but you can choose any combination of (json, perfcounters) that you like.
NSMutableDictionary *statusDict = [[NSMutableDictionary alloc] init];
- statusDict[@"performance"] = [ctl printPerformanceCounters];
- if (!json) {
- print_result(statusDict, false);
- if (status) {
- [ctl status_custom:view];
- }
- } else {
- if (status) {
- statusDict[@"status"] = [ctl status:view];
- }
- print_result(statusDict, true);
+ if(perfCounters) {
+ statusDict[@"performance"] = [ctl fetchPerformanceCounters];
}
+ if (json) {
+ statusDict[@"status"] = [ctl fetchStatus:view];
+ }
+ if(json || perfCounters) {
+ print_result(statusDict, true);
+ printf("\n");
+ }
+
+ if(!json) {
+ [ctl printHumanReadableStatus:view];
+ }
+ } else if(perfCounters) {
+ NSMutableDictionary *statusDict = [[NSMutableDictionary alloc] init];
+ statusDict[@"performance"] = [ctl fetchPerformanceCounters];
+ print_result(statusDict, false);
+
} else if(fetch) {
[ctl fetch:view];
} else if(push) {
_kb_save_bag_to_disk(service_user_record_t * ur, const char * bag_file, void * data, size_t length)
{
bool result = false;
+ char tmp_bag[PATH_MAX];
int fd = -1;
require(bag_file, done);
_set_thread_credentials(ur);
require(_kb_verify_create_path(ur), done);
- fd = open(bag_file, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0600);
- require_action(fd != -1, done, os_log(OS_LOG_DEFAULT, "could not create file: %s (%s)", bag_file, strerror(errno)));
- require_action(write(fd, data, length) == length, done, os_log(OS_LOG_DEFAULT, "failed to write keybag to disk %s (%s)", bag_file, strerror(errno)));
+ require_action(snprintf(tmp_bag, sizeof(tmp_bag), "%s.tmp", bag_file) < sizeof(tmp_bag), done, os_log(OS_LOG_DEFAULT, "path too large"));
+
+ fd = open(tmp_bag, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0600);
+ require_action(fd != -1, done, os_log(OS_LOG_DEFAULT, "could not create file: %s (%s)", tmp_bag, strerror(errno)));
+ require_action(write(fd, data, length) == length, done, os_log(OS_LOG_DEFAULT, "failed to write keybag to disk %s (%s)", tmp_bag, strerror(errno)));
+
+ /* try atomic swap (will fail if destination doesn't exist); if that fails, try regular rename */
+ if (renamex_np(tmp_bag, bag_file, RENAME_SWAP) != 0) {
+ os_log(OS_LOG_DEFAULT, "Warning: atomic swap failed");
+ require_noerr_action(rename(tmp_bag, bag_file), done, os_log(OS_LOG_DEFAULT, "could not save keybag file"));
+ }
result = true;
bool SecCertificateIsValidX(SecCertificateRef certificate, CFAbsoluteTime verifyTime)
__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_7, __MAC_10_9, __IPHONE_NA, __IPHONE_NA);
+/*!
+ @function SecCertificateCopyPublicKeySHA1DigestFromCertificateData
+ @abstract Returns the SHA1 hash of the public key of a certificate or NULL
+ @param allocator CFAllocator to allocate the certificate with.
+ @param der_certificate DER encoded X.509 certificate.
+ @result SHA1 hash of the public key of a certificate or NULL
+*/
+CFDataRef SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator,
+ CFDataRef der_certificate)
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_7, __MAC_10_13_2, __IPHONE_NA, __IPHONE_NA); // Likely incorrect.
+
#endif /* SEC_OS_OSX */
__END_DECLS
/*!
@function SecPolicyCreateAppleSoftwareSigning
@abstract Returns a policy object for verifying the Apple Software Signing certificate.
- @discussion The resulting policy uses the Basic X.509 policy with validity check and
+ @discussion The resulting policy uses the Basic X.509 policy with no validity check and
pinning options:
* The chain is anchored to any of the production Apple Root CAs. Internal releases allow
the chain to be anchored to Test Apple Root CAs if a defaults write for the policy is set.
OTHER_LDFLAGS_CLOUDKIT_BRIDGE_YES =
OTHER_LDFLAGS_CLOUDKIT = $(OTHER_LDFLAGS_CLOUDKIT_BRIDGE_$(BRIDGE))
-OTHER_LDFLAGS_PROTOBUF_BRIDGE_NO = -framework ProtocolBuffer
-OTHER_LDFLAGS_PROTOBUF_BRIDGE_YES =
-OTHER_LDFLAGS_PROTOBUF = $(OTHER_LDFLAGS_PROTOBUF_BRIDGE_$(BRIDGE))
+// The bridge appears to support protocol buffers.
+OTHER_LDFLAGS_PROTOBUF = -framework ProtocolBuffer
OTHER_LDFLAGS_SHAREDWEBCREDENTIALS_IOS_NO = -framework SharedWebCredentials
OTHER_LDFLAGS_SHAREDWEBCREDENTIALS_IOS_YES =
ARCHS[sdk=macosx*] = $(ARCHS_STANDARD)
#include "xcconfig/PlatformFeatures.xcconfig"
+#include "xcconfig/Version.xcconfig"
// Note that the 'Settings' view in Xcode will display the wrong values for platform-dependent settings
// Refer to the actual build command for final computed value
-GCC_PREPROCESSOR_DEFINITIONS = __KEYCHAINCORE__=1 CORECRYPTO_DONOT_USE_TRANSPARENT_UNION=1 OCTAGON=$(OCTAGON_ON) PLATFORM=$(PLATFORM_STR) $(GCC_PREPROCESSOR_DEFINITIONS)
+GCC_PREPROCESSOR_DEFINITIONS = __KEYCHAINCORE__=1 CORECRYPTO_DONOT_USE_TRANSPARENT_UNION=1 OCTAGON=$(OCTAGON_ON) PLATFORM=$(PLATFORM_STR) SECURITY_BUILD_VERSION=\"$(SECURITY_BUILD_VERSION)\" $(GCC_PREPROCESSOR_DEFINITIONS)
SECURITY_FUZZER_BASE_DIR = /AppleInternal/CoreOS/Fuzzers/Security
--- /dev/null
+
+VERSIONING_SYSTEM = apple-generic
+
+SECURITY_BUILD_VERSION = $(SECURITY_BUILD_VERSION_$(SECURITY_BUILD_VERSION_EVAL_$(RC_ProjectSourceVersion)))
+SECURITY_BUILD_VERSION_ = $(RC_ProjectSourceVersion)
+SECURITY_BUILD_VERSION_EVAL_ = noVer
+SECURITY_BUILD_VERSION_noVer = 0
+
+CURRENT_PROJECT_VERSION = $(SECURITY_BUILD_VERSION)
// For frameworks that should only be built when the modern obj-c runtime is available.
// This file must be updated when new architectures are introduced.
-VALID_ARCHS = armv6 armv7 arm64 x86_64 x86_64h
+VALID_ARCHS = armv6 armv7 armv7k arm64 x86_64 x86_64h
-VALID_ARCHS[sdk=*simulator*] = armv6 armv7 arm64 i386 x86_64 x86_64h
+VALID_ARCHS[sdk=*simulator*] = armv6 armv7 armv7k arm64 i386 x86_64 x86_64h