]> git.saurik.com Git - apple/security.git/commitdiff
Security-58286.31.2.tar.gz macos-10132 v58286.31.2
authorApple <opensource@apple.com>
Sat, 3 Feb 2018 22:13:27 +0000 (22:13 +0000)
committerApple <opensource@apple.com>
Sat, 3 Feb 2018 22:13:27 +0000 (22:13 +0000)
223 files changed:
Analytics/SFAnalyticsLogger.h
Analytics/SFAnalyticsLogger.m
Analytics/SQLite/SFSQLite.h
Analytics/SQLite/SFSQLite.m
Analytics/SQLite/SFSQLiteStatement.m
CircleJoinRequested/CircleJoinRequested.m
KeychainCircle/Tests/KCJoiningSessionTest.m
KeychainCircle/Tests/KCPairingTest.m
KeychainEntitledTestApp_ios/Assets.xcassets/AppIcon.appiconset/Contents.json
KeychainSyncingOverIDSProxy/KeychainSyncingOverIDSProxy+SendMessage.m
OSX/authd/authitems.c
OSX/authd/debugging.h
OSX/authd/engine.c
OSX/config/base.xcconfig
OSX/config/security_macos.xcconfig
OSX/lib/en.lproj/authorization.prompts.strings
OSX/libsecurity_authorization/lib/AuthorizationPriv.h
OSX/libsecurity_codesigning/lib/StaticCode.cpp
OSX/libsecurity_keychain/lib/CertificateValues.cpp
OSX/libsecurity_keychain/lib/CertificateValues.h
OSX/libsecurity_keychain/lib/SecBase64P.c [deleted file]
OSX/libsecurity_keychain/lib/SecCertificate.cpp
OSX/libsecurity_keychain/lib/SecCertificateInternalP.h [deleted file]
OSX/libsecurity_keychain/lib/SecCertificateP.c [deleted file]
OSX/libsecurity_keychain/lib/SecCertificateP.h [deleted file]
OSX/libsecurity_keychain/lib/SecCertificatePrivP.h [deleted file]
OSX/libsecurity_keychain/lib/SecFrameworkP.c [deleted file]
OSX/libsecurity_keychain/lib/SecItem.cpp
OSX/libsecurity_keychain/lib/SecTrust.cpp
OSX/libsecurity_keychain/libDER/Tests/AppleMobilePersonalizedTicket.h [deleted file]
OSX/libsecurity_keychain/libDER/Tests/DER_Ticket.c [deleted file]
OSX/libsecurity_keychain/libDER/Tests/DER_Ticket.h [deleted file]
OSX/libsecurity_keychain/libDER/Tests/parseCert.c [deleted file]
OSX/libsecurity_keychain/libDER/Tests/parseCrl.c [deleted file]
OSX/libsecurity_keychain/libDER/Tests/parseTicket.c [deleted file]
OSX/libsecurity_keychain/libDER/libDERUtils/fileIo.c [deleted file]
OSX/libsecurity_keychain/libDER/libDERUtils/fileIo.h [deleted file]
OSX/libsecurity_keychain/libDER/libDERUtils/libDERUtils.c [deleted file]
OSX/libsecurity_keychain/libDER/libDERUtils/libDERUtils.h [deleted file]
OSX/libsecurity_keychain/libDER/libDERUtils/printFields.c [deleted file]
OSX/libsecurity_keychain/libDER/libDERUtils/printFields.h [deleted file]
OSX/libsecurity_keychain/regressions/si-20-sectrust-provisioning.c
OSX/libsecurity_keychain/xpc-tsa/main-tsa.m
OSX/libsecurity_smime/lib/tsaSupport.c
OSX/sec/SOSCircle/Regressions/SOSRegressionUtilities.h
OSX/sec/SOSCircle/Regressions/SOSRegressionUtilities.m
OSX/sec/SOSCircle/Regressions/sc-130-resignationticket.c
OSX/sec/SOSCircle/Regressions/sc-150-ring.m
OSX/sec/SOSCircle/Regressions/sc-153-backupslicekeybag.c
OSX/sec/SOSCircle/Regressions/sc-20-keynames.m
OSX/sec/SOSCircle/Regressions/sc-30-peerinfo.c
OSX/sec/SOSCircle/Regressions/sc-31-peerinfo-simplefuzz.c
OSX/sec/SOSCircle/Regressions/sc-40-circle.c
OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.m
OSX/sec/SOSCircle/SecureObjectSync/SOSAccountFullPeerInfo.m
OSX/sec/SOSCircle/SecureObjectSync/SOSAccountGhost.m
OSX/sec/SOSCircle/SecureObjectSync/SOSAccountPriv.h
OSX/sec/SOSCircle/SecureObjectSync/SOSAccountTransaction.m
OSX/sec/SOSCircle/SecureObjectSync/SOSAccountTrustClassic+Circle.m
OSX/sec/SOSCircle/SecureObjectSync/SOSAccountTrustClassic+Identity.h
OSX/sec/SOSCircle/SecureObjectSync/SOSAccountTrustClassic+Identity.m
OSX/sec/SOSCircle/SecureObjectSync/SOSCloudCircle.h
OSX/sec/SOSCircle/SecureObjectSync/SOSCloudCircle.m
OSX/sec/SOSCircle/SecureObjectSync/SOSCoder.c
OSX/sec/SOSCircle/SecureObjectSync/SOSEngine.c
OSX/sec/SOSCircle/SecureObjectSync/SOSExports.exp-in
OSX/sec/SOSCircle/SecureObjectSync/SOSFullPeerInfo.h
OSX/sec/SOSCircle/SecureObjectSync/SOSFullPeerInfo.m
OSX/sec/SOSCircle/SecureObjectSync/SOSPeerInfo.h
OSX/sec/SOSCircle/SecureObjectSync/SOSPeerInfo.m
OSX/sec/SOSCircle/SecureObjectSync/SOSPeerInfoDER.m
OSX/sec/SOSCircle/SecureObjectSync/SOSPeerInfoPriv.h
OSX/sec/SOSCircle/SecureObjectSync/SOSTransportMessageIDS.m
OSX/sec/SOSCircle/SecureObjectSync/SOSTransportMessageKVS.m
OSX/sec/SOSCircle/SecureObjectSync/ViewList.list
OSX/sec/SOSCircle/Tool/recovery_key.h
OSX/sec/SOSCircle/Tool/recovery_key.m
OSX/sec/Security/Regressions/secitem/si-66-smime.c
OSX/sec/Security/SecCertificate.c
OSX/sec/Security/SecCertificateInternal.h
OSX/sec/Security/SecExports.exp-in
OSX/sec/Security/SecIdentity.c
OSX/sec/Security/SecItem.c
OSX/sec/Security/SecPolicy.c
OSX/sec/Security/SecRecoveryKey.m
OSX/sec/ipc/server.c
OSX/sec/ipc/server_xpc.m
OSX/sec/securityd/Regressions/SOSAccountTesting.h
OSX/sec/securityd/Regressions/secd-60-account-cloud-identity.m
OSX/sec/securityd/Regressions/secd-668-ghosts.m
OSX/sec/securityd/SOSCloudCircleServer.h
OSX/sec/securityd/SOSCloudCircleServer.m
OSX/sec/securityd/SecCertificateServer.c
OSX/sec/securityd/SecCertificateServer.h
OSX/sec/securityd/SecCertificateSource.c
OSX/sec/securityd/SecItemSchema.c
OSX/sec/securityd/SecItemServer.c
OSX/sec/securityd/SecPinningDb.m
OSX/sec/securityd/SecRevocationDb.c
OSX/sec/securityd/SecRevocationNetworking.m
OSX/sec/securityd/SecTrustServer.c
OSX/sec/securityd/SecTrustServer.h
OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODIDSWCA37-noRequireExplicitPolicies.cer [changed mode: 0755->0644]
OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODIDSWCA37.cer [changed mode: 0755->0644]
OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODInteroperability-extraPolicies.cer [changed mode: 0755->0644]
OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODInteroperability-noRequireExplicitPolicies.cer [changed mode: 0755->0644]
OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODInteroperability.cer [changed mode: 0755->0644]
OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODLeaf.cer [changed mode: 0755->0644]
OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODRootCA3-noRequireExplicitPolicies.cer [changed mode: 0755->0644]
OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODRootCA3.cer [changed mode: 0755->0644]
OSX/shared_regressions/si-20-sectrust-policies-data/FakeFederalBridgeCA-extraPolicies.cer [changed mode: 0755->0644]
OSX/shared_regressions/si-20-sectrust-policies-data/FakeFederalBridgeCA.cer [changed mode: 0755->0644]
OSX/shared_regressions/si-20-sectrust-policies-data/FakeFederalCommonPolicy.cer [changed mode: 0755->0644]
OSX/trustd/trustd.c
RegressionTests/manifeststresstest/Keychain.m
RegressionTests/manifeststresstest/Monkey.m
RegressionTests/manifeststresstest/mark.m
Security.exp-in
Security.xcodeproj/project.pbxproj
Security.xcodeproj/xcshareddata/xcschemes/CKKSTests.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/osx - secdtests.xcscheme
keychain/SecAccessControl.h
keychain/SecItemPriv.h
keychain/SecKey.h
keychain/ckks/CKKS.h
keychain/ckks/CKKS.m
keychain/ckks/CKKSAnalyticsLogger.h
keychain/ckks/CKKSAnalyticsLogger.m
keychain/ckks/CKKSCKAccountStateTracker.h
keychain/ckks/CKKSCKAccountStateTracker.m
keychain/ckks/CKKSControl.h [new file with mode: 0644]
keychain/ckks/CKKSControl.m [new file with mode: 0644]
keychain/ckks/CKKSControlProtocol.m
keychain/ckks/CKKSCurrentItemPointer.h
keychain/ckks/CKKSCurrentItemPointer.m
keychain/ckks/CKKSCurrentKeyPointer.h
keychain/ckks/CKKSCurrentKeyPointer.m
keychain/ckks/CKKSFetchAllRecordZoneChangesOperation.m
keychain/ckks/CKKSFixups.h [new file with mode: 0644]
keychain/ckks/CKKSFixups.m [new file with mode: 0644]
keychain/ckks/CKKSGroupOperation.h
keychain/ckks/CKKSGroupOperation.m
keychain/ckks/CKKSHealKeyHierarchyOperation.m
keychain/ckks/CKKSHealTLKSharesOperation.h [new file with mode: 0644]
keychain/ckks/CKKSHealTLKSharesOperation.m [new file with mode: 0644]
keychain/ckks/CKKSIncomingQueueEntry.h
keychain/ckks/CKKSIncomingQueueEntry.m
keychain/ckks/CKKSIncomingQueueOperation.m
keychain/ckks/CKKSItem.h
keychain/ckks/CKKSItem.m
keychain/ckks/CKKSKey.h
keychain/ckks/CKKSKey.m
keychain/ckks/CKKSKeychainView.h
keychain/ckks/CKKSKeychainView.m
keychain/ckks/CKKSLockStateTracker.m
keychain/ckks/CKKSManifest.m
keychain/ckks/CKKSMirrorEntry.h
keychain/ckks/CKKSMirrorEntry.m
keychain/ckks/CKKSNewTLKOperation.m
keychain/ckks/CKKSOutgoingQueueEntry.m
keychain/ckks/CKKSOutgoingQueueOperation.m
keychain/ckks/CKKSPeer.h [new file with mode: 0644]
keychain/ckks/CKKSPeer.m [new file with mode: 0644]
keychain/ckks/CKKSProcessReceivedKeysOperation.m
keychain/ckks/CKKSRecordHolder.h
keychain/ckks/CKKSRecordHolder.m
keychain/ckks/CKKSResultOperation.h [new file with mode: 0644]
keychain/ckks/CKKSResultOperation.m [new file with mode: 0644]
keychain/ckks/CKKSSIV.h
keychain/ckks/CKKSSIV.m
keychain/ckks/CKKSSQLDatabaseObject.m
keychain/ckks/CKKSScanLocalItemsOperation.m
keychain/ckks/CKKSTLKShare.h [new file with mode: 0644]
keychain/ckks/CKKSTLKShare.m [new file with mode: 0644]
keychain/ckks/CKKSUpdateCurrentItemPointerOperation.h
keychain/ckks/CKKSUpdateCurrentItemPointerOperation.m
keychain/ckks/CKKSUpdateDeviceStateOperation.m
keychain/ckks/CKKSViewManager.h
keychain/ckks/CKKSViewManager.m
keychain/ckks/CKKSZone.h
keychain/ckks/CKKSZone.m
keychain/ckks/CKKSZoneChangeFetcher.h
keychain/ckks/CKKSZoneChangeFetcher.m
keychain/ckks/CKKSZoneStateEntry.h
keychain/ckks/CKKSZoneStateEntry.m
keychain/ckks/CloudKitCategories.h
keychain/ckks/CloudKitCategories.m
keychain/ckks/CloudKitDependencies.h
keychain/ckks/NSOperationCategories.h [new file with mode: 0644]
keychain/ckks/NSOperationCategories.m [new file with mode: 0644]
keychain/ckks/proto/CKKSSerializedKey.proto [new file with mode: 0644]
keychain/ckks/proto/source/CKKSSerializedKey.h [new file with mode: 0644]
keychain/ckks/proto/source/CKKSSerializedKey.m [new file with mode: 0644]
keychain/ckks/tests/CKKSAESSIVEncryptionTests.m [new file with mode: 0644]
keychain/ckks/tests/CKKSCloudKitTests.m
keychain/ckks/tests/CKKSEncryptionTests.m [deleted file]
keychain/ckks/tests/CKKSLoggerTests.m
keychain/ckks/tests/CKKSManifestTests.m
keychain/ckks/tests/CKKSOperationTests.m
keychain/ckks/tests/CKKSSOSTests.m
keychain/ckks/tests/CKKSSQLTests.m
keychain/ckks/tests/CKKSServerValidationRecoveryTests.m [new file with mode: 0644]
keychain/ckks/tests/CKKSTLKSharingEncryptionTests.m [new file with mode: 0644]
keychain/ckks/tests/CKKSTLKSharingTests.m [new file with mode: 0644]
keychain/ckks/tests/CKKSTests+API.m
keychain/ckks/tests/CKKSTests+CurrentPointerAPI.m
keychain/ckks/tests/CKKSTests.m
keychain/ckks/tests/CloudKitKeychainSyncingFixupTests.m [new file with mode: 0644]
keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.h
keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.m
keychain/ckks/tests/CloudKitMockXCTest.h
keychain/ckks/tests/CloudKitMockXCTest.m
keychain/ckks/tests/MockCloudKit.h
keychain/ckks/tests/MockCloudKit.m
keychain/ckksctl/ckksctl.h [deleted file]
keychain/ckksctl/ckksctl.m
securityd/securityd_service/securityd_service/main.c
trust/SecCertificatePriv.h
trust/SecPolicyPriv.h
xcconfig/PlatformLibraries.xcconfig
xcconfig/Security.xcconfig
xcconfig/Version.xcconfig [new file with mode: 0644]
xcconfig/framework_requiring_modern_objc_runtime.xcconfig

index ebdba8b7dc43a3268dfa0fa0f5c46d0246fcbdad..6dfed9ffe2221b7bb214ec6be8b2774cd3565b55 100644 (file)
@@ -54,7 +54,7 @@
 // Things below are for utilities to drive and/or test the system
 
 - (NSString*)getSysdiagnoseDumpWithError:(NSError**)error;
 // 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;
 
 // --------------------------------
 - (BOOL)forceUploadWithError:(NSError**)error;
 
 // --------------------------------
index 2a750ca56be59dc443cfb0bc19dfc01776bed3eb..2bc376f90bb8899a7826c946bfb3106ec455b0de 100644 (file)
@@ -28,6 +28,8 @@
 #import "CKKSViewManager.h"
 #import "debugging.h"
 #import <objc/runtime.h>
 #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 SFAnalyticsLoggerTableSuccessCount = @"success_count";
 NSString* const SFAnalyticsLoggerColumnEventType = @"event_type";
@@ -47,9 +49,13 @@ NSString* const SFAnalyticsLoggerSplunkTopic = @"topic";
 NSString* const SFAnalyticsLoggerSplunkEventTime = @"eventTime";
 NSString* const SFAnalyticsLoggerSplunkPostTime = @"postTime";
 NSString* const SFAnalyticsLoggerSplunkEventType = @"eventType";
 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 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"
 NSString* const SFAnalyticsUserDefaultsSuite = @"com.apple.security.analytics";
 
 static NSString* const SFAnalyticsLoggerTableSchema = @"CREATE TABLE IF NOT EXISTS hard_failures (\n"
@@ -89,15 +95,16 @@ static NSString* const SFAnalyticsLoggerTableSchema = @"CREATE TABLE IF NOT EXIS
 #define SFANALYTICS_SPLUNK_DEV 0
 #define SFANALYTICS_MAX_EVENTS_TO_REPORT 999
 
 #define SFANALYTICS_SPLUNK_DEV 0
 #define SFANALYTICS_MAX_EVENTS_TO_REPORT 999
 
+#define SECONDS_PER_DAY (60 * 60 * 24)
+
 #if SFANALYTICS_SPLUNK_DEV
 #if SFANALYTICS_SPLUNK_DEV
-#define SECONDS_BETWEEN_UPLOADS 10
+#define SECONDS_BETWEEN_UPLOADS_CUSTOMER 10
+#define SECONDS_BETWEEN_UPLOADS_INTERNAL 10
 #else
 #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
 
 #endif
 
-#define SECONDS_PER_DAY (60 * 60 * 24)
-
 typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
     SFAnalyticsEventClassSuccess,
     SFAnalyticsEventClassHardFailure,
 typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
     SFAnalyticsEventClassSuccess,
     SFAnalyticsEventClassHardFailure,
@@ -121,6 +128,7 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
 - (NSInteger)softFailureCountForEventType:(NSString*)eventType;
 - (void)addEventDict:(NSDictionary*)eventDict toTable:(NSString*)table;
 - (void)clearAllData;
 - (NSInteger)softFailureCountForEventType:(NSString*)eventType;
 - (void)addEventDict:(NSDictionary*)eventDict toTable:(NSString*)table;
 - (void)clearAllData;
+- (BOOL)tryToOpenDatabase;
 
 - (NSDictionary*)summaryCounts;
 
 
 - (NSDictionary*)summaryCounts;
 
@@ -201,7 +209,12 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
     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);
     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"];
 
         NSDictionary* systemDefaultValues = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle bundleWithPath:@"/System/Library/Frameworks/Security.framework"] pathForResource:@"SFAnalyticsLogging" ofType:@"plist"]];
         _splunkTopicName = systemDefaultValues[@"splunk_topic"];
@@ -256,12 +269,12 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
 
 - (void)logHardFailureForEventNamed:(NSString*)eventName withAttributes:(NSDictionary*)attributes
 {
 
 - (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
 {
 }
 
 - (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
 }
 
 - (void)noteEventNamed:(NSString*)eventName
@@ -401,8 +414,11 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
     dispatch_semaphore_t sem = dispatch_semaphore_create(0);
 
     __block NSError* error = nil;
     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];
 
                                                                   delegate:self
                                                              delegateQueue:nil];
 
@@ -475,7 +491,7 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
 - (BOOL)forceUploadWithError:(NSError**)error
 {
     __block BOOL result = NO;
 - (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);
     dispatch_sync(_queue, ^{
         if (json && [self _onQueuePostJSON:json error:error]) {
             secinfo("ckks", "uploading sync health data: %@", json);
@@ -500,8 +516,11 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
      * Create the NSURLSession
      *  We use the ephemeral session config because we don't need cookies or cache
      */
      * 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];
 
                                                               delegate:self
                                                          delegateQueue:nil];
 
@@ -605,7 +624,6 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
 - (NSString*)getSysdiagnoseDumpWithError:(NSError**)error
 {
     NSMutableString* sysdiagnose = [[NSMutableString alloc] init];
 - (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];
     NSDictionary* extraValues = self.extraValuesToUploadToServer;
     [extraValues enumerateKeysAndObjectsUsingBlock:^(NSString* key, id object, BOOL* stop) {
         [sysdiagnose appendFormat:@"Key: %@, Value: %@\n", key, object];
@@ -623,11 +641,39 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
     return sysdiagnose;
 }
 
     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, ^{
 {
     __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;
         NSArray* failureRecords = self->_database.failureRecords;
 
         NSDictionary* successCounts = self->_database.summaryCounts;
@@ -649,13 +695,19 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
         healthSummaryEvent[SFAnalyticsLoggerColumnSuccessCount] = @(totalSuccessCount);
         healthSummaryEvent[SFAnalyticsLoggerColumnHardFailureCount] = @(totalHardFailureCount);
         healthSummaryEvent[SFAnalyticsLoggerColumnSoftFailureCount] = @(totalSoftFailureCount);
         healthSummaryEvent[SFAnalyticsLoggerColumnSuccessCount] = @(totalSuccessCount);
         healthSummaryEvent[SFAnalyticsLoggerColumnHardFailureCount] = @(totalHardFailureCount);
         healthSummaryEvent[SFAnalyticsLoggerColumnSoftFailureCount] = @(totalSoftFailureCount);
+        [SFAnalyticsLogger addOSVersion:healthSummaryEvent];
 
         NSMutableArray* splunkRecords = failureRecords.mutableCopy;
         [splunkRecords addObject: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;
     });
 
     return json;
@@ -746,7 +798,11 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
             loggingStores[standardizedPath] = store;
         }
 
             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;
     }
 
     return store;
@@ -757,120 +813,171 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) {
     [self close];
 }
 
     [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
 {
 - (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
 {
 }
 
 - (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
 {
 }
 
 - (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
 {
 }
 
 - (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
 {
 }
 
 - (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
 {
 }
 
 - (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
 {
 }
 
 - (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*)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*)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
 {
 }
 
 - (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
 {
 - (NSDate*)uploadDate
 {
-    return [self datePropertyForKey:SFAnalyticsLoggerUploadDate];
+    if ([self tryToOpenDatabase]) {
+        return [self datePropertyForKey:SFAnalyticsLoggerUploadDate];
+    }
+    return nil;
 }
 
 - (void)setUploadDate:(NSDate*)uploadDate
 {
 }
 
 - (void)setUploadDate:(NSDate*)uploadDate
 {
-    [self setDateProperty:uploadDate forKey:SFAnalyticsLoggerUploadDate];
+    if ([self tryToOpenDatabase]) {
+        [self setDateProperty:uploadDate forKey:SFAnalyticsLoggerUploadDate];
+    }
 }
 
 - (void)clearAllData
 {
 }
 
 - (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
 }
 
 @end
index 9a5a4b200f30b728638e514cb3c4108528c2af24..cb6a80df7b54a9ad578d1e545a044042695718dc 100644 (file)
@@ -103,9 +103,6 @@ typedef NS_ENUM(NSInteger, SFSQLiteSynchronousMode) {
 - (void)analyze;
 - (void)vacuum;
 
 - (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;
 
 // The rowID assigned to the last record inserted into the database.
 - (SFSQLiteRowID)lastInsertRowID;
 
@@ -113,8 +110,8 @@ typedef NS_ENUM(NSInteger, SFSQLiteSynchronousMode) {
 - (int)changes;
 
 // Execute one-or-more queries. Use prepared statements for anything performance critical.
 - (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;
 
 // Prepared statement pool accessors. Statements must be reset after they're used.
 - (SFSQLiteStatement *)statementForSQL:(NSString *)SQL;
index 4590e889ab72be82298ded8da673c1c3c3a39070..f83444e17a308d5f53580605a50b4a752bd2547e 100644 (file)
@@ -25,7 +25,7 @@
 #import "SFSQLiteStatement.h"
 #include <sqlite3.h>
 #include <CommonCrypto/CommonDigest.h>
 #import "SFSQLiteStatement.h"
 #include <sqlite3.h>
 #include <CommonCrypto/CommonDigest.h>
-
+#import "debugging.h"
 
 #define kSFSQLiteBusyTimeout       (5*60*1000)
 
 
 #define kSFSQLiteBusyTimeout       (5*60*1000)
 
@@ -42,10 +42,6 @@ static NSString *const kSFSQLiteCreatePropertiesTableSQL =
     @"    value  text\n"
     @");\n";
 
     @"    value  text\n"
     @");\n";
 
-@interface SFSQLiteError : NSObject
-+ (void)raise:(NSString *)reason code:(int)code extended:(int)extended;
-@end
-
 
 NSArray *SFSQLiteJournalSuffixes() {
     return @[@"-journal", @"-wal", @"-shm"];
 
 NSArray *SFSQLiteJournalSuffixes() {
     return @[@"-journal", @"-wal", @"-shm"];
@@ -339,9 +335,15 @@ allDone:
     }
     
     // You don't argue with the Ben: rdar://12685305
     }
     
     // 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"];
     
     // rdar://problem/32168789
     // [self executeSQL:@"pragma foreign_keys = 1"];
@@ -410,7 +412,15 @@ allDone:
     success = YES;
     
 done:
     success = YES;
     
 done:
+    if (!success) {
+        sqlite3_close_v2(_db);
+        _db = nil;
+    }
+    
     if (!success && error) {
     if (!success && error) {
+        if (!localError) {
+            localError = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithFormat:@"Error opening db at %@, ", _path]}];
+        }
         *error = localError;
     }
     return success;
         *error = localError;
     }
     return success;
@@ -419,7 +429,8 @@ done:
 - (void)open {
     NSError *error;
     if (![self openWithError:&error]) {
 - (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;
     }
 }
 
     }
 }
 
@@ -432,7 +443,8 @@ done:
             [self removeAllStatements];
             
             if (sqlite3_close(_db)) {
             [self removeAllStatements];
             
             if (sqlite3_close(_db)) {
-                [self raise:@"Error closing database"];
+                secerror("sfsqlite: Error closing database");
+                return;
             }
             _db = NULL;
         }
             }
             _db = NULL;
         }
@@ -468,44 +480,10 @@ done:
     [self executeSQL:@"vacuum"];
 }
 
     [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) {
 - (SFSQLiteRowID)lastInsertRowID {
     if (!_db) {
-        [self raise:@"Database is closed"];
+        secerror("sfsqlite: Database is closed");
+        return -1;
     }
     
     return sqlite3_last_insert_rowid(_db);
     }
     
     return sqlite3_last_insert_rowid(_db);
@@ -514,33 +492,40 @@ done:
 - (int)changes
 {
     if (!_db) {
 - (int)changes
 {
     if (!_db) {
-        [self raise:@"Database is closed"];
+        secerror("sfsqlite: Database is closed");
+        return -1;
     }
     
     return sqlite3_changes(_db);
 }
 
     }
     
     return sqlite3_changes(_db);
 }
 
-- (void)executeSQL:(NSString *)format, ... {
+- (BOOL)executeSQL:(NSString *)format, ... {
     va_list args;
     va_start(args, format);
     va_list args;
     va_start(args, format);
-    [self executeSQL:format arguments:args];
+    BOOL result = [self executeSQL:format arguments:args];
     va_end(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) {
     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) {
     }
     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) {
 }
 
 - (SFSQLiteStatement *)statementForSQL:(NSString *)SQL {
     if (!_db) {
-        [self raise:@"Database is closed"];
+        secerror("sfsqlite: Database is closed");
+        return nil;
     }
     
     SFSQLiteStatement *statement = _statementsBySQL[SQL];
     }
     
     SFSQLiteStatement *statement = _statementsBySQL[SQL];
@@ -550,7 +535,8 @@ done:
         sqlite3_stmt *handle = NULL;
         NS_VALID_UNTIL_END_OF_SCOPE NSString *arcSafeSQL = SQL;
         if (sqlite3_prepare_v2(_db, [arcSafeSQL UTF8String], -1, &handle, NULL)) {
         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];
         }
         
         statement = [[SFSQLiteStatement alloc] initWithSQLite:self SQL:SQL handle:handle];
@@ -880,7 +866,8 @@ done:
 - (NSString *)_tableNameForClass:(Class)objectClass {
     NSString *className = [objectClass SFSQLiteClassName];
     if (![className hasPrefix:_objectClassPrefix]) {
 - (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];
 }
     }
     return [className substringFromIndex:_objectClassPrefix.length];
 }
@@ -897,171 +884,3 @@ done:
 }
 
 @end
 }
 
 @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
index c8de43a8bb6b5aca2b4214de4dc745a127331d80..a142bc9ffe3e5b10b39096950c835fcbed7e2c25 100644 (file)
@@ -24,6 +24,7 @@
 #import "SFSQLite.h"
 #import "SFSQLiteStatement.h"
 #import "SFObjCType.h"
 #import "SFSQLite.h"
 #import "SFSQLiteStatement.h"
 #import "SFObjCType.h"
+#import "debugging.h"
 
 @interface SFSQLiteStatement ()
 @property (nonatomic, strong) NSMutableArray *temporaryBoundObjects;
 
 @interface SFSQLiteStatement ()
 @property (nonatomic, strong) NSMutableArray *temporaryBoundObjects;
 }
 
 - (void)finalizeStatement {
 }
 
 - (void)finalizeStatement {
-    SFSQLite *strongSQLite = _SQLite;
-
     if (!_reset) {
     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)) {
     }
     if (sqlite3_finalize(_handle)) {
-        [strongSQLite raise:@"Error finalizing prepared statement: \"%@\"", _SQL];
+        secerror("sfsqlite: Error finalizing prepared statement: \"%@\"", _SQL);
+        return;
     }
 }
 
     }
 }
 
         return NO;
     } else {
         [self resetAfterStepError];
         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 {
         return NO;
     }
 }
 
 - (void)reset {
-    SFSQLite *strongSQLite = _SQLite;
-
     if (!_reset) {
         if (sqlite3_reset(_handle)) {
     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)) {
         }
         
         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;
         }
         [_temporaryBoundObjects removeAllObjects];
         _reset = YES;
 }
 
 - (void)bindInt:(SInt32)value atIndex:(NSUInteger)index {
 }
 
 - (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)) {
     
     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 {
     }
 }
 
 - (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)) {
     
     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 {
     }
 }
 
 - (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)) {
     
     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 {
     }
 }
 
 - (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)) {
     
     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];
         }
     } else {
         [self bindNullAtIndex:index];
 }
 
 - (void)bindText:(NSString *)value atIndex:(NSUInteger)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)) {
     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];
         }
     } else {
         [self bindNullAtIndex:index];
 - (void)bindNullAtIndex:(NSUInteger)index {
     int rc = sqlite3_bind_null(_handle, (int)index+1);
     if ((rc & 0x00FF) != SQLITE_OK) {
 - (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 {
     } 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:
             return nil;
             
         default:
-            [NSException raise:NSGenericException format:@"Unexpected column type: %d", type];
+            secerror("sfsqlite: Unexpected column type: %d", type);
             return nil;
     }
 }
             return nil;
     }
 }
index d6942e5708b3cdb265d8a701e19a35ac8d319598..19f2a4b689412339cba0150d4075c6239ddaabea 100644 (file)
@@ -60,6 +60,7 @@
 #include "utilities/debugging.h"
 #include "utilities/SecAKSWrappers.h"
 #include "utilities/SecCFWrappers.h"
 #include "utilities/debugging.h"
 #include "utilities/SecAKSWrappers.h"
 #include "utilities/SecCFWrappers.h"
+#include <utilities/SecXPCError.h>
 
 #import "CoreCDP/CDPFollowUpController.h"
 #import "CoreCDP/CDPFollowUpContext.h"
 
 #import "CoreCDP/CDPFollowUpController.h"
 #import "CoreCDP/CDPFollowUpContext.h"
@@ -122,7 +123,11 @@ static void keybagDidUnlock()
     }
 
     SOSCCStatus circleStatus = SOSCCThisDeviceIsInCircle(&error);
     }
 
     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];
         NSError *localError = nil;
         CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
         CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
@@ -749,9 +754,19 @@ static bool processEvents()
        CFErrorRef                      error                    = NULL;
        CFErrorRef                      departError              = NULL;
        SOSCCStatus                     circleStatus     = SOSCCThisDeviceIsInCircle(&error);
        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];
        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
        secnotice("cjr", "CircleStatus %d -> %d{%d} (s=%p)", state.lastCircleStatus, circleStatus, departureReason, state);
 
        // Pending application reminder
index 4f95e3c50dc863cc64471775d91467c8e5bf922e..7a437f071b0b7808898adead97c7da8c6efce1be 100644 (file)
 
 
 __unused static SOSFullPeerInfoRef SOSNSFullPeerInfoCreate(NSDictionary* gestalt,
 
 
 __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;
 
 {
     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;
 
     if (errorRef && error) {
         *error = (__bridge_transfer NSError*) errorRef;
@@ -54,24 +56,6 @@ static SecKeyRef GenerateFullECKey(int keySize, NSError** error) {
 }
 
 
 }
 
 
-__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;
 
 @interface KCJoiningRequestTestDelegate : NSObject <KCJoiningRequestSecretDelegate, KCJoiningRequestCircleDelegate>
 @property (readwrite) NSString* sharedSecret;
 
@@ -120,8 +104,9 @@ __unused static SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(NSString* name,
 
     SecKeyRef signingKey = GenerateFullECKey(256, NULL);
     SecKeyRef octagonSigningKey = GenerateFullECKey(384, NULL);
 
     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;
 
     if (self.peerInfo == NULL)
         return nil;
index eb77f2dd50376de9dca542cec8836e6a0c06c28d..7e106adac958e2da58b10645022311fc77b23d90 100644 (file)
@@ -50,6 +50,7 @@
 @property (assign) SecKeyRef accountPublicKey;
 @property (assign) SecKeyRef deviceKey;
 @property (assign) SecKeyRef octagonSigningKey;
 @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;
 @property (assign) SOSCircleRef circle;
 @property (assign) SOSFullPeerInfoRef fullPeerInfo;
 @property (assign) bool application;
         }
         CFReleaseNull(publicKey);
 
         }
         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;
         _circle = (SOSCircleRef)CFRetain(circle);
 
         CFErrorRef error = NULL;
             @"ComputerName" : @"name",
         };
 
             @"ComputerName" : @"name",
         };
 
-        _fullPeerInfo = SOSFullPeerInfoCreate(NULL, gestalt, NULL, _deviceKey, _octagonSigningKey, &error);
+        _fullPeerInfo = SOSFullPeerInfoCreate(NULL, gestalt, NULL, _deviceKey, _octagonSigningKey, _octagonEncryptionKey, &error);
         CFReleaseNull(error);
 
         if (randomAccountKey) {
         CFReleaseNull(error);
 
         if (randomAccountKey) {
         SecItemDelete((__bridge CFTypeRef)@{ (__bridge id)kSecValueRef : (__bridge id)_octagonSigningKey });
         CFReleaseNull(_octagonSigningKey);
     }
         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);
 }
     CFReleaseNull(_circle);
     CFReleaseNull(_fullPeerInfo);
 }
index 1d060ed28827ed6aca9565d946e6b5595c8978df..d8db8d65fd79fd541b2b7eba75c7378af3448f9c 100644 (file)
       "idiom" : "ipad",
       "size" : "83.5x83.5",
       "scale" : "2x"
       "idiom" : "ipad",
       "size" : "83.5x83.5",
       "scale" : "2x"
+    },
+    {
+      "idiom" : "ios-marketing",
+      "size" : "1024x1024",
+      "scale" : "1x"
     }
   ],
   "info" : {
     }
   ],
   "info" : {
index 3136a7e770a51878af7eb737760fbc72784ba7b8..168f25183de60b03c6643459e1e3fe7cb15e2b92 100644 (file)
@@ -39,6 +39,7 @@
 #import <os/activity.h>
 
 #include <utilities/SecAKSWrappers.h>
 #import <os/activity.h>
 
 #include <utilities/SecAKSWrappers.h>
+#include <utilities/SecADWrapper.h>
 #include <utilities/SecCFRelease.h>
 #include <AssertMacros.h>
 
 #include <utilities/SecCFRelease.h>
 #include <AssertMacros.h>
 
@@ -129,6 +130,7 @@ static const NSUInteger kMaxIDSMessagePayloadSize = 64000;
 
 - (void) sendMessageToKVS: (NSDictionary<NSString*, NSDictionary*>*) encapsulatedKeychainMessage
 {
 
 - (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];
     [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];
@@ -442,6 +444,7 @@ static const NSUInteger kMaxIDSMessagePayloadSize = 64000;
     if([encapsulatedKeychainMessage isKindOfClass:[NSDictionary class]]){
         secnotice("IDS Transport", "Encapsulated message: %@", encapsulatedKeychainMessage);
         [self sendMessageToKVS:encapsulatedKeychainMessage];
     if([encapsulatedKeychainMessage isKindOfClass:[NSDictionary class]]){
         secnotice("IDS Transport", "Encapsulated message: %@", encapsulatedKeychainMessage);
         [self sendMessageToKVS:encapsulatedKeychainMessage];
+
     }
 }
 
     }
 }
 
index 50dc0a45c5406e48efae1212e97461b395fe53df..0468eaf74f72e723ec1ae3074c096939b89a6451 100644 (file)
@@ -649,6 +649,9 @@ void
 auth_items_copy(auth_items_t items, auth_items_t src)
 {
     auth_items_iterate(src, ^bool(const char *key) {
 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);
         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);
index 0e0638b3a20563abe00f1713120f93f73aabd3ff..b1dca37f50cac0244c4372a5d937a62bab698770 100644 (file)
@@ -24,7 +24,12 @@ return log; \
 #define CFReleaseNull(CF) { CFTypeRef _cf = (CF); \
     if (_cf) { (CF) = NULL; CFRelease(_cf); } }
 #define CFRetainSafe(CF) { CFTypeRef _cf = (CF); if (_cf) CFRetain(_cf); }
 #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; }
     
 #define xpc_release_safe(obj)  if (obj) { xpc_release(obj); obj = NULL; }
 #define free_safe(obj)  if (obj) { free(obj); obj = NULL; }
     
index 61bc15eefb6be159f9658ec93913c83e7700ab1a..08780ce1d66490f8eff30e3d8bb517eaf52073a2 100644 (file)
@@ -21,11 +21,14 @@ int checkpw_internal( const struct passwd *pw, const char* password );
 
 #include <Security/AuthorizationTags.h>
 #include <Security/AuthorizationTagsPriv.h>
 
 #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 <Security/AuthorizationPlugin.h>
 #include <LocalAuthentication/LAPublicDefines.h>
 #include <LocalAuthentication/LAPrivateDefines.h>
 #include <sandbox.h>
 #include <coreauthd_spi.h>
+#include <ctkloginhelper.h>
+
 
 AUTHD_DEFINE_LOG
 
 
 AUTHD_DEFINE_LOG
 
@@ -100,6 +103,7 @@ _engine_finalizer(CFTypeRef value)
     CFReleaseNull(engine->credentials);
     CFReleaseNull(engine->effectiveCredentials);
     CFReleaseNull(engine->authenticateRule);
     CFReleaseNull(engine->credentials);
     CFReleaseNull(engine->effectiveCredentials);
     CFReleaseNull(engine->authenticateRule);
+    CFReleaseNull(engine->la_context);
 }
 
 AUTH_TYPE_INSTANCE(engine,
 }
 
 AUTH_TYPE_INSTANCE(engine,
@@ -417,17 +421,19 @@ _evaluate_builtin_mechanism(engine_t engine, mechanism_t mech)
 
 
 static bool
 
 
 static bool
-_extract_password_from_la(engine_t engine, CFTypeRef la_context)
+_extract_password_from_la(engine_t engine)
 {
        bool retval = false;
 {
        bool retval = false;
+
+       if (!engine->la_context) {
+               return retval;
+       }
+
        // try to retrieve secret
        // 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));
        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);
        }
                }
                CFRelease(passdata);
        }
@@ -449,53 +455,63 @@ _evaluate_mechanisms(engine_t engine, CFArrayRef mechanisms)
        CFDictionaryRef la_result = NULL;
 
     CFIndex count = CFArrayGetCount(mechanisms);
        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 {
         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 (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) {
                                // 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) {
                                } 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"));
 
                                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"));
 
@@ -632,6 +648,12 @@ _evaluate_authentication(engine_t engine, rule_t rule)
     require_action(CFArrayGetCount(mechanisms) > 0, done, os_log_debug(AUTHD_LOG, "engine: error no mechanisms found"));
     
     int64_t ruleTries = rule_get_tries(rule);
     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));
     for (engine->tries = 0; engine->tries < ruleTries; engine->tries++) {
         
         auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason));
@@ -697,11 +719,6 @@ _evaluate_authentication(engine_t engine, rule_t rule)
         } else if (status == errAuthorizationDenied) {
                        os_log_error(AUTHD_LOG, "engine: evaluate denied");
                        engine->reason = invalidPassphrase;
         } 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);
-                       }
         }
     }
     
         }
     }
     
@@ -709,8 +726,6 @@ _evaluate_authentication(engine_t engine, rule_t rule)
         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);
         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);
     }
     
         ccaudit_log(ccaudit, engine->currentRightName, NULL, 1113);
     }
     
@@ -1132,7 +1147,7 @@ static void _parse_environment(engine_t engine, auth_items_t environment)
 #endif
 
     // Check if a credential was passed into the environment and we were asked to extend the rights
 #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
         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
@@ -1205,11 +1220,8 @@ OSStatus engine_preauthorize(engine_t engine, auth_items_t credentials)
 
        engine->flags = kAuthorizationFlagExtendRights;
        engine->preauthorizing = true;
 
        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);
 
        const char *user = auth_items_get_string(credentials, kAuthorizationEnvironmentUsername);
        require(user, done);
@@ -1338,15 +1350,73 @@ OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t en
     if (auth_rights_get_count(rights) > 0) {
         ccaudit_log(ccaudit, "begin evaluation", NULL, 0);
     }
     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->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));
        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));
@@ -1357,6 +1427,7 @@ OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t en
     engine->dismissed = false;
     auth_rights_clear(engine->grantedRights);
 
     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.
        {
                // 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.
@@ -1377,6 +1448,8 @@ OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t en
                        return true;
                });
                authdb_connection_release(&dbconn); // release db handle
                        return true;
                });
                authdb_connection_release(&dbconn); // release db handle
+       } else {
+               os_log_info(AUTHD_LOG, "engine: password-only ignored");
        }
 
        if (password_only) {
        }
 
        if (password_only) {
@@ -1486,7 +1559,11 @@ OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t en
     }
     
     os_log_debug(AUTHD_LOG, "engine: authorize result: %d", (int)status);
     }
     
     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;
     if ((engine->flags & kAuthorizationFlagExtendRights) && !(engine->flags & kAuthorizationFlagDestroyRights)) {
         _cf_set_iterate(engine->credentials, ^bool(CFTypeRef value) {
             credential_t cred = (credential_t)value;
@@ -1727,20 +1804,14 @@ CFTypeRef engine_copy_context(engine_t engine, auth_items_t source)
 
 bool engine_acquire_sheet_data(engine_t engine)
 {
 
 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) {
        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);
                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
                return true;
        } else {
                // this is not real failure as no LA context in authorization context is very valid scenario
index 0e70fa2a73911272c6a39e42b78c506d11fc557e..56529e08d05a75d933082ca99cad897ffb2ed150 100644 (file)
@@ -1,11 +1,10 @@
+
 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
 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
 
 
 DEAD_CODE_STRIPPING = YES
 
index 73f31cacc06d10ab10901555dbe5b566e1d8f7ea..0a2b9ed2b447f0853979dd9f4a288d2f5af362d5 100644 (file)
@@ -1,4 +1,5 @@
 #include "OSX/config/base.xcconfig"
 #include "OSX/config/base.xcconfig"
+#include "xcconfig/Version.xcconfig"
 
 GCC_PRECOMPILE_PREFIX_HEADER = YES
 
 
 GCC_PRECOMPILE_PREFIX_HEADER = YES
 
@@ -14,7 +15,7 @@ GCC_C_LANGUAGE_STANDARD = gnu99
 SUPPORTED_PLATFORMS = macOS
 
 // Don't use the inherited cflags; they set SEC_IOS_ON_OSX
 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
 
 GCC_TREAT_WARNINGS_AS_ERRORS = YES
 GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO
index 29b23439defa5dab82cbfd30a29fa4172e9806dc..1726027720c06e81f7e612464a45ebab25581a59 100644 (file)
 
 "com.apple.ctkbind.admin" = "__APPNAME__ is trying to pair the current user with the SmartCard identity.";
 
 
 "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.";
 
 
 "com.apple.security.sudo" = "__APPNAME__ is trying to execute a command as administrator.";
 
index 332740ba1b8c57660b2947129c0c947a7210c6b3..e5389d6f25fe8d879523cc2859876f513202611e 100644 (file)
@@ -53,7 +53,9 @@ extern "C" {
        @enum Private (for now) AuthorizationFlags
 */
 enum {
        @enum Private (for now) AuthorizationFlags
 */
 enum {
-       kAuthorizationFlagLeastPrivileged               = (1 << 5)
+       kAuthorizationFlagLeastPrivileged               = (1 << 5),
+       kAuthorizationFlagSheet                                 = (1 << 6),
+       kAuthorizationFlagIgnorePasswordOnly    = (1 << 7),
 };
 
 /*!
 };
 
 /*!
index 8ec4a43745bfbd8520de0d30ba56737e270eae28..ff621d537a71b80e2d21ce347b2186535a0b897b 100644 (file)
@@ -883,7 +883,7 @@ bool SecStaticCode::verifySignature()
     }
     
     // Did we implicitly trust the signer?
     }
     
     // Did we implicitly trust the signer?
-    mTrustedSigningCertChain = (trustResult == kSecTrustResultUnspecified);
+    mTrustedSigningCertChain = (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed);
 
     return false; // XXX: Not checking for expired certs
 #endif
 
     return false; // XXX: Not checking for expired certs
 #endif
index 7fd526d1e85a946abb5459aedddcd29b3a1ddd43..66470a3fd8ff0211d20d9a577d0e2f02e2b34672 100644 (file)
@@ -1,15 +1,15 @@
 /*
 /*
- * Copyright (c) 2002-2014 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2002-2017 Apple Inc. All Rights Reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
  * @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.
  * 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,
  * 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,
@@ -17,7 +17,7 @@
  * 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.
  * 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@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
 #include <Security/SecCertificatePriv.h>
 #include "SecCertificateOIDs.h"
 #include "CertificateValues.h"
 #include <Security/SecCertificatePriv.h>
 #include "SecCertificateOIDs.h"
 #include "CertificateValues.h"
-#include "SecCertificateP.h"
-#include "SecCertificatePrivP.h"
 #include <CoreFoundation/CFNumber.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;
 
 extern const CFStringRef __nonnull kSecPropertyKeyType;
 extern const CFStringRef __nonnull kSecPropertyKeyLabel;
@@ -75,7 +72,8 @@ typedef struct FieldValueFilterContext
 } FieldValueFilterContext;
 
 CertificateValues::CertificateValues(SecCertificateRef certificateRef) : mCertificateRef(certificateRef),
 } FieldValueFilterContext;
 
 CertificateValues::CertificateValues(SecCertificateRef certificateRef) : mCertificateRef(certificateRef),
-       mCertificateData(NULL)
+       mCertificateData(NULL),
+       mCertificateProperties(NULL)
 {
        if (mCertificateRef)
                CFRetain(mCertificateRef);
 {
        if (mCertificateRef)
                CFRetain(mCertificateRef);
@@ -83,12 +81,29 @@ CertificateValues::CertificateValues(SecCertificateRef certificateRef) : mCertif
 
 CertificateValues::~CertificateValues() throw()
 {
 
 CertificateValues::~CertificateValues() throw()
 {
+       if (mCertificateProperties)
+               CFRelease(mCertificateProperties);
        if (mCertificateData)
                CFRelease(mCertificateData);
        if (mCertificateRef)
                CFRelease(mCertificateRef);
 }
 
        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)
 CFDictionaryRef CertificateValues::copyFieldValues(CFArrayRef keys, CFErrorRef *error)
 {
        if (keys)
@@ -119,8 +134,8 @@ CFDictionaryRef CertificateValues::copyFieldValues(CFArrayRef keys, CFErrorRef *
                }
        }
 
                }
        }
 
-       SecCertificateRefP certificateP = SecCertificateCreateWithDataP(kCFAllocatorDefault, mCertificateData);
-       if (!certificateP)
+       SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, mCertificateData);
+       if (!certificate)
        {
                if (error)
                        *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateGroup, NULL);
        {
                if (error)
                        *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateGroup, NULL);
@@ -131,93 +146,93 @@ CFDictionaryRef CertificateValues::copyFieldValues(CFArrayRef keys, CFErrorRef *
                &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
 
        // Return an array of CFStringRefs representing the common names in the certificates subject if any
                &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);
        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
                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);
        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);
        }
 
                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);
        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
                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);
        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);
        }
 
                CFDictionaryAddValue(fieldValues, kSecOIDEmailAddress, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0));
                CFRelease(emailAddrs);
                CFRelease(additionalValues);
        }
 
-       CFAbsoluteTime notBefore = SecCertificateNotValidBeforeP(certificateP);
+       CFAbsoluteTime notBefore = SecCertificateNotValidBefore(certificate);
        CFNumberRef notBeforeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &notBefore);
        if (notBeforeRef)
        {
                CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
        CFNumberRef notBeforeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &notBefore);
        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);
        }
 
                CFDictionaryAddValue(fieldValues, kSecOIDX509V1ValidityNotBefore, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0));
                CFRelease(notBeforeRef);
                CFRelease(additionalValues);
        }
 
-       CFAbsoluteTime notAfter = SecCertificateNotValidAfterP(certificateP);
+       CFAbsoluteTime notAfter = SecCertificateNotValidAfter(certificate);
        CFNumberRef notAfterRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &notAfter);
        if (notAfterRef)
        {
                CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
        CFNumberRef notAfterRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &notAfter);
        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);
        }
 
                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);
        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);
        }
 
                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);
        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
                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));
        if (properties)
        {
                CFRange range = CFRangeMake(0, CFArrayGetCount((CFArrayRef)properties));
@@ -228,7 +243,7 @@ CFDictionaryRef CertificateValues::copyFieldValues(CFArrayRef keys, CFErrorRef *
 
        CFAbsoluteTime verifyTime = CFAbsoluteTimeGetCurrent();
        CFMutableArrayRef summaryProperties =
 
        CFAbsoluteTime verifyTime = CFAbsoluteTimeGetCurrent();
        CFMutableArrayRef summaryProperties =
-               SecCertificateCopySummaryPropertiesP(certificateP, verifyTime);
+               SecCertificateCopySummaryProperties(certificate, verifyTime);
        if (summaryProperties)
        {
                CFRange range = CFRangeMake(0, CFArrayGetCount((CFArrayRef)summaryProperties));
        if (summaryProperties)
        {
                CFRange range = CFRangeMake(0, CFArrayGetCount((CFArrayRef)summaryProperties));
@@ -238,8 +253,8 @@ CFDictionaryRef CertificateValues::copyFieldValues(CFArrayRef keys, CFErrorRef *
                CFRelease(summaryProperties);
        }
 
                CFRelease(summaryProperties);
        }
 
-       if (certificateP)
-               CFRelease(certificateP);
+       if (certificate)
+               CFRelease(certificate);
 
        if (keys==NULL)
                return (CFDictionaryRef)fieldValues;
 
        if (keys==NULL)
                return (CFDictionaryRef)fieldValues;
@@ -362,12 +377,12 @@ CFStringRef CertificateValues::remapLabelToKey(CFStringRef label)
 CFDataRef CertificateValues::copySerialNumber(CFErrorRef *error)
 {
        CFDataRef result = NULL;
 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;
 }
        }
        return result;
 }
@@ -375,11 +390,14 @@ CFDataRef CertificateValues::copySerialNumber(CFErrorRef *error)
 CFDataRef CertificateValues::copyNormalizedIssuerContent(CFErrorRef *error)
 {
        CFDataRef result = NULL;
 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;
 }
        }
        return result;
 }
@@ -387,11 +405,14 @@ CFDataRef CertificateValues::copyNormalizedIssuerContent(CFErrorRef *error)
 CFDataRef CertificateValues::copyNormalizedSubjectContent(CFErrorRef *error)
 {
        CFDataRef result = NULL;
 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;
 }
        }
        return result;
 }
@@ -399,11 +420,11 @@ CFDataRef CertificateValues::copyNormalizedSubjectContent(CFErrorRef *error)
 CFDataRef CertificateValues::copyIssuerSequence(CFErrorRef *error)
 {
        CFDataRef result = NULL;
 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;
 }
        }
        return result;
 }
@@ -411,35 +432,59 @@ CFDataRef CertificateValues::copyIssuerSequence(CFErrorRef *error)
 CFDataRef CertificateValues::copySubjectSequence(CFErrorRef *error)
 {
        CFDataRef result = NULL;
 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)
 {
        }
        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;
 }
 
 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;
 }
        }
        return result;
 }
@@ -447,11 +492,11 @@ bool CertificateValues::isValid(CFAbsoluteTime verifyTime, CFErrorRef *error)
 CFAbsoluteTime CertificateValues::notValidBefore(CFErrorRef *error)
 {
        CFAbsoluteTime result = 0;
 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;
 }
        }
        return result;
 }
@@ -459,16 +504,16 @@ CFAbsoluteTime CertificateValues::notValidBefore(CFErrorRef *error)
 CFAbsoluteTime CertificateValues::notValidAfter(CFErrorRef *error)
 {
        CFAbsoluteTime result = 0;
 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;
 }
 
        }
        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
 {
        // SecCertificateCopyData returns an object created with CFDataCreate, so we
        // own it and must release it
@@ -480,20 +525,26 @@ SecCertificateRefP CertificateValues::getSecCertificateRefP(CFErrorRef *error)
        }
 
        mCertificateData = SecCertificateCopyData(mCertificateRef);     // OK to call, no big lock
        }
 
        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;
        }
 
                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 NULL;
        }
 
-       return certificateP;
+       return certificate;
 }
 
 #pragma mark ---------- OID Constants ----------
 }
 
 #pragma mark ---------- OID Constants ----------
index 37de693fca3a9b0c09c3fe9990d09eae3fd52b0e..41d17c3fe53972368de6c72dc065fff96e9aaabb 100644 (file)
@@ -1,15 +1,15 @@
 /*
 /*
- * Copyright (c) 2002-2014 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2002-2017 Apple Inc. All Rights Reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
  * @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.
  * 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,
  * 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,
@@ -17,7 +17,7 @@
  * 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.
  * 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@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
@@ -27,9 +27,8 @@
 #ifndef _SECURITY_CERTIFICATEVALUES_H_
 #define _SECURITY_CERTIFICATEVALUES_H_
 
 #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 Security
 {
@@ -37,33 +36,37 @@ namespace Security
 namespace KeychainCore
 {
 
 namespace KeychainCore
 {
 
-class CertificateValues// : public SecCFObject
+class CertificateValues
 {
        NOCOPY(CertificateValues)
 
 public:
 
        CertificateValues(SecCertificateRef certificateRef);
 {
        NOCOPY(CertificateValues)
 
 public:
 
        CertificateValues(SecCertificateRef certificateRef);
-    virtual ~CertificateValues() throw();
+       virtual ~CertificateValues() throw();
 
        static CFStringRef remapLabelToKey(CFStringRef label);
 
        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 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:
 
        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;
 
        SecCertificateRef mCertificateRef;
        CFDataRef mCertificateData;
+       CFArrayRef mCertificateProperties;
        static CFDictionaryRef mOIDRemap;
 };
 
        static CFDictionaryRef mOIDRemap;
 };
 
diff --git a/OSX/libsecurity_keychain/lib/SecBase64P.c b/OSX/libsecurity_keychain/lib/SecBase64P.c
deleted file mode 100644 (file)
index c043906..0000000
+++ /dev/null
@@ -1,489 +0,0 @@
-/* /////////////////////////////////////////////////////////////////////////////
- * 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);
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
index a9b145d48ba6455a5735ff23d3b5ce35126ad358..ea044d6c25111aa421fde714cbc33c7e8b3892c2 100644 (file)
@@ -45,8 +45,6 @@
 #include <sys/param.h>
 #include <syslog.h>
 #include "CertificateValues.h"
 #include <sys/param.h>
 #include <syslog.h>
 #include "CertificateValues.h"
-#include "SecCertificateP.h"
-#include "SecCertificatePrivP.h"
 
 #include "AppleBaselineEscrowCertificates.h"
 
 
 #include "AppleBaselineEscrowCertificates.h"
 
@@ -992,3 +990,20 @@ bool SecCertificateIsValidX(SecCertificateRef certificate, CFAbsoluteTime verify
      */
        return SecCertificateIsValid(certificate, verifyTime);
 }
      */
        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;
+}
+
diff --git a/OSX/libsecurity_keychain/lib/SecCertificateInternalP.h b/OSX/libsecurity_keychain/lib/SecCertificateInternalP.h
deleted file mode 100644 (file)
index 276de22..0000000
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * 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_ */
diff --git a/OSX/libsecurity_keychain/lib/SecCertificateP.c b/OSX/libsecurity_keychain/lib/SecCertificateP.c
deleted file mode 100644 (file)
index 77210b5..0000000
+++ /dev/null
@@ -1,4762 +0,0 @@
-/*
- * 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;
-}
diff --git a/OSX/libsecurity_keychain/lib/SecCertificateP.h b/OSX/libsecurity_keychain/lib/SecCertificateP.h
deleted file mode 100644 (file)
index da8fc8c..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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_ */
diff --git a/OSX/libsecurity_keychain/lib/SecCertificatePrivP.h b/OSX/libsecurity_keychain/lib/SecCertificatePrivP.h
deleted file mode 100644 (file)
index 1ed8e90..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * 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_ */
diff --git a/OSX/libsecurity_keychain/lib/SecFrameworkP.c b/OSX/libsecurity_keychain/lib/SecFrameworkP.c
deleted file mode 100644 (file)
index febe7c1..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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
index 49a0a4d8a76f58be9d622e19d95a3705011c0540..a59b18b54012adfcaa9a955f362d548af9cfef51 100644 (file)
@@ -36,7 +36,6 @@
 #include "SecIdentitySearchPriv.h"
 #include "SecKeychainPriv.h"
 #include "SecCertificatePriv.h"
 #include "SecIdentitySearchPriv.h"
 #include "SecKeychainPriv.h"
 #include "SecCertificatePriv.h"
-#include "SecCertificatePrivP.h"
 #include "TrustAdditions.h"
 #include "TrustSettingsSchema.h"
 #include <Security/SecTrustPriv.h>
 #include "TrustAdditions.h"
 #include "TrustSettingsSchema.h"
 #include <Security/SecTrustPriv.h>
index 3aea2e977d11d427c4afb6965317694ed7b75109..502d187fa2b663205e4afa87d77ce4b6c0625900 100644 (file)
@@ -31,8 +31,6 @@
 #include "SecTrustSettingsPriv.h"
 #include "SecTrustStatusCodes.h"
 #include "SecCertificatePriv.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>
 #include "SecPolicyPriv.h"
 #include <security_utilities/cfutilities.h>
 #include <security_utilities/cfmunge.h>
diff --git a/OSX/libsecurity_keychain/libDER/Tests/AppleMobilePersonalizedTicket.h b/OSX/libsecurity_keychain/libDER/Tests/AppleMobilePersonalizedTicket.h
deleted file mode 100644 (file)
index 37b4033..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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 */
diff --git a/OSX/libsecurity_keychain/libDER/Tests/DER_Ticket.c b/OSX/libsecurity_keychain/libDER/Tests/DER_Ticket.c
deleted file mode 100644 (file)
index c5be481..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * 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;
-}
diff --git a/OSX/libsecurity_keychain/libDER/Tests/DER_Ticket.h b/OSX/libsecurity_keychain/libDER/Tests/DER_Ticket.h
deleted file mode 100644 (file)
index 080b98f..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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 */
diff --git a/OSX/libsecurity_keychain/libDER/Tests/parseCert.c b/OSX/libsecurity_keychain/libDER/Tests/parseCert.c
deleted file mode 100644 (file)
index 2ad3a91..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * 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;
-}
diff --git a/OSX/libsecurity_keychain/libDER/Tests/parseCrl.c b/OSX/libsecurity_keychain/libDER/Tests/parseCrl.c
deleted file mode 100644 (file)
index 4eabc13..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * 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;
-}
diff --git a/OSX/libsecurity_keychain/libDER/Tests/parseTicket.c b/OSX/libsecurity_keychain/libDER/Tests/parseTicket.c
deleted file mode 100644 (file)
index 1d70734..0000000
+++ /dev/null
@@ -1,563 +0,0 @@
-#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;
-}
diff --git a/OSX/libsecurity_keychain/libDER/libDERUtils/fileIo.c b/OSX/libsecurity_keychain/libDER/libDERUtils/fileIo.c
deleted file mode 100644 (file)
index a050fe5..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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;
-}
diff --git a/OSX/libsecurity_keychain/libDER/libDERUtils/fileIo.h b/OSX/libsecurity_keychain/libDER/libDERUtils/fileIo.h
deleted file mode 100644 (file)
index 26e31f7..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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_ */
diff --git a/OSX/libsecurity_keychain/libDER/libDERUtils/libDERUtils.c b/OSX/libsecurity_keychain/libDER/libDERUtils/libDERUtils.c
deleted file mode 100644 (file)
index 62cfe71..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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));
-}
-
diff --git a/OSX/libsecurity_keychain/libDER/libDERUtils/libDERUtils.h b/OSX/libsecurity_keychain/libDER/libDERUtils/libDERUtils.h
deleted file mode 100644 (file)
index 6cd9307..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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_ */
diff --git a/OSX/libsecurity_keychain/libDER/libDERUtils/printFields.c b/OSX/libsecurity_keychain/libDER/libDERUtils/printFields.c
deleted file mode 100644 (file)
index 4df3133..0000000
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * 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);
-}
-
diff --git a/OSX/libsecurity_keychain/libDER/libDERUtils/printFields.h b/OSX/libsecurity_keychain/libDER/libDERUtils/printFields.h
deleted file mode 100644 (file)
index 182cfc5..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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_ */
index 11c6fdf28be5ee5d1ab477174896de69b2d9a67c..3793fa6fcd25cf13dccb512d639e56e6e6754e00 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2015 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2015-2017 Apple Inc. All Rights Reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -199,6 +199,164 @@ static unsigned char c1[1063]={
 };
 
 
 };
 
 
+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;
 static void tests(void)
 {
     SecTrustRef trust = NULL;
@@ -242,6 +400,9 @@ static void tests(void)
     is(signerStatus, kCMSSignerValid, "signer status valid");
     is(verifyResult, errSecSuccess, "verify result valid");
 
     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);
     CFReleaseSafe(decoder);
     CFReleaseSafe(date);
     CFReleaseSafe(trust);
@@ -255,7 +416,7 @@ int si_20_sectrust_provisioning(int argc, char *const *argv);
 
 int si_20_sectrust_provisioning(int argc, char *const *argv)
 {
 
 int si_20_sectrust_provisioning(int argc, char *const *argv)
 {
-    plan_tests(15);
+    plan_tests(17);
 
     tests();
 
 
     tests();
 
index ebcbe4668c145aeb4a406418da4992b9e60a44e2..382afe53c1e6c5cdf3759059519247043df086c8 100644 (file)
@@ -230,7 +230,7 @@ static void communicateWithTimeStampingServer(xpc_object_t event, const char *re
         } else if (tsaReply) {
             reply = xpc_create_reply_with_format(event, "{TimeStampReply: %value}", tsaReply);
         } else {
         } 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);
         }
         xpc_connection_send_message(peer, reply);
         xpc_release(reply);
index b1dccdda209b65a9e50d9039313aa573f36203b1..f2faff0e67a93f562f7e0befec4e3605debfcf7c 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2012-2016 Apple Inc. All Rights Reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
 #include "tsaTemplates.h"
 #include <Security/SecAsn1Coder.h>
 #include <AssertMacros.h>
 #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/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>
 
 #include <utilities/SecCFRelease.h>
 #include <utilities/SecDispatchRelease.h>
 
@@ -879,36 +878,29 @@ xit:
     return result;
 }
 
     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,};
 {
     // 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);
 
     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:
         result = noErr;
     else
         result = errSecTimestampInvalid;
     if (timestampTime)
         *timestampTime = genTime;
 xit:
-    if (signingCertificate)
-        CFReleaseNull(signingCertificate);
     return result;
 }
 
     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;
 {
     OSStatus status = paramErr;
     SecAsn1CoderRef coder = NULL;
@@ -930,7 +922,7 @@ static OSStatus verifyTSTInfo(const CSSM_DATA_PTR content, CSSM_DATA **signingCe
         require_action(expectedNonce==nonce, xit, status = errSecTimestampRejection);
     }
 
         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:
     dtprintf("SecTSAValidateTimestamp result: %ld\n", (long)status);
 
 xit:
@@ -1190,14 +1182,11 @@ static const char *cfabsoluteTimeToString(CFAbsoluteTime abstime)
 
 static OSStatus setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo)
 {
 
 static OSStatus setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo)
 {
-    OSStatus status = noErr;
+    SecCertificateRef tsaLeaf = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL);
 
 
-    if (!signerinfo->timestampCertList || (CFArrayGetCount(signerinfo->timestampCertList) == 0))
+    if (!tsaLeaf)
         return SecCmsVSSigningCertNotFound;
 
         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 */
 
     signerinfo->tsaLeafNotBefore = SecCertificateNotValidBefore(tsaLeaf); /* Start date for Timestamp Authority leaf */
     signerinfo->tsaLeafNotAfter = SecCertificateNotValidAfter(tsaLeaf);   /* Expiration date for Timestamp Authority leaf */
 
@@ -1209,15 +1198,7 @@ static OSStatus setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo)
         free((void *)nbefore);free((void *)nafter);
     }
 
         free((void *)nbefore);free((void *)nafter);
     }
 
-/*
-               if(at < nb)
-                       status = errSecCertificateNotValidYet;
-               else if (at > na)
-                       status = errSecCertificateExpired;
-*/
-
-xit:
-    return status;
+    return errSecSuccess;
 }
 
 /*
 }
 
 /*
@@ -1387,7 +1368,8 @@ OSStatus decodeTimeStampTokenWithPolicy(SecCmsSignerInfoRef signerinfo, CFTypeRe
         case SEC_OID_PKCS9_ID_CT_TSTInfo:
         {
             SecAsn1TSATSTInfo tstInfo = {{0},};
         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);
             if (signerinfo->timestampTime)
             {
                 const char *tstamp = cfabsoluteTimeToString(signerinfo->timestampTime);
index 3fdc2205810e10ee5b5425058466b28d80a8f95d..c13a49aaaef6853cdec2c2d908cd75abbcde41fa 100644 (file)
@@ -61,8 +61,19 @@ bool testClearAll(dispatch_queue_t processQueue, dispatch_group_t dgroup);
 
 CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name);
 
 
 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
 
 
 __END_DECLS
 
index 1b3295bbb616f2b11b674ceac18f90bc29554536..8d733f8420f0cadfbf552ef527e7c0e808f28883 100644 (file)
@@ -292,32 +292,44 @@ CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name)
 }
 
 
 }
 
 
-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;
 {
     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);
     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);
 
 
     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);
 
 exit:
     CFReleaseNull(gestalt);
     CFReleaseNull(publicKey);
-    CFReleaseNull(octagonPublicKey);
+    CFReleaseNull(octagonSigningPublicKey);
+    CFReleaseNull(octagonEncryptionPublicKey);
 
     return result;
 }
 
 
     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;
 {
     SOSFullPeerInfoRef result = NULL;
     SecKeyRef publicKey = NULL;
@@ -331,10 +343,19 @@ SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(CFStringRef name, SecKeyRef* ou
     *outOctagonSigningKey = GeneratePermanentFullECKey(384, name, error);
     require(*outOctagonSigningKey, exit);
 
     *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);
 
     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);
 
 exit:
     CFReleaseNull(gestalt);
index d565e7977a9059b2d4ad6dacc69b7c456ac19173..5bb9b9e71c0c1b13d86fed637f9def086cc6e9d1 100644 (file)
@@ -44,6 +44,7 @@
 typedef struct piStuff_t {
     SecKeyRef signingKey;
     SecKeyRef octagonSigningKey;
 typedef struct piStuff_t {
     SecKeyRef signingKey;
     SecKeyRef octagonSigningKey;
+    SecKeyRef octagonEncryptionKey;
     SOSFullPeerInfoRef fpi;
     SOSPeerInfoRef pi;
     SOSPeerInfoRef resignation_ticket;
     SOSFullPeerInfoRef fpi;
     SOSPeerInfoRef pi;
     SOSPeerInfoRef resignation_ticket;
@@ -55,7 +56,7 @@ static piStuff *makeSimplePeer(char *name) {
     if(!pi) return NULL;
     pi->signingKey = NULL;
     CFStringRef cfName = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingMacRoman);
     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(cfName);
     pi->pi = SOSFullPeerInfoGetPeerInfo(pi->fpi);
     pi->resignation_ticket = SOSPeerInfoCreateRetirementTicket(kCFAllocatorDefault, pi->signingKey, pi->pi, NULL);
@@ -67,6 +68,7 @@ static void freeSimplePeer(piStuff *pi)
     CFReleaseSafe(pi->fpi);
     CFReleaseSafe(pi->signingKey);
     CFReleaseSafe(pi->octagonSigningKey);
     CFReleaseSafe(pi->fpi);
     CFReleaseSafe(pi->signingKey);
     CFReleaseSafe(pi->octagonSigningKey);
+    CFReleaseSafe(pi->octagonEncryptionKey);
     CFReleaseSafe(pi->resignation_ticket);
     free(pi);
 }
     CFReleaseSafe(pi->resignation_ticket);
     free(pi);
 }
index bbff1e104224817516424d83a6cbe36e3cf5ec4f..846ac3bc50fdae65cab159842b16f5e176059878 100644 (file)
 #include "SOSCircle_regressions.h"
 #include "SOSRegressionUtilities.h"
 
 #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 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);
 
     if (fullPeer && SOSFullPeerInfoPromoteToApplication(fullPeer, user_private_key, error))
         CFTransferRetained(result, fullPeer);
@@ -74,9 +78,12 @@ static void tests(void)
     SecKeyRef dev_a_key = NULL;
     SecKeyRef dev_b_key = NULL;
     SecKeyRef dev_c_key = NULL;
     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);
 
     CFErrorRef error = NULL;
     CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
 
@@ -93,9 +100,9 @@ static void tests(void)
     SecKeyRef user_pubkey = SecKeyCreatePublicFromPrivate(user_privkey);
 
 
     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);
     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);
@@ -147,9 +154,12 @@ static void tests(void)
     CFReleaseNull(dev_a_key);
     CFReleaseNull(dev_b_key);
     CFReleaseNull(dev_c_key);
     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);
     CFReleaseNull(cfpassword);
 
     CFReleaseNull(user_privkey);
index 656f81c9ddcf791de9102626bde3b2853e4b8c47..3bf87c7ac991e1808f3e1ec1377aa7551d4198a7 100644 (file)
@@ -94,7 +94,8 @@ static void tests(void)
 
     SecKeyRef peer1SigningKey = NULL;
     SecKeyRef peer1OctagonSigningKey = NULL;
 
     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);
 
     ok(fullPeer1WithBackup, "Allocate peer 1 (%@)", localError);
     CFReleaseNull(localError);
 
@@ -108,7 +109,8 @@ static void tests(void)
 
     SecKeyRef peer2SigningKey = NULL;
     SecKeyRef peer2OctagonSigningKey = NULL;
 
     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);
 
     ok(fullPeer2WithBackup, "Allocate peer 2 (%@)", localError);
     CFReleaseNull(localError);
 
index b75af154fdbcfbeee71dcdb14354530f7cb236f4..3dbb178479a1a0a6a2cf5de8b0031768f0c6e41a 100644 (file)
@@ -48,8 +48,9 @@ static int kTestTestCount = 15;
 static void tests(void)
 {
     SecKeyRef publicKey = NULL;
 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);
     CFErrorRef error = NULL;
 
     SOSCircleRef circle = SOSCircleCreate(NULL, CFSTR("Test Circle"), &error);
@@ -65,7 +66,7 @@ static void tests(void)
     CFReleaseNull(circle_name);
     CFReleaseNull(circle_key);
     
     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");
     
     
     CFStringRef other_peer_id = CFSTR("OTHER PEER");
     
@@ -116,7 +117,8 @@ static void tests(void)
        SOSPeerInfoGetPeerID(pi), retirement_peer_id);
     
     CFReleaseNull(publicKey);
        SOSPeerInfoGetPeerID(pi), retirement_peer_id);
     
     CFReleaseNull(publicKey);
-    CFReleaseNull(octagonPublicKey);
+    CFReleaseNull(octagonSigningPublicKey);
+    CFReleaseNull(octagonEncryptionPublicKey);
     CFReleaseNull(circle);
     CFReleaseNull(error);
     CFReleaseNull(pi);
     CFReleaseNull(circle);
     CFReleaseNull(error);
     CFReleaseNull(pi);
index 24cf068c92f90137346cecb4ea4fa537b4a7cd34..83d4013100d57300c2107aa2cd6004016687a1f8 100644 (file)
@@ -93,7 +93,8 @@ static void tests(void)
 {
     SecKeyRef signingKey = NULL;
     SecKeyRef octagonSigningKey = NULL;
 {
     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");
     SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
 
     ok(NULL != pi, "info creation");
@@ -148,6 +149,7 @@ static void tests(void)
 
     CFReleaseNull(signingKey);
     CFReleaseNull(octagonSigningKey);
 
     CFReleaseNull(signingKey);
     CFReleaseNull(octagonSigningKey);
+    CFReleaseNull(octagonEncryptionKey);
     CFReleaseNull(fpi);
 }
 
     CFReleaseNull(fpi);
 }
 
index 711f6197126070ca29a391d335e0774c27562cbb..9d1ff9d0ad8b4740013aa8fca014fcfdc9281804 100644 (file)
@@ -40,7 +40,8 @@ static void tests(void)
 {
     SecKeyRef signingKey = NULL;
     SecKeyRef octagonSigningKey = NULL;
 {
     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;
     
     SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
     unsigned long count;
     
@@ -75,6 +76,7 @@ static void tests(void)
 errOut:
     CFReleaseNull(signingKey);
     CFReleaseNull(octagonSigningKey);
 errOut:
     CFReleaseNull(signingKey);
     CFReleaseNull(octagonSigningKey);
+    CFReleaseNull(octagonEncryptionKey);
     CFReleaseNull(fpi);
 }
 
     CFReleaseNull(fpi);
 }
 
index cf379780f4d3940fbb61668651f771ea74eb51e5..2e706b00e90c0c28f129d5f65fa6c46ca761d82e 100644 (file)
@@ -77,10 +77,14 @@ static void tests(void)
     SecKeyRef dev_b_key = NULL;
     SecKeyRef dev_c_key = NULL;
     SecKeyRef dev_d_key = NULL;
     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);
     
     CFErrorRef error = NULL;
     CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
     
@@ -94,13 +98,13 @@ static void tests(void)
     SecKeyRef user_privkey = SOSUserKeygen(cfpassword, parameters, &error);
     CFReleaseNull(parameters);
 
     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));
 
     ok(SOSCircleRequestAdmission(circle, user_privkey, peer_a_full_info, NULL));
     ok(SOSCircleRequestAdmission(circle, user_privkey, peer_a_full_info, NULL));
@@ -159,10 +163,14 @@ static void tests(void)
     CFReleaseNull(dev_b_key);
     CFReleaseNull(dev_c_key);
     CFReleaseNull(dev_d_key);
     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);
 
 
     CFReleaseNull(cfpassword);
 
index ba3b67ffb45472828f0d4c0f1e1099eba2865665..b76fbef880ae03ee7198d96ba4000d7c88157677 100644 (file)
@@ -375,6 +375,11 @@ const CFStringRef kSOSAccountDebugScope = CFSTR("Scope");
     return SOSAccountInflateTransports(self, (__bridge CFStringRef) circle_name, NULL);
 }
 
     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];
 -(id) initWithGestalt:(CFDictionaryRef)newGestalt factory:(SOSDataSourceFactoryRef)f
 {
     self = [super init];
@@ -652,6 +657,15 @@ static bool Flush(CFErrorRef *error) {
         CFReleaseNull(accountPrivateKey);
         complete(true, NULL);
     }];
         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
 }
 
 - (void)myPeerInfo:(void (^)(NSData *, NSError *))complete
index 25e4d32e4c954626e22b4ea450dbc0cf66c43086..40811eaa6c7c4bf763b7a64048d8359bbe37abca 100644 (file)
@@ -169,7 +169,6 @@ static bool UpdateKeyName(SecKeyRef key, SOSPeerInfoRef peer, CFErrorRef* error)
 
 SOSPeerInfoRef GenerateNewCloudIdentityPeerInfo(CFErrorRef *error) {
     SecKeyRef cloud_key = GeneratePermanentFullECKeyForCloudIdentity(256, kicloud_identity_name, error);
 
 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;
     SOSPeerInfoRef cloud_peer = NULL;
     
     CFDictionaryRef gestalt = NULL;
@@ -181,17 +180,15 @@ SOSPeerInfoRef GenerateNewCloudIdentityPeerInfo(CFErrorRef *error) {
                                                            NULL);
     require_action_quiet(gestalt, fail, SecError(errSecAllocate, error, CFSTR("Can't allocate gestalt")));
     
                                                            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);
     
     require(cloud_peer, fail);
     
     UpdateKeyName(cloud_key, cloud_peer, error);
-    UpdateKeyName(octagon_key, cloud_peer, error);
-    
+
 fail:
     CFReleaseNull(gestalt);
     CFReleaseNull(cloud_key);
 fail:
     CFReleaseNull(gestalt);
     CFReleaseNull(cloud_key);
-    CFReleaseNull(octagon_key);
     
     return cloud_peer;
 }
     
     return cloud_peer;
 }
index 9ff992188301eb445f2b20616cbfd9335ec2d29c..e65f60ccf694a47b42f2014dfbf78bc8e499f116 100644 (file)
@@ -102,7 +102,7 @@ bool SOSAccountTrustedCircleHasNoGhostOfMe(SOSAccount* account) {
 }
 
 bool SOSAccountGhostResultsInReset(SOSAccount* account) {
 }
 
 bool SOSAccountGhostResultsInReset(SOSAccount* account) {
-    return SOSTrustedCircleGhostSetCount(account) == SOSCircleCountActivePeers([account.trust getCircle:NULL]);
+    return SOSTrustedCircleGhostSetCount(account) == SOSCircleCountPeers([account.trust getCircle:NULL]);
 }
 
 
 }
 
 
index 7791a59da11cec2589aee293acc65148f6485832..71072b261d83da86169dbc763e3f30d49827834d 100644 (file)
@@ -131,6 +131,7 @@ void SOSAccountAddSyncablePeerBlock(SOSAccount*  a,
                                     SOSAccountSyncablePeersBlock changeBlock);
 
 -(bool) ensureFactoryCircles;
                                     SOSAccountSyncablePeersBlock changeBlock);
 
 -(bool) ensureFactoryCircles;
+-(void) ensureOctagonPeerKeys;
 
 -(void) flattenToSaveBlock;
 
 
 -(void) flattenToSaveBlock;
 
index b54350b3305d3de7509d90cbb17b1d738da3f3e9..09e6fbe8aa830eebedfa871f280bf418f79f1839 100644 (file)
     self.initialKeyParameters = self.account.accountKeyDerivationParamters ? [NSData dataWithData:self.account.accountKeyDerivationParamters] : nil;
 
     SOSPeerInfoRef mpi = self.account.peerInfo;
     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) {
     self.peersToRequestSync = nil;
 
     CFStringSetPerformWithDescription((__bridge CFSetRef) self.initialViews, ^(CFStringRef description) {
index 770135fdbaaa1829f9eb8d34d1735bd27bf8766c..e699457e6e9ded87c84fa3092c18ad8f4e0269a1 100644 (file)
@@ -130,15 +130,18 @@ fail:
 
 -(bool) ghostBustingOK:(SOSCircleRef) oldCircle updatingTo:(SOSCircleRef) newCircle {
     bool retval = false;
 
 -(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);
     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);
     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;
         retval = true;
     } else if (!iSignedNew) { // Piggybacking peers always have signed as part of genSigning - so this indicates we're safe to bust.
         retval = true;
@@ -163,6 +166,53 @@ fail:
     return false;
 }
 
     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;
 -(bool) handleUpdateCircle:(SOSCircleRef) prospective_circle transport:(SOSKVSCircleStorageTransport*)circleTransport update:(bool) writeUpdate err:(CFErrorRef*)error
 {
     bool success = true;
@@ -362,6 +412,9 @@ fail:
     }
     
     if (circle_action == accept) {
     }
     
     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;
         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;
index de6856c0de665d5972ba51305d63f2cbc1e4a8c1..9f06f2a2be6d13edd889f13f40060c5acf1149ca 100644 (file)
@@ -20,6 +20,9 @@
 -(bool) ensureFullPeerAvailable:(CFDictionaryRef)gestalt deviceID:(CFStringRef)deviceID backupKey:(CFDataRef)backup err:(CFErrorRef *) error;
 -(bool) isMyPeerActive:(CFErrorRef*) error;
 -(void) purgeIdentity;
 -(bool) ensureFullPeerAvailable:(CFDictionaryRef)gestalt deviceID:(CFStringRef)deviceID backupKey:(CFDataRef)backup err:(CFErrorRef *) error;
 -(bool) isMyPeerActive:(CFErrorRef*) error;
 -(void) purgeIdentity;
+
+- (void)ensureOctagonPeerKeys:(SOSKVSCircleStorageTransport*)circleTransport;
+
 @end
 
 
 @end
 
 
index fb446c1fcd759cb030689cc27ec31cf57e7a31bd..1851591929b764e5044d6eff2b5303ef01628ca5 100644 (file)
     return SOSFullPeerInfoCopyFullPeerInfo(self.fullPeerInfo);
 }
 
     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, &copyError);
+    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, &copyError);
+    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) {
 -(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));
         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;
             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);
             CFDictionaryRef v2dictionaryTestUpdates = [self getValueFromExpansion:kSOSTestV2Settings err:NULL];
             if(v2dictionaryTestUpdates) SOSFullPeerInfoUpdateV2Dictionary(self.fullPeerInfo, v2dictionaryTestUpdates, NULL);
             CFReleaseNull(initialViews);
             secerror("No full_key: %@:", error ? *error : NULL);
             
         }
             secerror("No full_key: %@:", error ? *error : NULL);
             
         }
-        
+
+        CFReleaseNull(full_key);
+        CFReleaseNull(octagonSigningFullKey);
+        CFReleaseNull(octagonEncryptionFullKey);
         CFReleaseNull(keyName);
     }
     
         CFReleaseNull(keyName);
     }
     
index ef9002aae4262a69e2cd077fed294b22ca0bbb46..ed513c967892b08f14904d03f643a8eaa01b0750 100644 (file)
@@ -71,7 +71,7 @@ enum {
     kSOSCCNotInCircle       = 1,
     kSOSCCRequestPending    = 2,
     kSOSCCCircleAbsent      = 3,
     kSOSCCNotInCircle       = 1,
     kSOSCCRequestPending    = 2,
     kSOSCCCircleAbsent      = 3,
-    kSOSCCError             = -1,
+    kSOSCCError             = -1,   // unable to determine circle status, inspect CFError to find out why
 };
 
 typedef int SOSCCStatus;
 };
 
 typedef int SOSCCStatus;
@@ -82,6 +82,7 @@ extern const char * kSOSCCInitialSyncChangedNotification;
 extern const char * kSOSCCHoldLockForInitialSync;
 extern const char * kSOSCCPeerAvailable;
 extern const char * kSOSCCRecoveryKeyChanged;
 extern const char * kSOSCCHoldLockForInitialSync;
 extern const char * kSOSCCPeerAvailable;
 extern const char * kSOSCCRecoveryKeyChanged;
+extern const char * kSOSCCCircleOctagonKeysChangedNotification;
 
 /*!
  @function SOSCCSetUserCredentials
 
 /*!
  @function SOSCCSetUserCredentials
@@ -557,6 +558,7 @@ extern const CFStringRef kCKKSViewEngram;
 extern const CFStringRef kCKKSViewManatee;
 extern const CFStringRef kCKKSViewAutoUnlock;
 extern const CFStringRef kCKKSViewHealth;
 extern const CFStringRef kCKKSViewManatee;
 extern const CFStringRef kCKKSViewAutoUnlock;
 extern const CFStringRef kCKKSViewHealth;
+extern const CFStringRef kCKKSViewApplePay;
 
 
 /*!
 
 
 /*!
index 77c15ccfefd6d99033357ce462824ace09d4fade..824a84b48376b37532a7f0f61fcbcfecdfa675ec 100644 (file)
@@ -73,6 +73,7 @@ const char * kSOSCCInitialSyncChangedNotification = "com.apple.security.secureob
 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 * 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__); }
 
 
 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
 
@@ -484,7 +485,15 @@ static bool recovery_and_bool_to_bool_error_request(enum SecXPCOperation op, CFD
 {
     secdebug("sosops", "enter - operation: %d", op);
     return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
 {
     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);
         bool success = false;
         if (xData){
             xpc_dictionary_set_value(message, kSecXPCKeyRecoveryPublicKey, xData);
@@ -1086,13 +1095,8 @@ bool SOSCCRegisterRecoveryPublicKey(CFDataRef recovery_key, CFErrorRef *error){
     sec_trace_return_bool_api(^{
         bool retval = false;
         do_if_registered(soscc_RegisterRecoveryPublicKey, recovery_key, error);
     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);
 }
         return retval;
     }, NULL);
 }
index 44cecd77c549102a86ea40b3def1e48b7881c097..fbabbb4c2ec570bf29b59e4ae6b0c2dab0303957 100644 (file)
@@ -48,6 +48,7 @@
 
 #include <corecrypto/ccder.h>
 #include <utilities/iCloudKeychainTrace.h>
 
 #include <corecrypto/ccder.h>
 #include <utilities/iCloudKeychainTrace.h>
+#include <utilities/SecADWrapper.h>
 
 #include "AssertMacros.h"
 
 
 #include "AssertMacros.h"
 
@@ -565,6 +566,7 @@ SOSCoderStatus SOSCoderUnwrap(SOSCoderRef coder, CFDataRef codedMessage, CFMutab
                     case errSecDecode:
                         CFStringAppend(action, CFSTR("resending dh"));
                         result = SOSCoderResendDH(coder, error);
                     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);
                         break;
                     default:
                         SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot negotiate session (%ld)"), clientId, (long)ppstatus);
@@ -591,6 +593,7 @@ SOSCoderStatus SOSCoderUnwrap(SOSCoderRef coder, CFDataRef codedMessage, CFMutab
             if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) {
                 CFStringAppend(action, CFSTR("not ready for data; resending DH packet"));
                                SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed, 1);
             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) {
                 result = SOSCoderResendDH(coder, error);
             } else {
                 if (coder->waitingForDataPacket) {
index c6fa97b2da336196181b8a319bf0ec27f1ff1b3d..733795d69173a13b37497b31344f58f1edda4d8b 100644 (file)
@@ -1825,6 +1825,7 @@ static void ReportItemSyncTime(SOSDataSourceRef ds, bool known, SOSObjectRef obj
         SecADClientPushValueForDistributionKey(known ? kSecADSecurityKnownItemSyncTimeKey : kSecADSecurityNewItemSyncTimeKey,
                                                SecBucket2Significant(syncTime));
     }
         SecADClientPushValueForDistributionKey(known ? kSecADSecurityKnownItemSyncTimeKey : kSecADSecurityNewItemSyncTimeKey,
                                                SecBucket2Significant(syncTime));
     }
+    CFReleaseNull(itemModDate);
 }
 
 /* Handle incoming message from peer p.  Return false if there was an error, true otherwise. */
 }
 
 /* Handle incoming message from peer p.  Return false if there was an error, true otherwise. */
index 6269f69aa5b88a34a7d6ed101f78a41fad06b0af..239c1b941240a19765b4570fd2d5fa46eced640a 100644 (file)
@@ -107,6 +107,7 @@ _kSOSCCInitialSyncChangedNotification
 _kSOSCCHoldLockForInitialSync
 _kSOSCCPeerAvailable
 _kSOSCCRecoveryKeyChanged
 _kSOSCCHoldLockForInitialSync
 _kSOSCCPeerAvailable
 _kSOSCCRecoveryKeyChanged
+_kSOSCCCircleOctagonKeysChangedNotification
 
 _SOSCCSetLastDepartureReason
 _SOSCCAccountSetToNew
 
 _SOSCCSetLastDepartureReason
 _SOSCCAccountSetToNew
index 324ccde73dcdece3abec8a4450fdcfc9a7d451c6..f6d269fbb42866f7bedddc75d2e732f937722422 100644 (file)
@@ -39,13 +39,13 @@ enum {
     kSOSFullPeerVersion = 1,
 };
 
     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,
 
 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);
 
 
 SOSFullPeerInfoRef SOSFullPeerInfoCopyFullPeerInfo(SOSFullPeerInfoRef toCopy);
 
@@ -54,7 +54,12 @@ SOSFullPeerInfoRef SOSFullPeerInfoCreateCloudIdentity(CFAllocatorRef allocator,
 SOSPeerInfoRef SOSFullPeerInfoGetPeerInfo(SOSFullPeerInfoRef fullPeer);
 SecKeyRef      SOSFullPeerInfoCopyDeviceKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error);
 SecKeyRef SOSFullPeerInfoCopyPubKey(SOSFullPeerInfoRef fpi, CFErrorRef *error);
 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 SOSFullPeerInfoCopyOctagonSigningKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error);
+SecKeyRef SOSFullPeerInfoCopyOctagonEncryptionKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error);
 
 bool SOSFullPeerInfoPurgePersistentKey(SOSFullPeerInfoRef peer, CFErrorRef* error);
 
 
 bool SOSFullPeerInfoPurgePersistentKey(SOSFullPeerInfoRef peer, CFErrorRef* error);
 
@@ -108,6 +113,9 @@ bool SOSFullPeerInfoUpdateTransportPreference(SOSFullPeerInfoRef peer, CFBoolean
 bool SOSFullPeerInfoUpdateTransportFragmentationPreference(SOSFullPeerInfoRef peer, CFBooleanRef preference, CFErrorRef* error);
 bool SOSFullPeerInfoUpdateTransportAckModelPreference(SOSFullPeerInfoRef peer, CFBooleanRef preference, 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);
 SOSSecurityPropertyResultCode SOSFullPeerInfoUpdateSecurityProperty(SOSFullPeerInfoRef peer, SOSViewActionCode action, CFStringRef property, CFErrorRef* error);
 SOSSecurityPropertyResultCode SOSFullPeerInfoSecurityPropertyStatus(SOSFullPeerInfoRef peer, CFStringRef property, CFErrorRef *error);
 CFDataRef SOSPeerInfoCopyData(SOSPeerInfoRef fpi, CFErrorRef *error);
index ce803c327fd9828ded4b509f817ece48b3b4d42b..1a5e5a65996fae03cf0e90dfef31e419b0e7ab78 100644 (file)
@@ -80,7 +80,8 @@ struct __OpaqueSOSFullPeerInfo {
     
     SOSPeerInfoRef         peer_info;
     CFDataRef              key_ref;
     
     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);
 };
 
 CFGiblisWithHashFor(SOSFullPeerInfo);
@@ -92,17 +93,17 @@ CFStringRef kSOSFullPeerInfoSignatureKey = CFSTR("SOSFullPeerInfoSignature");
 CFStringRef kSOSFullPeerInfoNameKey = CFSTR("SOSFullPeerInfoName");
 
 
 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;
     bool result = false;
     
     SOSPeerInfoRef newPeer = NULL;
-    SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(peer, error);
+    SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(fullPeerInfo, error);
     require_quiet(device_key, fail);
     
     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);
     
     require_quiet(newPeer, fail);
     
-    CFTransferRetained(peer->peer_info, newPeer);
+    CFTransferRetained(fullPeerInfo->peer_info, newPeer);
     
     result = true;
     
     
     result = true;
     
@@ -119,14 +120,21 @@ bool SOSFullPeerInfoUpdateToThisPeer(SOSFullPeerInfoRef peer, SOSPeerInfoRef pi,
 }
 
 SOSFullPeerInfoRef SOSFullPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt,
 }
 
 SOSFullPeerInfoRef SOSFullPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt,
-                                         CFDataRef backupKey, SecKeyRef signingKey, SecKeyRef octagonSigningKey,
+                                         CFDataRef backupKey,
+                                         SecKeyRef signingKey,
+                                         SecKeyRef octagonPeerSigningKey,
+                                         SecKeyRef octagonPeerEncryptionKey,
                                          CFErrorRef* error) {
                                          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,
 }
 
 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);
 
     SOSFullPeerInfoRef result = NULL;
     SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator);
@@ -140,14 +148,16 @@ SOSFullPeerInfoRef SOSFullPeerInfoCreateWithViews(CFAllocatorRef allocator,
     fpi->peer_info = SOSPeerInfoCreateWithTransportAndViews(allocator, gestalt, backupKey,
                                                             IDSID, transportType, preferIDS,
                                                             preferIDSFragmentation, preferACKModel, initialViews,
     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);
     
     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);
 
 
     CFTransferRetained(result, fpi);
 
@@ -204,6 +214,20 @@ bool SOSFullPeerInfoUpdateTransportAckModelPreference(SOSFullPeerInfoRef peer, C
     });
 }
 
     });
 }
 
+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;
 CFDataRef SOSPeerInfoCopyData(SOSPeerInfoRef pi, CFErrorRef *error)
 {
     CFTypeRef vData = NULL;
@@ -296,6 +320,8 @@ static void SOSFullPeerInfoDestroy(CFTypeRef aObj) {
     
     CFReleaseNull(fpi->peer_info);
     CFReleaseNull(fpi->key_ref);
     
     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) {
 }
 
 static Boolean SOSFullPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) {
@@ -507,18 +533,31 @@ errOut:
     return retval;
 }
 
     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);
 {
     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;
 }
 
     
 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;
 
 static SecKeyRef SOSFullPeerInfoCopyMatchingPrivateKey(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
     SecKeyRef retval = NULL;
 
@@ -530,10 +569,10 @@ exit:
     return retval;
 }
 
     return retval;
 }
 
-static SecKeyRef SOSFullPeerInfoCopyMatchingOctagonPrivateKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
+static SecKeyRef SOSFullPeerInfoCopyMatchingOctagonSigningPrivateKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
 {
     SecKeyRef retval = NULL;    
 {
     SecKeyRef retval = NULL;    
-    SecKeyRef pub = SOSFullPeerInfoCopyOctagonPubKey(fpi, error);
+    SecKeyRef pub = SOSFullPeerInfoCopyOctagonPublicSigningKey(fpi, error);
     require_quiet(pub, exit);
     retval = SecKeyCopyMatchingPrivateKey(pub, error);
     
     require_quiet(pub, exit);
     retval = SecKeyCopyMatchingPrivateKey(pub, error);
     
@@ -541,6 +580,18 @@ exit:
     CFReleaseNull(pub);
     return retval;
 }
     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;
 
 static OSStatus SOSFullPeerInfoGetMatchingPrivateKeyStatus(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
     OSStatus retval = errSecParam;
@@ -573,23 +624,37 @@ bool SOSFullPeerInfoPurgePersistentKey(SOSFullPeerInfoRef fpi, CFErrorRef* error
     CFMutableDictionaryRef octagonQuery = NULL;
 
     SecKeyRef pub = SOSFullPeerInfoCopyPubKey(fpi, error);
     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(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"));
 
     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
     // 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);
 
 fail:
     CFReleaseNull(privQuery);
@@ -597,17 +662,24 @@ fail:
     CFReleaseNull(pub);
     CFReleaseNull(octagonPrivQuery);
     CFReleaseNull(octagonQuery);
     CFReleaseNull(pub);
     CFReleaseNull(octagonPrivQuery);
     CFReleaseNull(octagonQuery);
-    CFReleaseNull(octagonPub);
+    CFReleaseNull(octagonSigningPub);
+    CFReleaseNull(octagonEncryptionPub);
     return result;
 }
 
     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 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);
 }
 
 //
 }
 
 //
index 86b6dc8c801c1c192a3e5a0fa099c5747772c256..6bb92ea03002411e584573ca7db27db726264673 100644 (file)
@@ -61,13 +61,13 @@ static inline SOSPeerInfoRef asSOSPeerInfo(CFTypeRef obj) {
     return isSOSPeerInfo(obj) ? (SOSPeerInfoRef) obj : NULL;
 }
 
     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,
 
 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,
 
 SOSPeerInfoRef SOSPeerInfoCreateCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFErrorRef* error);
 SOSPeerInfoRef SOSPeerInfoCreateCurrentCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy,
@@ -172,7 +172,10 @@ CFStringRef SOSPeerGestaltGetName(CFDictionaryRef gestalt);
 CFTypeRef SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt, CFStringRef question);
 
 SecKeyRef SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer, CFErrorRef *error);
 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);
 
 
 CFDataRef SOSPeerInfoGetAutoAcceptInfo(SOSPeerInfoRef peer);
 
@@ -220,6 +223,22 @@ bool SOSPeerInfoHasDeviceID(SOSPeerInfoRef peer);
 CFStringRef SOSPeerInfoCopyDeviceID(SOSPeerInfoRef peer);
 SOSPeerInfoRef SOSPeerInfoSetDeviceID(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFStringRef IDS, SecKeyRef signingKey, CFErrorRef *error);
 
 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);
 
 CFStringRef SOSPeerInfoCopySerialNumber(SOSPeerInfoRef pi);
 CFStringRef SOSPeerInfoCopyOSVersion(SOSPeerInfoRef pi);
 
index 24d0cb0f66a1783e37526e4698842018351e3aa0..b1b72e75efd75ddf963072319c1a3b16550225b5 100644 (file)
@@ -78,7 +78,8 @@ const CFStringRef kPIOSVersionKey               = CFSTR("OSVersion");
 
 // Description Dictionary Entries
 static CFStringRef sPublicKeyKey        = CFSTR("PublicSigningKey");
 
 // 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");
 const CFStringRef sGestaltKey          = CFSTR("DeviceGestalt");
 const CFStringRef sVersionKey          = CFSTR("ConflictVersion");
 static CFStringRef sCloudIdentityKey    = CFSTR("CloudIdentity");
@@ -124,11 +125,17 @@ SecKeyRef SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer, CFErrorRef* error) {
     return _SOSPeerInfoCopyPubKey(peer, sPublicKeyKey, error);
 }
 
     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;
 
 CFDataRef SOSPeerInfoGetAutoAcceptInfo(SOSPeerInfoRef peer) {
        CFDataRef pubKeyBytes = NULL;
 
@@ -221,7 +228,10 @@ static SOSPeerInfoRef SOSPeerInfoCreate_Internal(CFAllocatorRef allocator,
                                                  CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS,
                                                  CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel,
                                                  CFSetRef enabledViews,
                                                  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;
                                                  void (^ description_modifier)(CFMutableDictionaryRef description)) {
     SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
     pi->gestalt = gestalt;
@@ -229,7 +239,8 @@ static SOSPeerInfoRef SOSPeerInfoCreate_Internal(CFAllocatorRef allocator,
     
     pi->version = SOSPeerInfoGetPeerProtocolVersion(pi);
     CFDataRef publicBytes = NULL;
     
     pi->version = SOSPeerInfoGetPeerProtocolVersion(pi);
     CFDataRef publicBytes = NULL;
-    CFDataRef octagonPublicBytes = NULL;
+    CFDataRef octagonPeerSigningPublicBytes = NULL;
+    CFDataRef octagonPeerEncryptionPublicBytes = NULL;
     CFNumberRef versionNumber = NULL;
 
     SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(signingKey);
     CFNumberRef versionNumber = NULL;
 
     SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(signingKey);
@@ -246,22 +257,41 @@ static SOSPeerInfoRef SOSPeerInfoCreate_Internal(CFAllocatorRef allocator,
         CFReleaseNull(pi);
         goto exit;
     }
         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->signature = CFDataCreateMutable(allocator, 0);
     
     versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version);
@@ -269,19 +299,24 @@ static SOSPeerInfoRef SOSPeerInfoCreate_Internal(CFAllocatorRef allocator,
     pi->description = CFDictionaryCreateMutableForCFTypesWith(allocator,
                                                               sVersionKey,   versionNumber,
                                                               sPublicKeyKey, publicBytes,
     pi->description = CFDictionaryCreateMutableForCFTypesWith(allocator,
                                                               sVersionKey,   versionNumber,
                                                               sPublicKeyKey, publicBytes,
-                                                              sOctagonPublicKeyKey, octagonPublicBytes,
                                                               sGestaltKey,   pi->gestalt,
                                                               NULL);
                                                               sGestaltKey,   pi->gestalt,
                                                               NULL);
+    if (octagonPeerSigningPublicBytes) {
+        CFDictionarySetValue(pi->description, sOctagonPeerSigningPublicKeyKey, octagonPeerSigningPublicBytes);
+    }
+    if (octagonPeerEncryptionPublicBytes) {
+        CFDictionarySetValue(pi->description, sOctagonPeerEncryptionPublicKeyKey, octagonPeerEncryptionPublicBytes);
+    }
 
 
     description_modifier(pi->description);
     
     
 
 
     description_modifier(pi->description);
     
     
-    pi->id = SOSCopyIDOfKey(publicKey, error);
+    pi->peerID = SOSCopyIDOfKey(publicKey, error);
+
     CFReleaseNull(publicKey);
     CFReleaseNull(publicKey);
-    CFReleaseNull(octagonPublicKey);
-    
-    require_quiet(pi->id, exit);
+
+    require_quiet(pi->peerID, exit);
     
     // ================ V2 Additions Start
     
     
     // ================ V2 Additions Start
     
@@ -309,24 +344,29 @@ static SOSPeerInfoRef SOSPeerInfoCreate_Internal(CFAllocatorRef allocator,
 exit:
     CFReleaseNull(versionNumber);
     CFReleaseNull(publicBytes);
 exit:
     CFReleaseNull(versionNumber);
     CFReleaseNull(publicBytes);
-    CFReleaseNull(octagonPublicBytes);
+    CFReleaseNull(octagonPeerSigningPublicBytes);
+    CFReleaseNull(octagonPeerEncryptionPublicBytes);
     return pi;
 }
 
     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,
 }
 
 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);
     });
 
         CFDictionarySetValue(description, sCloudIdentityKey, kCFBooleanTrue);
     });
 
@@ -341,7 +381,7 @@ SOSPeerInfoRef SOSPeerInfoCreateCopy(CFAllocatorRef allocator, SOSPeerInfoRef to
     pi->signature = CFDataCreateCopy(allocator, toCopy->signature);
 
     pi->gestalt = CFDictionaryCreateCopy(allocator, toCopy->gestalt);
     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);
 
     pi->version = toCopy->version;
     if(!SOSPeerInfoVersionHasV2Data(pi)) SOSPeerInfoExpandV2Data(pi, error);
@@ -520,8 +560,8 @@ SOSPeerInfoRef SOSPeerInfoCopyWithPing(CFAllocatorRef allocator, SOSPeerInfoRef
     SOSPeerInfoV2DictionarySetValue(pi, sPingKey, ping);
     SecKeyRef pub_key = SOSPeerInfoCopyPubKey(pi, error);
     require_quiet(pub_key, exit);
     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);
     require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, CFReleaseNull(pi));
 exit:
     CFReleaseNull(ping);
@@ -568,8 +608,8 @@ static void SOSPeerInfoDestroy(CFTypeRef aObj) {
     CFReleaseNull(pi->description);
     CFReleaseNull(pi->signature);
     CFReleaseNull(pi->gestalt);
     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) {
 }
 
 static Boolean SOSPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) {
@@ -728,7 +768,7 @@ CFTypeRef SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi, CFStringRef key) {
 }
 
 CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) {
 }
 
 CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) {
-    return pi ? pi->id : NULL;
+    return pi ? pi->peerID : NULL;
 }
 
 bool SOSPeerInfoPeerIDEqual(SOSPeerInfoRef pi, CFStringRef myPeerID) {
 }
 
 bool SOSPeerInfoPeerIDEqual(SOSPeerInfoRef pi, CFStringRef myPeerID) {
@@ -1001,6 +1041,56 @@ SOSPeerInfoRef SOSPeerInfoSetIDSACKModelPreference(CFAllocatorRef allocator, SOS
                                            });
 }
 
                                            });
 }
 
+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);
 }
 bool SOSPeerInfoTransportTypeIs(SOSPeerInfoRef pi, CFStringRef transportType) {
     return SOSPeerInfoV2DictionaryHasStringValue(pi, sTransportType, transportType);
 }
@@ -1116,3 +1206,21 @@ SOSPeerInfoDeviceClass SOSPeerInfoGetClass(SOSPeerInfoRef pi) {
 errOut:
     return retval;
 }
 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;
+}
index dc4df8970807096643abe012dbeb95c1e03cd1bf..f5d7be07f79df44cb853399faf6b67a87c2a155d 100644 (file)
@@ -116,8 +116,8 @@ SOSPeerInfoRef SOSPeerInfoCreateFromDER(CFAllocatorRef allocator, CFErrorRef* er
     pubKey = SOSPeerInfoCopyPubKey(pi, error);
     require_quiet(pubKey, fail);
     
     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);
     
     
     if(pi->version >= 2) SOSPeerInfoExpandV2Data(pi, error);
     
index b7aba7143d5e34da96f72f2ba36c35c466836b9a..c5cb83900352901ea1fb0a45e097ba1d5436e48f 100644 (file)
@@ -21,7 +21,7 @@ struct __OpaqueSOSPeerInfo {
     
     // Cached data
     CFDictionaryRef         gestalt;
     
     // Cached data
     CFDictionaryRef         gestalt;
-    CFStringRef             id;
+    CFStringRef             peerID;
     CFIndex                 version;
     /* V2 and beyond are listed below */
     CFMutableDictionaryRef  v2Dictionary;
     CFIndex                 version;
     /* V2 and beyond are listed below */
     CFMutableDictionaryRef  v2Dictionary;
index 96dce491e954286b860fa50fc2e8a2e26fcb8c4e..baccac59aa72241c3fc671db013c5c10a4d1fe13 100644 (file)
@@ -21,6 +21,7 @@
 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
 #include <SOSCircle/CKBridge/SOSCloudKeychainConstants.h>
 #include <Security/SecureObjectSync/SOSInternal.h>
 #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"
 #import "Security/SecureObjectSync/SOSAccountTrustClassic.h"
 
 #define IDS "IDS transport"
@@ -235,7 +236,8 @@ static bool sendToPeer(SOSMessageIDS* transport, bool shouldUseAckModel, CFStrin
     dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
 
     secnotice("IDS Transport", "Starting");
     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) {
     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) {
index c398e55c6d73ef80da74d2ba76f000138e02de9d..0cc26cc72ab8074ed6002aab3906558f582f8654 100644 (file)
@@ -3,6 +3,7 @@
 #import <Security/SecureObjectSync/SOSTransportMessageKVS.h>
 #include <Security/SecureObjectSync/SOSKVSKeys.h>
 #include <utilities/SecCFWrappers.h>
 #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>
 #include <SOSInternal.h>
 #include <AssertMacros.h>
 #include <SOSCloudKeychainClient.h>
@@ -76,6 +77,8 @@ fail:
 
 static bool SOSTransportMessageKVSUpdateKVS(SOSMessageKVS* transport, CFDictionaryRef changes, CFErrorRef *error){
 
 
 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);
     CloudKeychainReplyBlock log_error = ^(CFDictionaryRef returnedValues __unused, CFErrorRef block_error) {
         if (block_error) {
             secerror("Error putting: %@", block_error);
index 619beea3b50f8392afa6ba8d597ae1aec2a01b1d..5cfd60f0a0c711505bc3b1c5f7d997db031487e6 100644 (file)
@@ -45,4 +45,5 @@ DOVIEWMACRO(Engram,                 "Engram",               "engram",
 DOVIEWMACRO(Manatee,                "Manatee",              "manatee",                 CKKS, D,  , A,  ,  )
 DOVIEWMACRO(AutoUnlock,             "AutoUnlock",           "autounlock",              CKKS, D,  , A,  ,  )
 DOVIEWMACRO(Health,                 "Health",               "health",                  CKKS, D,  , A,  ,  )
 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,  ,  )
 
 
index d4d54c72cb5f39e2c09149c8ffacdff365138879..b4af69df1cb157c22c5173085df6fe4328354221 100644 (file)
 #include <SecurityTool/security_tool_commands.h>
 
 SECURITY_COMMAND(
 #include <SecurityTool/security_tool_commands.h>
 
 SECURITY_COMMAND(
-                 "recovery-key", recovery_key,
+                 "recovery", recovery_key,
                  "[options]\n"
                  "[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" )
index 19effd745a4f4253b3be8412c516ab905ec2fc68..882111ccb9ba40c3cf1af596212a8e68852b355b 100644 (file)
@@ -49,11 +49,13 @@ recovery_key(int argc, char * const *argv)
     static struct option long_options[] =
     {
         /* These options set a flag. */
     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'},
         {"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;
         {0, 0, 0, 0}
     };
     int option_index = 0;
@@ -75,7 +77,7 @@ recovery_key(int argc, char * const *argv)
                 if(publicKey == nil)
                     return 2;
 
                 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': {
                 break;
             }
             case 'R': {
@@ -99,12 +101,13 @@ recovery_key(int argc, char * const *argv)
                     printmsg(CFSTR("SecRKCreateRecoveryKeyWithError: %@\n"), nserror);
                     return 2;
                 }
                     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;
                     return 2;
-
-                hadError = SOSCCRegisterRecoveryPublicKey((__bridge CFDataRef)(publicKey), &error) != true;
+                }
                 break;
             }
             case 'g':
                 break;
             }
             case 'g':
index e02ffeef6e51797606e00618cfb47049388f1463..b052146e5ebe8ac82a7b657dbc26710402292931 100644 (file)
@@ -2840,6 +2840,7 @@ static void test_sign_no_priv(void) {
 #else
     ok(cmsg = SecCmsMessageCreate(NULL), "create message");
 #endif
 #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
     ok(sigd = SecCmsSignedDataCreate(cmsg), "create signed message");
     ok(cinfo = SecCmsMessageGetContentInfo(cmsg), "get content info");
 #if TARGET_OS_IPHONE
@@ -2856,6 +2857,7 @@ static void test_sign_no_priv(void) {
     is(SecCmsSignerInfoCreate(cmsg, signer, SEC_OID_SHA1), NULL, "set up signer with no private key");
 #endif
 
     is(SecCmsSignerInfoCreate(cmsg, signer, SEC_OID_SHA1), NULL, "set up signer with no private key");
 #endif
 
+out:
     CFReleaseNull(cert);
     CFReleaseNull(publicKey);
     CFReleaseNull(privateKey);
     CFReleaseNull(cert);
     CFReleaseNull(publicKey);
     CFReleaseNull(privateKey);
index a9c0a1f281bfdb602dca09edd852536f07a96ab4..97e853b17c9a33877c21979aae9371a0edd2d2aa 100644 (file)
@@ -1665,7 +1665,7 @@ static bool SecCertificateParse(SecCertificateRef certificate)
     }
     require_quiet(tbsCert.serialNum.data != NULL &&
                   tbsCert.serialNum.length >= 1 &&
     }
     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);
 
     certificate->_serialNumber = CFDataCreate(allocator,
         tbsCert.serialNum.data, tbsCert.serialNum.length);
 
@@ -2755,41 +2755,43 @@ static void appendDERThingProperty(CFMutableArrayRef properties,
 }
 
 static OSStatus appendRDNProperty(void *context, const DERItem *rdnType,
 }
 
 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);
                            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);
     appendDERThingProperty(properties, label, localizedLabel, rdnValue);
     CFReleaseSafe(label);
     CFReleaseSafe(localizedLabel);
@@ -4078,6 +4080,85 @@ CFMutableArrayRef SecCertificateCopySummaryProperties(
        return summary;
 }
 
        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);
 CFArrayRef SecCertificateCopyProperties(SecCertificateRef certificate) {
        if (!certificate->_properties) {
                CFAllocatorRef allocator = CFGetAllocator(certificate);
index 45289d57d152d7107f6a4fd484cb30c474830d65..948d523ca72e38724a6504b1e583ee322f0302a2 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2007-2015 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2007-2016 Apple Inc. All Rights Reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -91,7 +91,6 @@ SecCertificateGetBasicConstraints(SecCertificateRef certificate);
    any. */
 CFArrayRef SecCertificateGetPermittedSubtrees(SecCertificateRef certificate);
 
    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. */
 /* Returns array of CFDataRefs containing the generalNames that are
    Excluded Subtree Name Constraints for this certificate if it has
    any. */
@@ -125,6 +124,9 @@ const DERAlgorithmId *SecCertificateGetPublicKeyAlgorithm(
 /* Return the raw public key data for certificate.  */
 const DERItem *SecCertificateGetPublicKeyData(SecCertificateRef certificate);
 
 /* 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
 
 // MARK: -
 // MARK: Certificate Operations
 
index fb94cab5cc9c28082baeb5e72de989ffb9d07493..d0ea3926258b619404e6555903bc131a3168e4ab 100644 (file)
@@ -517,6 +517,7 @@ _SecCertificateCopyIssuerSHA1Digest
 _SecCertificateCopyIssuerSequence
 _SecCertificateCopyIssuerSummary
 _SecCertificateCopyKeychainItem
 _SecCertificateCopyIssuerSequence
 _SecCertificateCopyIssuerSummary
 _SecCertificateCopyKeychainItem
+_SecCertificateCopyLegacyProperties
 _SecCertificateCopyNormalizedIssuerSequence
 _SecCertificateCopyNormalizedSubjectSequence
 _SecCertificateCopyNTPrincipalNames
 _SecCertificateCopyNormalizedIssuerSequence
 _SecCertificateCopyNormalizedSubjectSequence
 _SecCertificateCopyNTPrincipalNames
index 9c33051d749cde0908ff5c8799e7d15bc9b8d5cd..ab1a651689a23e11d8da8bc780bd7b8c35ec19c8 100644 (file)
@@ -92,6 +92,10 @@ OSStatus SecIdentityCopyPrivateKey(SecIdentityRef identity,
 
 SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator,
        SecCertificateRef certificate, SecKeyRef privateKey) {
 
 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);
     CFIndex size = sizeof(struct __SecIdentity);
     SecIdentityRef result = (SecIdentityRef)_CFRuntimeCreateInstance(
                allocator, SecIdentityGetTypeID(), size - sizeof(CFRuntimeBase), 0);
index 05a3e566b380b84777dc389a1628c5337eeef558..6e4f032e71856fe95f1956196a7b3f5e044c024a 100644 (file)
@@ -685,7 +685,7 @@ static const uint8_t tk_persistent_ref_id[] = {'t', 'k', 'p', 'r'};
 CFDataRef _SecItemCreatePersistentRef(CFTypeRef class, sqlite_int64 rowid, CFDictionaryRef attributes)
 {
     CFDataRef result = NULL;
 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));
         CFDataRef tokenPersistentRef = CreateTokenPersistentRefData(class, attributes);
         require(tokenPersistentRef, out);
         CFMutableDataRef tmpData = CFDataCreateMutable(kCFAllocatorDefault, sizeof(tk_persistent_ref_id) + CFDataGetLength(tokenPersistentRef));
index ef7ba8b3b98eeb19acc10cfd7924fb3755155c45..30d663c62ceba679e555925781427aafc3f2e9fd 100644 (file)
@@ -4005,7 +4005,7 @@ SecPolicyRef SecPolicyCreateAppleSoftwareSigning(void) {
                                                 &kCFTypeDictionaryKeyCallBacks,
                                                 &kCFTypeDictionaryValueCallBacks), errOut);
 
                                                 &kCFTypeDictionaryKeyCallBacks,
                                                 &kCFTypeDictionaryValueCallBacks), errOut);
 
-    SecPolicyAddBasicX509Options(options);
+    SecPolicyAddBasicCertOptions(options);
 
     /* Anchored to the Apple Roots */
     require_quiet(SecPolicyAddAppleAnchorOptions(options, kSecPolicyNameAppleSoftwareSigning),
 
     /* Anchored to the Apple Roots */
     require_quiet(SecPolicyAddAppleAnchorOptions(options, kSecPolicyNameAppleSoftwareSigning),
index 0c8455b0b0a67a4893c4424de4fc45e98732e430..261800e6772fe8df861c17407cdebbab52131cd3 100644 (file)
@@ -286,10 +286,11 @@ SecRKCopyAccountRecoveryVerifier(NSString *recoveryKey,
 
 }
 
 
 }
 
+// This recreates the key pair using the recovery key string.
 static NSData *
 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;
     CFDataRef derivedSecret = NULL;
     ccec_const_cp_t cp = ccec_cp_256();
     CFDataRef result = NULL;
@@ -308,16 +309,16 @@ RKBackupCreateECKey(SecRecoveryKey *rk, bool fullkey)
                                              fullKey);
     require_noerr(status, fail);
 
                                              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);
 fail:
     CFReleaseNull(derivedSecret);
-    CFReleaseNull(publicKeyData);
+    CFReleaseNull(keyData);
 
     return (__bridge NSData *)result;
 }
 
     return (__bridge NSData *)result;
 }
@@ -341,7 +342,7 @@ SecRKRegisterBackupPublicKey(SecRecoveryKey *rk, CFErrorRef *error)
     CFDataRef backupKey = (__bridge CFDataRef)SecRKCopyBackupPublicKey(rk);
     bool res = false;
 
     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);
 
 
     res = SOSCCRegisterRecoveryPublicKey(backupKey, error);
 
index 69dedc799d9ea3dc6d635447be5f0e541691bc6c..9e6a40aa3963523d8f638972b76655fea9fdc607 100644 (file)
@@ -1080,6 +1080,12 @@ static void securityd_xpc_dictionary_handler(const xpc_connection_t connection,
                 {
                     if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementRestoreKeychain, &error)) {
                         CFDataRef recovery_key = SecXPCDictionaryCopyData(event, kSecXPCKeyRecoveryPublicKey, &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);
                     }
                         xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCRegisterRecoveryPublicKey_Server(recovery_key, &error));
                         CFReleaseNull(recovery_key);
                     }
index 1b302fdacde7a769578c9b9d336122a62f3b3f6e..b4cf801998005a9842b7ddec06fddc484a8b1456 100644 (file)
 #include <ipc/server_security_helpers.h>
 #include <ipc/server_endpoint.h>
 
 #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>
 #include <Security/SecEntitlements.h>
 #include <Security/SecItemPriv.h>
 #include <securityd/SecItemServer.h>
 
 - (void) SecItemAddAndNotifyOnSync:(NSDictionary*) attributes
                       syncCallback:(id<SecuritydXPCCallbackProtocol>) callback
 
 - (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);
     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
                                      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
 {
 #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);
     __block CFErrorRef cferror = NULL;
     if([self clientHasBooleanEntitlement: (__bridge NSString*) kSecEntitlementKeychainDeny]) {
         SecError(errSecNotAvailable, &cferror, CFSTR("SecItemSetCurrentItemAcrossAllDevices: %@ has entitlement %@"), _client.task, kSecEntitlementKeychainDeny);
                                            1,
                                            &cferror);
         if(cferror) {
                                            1,
                                            &cferror);
         if(cferror) {
-            secerror("couldn't create query: %@", cferror);
+            secerror("couldn't create query for new item pref: %@", cferror);
             return false;
         }
 
             return false;
         }
 
             newItem = CFRetainSafe(item);
         })) {
             query_destroy(q, NULL);
             newItem = CFRetainSafe(item);
         })) {
             query_destroy(q, NULL);
+            secerror("couldn't run query for new item pref: %@", cferror);
             return false;
         }
 
         if(!query_destroy(q, &cferror)) {
             return false;
         }
 
         if(!query_destroy(q, &cferror)) {
+            secerror("couldn't destroy query for new item pref: %@", cferror);
             return false;
         };
 
             return false;
         };
 
                 oldItem = CFRetainSafe(item);
             })) {
                 query_destroy(q, NULL);
                 oldItem = CFRetainSafe(item);
             })) {
                 query_destroy(q, NULL);
+                secerror("couldn't run query for old item pref: %@", cferror);
                 return false;
             }
 
             if(!query_destroy(q, &cferror)) {
                 return false;
             }
 
             if(!query_destroy(q, &cferror)) {
+                secerror("couldn't destroy query for old item pref: %@", cferror);
                 return false;
             };
         }
                 return false;
             };
         }
     }
     CFReleaseNull(cferror);
 #else // ! OCTAGON
     }
     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
 }
 
 #endif // OCTAGON
 }
 
                                     identifier:(NSString*)identifier
                                       viewHint:(NSString*)viewHint
                                fetchCloudValue:(bool)fetchCloudValue
                                     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
 {
 #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);
     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) {
                                             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.
                                                            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
                                                        [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
 #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++) {
     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;
         }
 
         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,
         _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);
     complete((__bridge NSData*) result, (__bridge NSError*) cferror);
     CFReleaseNull(result);
     CFReleaseNull(cferror);
index d3943d81173ae3cdfd1193133090cbd889fa3aa9..fee51336fad2f42597fee26f58804769f5f69dbb 100644 (file)
@@ -927,6 +927,66 @@ static inline bool SOSTestJoinWithApproval(CFDataRef cfpassword, CFStringRef cfa
 }
 
 
 }
 
 
+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);
 }
 static inline SOSAccount* SOSTestCreateAccountAsSerialClone(CFStringRef name, SOSPeerInfoDeviceClass devClass, CFStringRef serial, CFStringRef idsID) {
     return CreateAccountForLocalChangesWithStartingAttributes(name, CFSTR("TestSource"), devClass, serial, kCFBooleanTrue, kCFBooleanTrue, kCFBooleanTrue, SOSTransportMessageTypeIDSV2, idsID);
 }
@@ -944,17 +1004,6 @@ retOut:
     return retval;
 }
 
     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();
 static inline void SOSTestCleanup() {
     SOSUnregisterAllTransportMessages();
     SOSUnregisterAllTransportCircles();
index 09d267aec713f2597252ecf329e92d6b51d26814..0554e69602db5818a6ff103c135d149a45cc3277 100644 (file)
@@ -52,7 +52,7 @@
 
 #include "SecdTestKeychainUtilities.h"
 
 
 #include "SecdTestKeychainUtilities.h"
 
-static int kTestTestCount = 213;
+static int kTestTestCount = 161;
 
 static bool purgeICloudIdentity(SOSAccount* account) {
     bool retval = false;
 
 static bool purgeICloudIdentity(SOSAccount* account) {
     bool retval = false;
index 77cefbb7395c0166411448a328f0fc129871d674..c018e6b530c609654125a4dcdaa9d68c57c5f155 100644 (file)
@@ -36,8 +36,6 @@
 #include "SOSAccountTesting.h"
 #include "SecdTestKeychainUtilities.h"
 
 #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 a circle with two peers - alice and bob(bob is iOS and serial#"abababababab")
  have alice leave the circle
@@ -75,6 +73,8 @@ static void hauntedCircle(SOSPeerInfoDeviceClass devClass, bool expectGhostBuste
     // Make new bob - same as the old bob except peerID
 
     SOSAccount* bobFinal = SOSTestCreateAccountAsSerialClone(CFSTR("BobFinal"), devClass, ghostSerialID, ghostIdsID);
     // 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);
 
     ok(SOSTestJoinWith(cfpassword, cfaccount, changes, bobFinal), "Application Made");
     CFReleaseNull(cfpassword);
 
@@ -96,7 +96,7 @@ static void hauntedCircle(SOSPeerInfoDeviceClass devClass, bool expectGhostBuste
     SOSTestCleanup();
 }
 
     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");
     CFErrorRef error = NULL;
     CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
     CFStringRef cfaccount = CFSTR("test@test.org");
@@ -121,17 +121,28 @@ static void multiBob(SOSPeerInfoDeviceClass devClass, bool expectGhostBusted, bo
     SOSTestMakeGhostInCircle(CFSTR("Bob4"), devClass, ghostSerialID, ghostIdsID, cfpassword, cfaccount, changes, alice_account, 5);
     
     SOSAccount* bobFinal_account = SOSTestCreateAccountAsSerialClone(CFSTR("BobFinal"), devClass, ghostSerialID, ghostIdsID);
     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");
         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");
         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);
 
     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);
     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);
@@ -187,15 +198,16 @@ static void iosICloudIdentity() {
 
 int secd_68_ghosts(int argc, char *const *argv)
 {
 
 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);
     
     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;
     iosICloudIdentity();
     
     return 0;
index 25890f74890c82684f6bc92940541b3456648cb3..c0e0f39498fd2adb134b11c07c5a56047a4f479e 100644 (file)
@@ -194,6 +194,9 @@ bool SOSCCSendToPeerIsPending_Server(SOSPeerInfoRef peer, CFErrorRef *error);
 XPC_RETURNS_RETAINED xpc_endpoint_t SOSCCCreateSOSEndpoint_server(CFErrorRef *error);
 
 void SOSCCPerformWithOctagonSigningKey(void (^action)(SecKeyRef octagonPrivKey, CFErrorRef error));
 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);
 
 void SOSCCResetOTRNegotiation_Server(CFStringRef peerid);
 void SOSCCPeerRateLimiterSendNextMessage_Server(CFStringRef peerid, CFStringRef accessGroup);
 
index 272e9aacbf8d5b15a43509e001f4ab4ebb380d89..b49cb6eef2ce9d9d9d73cdc5c6700ba5e29820e0 100644 (file)
@@ -2176,7 +2176,7 @@ SOSCCCreateSOSEndpoint_server(CFErrorRef *error)
     return [account xpcControlEndpoint];
 }
 
     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 error = NULL;
     do_with_account_if_after_first_unlock(&error, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
@@ -2185,6 +2185,38 @@ void SOSCCPerformWithOctagonSigningKey(void (^action)(SecKeyRef octagonPrivKey,
         CFErrorRef errorArg = err ? *err : NULL;
         action(signingKey, errorArg);
         CFReleaseNull(signingKey);
         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);
+}
+
index 5caa4a938ebd3e275c5246d1268dbff0ac02b985..7561db856e585210c615618c2b6de2ca7b575bdb 100644 (file)
@@ -683,6 +683,23 @@ SecPathVerifyStatus SecCertificatePathVCVerify(SecCertificatePathVCRef certifica
     return kSecPathVerifySuccess;
 }
 
     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) {
 bool SecCertificatePathVCIsValid(SecCertificatePathVCRef certificatePath, CFAbsoluteTime verifyTime) {
     __block bool result = true;
     SecCertificatePathVCForEachCertificate(certificatePath, ^(SecCertificateRef certificate, bool *stop) {
index 51585c2faac5c2c35c273576ad2c77e02e84a22c..7b5a1481d16596966405d5b5ee2c5105bfe3d6f8 100644 (file)
@@ -108,6 +108,8 @@ enum {
 
 SecPathVerifyStatus SecCertificatePathVCVerify(SecCertificatePathVCRef certificatePath);
 
 
 SecPathVerifyStatus SecCertificatePathVCVerify(SecCertificatePathVCRef certificatePath);
 
+bool SecCertificatePathVCIsCycleInGraph(SecCertificatePathVCRef path);
+
 bool SecCertificatePathVCIsValid(SecCertificatePathVCRef certificatePath, CFAbsoluteTime verifyTime);
 
 bool SecCertificatePathVCHasWeakHash(SecCertificatePathVCRef certificatePath);
 bool SecCertificatePathVCIsValid(SecCertificatePathVCRef certificatePath, CFAbsoluteTime verifyTime);
 
 bool SecCertificatePathVCHasWeakHash(SecCertificatePathVCRef certificatePath);
index 97e2b64ac1e2dbc5f9394b037a48750d55be8d4a..d954f64c8ca7b07d8755f0e17174ae2cafcaae5d 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2016 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2016-2017 Apple Inc. All Rights Reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -578,7 +578,15 @@ void SecMemoryCertificateSourceDestroy(SecCertificateSourceRef source) {
 static bool SecCAIssuerCertificateSourceCopyParents(
                                                     SecCertificateSourceRef source, SecCertificateRef certificate,
                                                     void *context, SecCertificateSourceParents callback) {
 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(
 }
 
 static bool SecCAIssuerCertificateSourceContains(
index bb340052c16bf5484b7ead6d11efd5ea29b9705f..069180a2393737553d49af266dbb39d11b6a6bed 100644 (file)
@@ -212,6 +212,7 @@ SECDB_ATTR(v10_1archiveEscrowID,"archive_escrowid", String, SecDbFlags( ,L, , ,
 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_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_1currentPtrIdentifier,"identifier",String,  SecDbFlags(P,L, , , , , , , , , , , , ,  , ), NULL, NULL);
 
 SECDB_ATTR(v10_2device,      "device",        String,      SecDbFlags(P,L, , , , , , , , , , , , ,  , ), NULL, NULL);
@@ -222,6 +223,66 @@ SECDB_ATTR(v10_2currentTLK,  "currentTLK",    String,      SecDbFlags( ,L, , , ,
 SECDB_ATTR(v10_2currentClassA,"currentClassA",String,      SecDbFlags( ,L, , , , , , , , , , , , ,  , ), NULL, NULL);
 SECDB_ATTR(v10_2currentClassC,"currentClassC",String,      SecDbFlags( ,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,
 const SecDbClass v10_3_ckdevicestate_class = {
     .name = CFSTR("ckdevicestate"),
     .itemclass = false,
@@ -833,6 +894,75 @@ const SecDbClass v_identity_class = {
     },
 };
 
     },
 };
 
+
+/*
+ * 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
  */
 /*
  * Version 10.3
  */
@@ -2169,6 +2299,8 @@ static const SecDbSchema v5_schema = {
 SecDbSchema const * const * kc_schemas = NULL;
 
 const SecDbSchema *v10_kc_schemas[] = {
 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,
     &v10_3_schema,
     &v10_2_schema,
     &v10_1_schema,
index 9121fbf71c169c2b6a674af956d64b8426dae1da..6c7a38718aa7de532614ccb2c003afc56283420d 100644 (file)
@@ -430,7 +430,7 @@ static bool UpgradeSchemaPhase1(SecDbConnectionRef dbt, const SecDbSchema *oldSc
                 comma = true;
             }
 
                 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);
 
             CFReleaseNull(columns);
             require_quiet(ok &= SecDbExec(dbt, sql, error), out);
index fd0531f513c3feaf85bb737c25ef891800b84a92..0a498a7adc58043d916f9e8ff0c59815f0c9877b 100644 (file)
@@ -652,7 +652,7 @@ static BOOL PinningDbCanCheckMobileAsset(void) {
     mode_t mode = 0644;
 
     CFStringRef path = CFStringCreateWithCString(NULL, [_dbPath fileSystemRepresentation], kCFStringEncodingUTF8);
     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.
          ^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.
index 76347c814b89ab0c2de6294af0d4d2d9fa371757..f102eb640a21b6a18d49580d5d4037295a2756df 100644 (file)
@@ -215,7 +215,7 @@ static CFDataRef copyInflatedDataToFile(CFDataRef data, char *fileName) {
         zs.avail_out = (uInt)buf_sz;
         rc = inflate(&zs, 0);
         if (off < (int64_t)zs.total_out) {
         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);
         }
     } while (rc == Z_OK);
     close(fd);
@@ -408,7 +408,7 @@ static bool SecValidUpdateProcessData(CFIndex format, CFDataRef updateData) {
     CFIndex version = 0;
     CFIndex interval = 0;
     const UInt8* p = CFDataGetBytePtr(updateData);
     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);
     /* 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);
@@ -620,9 +620,12 @@ static bool SecValidUpdateSatisfiedLocally(CFStringRef server, CFIndex version,
         if (semPathBuf) {
             struct stat sb;
             int fd = open(semPathBuf, O_WRONLY | O_CREAT, DEFFILEMODE);
         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);
             }
                 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
             free(semPathBuf);
         }
         // exit as gracefully as possible so we can replace the database
index d64069acae87f2f97780dff2cb7d2b3338efd41f..21a0742a754dc0d54a80d2883e69a5ac2d5e1471 100644 (file)
@@ -390,7 +390,7 @@ static ValidUpdateRequest *request = nil;
         });
 
         NSURLSessionDataTask *dataTask = [self.backgroundSession dataTaskWithURL:validUrl];
         });
 
         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());
     });
         [dataTask resume];
         secnotice("validupdate", "scheduled background data task %@ at %f", dataTask, CFAbsoluteTimeGetCurrent());
     });
index ca01d37cc0eb39e1390018f7d32a0639277a03c0..7667fb98934de1d53c419d5a4a9a7c9231f640f7 100644 (file)
@@ -75,6 +75,7 @@
 #endif
 
 #define MAX_CHAIN_LENGTH  15
 #endif
 
 #define MAX_CHAIN_LENGTH  15
+#define MAX_NUM_CHAINS    100
 #define ACCEPT_PATH_SCORE 10000000
 
 /* Forward declaration for use in SecCertificateSource. */
 #define ACCEPT_PATH_SCORE 10000000
 
 /* Forward declaration for use in SecCertificateSource. */
@@ -449,6 +450,20 @@ CFAbsoluteTime SecPathBuilderGetVerifyTime(SecPathBuilderRef builder) {
     return builder->verifyTime;
 }
 
     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);
 }
 CFIndex SecPathBuilderGetCertificateCount(SecPathBuilderRef builder) {
     return SecCertificatePathVCGetCount(builder->path);
 }
@@ -658,9 +673,21 @@ static bool SecPathBuilderIsPartial(SecPathBuilderRef builder,
             secdebug("trust", "Adding candidate %@", path);
                        CFArrayAppendValue(builder->candidatePaths, 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;
         }
        }
             return false;
         }
        }
@@ -887,7 +914,7 @@ static bool SecPathBuilderGetNext(SecPathBuilderRef builder) {
                directly.
                FIXME we might not want to consider partial paths that
                are subsets of other partial paths, or not consider them
                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");
             if (!builder->considerRejected) {
                 builder->considerRejected = true;
                 secdebug("trust", "considering rejected paths");
@@ -920,6 +947,14 @@ static bool SecPathBuilderGetNext(SecPathBuilderRef builder) {
         return true;
     }
 
         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 ": %@",
     /* 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 ": %@",
index 5c7dcd20e68536e44bf862d548cdb77148c6af54..1f068a66a0be3320c74616a73086051714e1d69a 100644 (file)
@@ -58,7 +58,7 @@ struct OpaqueSecPVC {
     SecTrustResultType result;
 };
 
     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);
 typedef void(*SecPathBuilderCompleted)(const void *userData,
     SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
     SecTrustResultType result);
@@ -89,6 +89,7 @@ CFAbsoluteTime SecPathBuilderGetVerifyTime(SecPathBuilderRef builder);
 CFIndex SecPathBuilderGetCertificateCount(SecPathBuilderRef builder);
 SecCertificateRef SecPathBuilderGetCertificateAtIndex(SecPathBuilderRef builder, CFIndex ix);
 CFArrayRef SecPathBuilderGetExceptions(SecPathBuilderRef builder);
 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
 
 /* 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
index 503aaa6b6b316c9232b5eb2ba8ece67dea8a3382..6975da2576a16deeed7725cab42344bc5180ae34 100644 (file)
@@ -30,6 +30,7 @@
 #include <notify.h>
 #include <xpc/private.h>
 #include <xpc/xpc.h>
 #include <notify.h>
 #include <xpc/private.h>
 #include <xpc/xpc.h>
+#include <CoreFoundation/CFStream.h>
 
 #include <Security/SecuritydXPC.h>
 #include <Security/SecTrustStore.h>
 
 #include <Security/SecuritydXPC.h>
 #include <Security/SecTrustStore.h>
@@ -595,6 +596,14 @@ static void trustd_sandbox(void) {
 }
 #endif
 
 }
 #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");
 int main(int argc, char *argv[])
 {
     char *wait4debugger = getenv("WAIT4DEBUGGER");
@@ -649,6 +658,7 @@ int main(int argc, char *argv[])
 #if TARGET_OS_OSX
     SecTrustLegacySourcesListenForKeychainEvents();
 #endif
 #if TARGET_OS_OSX
     SecTrustLegacySourcesListenForKeychainEvents();
 #endif
+    trustd_cfstream_init();
     trustd_xpc_init(serviceName);
 
     dispatch_main();
     trustd_xpc_init(serviceName);
 
     dispatch_main();
index fabb306d13e1037fc177b68f1923f643104fb5bd..b5106c4b2ffea3a93afba024ac1fda395d4f4f72 100644 (file)
@@ -158,7 +158,7 @@ static NSString *kService = @"manifeststresstest";
         return @{};
     }
     if (status != errSecSuccess) {
         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);
         exit(1);
     }
     NSArray *arr = CFBridgingRelease(result);
index 5c19899fa40124155f88805782b2ab122314c47e..17a4b3fce86ae8b3bf0d7e8d040498e3318dcf63 100644 (file)
 
 - (void)unexpectedError:(OSStatus)status
 {
 
 - (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);
 }
 
     exit(1);
 }
 
index a5843b5af222bd679615f28b9420cf23895daf5c..acc39b5ede392c0cc8e4ba1ed56bf26f8f228f14 100644 (file)
@@ -42,7 +42,7 @@ void writeMark(NSString *ident, NSString *view)
                 NSLog(@"(mark was already there, fine)");
                 break;
             default:
                 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);
         }
     }];
                 exit(1);
         }
     }];
@@ -72,7 +72,7 @@ void updateMark(NSString *ident)
                 NSLog(@"(updated mark was already there, fine)");
                 break;
             default:
                 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);
         }
     }];
                 exit(1);
         }
     }];
index 7f855391e23c6554099b7c665fe6486a1ac0dc48..e9c748cccf33cdf298bf3790f3d7a5375e7ba85a 100644 (file)
@@ -297,7 +297,11 @@ _kSSLSessionConfig_anonymous
 
 _SecAbsoluteTimeFromDateContent
 
 
 _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
 _OBJC_CLASS_$_SecuritydXPCClient
 _SecAccessGroupsGetCurrent
 _SecAccessGroupsSetCurrent
index 1f22215a3cd7c60e4d18f01c8b332f0f271e5ac3..1195965e5919c6fddaa9a8dc05505abe659e0f04 100644 (file)
                        buildPhases = (
                        );
                        dependencies = (
                        buildPhases = (
                        );
                        dependencies = (
-                               DC71D9FD1D95BB440065FB93 /* PBXTargetDependency */,
                                DC71D9E11D95BAC40065FB93 /* PBXTargetDependency */,
                                DC5AC1341D835C2300CF422C /* PBXTargetDependency */,
                                DC178BF31D77ABE300B50D50 /* 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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
                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, ); }; };
                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";
                };
                        );
                        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;
                E7B006FF170B56E700B27966 /* PBXBuildRule */ = {
                        isa = PBXBuildRule;
                        compilerSpec = com.apple.compilers.proxy.script;
                        remoteGlobalIDString = 79DC33610D4E6EEA0039E4BC;
                        remoteInfo = libCMS;
                };
                        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 */;
                DC59EA751D91CC5E001BDDF5 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
                        remoteGlobalIDString = DC71D99F1D95BA6C0065FB93;
                        remoteInfo = ASN1;
                };
                        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 */;
                DC71DA021D95BDEA0065FB93 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                };
                        );
                        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;
                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>"; };
                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>"; };
                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>"; };
                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>"; };
                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; };
                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>"; };
                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>"; };
                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; };
                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>"; };
                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>"; };
                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; };
                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>"; };
                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; };
                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>"; };
                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>"; };
                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>"; };
                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>"; };
                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; };
                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>"; };
                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>"; };
                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; };
                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>"; };
                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 */,
                        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 */,
                                DC00AB6F1D821C3400513D74 /* libSecItemShimOSX.a in Frameworks */,
                                DC00AB701D821C3800513D74 /* libSecOtrOSX.a in Frameworks */,
                                DC00AB6B1D821C1A00513D74 /* libSecTrustOSX.a in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        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;
                DC5ABDC21D832DAB00CF422C /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
-               DC71D9F71D95BB0A0065FB93 /* Frameworks */ = {
-                       isa = PBXFrameworksBuildPhase;
-                       buildActionMask = 2147483647;
-                       files = (
-                       );
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
                DC8834041D8A218F00CE0ACA /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                DC8834041D8A218F00CE0ACA /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                                DCE4E8C61D7F354700AFB96E /* CoreFoundation.framework in Frameworks */,
                                DCE4E8C51D7F354300AFB96E /* IOKit.framework in Frameworks */,
                                F6AF96681E646CAF00917214 /* libcoreauthd_client.a in Frameworks */,
                                DCE4E8C61D7F354700AFB96E /* CoreFoundation.framework in Frameworks */,
                                DCE4E8C51D7F354300AFB96E /* IOKit.framework in Frameworks */,
                                F6AF96681E646CAF00917214 /* libcoreauthd_client.a in Frameworks */,
+                               F682C1D41F4486F700F1B029 /* libctkloginhelper.a in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                DCD06A741D8CE2D5007602F1 /* gkunpack */,
                                DCD06AB01D8E0D53007602F1 /* libsecurity_utilities.a */,
                                DC59E9EC1D91C9DC001BDDF5 /* libDER_not_installed.a */,
                                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 */,
                                DC3A4B581D91E9FB00E46D4A /* com.apple.CodeSigningHelper.xpc */,
                                DC71D9DF1D95BA6C0065FB93 /* libASN1.a */,
-                               DC71D9FB1D95BB0A0065FB93 /* libDER.a */,
                                EBF374721DC055580065D840 /* security-sysdiagnose */,
                                DA30D6761DF8C8FB00EC6B43 /* KeychainSyncAccountUpdater.bundle */,
                                DCD8A1991E09EE0F00E4FA0A /* libSecureObjectSyncFramework.a */,
                                EBF374721DC055580065D840 /* security-sysdiagnose */,
                                DA30D6761DF8C8FB00EC6B43 /* KeychainSyncAccountUpdater.bundle */,
                                DCD8A1991E09EE0F00E4FA0A /* libSecureObjectSyncFramework.a */,
                                DC15F79B1E68EAD5003B9A40 /* CKKSTests+API.m */,
                                DC6593C91ED8DA9200C19462 /* CKKSTests+CurrentPointerAPI.m */,
                                DC9A2C5E1EB3F556008FAC27 /* CKKSTests+Coalesce.m */,
                                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 */,
                                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 */,
                                6C34462F1E24F6BE00F9522B /* CKKSRateLimiterTests.m */,
                                DCD6C4B61EC5319600414FEE /* CKKSNearFutureSchedulerTests.m */,
                                DCFE1C3C1F17EFB5007640C8 /* CKKSConditionTests.m */,
                                6C588D791EAA149F00D7E322 /* RateLimiterTests.m */,
                                4723C9D11F1531970082882F /* CKKSLoggerTests.m */,
                                DCE7F2081F21726500DDB0F7 /* CKKSAPSReceiverTests.m */,
                                6C588D791EAA149F00D7E322 /* RateLimiterTests.m */,
                                4723C9D11F1531970082882F /* CKKSLoggerTests.m */,
                                DCE7F2081F21726500DDB0F7 /* CKKSAPSReceiverTests.m */,
+                               DC9C95951F748D0B000D19E5 /* CKKSServerValidationRecoveryTests.m */,
                        );
                        name = "Tests (Local)";
                        path = tests;
                        );
                        name = "Tests (Local)";
                        path = tests;
                        isa = PBXGroup;
                        children = (
                                DC59E9FD1D91CA0A001BDDF5 /* libDER */,
                        isa = PBXGroup;
                        children = (
                                DC59E9FD1D91CA0A001BDDF5 /* libDER */,
-                               DC59EA2C1D91CA2C001BDDF5 /* libDERUtils */,
                        );
                        name = DER;
                        sourceTree = "<group>";
                        );
                        name = DER;
                        sourceTree = "<group>";
                        path = OSX/libsecurity_keychain/libDER/libDER;
                        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 = (
                DC5ABD281D832D4C00CF422C /* SecurityTool macOS */ = {
                        isa = PBXGroup;
                        children = (
                                F667EC541E96E8C800203D5C /* authdtests */,
                                EB1055641E14DB370003C309 /* secfuzzer */,
                                DC0BCBD81D8C646700070CB0 /* regressionBase */,
                                F667EC541E96E8C800203D5C /* authdtests */,
                                EB1055641E14DB370003C309 /* secfuzzer */,
                                DC0BCBD81D8C646700070CB0 /* regressionBase */,
-                               DC59EA4B1D91CACE001BDDF5 /* libDER */,
                                DC0BCCB81D8C68F000070CB0 /* utilitiesRegressions */,
                                DC0BC5CD1D8B72FE00070CB0 /* test-checkpw */,
                                DC610AB81D7910E5002223DE /* gk_reset_check */,
                                DC0BCCB81D8C68F000070CB0 /* utilitiesRegressions */,
                                DC0BC5CD1D8B72FE00070CB0 /* test-checkpw */,
                                DC610AB81D7910E5002223DE /* gk_reset_check */,
                DC9B7AD31DCBF336004E9385 /* CloudKit Syncing */ = {
                        isa = PBXGroup;
                        children = (
                DC9B7AD31DCBF336004E9385 /* CloudKit Syncing */ = {
                        isa = PBXGroup;
                        children = (
+                               DC9FD3161F857FF800C8AAC8 /* Protocol Buffers */,
                                DCD662F21E3294DE00188186 /* CloudKit Support */,
                                DCFE1C311F17ECC3007640C8 /* dispatch Support */,
                                DCD662EB1E32946000188186 /* Sync Objects */,
                                DCD662F21E3294DE00188186 /* CloudKit Support */,
                                DCFE1C311F17ECC3007640C8 /* dispatch Support */,
                                DCD662EB1E32946000188186 /* Sync Objects */,
                                DCBDB3BA1E57CA7A00B61300 /* CKKSViewManager.m */,
                                DCBDB3B01E57C67500B61300 /* CKKSKeychainView.h */,
                                DCBDB3B11E57C67500B61300 /* CKKSKeychainView.m */,
                                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 */,
                                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>";
                };
                        );
                        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 = (
                DCA4D2121E5651950056214F /* Tests (Live CloudKit) */ = {
                        isa = PBXGroup;
                        children = (
                                DC207EB71ED4EAB600D46873 /* CKKSLockStateTracker.m */,
                                DCCD88E61E42622200F5AA71 /* CKKSGroupOperation.h */,
                                DCCD88E71E42622200F5AA71 /* CKKSGroupOperation.m */,
                                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>";
                        );
                        name = Helpers;
                        sourceTree = "<group>";
                                DCB342D21D8A32A20054D16E /* UnlockReferralItem.h */,
                                DCB342D31D8A32A20054D16E /* TrustSettingsUtils.cpp */,
                                DCB342D41D8A32A20054D16E /* TrustSettingsUtils.h */,
                                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 */,
                                DCB342DB1D8A32A20054D16E /* generateErrStrings.pl */,
                                DCB342DC1D8A32A20054D16E /* tsaDERUtilities.c */,
                                DCB342DD1D8A32A20054D16E /* tsaDERUtilities.h */,
                                DCEA5D541E2826DB0089CF55 /* CKKSSIV.m */,
                                DCE278DB1ED789EF0083B485 /* CKKSCurrentItemPointer.h */,
                                DCE278DC1ED789EF0083B485 /* CKKSCurrentItemPointer.m */,
                                DCEA5D541E2826DB0089CF55 /* CKKSSIV.m */,
                                DCE278DB1ED789EF0083B485 /* CKKSCurrentItemPointer.h */,
                                DCE278DC1ED789EF0083B485 /* CKKSCurrentItemPointer.m */,
+                               DC7341F11F8447AB00AB9BDF /* CKKSTLKShare.h */,
+                               DC7341F21F8447AB00AB9BDF /* CKKSTLKShare.m */,
                        );
                        name = "Sync Objects";
                        sourceTree = "<group>";
                        );
                        name = "Sync Objects";
                        sourceTree = "<group>";
                                DC15F7651E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m */,
                                DCD662F31E329B6800188186 /* CKKSNewTLKOperation.h */,
                                DCD662F41E329B6800188186 /* CKKSNewTLKOperation.m */,
                                DC15F7651E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m */,
                                DCD662F31E329B6800188186 /* CKKSNewTLKOperation.h */,
                                DCD662F41E329B6800188186 /* CKKSNewTLKOperation.m */,
+                               DCBF2F831F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.h */,
+                               DCBF2F841F913EF000ED0CA4 /* CKKSHealTLKSharesOperation.m */,
                                DC7A17EB1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.h */,
                                DC7A17EC1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.m */,
                                DCA4D2131E5684220056214F /* CKKSReencryptOutgoingItemsOperation.h */,
                                DC7A17EB1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.h */,
                                DC7A17EC1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.m */,
                                DCA4D2131E5684220056214F /* CKKSReencryptOutgoingItemsOperation.h */,
                                DCE278E71ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m */,
                                DCFE1C4F1F1825F7007640C8 /* CKKSUpdateDeviceStateOperation.h */,
                                DCFE1C501F1825F7007640C8 /* CKKSUpdateDeviceStateOperation.m */,
                                DCE278E71ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m */,
                                DCFE1C4F1F1825F7007640C8 /* CKKSUpdateDeviceStateOperation.h */,
                                DCFE1C501F1825F7007640C8 /* CKKSUpdateDeviceStateOperation.m */,
+                               DCAD9B421F8D939C00C5E2AE /* CKKSFixups.h */,
+                               DCAD9B431F8D939C00C5E2AE /* CKKSFixups.m */,
                        );
                        name = Operations;
                        sourceTree = "<group>";
                        );
                        name = Operations;
                        sourceTree = "<group>";
                E7FCBE401314471B000DE34E /* Frameworks */ = {
                        isa = PBXGroup;
                        children = (
                E7FCBE401314471B000DE34E /* Frameworks */ = {
                        isa = PBXGroup;
                        children = (
+                               F682C1CE1F4486F600F1B029 /* libctkloginhelper.a */,
                                5EAFA4CD1EF16059002DC188 /* LocalAuthentication.framework */,
                                D41D36701EB14D87007FA978 /* libDiagnosticMessagesClient.tbd */,
                                D47CA65C1EB036450038E2BB /* libMobileGestalt.dylib */,
                                5EAFA4CD1EF16059002DC188 /* LocalAuthentication.framework */,
                                D41D36701EB14D87007FA978 /* libDiagnosticMessagesClient.tbd */,
                                D47CA65C1EB036450038E2BB /* libMobileGestalt.dylib */,
                EB27FF051E402C3C00EC9E3A /* ckksctl */ = {
                        isa = PBXGroup;
                        children = (
                EB27FF051E402C3C00EC9E3A /* ckksctl */ = {
                        isa = PBXGroup;
                        children = (
-                               EB27FF0B1E402C8000EC9E3A /* ckksctl.h */,
                                EB27FF0C1E402C8000EC9E3A /* ckksctl.m */,
                                EB27FF2F1E408CC900EC9E3A /* ckksctl-Entitlements.plist */,
                        );
                                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 */,
                                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>";
                        );
                        name = xcconfig;
                        sourceTree = "<group>";
                                724340BA1ED3FEC800F8F566 /* SecSMIME.h in Headers */,
                                22A23B3E1E3AAC9800C41830 /* SecRequirement.h in Headers */,
                                22A23B3F1E3AAC9800C41830 /* SecCodeHost.h in Headers */,
                                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 */,
                                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 */,
                        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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                DC4DB1501E24692100CD6769 /* CKKSKey.h in Headers */,
                                DCE278DD1ED789EF0083B485 /* CKKSCurrentItemPointer.h in Headers */,
                                DCEA5D551E2826DB0089CF55 /* CKKSSIV.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        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;
                DC5ABDEF1D832E5C00CF422C /* Headers */ = {
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        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;
                DC8834021D8A218F00CE0ACA /* Headers */ = {
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        buildActionMask = 2147483647;
                        files = (
                                DCB3438F1D8A32A20054D16E /* tsaDERUtilities.h in Headers */,
                        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 */,
                                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 */,
                                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 */,
                                DCB3437F1D8A32A20054D16E /* TrustAdditions.h in Headers */,
                                DCB3436D1D8A32A20054D16E /* cssmdatetime.h in Headers */,
                                DCB343711D8A32A20054D16E /* DLDBListCFPref.h in Headers */,
                                6C0B0C481E2537E2007F95E5 /* CopyFiles */,
                        );
                        buildRules = (
                                6C0B0C481E2537E2007F95E5 /* CopyFiles */,
                        );
                        buildRules = (
+                               DC9FD3221F85877000C8AAC8 /* PBXBuildRule */,
                        );
                        dependencies = (
                        );
                        );
                        dependencies = (
                        );
                                6C0B0C4A1E253840007F95E5 /* CopyFiles */,
                        );
                        buildRules = (
                                6C0B0C4A1E253840007F95E5 /* CopyFiles */,
                        );
                        buildRules = (
+                               DC9FD3201F85818000C8AAC8 /* PBXBuildRule */,
                        );
                        dependencies = (
                        );
                        );
                        dependencies = (
                        );
                        productReference = DC59E9EC1D91C9DC001BDDF5 /* libDER_not_installed.a */;
                        productType = "com.apple.product-type.library.static";
                };
                        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" */;
                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";
                };
                        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" */;
                DC8834011D8A218F00CE0ACA /* ASN1_not_installed */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = DC8834051D8A218F00CE0ACA /* Build configuration list for PBXNativeTarget "ASN1_not_installed" */;
                                                CreatedOnToolsVersion = 8.0;
                                                ProvisioningStyle = Automatic;
                                        };
                                                CreatedOnToolsVersion = 8.0;
                                                ProvisioningStyle = Automatic;
                                        };
-                                       DC59EA361D91CA82001BDDF5 = {
-                                               CreatedOnToolsVersion = 8.0;
-                                               ProvisioningStyle = Automatic;
-                                       };
                                        DC5ABDC41D832DAB00CF422C = {
                                                CreatedOnToolsVersion = 8.0;
                                                DevelopmentTeam = XPSUQMMH5W;
                                        DC5ABDC41D832DAB00CF422C = {
                                                CreatedOnToolsVersion = 8.0;
                                                DevelopmentTeam = XPSUQMMH5W;
                                DC8834011D8A218F00CE0ACA /* ASN1_not_installed */,
                                DC71D99F1D95BA6C0065FB93 /* ASN1 */,
                                DC59E9AC1D91C9DC001BDDF5 /* DER_not_installed */,
                                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 */,
                                DCF782BA1D88B44300E694BB /* ==== macOS Libraries ====== */,
                                DCF7830A1D88B4DE00E694BB /* security_apple_csp */,
                                DCF785021D88B95500E694BB /* security_apple_cspdl */,
                                7913B1FF0D172B3900601FE9 /* sslServer */,
                                4C9DE9D11181AC4800CF5C27 /* sslEcdsa */,
                                4CE5A54C09C796E100D27A3F /* sslViewer */,
                                7913B1FF0D172B3900601FE9 /* sslServer */,
                                4C9DE9D11181AC4800CF5C27 /* sslEcdsa */,
                                4CE5A54C09C796E100D27A3F /* sslViewer */,
-                               DC59EA361D91CA82001BDDF5 /* parseCert */,
-                               DC59EA521D91CAF0001BDDF5 /* parseCrl */,
-                               DC59EA621D91CB9F001BDDF5 /* parseTicket */,
                                DC0BC5C51D8B72E700070CB0 /* test-checkpw */,
                                DC0BC5D51D8B73B000070CB0 /* perf-checkpw */,
                                6C98082C1E788AEB00E70590 /* CKKSCloudKitTests_mac */,
                                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;
                };
                        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;
                DC71DA011D95BD670065FB93 /* Why is this here? */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                                220179E91E3BF03200EFB6F3 /* dummy.cpp in Sources */,
                                4723C9CC1F152ED30082882F /* SFSQLiteStatement.m in Sources */,
                        files = (
                                220179E91E3BF03200EFB6F3 /* dummy.cpp in Sources */,
                                4723C9CC1F152ED30082882F /* SFSQLiteStatement.m in Sources */,
+                               DC9C95C11F79DD4B000D19E5 /* CKKSControlProtocol.m in Sources */,
                                DCA85B931E8D97E400BA7241 /* client.c 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 */,
                                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 */,
                                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 */,
                                4723C9E11F1540CE0082882F /* SFAnalyticsLogger.m in Sources */,
                                B61577E81F20151C004A3930 /* SecPaddingConfigurations.c in Sources */,
                                DC1789A21D779DF400B50D50 /* SecBreadcrumb.c in Sources */,
+                               DC9C95C21F79DD4D000D19E5 /* CKKSControlProtocol.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                        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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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;
                                DC5F35AC1EE0F27900900966 /* server_entitlement_helpers.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                        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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                DCA4D2171E5684220056214F /* CKKSReencryptOutgoingItemsOperation.m in Sources */,
                                5269658D1E6A154700627F9D /* SecBackupKeybagEntry.m in Sources */,
                                DC52E7D41D80BD1D00B0A59C /* iCloudTrace.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        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;
                DC5ABDC11D832DAB00CF422C /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        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;
                DC8834031D8A218F00CE0ACA /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                                DCB342FA1D8A32A20054D16E /* SecACL.cpp in Sources */,
                                DCB342F91D8A32A20054D16E /* SecAccess.cpp in Sources */,
                                DCB342FB1D8A32A20054D16E /* SecBase.cpp in Sources */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                                DCD66DB61D82050900DB1393 /* SecKey.c in Sources */,
                                DCD66DBC1D82052B00DB1393 /* SecKeyAdaptors.c in Sources */,
                                DCD66DBB1D82052700DB1393 /* SecPolicy.c in Sources */,
                        name = libCMS;
                        targetProxy = DC59E9A81D91C7CC001BDDF5 /* PBXContainerItemProxy */;
                };
                        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 */;
                DC59EA761D91CC5E001BDDF5 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = DC59E9AC1D91C9DC001BDDF5 /* DER_not_installed */;
                        target = DC71D99F1D95BA6C0065FB93 /* ASN1 */;
                        targetProxy = DC71D9E21D95BAD50065FB93 /* PBXContainerItemProxy */;
                };
                        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 */;
                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";
                                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;
                                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";
                                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;
                                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)";
                                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;
                };
                        };
                        name = Debug;
                };
                                INFOPLIST_FILE = keychain/trust/TrustedPeersTests/Info.plist;
                                PRODUCT_BUNDLE_IDENTIFIER = com.apple.TrustedPeersTests;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                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;
                };
                        };
                        name = Release;
                };
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COMBINE_HIDPI_IMAGES = YES;
                                GCC_DYNAMIC_NO_PIC = NO;
                                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;
                                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)",
                                );
                                        "-Wl,-upward_framework,Foundation",
                                        "$(OTHER_LDFLAGS_APPLEIDAUTHSUPPORT)",
                                );
-                               VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Debug;
                };
                        };
                        name = Debug;
                };
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COMBINE_HIDPI_IMAGES = YES;
                                COPY_PHASE_STRIP = NO;
                                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;
                                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)",
                                );
                                        "-Wl,-upward_framework,Foundation",
                                        "$(OTHER_LDFLAGS_APPLEIDAUTHSUPPORT)",
                                );
-                               VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                };
                        };
                        name = Release;
                };
                        };
                        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 = {
                DC5ABDCA1D832DAB00CF422C /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                        };
                        name = Release;
                };
                        };
                        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 = {
                DC82FFE81D90D3F60085674B /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
                        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 = (
                DC5ABDC91D832DAB00CF422C /* Build configuration list for PBXNativeTarget "securitytool_macos" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
                        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 = (
                DC82FFE71D90D3F60085674B /* Build configuration list for PBXAggregateTarget "security_utilities_DTrace" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
index 7ed8e0dd6dfce41e6f8e5e4cc7a1898c4f7722e6..5bfe9cf0715bf73518e1b5e9c41d4e6636898deb 100644 (file)
@@ -42,6 +42,7 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      enableAddressSanitizer = "YES"
       language = ""
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       language = ""
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
index 1996fb9829fbcea630c01ce4381c1f6ca1439582..6a53dbecd84bc1d4c1aaaab8445681cd5e1a79fc 100644 (file)
@@ -40,6 +40,7 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
       shouldUseLaunchSchemeArgsEnv = "YES">
       <Testables>
          <TestableReference
       shouldUseLaunchSchemeArgsEnv = "YES">
       <Testables>
          <TestableReference
@@ -69,6 +70,7 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       ignoresPersistentStateOnLaunch = "NO"
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       ignoresPersistentStateOnLaunch = "NO"
             isEnabled = "NO">
          </CommandLineArgument>
          <CommandLineArgument
             isEnabled = "NO">
          </CommandLineArgument>
          <CommandLineArgument
-            argument = "secd_668_ghosts"
+            argument = "secd_68_ghosts"
             isEnabled = "NO">
          </CommandLineArgument>
          <CommandLineArgument
             isEnabled = "NO">
          </CommandLineArgument>
          <CommandLineArgument
index 047e08d81de3d67ec2578f646163f197ebd8fdb1..98f7fd74a6b00317f1428b47d44d362481dabeb4 100644 (file)
@@ -46,25 +46,10 @@ CF_IMPLICIT_BRIDGING_ENABLED
 CFTypeID SecAccessControlGetTypeID(void)
 __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
 
 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) {
 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.
     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.
@@ -72,7 +57,6 @@ typedef CF_OPTIONS(CFOptionFlags, SecAccessControlCreateFlags) {
     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);
 
     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
 
 /*!
  @function SecAccessControlCreateWithFlags
index 80028dc074a09dfb247045436efa5944faada657..fe66f3eb4e908eb4cb0f1153ec7d1abe9106a06a 100644 (file)
@@ -364,6 +364,7 @@ extern const CFStringRef kSecAttrViewHintEngram;
 extern const CFStringRef kSecAttrViewHintManatee;
 extern const CFStringRef kSecAttrViewHintAutoUnlock;
 extern const CFStringRef kSecAttrViewHintHealth;
 extern const CFStringRef kSecAttrViewHintManatee;
 extern const CFStringRef kSecAttrViewHintAutoUnlock;
 extern const CFStringRef kSecAttrViewHintHealth;
+extern const CFStringRef kSecAttrViewHintApplePay;
 
 
 #if SEC_OS_IPHONE
 
 
 #if SEC_OS_IPHONE
index b18737ba28ff38780da04222958b2dc0311c8206..85467aa661fb463514bb187583f40ced07276302 100644 (file)
@@ -830,7 +830,7 @@ __OSX_AVAILABLE(10.12) __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AV
 
 /*!
     @function SecKeyGetBlockSize
 
 /*!
     @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
     @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
index 9e6129b49db36c3e5b7c278e668ee4433c2eec8f..95f1e88e3bc64d070cab1c32baac98cf8445e9c7 100644 (file)
@@ -80,7 +80,7 @@ extern NSString* const SecCKKSAPSNamedPort;
 
 /* Item CKRecords */
 extern NSString* const SecCKRecordItemType;
 
 /* 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 SecCKRecordEncryptionVersionKey;
 extern NSString* const SecCKRecordParentKeyRefKey;
 extern NSString* const SecCKRecordDataKey;
@@ -97,6 +97,20 @@ extern NSString* const SecCKRecordKeyClassKey;
 //extern NSString* const SecCKRecordWrappedKeyKey;
 //extern NSString* const SecCKRecordParentKeyRefKey;
 
 //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 Key CKRecord Keys */
 extern NSString* const SecCKRecordCurrentKeyType;
 // The key class will be the record name.
@@ -105,7 +119,7 @@ extern NSString* const SecCKRecordCurrentKeyType;
 /* Current Item CKRecord Keys */
 extern NSString* const SecCKRecordCurrentItemType;
 extern NSString* const SecCKRecordItemRefKey;
 /* 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;
 
 /* Device State CKRexord Keys */
 extern NSString* const SecCKRecordDeviceStateType;
@@ -157,6 +171,13 @@ extern CKKSZoneKeyState* const SecCKKSZoneKeyStateUnhealthy;
 extern CKKSZoneKeyState* const SecCKKSZoneKeyStateBadCurrentPointers;
 // Something has gone wrong creating new TLKs.
 extern CKKSZoneKeyState* const SecCKKSZoneKeyStateNewTLKsFailed;
 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.
 // Fatal error. Will not proceed unless fixed from outside class.
 extern CKKSZoneKeyState* const SecCKKSZoneKeyStateError;
 // This CKKS instance has been cancelled.
@@ -179,6 +200,9 @@ extern NSString* const SecCKKSAggdItemReencryption;
 
 extern NSString* const SecCKKSUserDefaultsSuite;
 
 
 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
 /* Queue limits: these should likely be configurable via plist */
 #define SecCKKSOutgoingQueueItemsAtOnce 100
 #define SecCKKSIncomingQueueItemsAtOnce 10
@@ -211,6 +235,10 @@ bool SecCKKSEnforceManifests(void);
 bool SecCKKSEnableEnforceManifests(void);
 bool SecCKKSSetEnforceManifests(bool value);
 
 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);
 // Testing support
 bool SecCKKSTestsEnabled(void);
 bool SecCKKSTestsEnable(void);
@@ -232,7 +260,49 @@ SecServerCreateCKKSEndpoint(void);
 
 // TODO: handle errors better
 typedef CF_ENUM(CFIndex, CKKSErrorCode) {
 
 // TODO: handle errors better
 typedef CF_ENUM(CFIndex, CKKSErrorCode) {
+    CKKSNotLoggedIn = 10,
+    CKKSNoSuchView = 11,
+
+    CKKSRemoteItemChangePending = 12,
+    CKKSLocalItemChangePending = 13,
+    CKKSItemChanged = 14,
     CKKSNoUUIDOnItem = 15,
     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) \
 };
 
 #define SecTranslateError(nserrorptr, cferror) \
index 324de08c451e081c3e3e7c0f5c93db97444fae12..d5da4e839cd175996302eed6ecca3bbb660f5e30 100644 (file)
@@ -64,7 +64,7 @@ NSString* const SecCKKSSubscriptionID = @"keychain-changes";
 NSString* const SecCKKSAPSNamedPort = @"com.apple.securityd.aps";
 
 NSString* const SecCKRecordItemType = @"item";
 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 SecCKRecordEncryptionVersionKey = @"encver";
 NSString* const SecCKRecordDataKey = @"data";
 NSString* const SecCKRecordParentKeyRefKey = @"parentkeyref";
@@ -79,6 +79,16 @@ NSString* const SecCKRecordServerWasCurrent = @"server_wascurrent";
 NSString* const SecCKRecordIntermediateKeyType = @"synckey";
 NSString* const SecCKRecordKeyClassKey = @"class";
 
 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";
 NSString* const SecCKRecordCurrentKeyType = @"currentkey";
 
 NSString* const SecCKRecordCurrentItemType = @"currentitem";
@@ -119,24 +129,31 @@ CKKSZoneKeyState* const SecCKKSZoneKeyStateWaitForUnlock = (CKKSZoneKeyState*) @
 CKKSZoneKeyState* const SecCKKSZoneKeyStateUnhealthy = (CKKSZoneKeyState*) @"unhealthy";
 CKKSZoneKeyState* const SecCKKSZoneKeyStateBadCurrentPointers = (CKKSZoneKeyState*) @"badcurrentpointers";
 CKKSZoneKeyState* const SecCKKSZoneKeyStateNewTLKsFailed = (CKKSZoneKeyState*) @"newtlksfailed";
 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 = @{
 
 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;
         };
     });
     return map;
@@ -182,6 +199,9 @@ NSString* const SecCKKSAggdItemReencryption = @"com.apple.security.ckks.reencryp
 
 NSString* const SecCKKSUserDefaultsSuite = @"com.apple.security.ckks";
 
 
 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;
 #if OCTAGON
 static bool enableCKKS = true;
 static bool testCKKS = false;
@@ -259,6 +279,31 @@ bool SecCKKSSetEnforceManifests(bool value) {
     return CKKSEnforceManifests;
 }
 
     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) {
 // Feature flags to twiddle behavior for tests
 static bool CKKSDisableAutomaticUUID = false;
 bool SecCKKSTestDisableAutomaticUUID(void) {
index bacbba6c8fe1616606041122ef7e15b8fdaea771..6d249200741d52aa5f5bb75cdbae2f288d2401d0 100644 (file)
@@ -34,6 +34,7 @@ typedef NSString<CKKSAnalyticsFailableEvent> CKKSAnalyticsFailableEvent;
 extern CKKSAnalyticsFailableEvent* const CKKSEventProcessIncomingQueueClassA;
 extern CKKSAnalyticsFailableEvent* const CKKSEventProcessIncomingQueueClassC;
 extern CKKSAnalyticsFailableEvent* const CKKSEventUploadChanges;
 extern CKKSAnalyticsFailableEvent* const CKKSEventProcessIncomingQueueClassA;
 extern CKKSAnalyticsFailableEvent* const CKKSEventProcessIncomingQueueClassC;
 extern CKKSAnalyticsFailableEvent* const CKKSEventUploadChanges;
+extern CKKSAnalyticsFailableEvent* const CKKSEventStateError;
 
 @protocol CKKSAnalyticsSignpostEvent
 @end
 
 @protocol CKKSAnalyticsSignpostEvent
 @end
@@ -46,8 +47,8 @@ extern CKKSAnalyticsSignpostEvent* const CKKSEventItemAddedToOutgoingQueue;
 + (instancetype)logger;
 
 - (void)logSuccessForEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view;
 + (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;
 
 
 - (void)noteEvent:(CKKSAnalyticsSignpostEvent*)event inView:(CKKSKeychainView*)view;
 
index a87a5d084261c5232a51e87824c40a6cd420e999..952ae55c58d957727a918acc94b4af0ca7ee81b6 100644 (file)
 #import "Analytics/SFAnalyticsLogger.h"
 #import <os/log.h>
 
 #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 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";
 
 CKKSAnalyticsSignpostEvent* const CKKSEventPushNotificationReceived = (CKKSAnalyticsSignpostEvent*)@"CKKSEventPushNotificationReceived";
 CKKSAnalyticsSignpostEvent* const CKKSEventItemAddedToOutgoingQueue = (CKKSAnalyticsSignpostEvent*)@"CKKSEventItemAddedToOutgoingQueue";
@@ -70,24 +73,40 @@ CKKSAnalyticsSignpostEvent* const CKKSEventItemAddedToOutgoingQueue = (CKKSAnaly
     [self setDateProperty:[NSDate date] forKey:[NSString stringWithFormat:@"last_success_%@-%@", view.zoneName, event]];
 }
 
     [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
 }
 
 - (void)noteEvent:(CKKSAnalyticsSignpostEvent*)event inView:(CKKSKeychainView*)view
@@ -103,7 +122,14 @@ CKKSAnalyticsSignpostEvent* const CKKSEventItemAddedToOutgoingQueue = (CKKSAnaly
 - (NSDictionary*)extraValuesToUploadToServer
 {
     NSMutableDictionary* values = [NSMutableDictionary dictionary];
 - (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];
         CKKSKeychainView* view = [CKKSViewManager findOrCreateView:viewName];
         NSDate* dateOfLastSyncClassA = [self dateOfLastSuccessForEvent:CKKSEventProcessIncomingQueueClassA inView:view];
         NSDate* dateOfLastSyncClassC = [self dateOfLastSuccessForEvent:CKKSEventProcessIncomingQueueClassC inView:view];
@@ -131,7 +157,7 @@ CKKSAnalyticsSignpostEvent* const CKKSEventItemAddedToOutgoingQueue = (CKKSAnaly
         values[incomingQueueIsErrorFreeKey] = @(incomingQueueIsErrorFree);
         values[outgoingQueueIsErrorFreeKey] = @(outgoingQueueIsErrorFree);
 
         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);
     }
         NSString* inSyncKey = [NSString stringWithFormat:@"%@-%@", viewName, CKKSAnalyticsInSync];
         values[inSyncKey] = @(weThinkWeAreInSync);
     }
index b676664d463d8d7e7fe8af8d7175e3682ec2be46..49d8de91e97b68c507c5a1d00b95933de41bb437 100644 (file)
@@ -61,9 +61,9 @@ typedef NS_ENUM(NSInteger, CKKSAccountStatus) {
 @property SOSCCStatus currentCircleStatus;
 
 // Fetched and memoized from CloudKit; we can't afford deadlocks with their callbacks
 @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;
 
 // Fetched and memoized from the Account when we're in-circle; our threading model is strange
 @property NSString*      accountCirclePeerID;
index 4d2e2d922ffa7e6c3e3fd42812859166fae03c1d..e29784cc7375c2cf68a214193aad647739aa8ca5 100644 (file)
     dispatch_semaphore_t finishedSema = dispatch_semaphore_create(0);
 
     SOSCCStatus circleStatus = [CKKSCKAccountStateTracker getCircleStatus];
     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;
 
     dispatch_sync(self.queue, ^{
         self.firstSOSCircleFetch = true;
 
         }];
     } else {
         // Not in-circle, reset circle ID
         }];
     } 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];
         self.accountCirclePeerID = nil;
         self.accountCirclePeerIDError = nil;
         self.accountCirclePeerIDInitialized = [[CKKSCondition alloc] init];
diff --git a/keychain/ckks/CKKSControl.h b/keychain/ckks/CKKSControl.h
new file mode 100644 (file)
index 0000000..29172c6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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__
diff --git a/keychain/ckks/CKKSControl.m b/keychain/ckks/CKKSControl.m
new file mode 100644 (file)
index 0000000..276c41b
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * 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__
index 0cb2bd696f38209281f8e8de4ac2e93ae0e2d0f1..10df4da8e4953d0a99272ae5536605d1444a4506 100644 (file)
@@ -71,8 +71,17 @@ NSXPCInterface* CKKSSetupControlProtocol(NSXPCInterface* interface) {
         char *classes[] = {
             "CKPrettyError",
             "CKRecordID",
         char *classes[] = {
             "CKPrettyError",
             "CKRecordID",
+            "NSArray",
+            "NSData",
+            "NSDate",
+            "NSDictionary",
+            "NSError",
+            "NSNull",
+            "NSNumber",
+            "NSOrderedSet",
+            "NSSet",
+            "NSString",
             "NSURL",
             "NSURL",
-            "NSError"
         };
 
         for (unsigned n = 0; n < sizeof(classes)/sizeof(classes[0]); n++) {
         };
 
         for (unsigned n = 0; n < sizeof(classes)/sizeof(classes[0]); n++) {
@@ -87,6 +96,7 @@ NSXPCInterface* CKKSSetupControlProtocol(NSXPCInterface* interface) {
         [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(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];
         [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];
index 694118fcc251b06d5d78410d53212201794f7a12..1008cf0e0b910a5067f5509eb188c04db479c9b2 100644 (file)
@@ -46,6 +46,7 @@
 
 + (NSArray<CKKSCurrentItemPointer*>*)remoteItemPointers: (CKRecordZoneID*)zoneID error:(NSError * __autoreleasing *)error;
 + (bool) deleteAll:(CKRecordZoneID*)zoneID error:(NSError * __autoreleasing *) error;
 
 + (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
 
 
 @end
 
index 1d4203fb67230f55ebbc41f9a6aa8e5386d3dff7..646a52914fee2b2e85cb82e016241bf523b6e167 100644 (file)
     return [self allWhere: @{@"state":  SecCKKSProcessedStateRemote, @"ckzone":zoneID.zoneName} error:error];
 }
 
     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];
 
 + (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 {
 }
 
 - (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 {
 }
 
 - (NSDictionary<NSString*,NSString*>*)sqlValues {
index a26b2b42c9e85001c7f2d4402d896c2582e3fe2f..b55efcfbe99e567147dc8e4934ed13cc0b25429d 100644 (file)
@@ -26,6 +26,8 @@
 #import "keychain/ckks/CKKS.h"
 #import "keychain/ckks/CKKSItem.h"
 #import "keychain/ckks/CKKSKey.h"
 #import "keychain/ckks/CKKS.h"
 #import "keychain/ckks/CKKSItem.h"
 #import "keychain/ckks/CKKSKey.h"
+#import "keychain/ckks/CKKSTLKShare.h"
+
 
 #if OCTAGON
 
 
 #if OCTAGON
 
@@ -58,6 +60,8 @@
 @property CKKSCurrentKeyPointer* currentClassAPointer;
 @property CKKSCurrentKeyPointer* currentClassCPointer;
 
 @property CKKSCurrentKeyPointer* currentClassAPointer;
 @property CKKSCurrentKeyPointer* currentClassCPointer;
 
+@property NSArray<CKKSTLKShare*>* tlkShares;
+
 -(instancetype)init;
 -(instancetype)initForZone:(CKRecordZoneID*)zoneID;
 @end
 -(instancetype)init;
 -(instancetype)initForZone:(CKRecordZoneID*)zoneID;
 @end
index c80520c5f28b8b716899889dc12959e14ec0152e..77e826bb7909839b688831e50642bd75754c8199 100644 (file)
         _classA = _currentClassAPointer.currentKeyUUID ? [CKKSKey tryFromDatabase:_currentClassAPointer.currentKeyUUID zoneID:zoneID error:&error] : nil;
         _classC = _currentClassCPointer.currentKeyUUID ? [CKKSKey tryFromDatabase:_currentClassCPointer.currentKeyUUID zoneID:zoneID error:&error] : nil;
 
         _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;
         _error = error;
+
     }
 
     return self;
     }
 
     return self;
index bdf3f14cf9006a5b42778df73a873e0bf7fc4f15..bf98cb51242996729afb95afad0fd90dd3524471 100644 (file)
 
                 NSMutableDictionary<NSString*, NSMutableArray*>* changedRecordsDict = [[NSMutableDictionary alloc] init];
 
 
                 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
                     // 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[SecCKRecordItemType]];
                     [strongSelf _onqueueRecordsChanged:changedRecordsDict[SecCKRecordCurrentItemType]];
                     [strongSelf _onqueueRecordsChanged:changedRecordsDict[SecCKRecordDeviceStateType]];
+                    [strongSelf _onqueueRecordsChanged:changedRecordsDict[SecCKRecordTLKShareType]];
 
                     [strongSelf _onqueueProcessRecordDeletions];
                     [strongSelf _onqueueScanForExtraneousLocalItems];
 
                     [strongSelf _onqueueProcessRecordDeletions];
                     [strongSelf _onqueueScanForExtraneousLocalItems];
diff --git a/keychain/ckks/CKKSFixups.h b/keychain/ckks/CKKSFixups.h
new file mode 100644 (file)
index 0000000..2346dba
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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
diff --git a/keychain/ckks/CKKSFixups.m b/keychain/ckks/CKKSFixups.m
new file mode 100644 (file)
index 0000000..ee99c72
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * 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
index f366093b8c203472c7841fff59d71abd0c6efe58..f0caf654965194908f121ae7ec919887019c1852 100644 (file)
 
 #import <Foundation/Foundation.h>
 #include <dispatch/dispatch.h>
 
 #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;
 
 @interface CKKSGroupOperation : CKKSResultOperation {
     BOOL executing;
index 443ef471c817192ba4517674c6b87e0e0547fc6f..727a12f2939021eea3ed4cf2933ed7f04ca7e0b6 100644 (file)
  */
 
 #import "CKKSGroupOperation.h"
  */
 
 #import "CKKSGroupOperation.h"
-#import "CKKSCondition.h"
 #include <utilities/debugging.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()
 @interface CKKSGroupOperation()
+@property bool fillInError;
 @property NSBlockOperation* startOperation;
 @property NSBlockOperation* finishOperation;
 @property NSBlockOperation* startOperation;
 @property NSBlockOperation* finishOperation;
+@property dispatch_queue_t queue;
 
 @property NSMutableArray<CKKSResultOperation*>* internalSuccesses;
 @end
 
 
 @property NSMutableArray<CKKSResultOperation*>* internalSuccesses;
 @end
 
-
 @implementation CKKSGroupOperation
 
 - (instancetype)init {
     if(self = [super init]) {
         __weak __typeof(self) weakSelf = self;
 
 @implementation CKKSGroupOperation
 
 - (instancetype)init {
     if(self = [super init]) {
         __weak __typeof(self) weakSelf = self;
 
+        _fillInError = true;
+
         _operationQueue = [[NSOperationQueue alloc] init];
         _internalSuccesses = [[NSMutableArray alloc] init];
 
         _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;
         // 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 {
 - (BOOL)isPending {
-    return [self.startOperation isPending];
+    return [self.startOperation isPending] && ![self isCancelled];
 }
 
 - (void)setName:(NSString*) name {
 }
 
 - (void)setName:(NSString*) name {
 }
 
 - (BOOL)isExecuting {
 }
 
 - (BOOL)isExecuting {
-    return self->executing;
+    __block BOOL ret = FALSE;
+    dispatch_sync(self.queue, ^{
+        ret = self->executing;
+    });
+    return ret;
 }
 
 - (BOOL)isFinished {
 }
 
 - (BOOL)isFinished {
-    return self->finished;
+    __block BOOL ret = FALSE;
+    dispatch_sync(self.queue, ^{
+        ret = self->finished;
+    });
+    return ret;
 }
 
 - (void)start {
 }
 
 - (void)start {
+    [self invalidateTimeout];
+
     if([self isCancelled]) {
         [self willChangeValueForKey:@"isFinished"];
     if([self isCancelled]) {
         [self willChangeValueForKey:@"isFinished"];
-        finished = YES;
+        dispatch_sync(self.queue, ^{
+            self->finished = YES;
+        });
         [self didChangeValueForKey:@"isFinished"];
         return;
     }
         [self didChangeValueForKey:@"isFinished"];
         return;
     }
     [self.operationQueue addOperation: self.startOperation];
 
     [self willChangeValueForKey:@"isExecuting"];
     [self.operationQueue addOperation: self.startOperation];
 
     [self willChangeValueForKey:@"isExecuting"];
-    executing = YES;
+    dispatch_sync(self.queue, ^{
+        self->executing = YES;
+    });
     [self didChangeValueForKey:@"isExecuting"];
 }
 
 - (void)cancel {
     [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];
 
 
     [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"];
 
 }
 
 - (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"];
 
     [self didChangeValueForKey:@"isExecuting"];
     [self didChangeValueForKey:@"isFinished"];
 - (void)runBeforeGroupFinished: (NSOperation*) suboperation {
     if([self isCancelled]) {
         // Cancelled operations can't add anything.
 - (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;
     }
 
         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
     // 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];
         }
     }
             [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
 }
 
 @end
index 8bdd550ecfa5bd8ecc9bb94ee6750f8c89131578..52df80c46b7070cd899392c029bf801bbfd276fd 100644 (file)
@@ -70,7 +70,7 @@
     }
 
     // Synchronous, on some thread. Get back on the CKKS queue for SQL thread-safety.
     }
 
     // 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;
         if(self.cancelled) {
             ckksnotice("ckksheal", ckks, "CKKSHealKeyHierarchyOperation cancelled, quitting");
             return false;
@@ -96,6 +96,7 @@
         //   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)
         //   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 && keyset.currentClassAPointer && keyset.currentClassCPointer &&
 
 
         if(keyset.currentTLKPointer.currentKeyUUID == nil || keyset.currentClassAPointer.currentKeyUUID == nil || keyset.currentClassCPointer.currentKeyUUID == nil ||
 
 
         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];
 
             // 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);
                 // 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.
             }
 
             // 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]) {
 
             // 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;
                     }
 
                         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;
                     }
                         keyset.currentClassCPointer.currentKeyUUID = key.uuid;
                         changedCurrentClassC = true;
                     }
                 [recordsToSave addObject: [keyset.currentClassCPointer CKRecordWithZoneID: ckks.zoneID]];
             }
 
                 [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
             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;
 
 
             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;
             // Get the CloudKit operation ready...
             modifyRecordsOp = [[CKModifyRecordsOperation alloc] initWithRecordsToSave:recordsToSave recordIDsToDelete:recordIDsToDelete];
             modifyRecordsOp.atomic = YES;
                         [keyset.currentClassAPointer saveToDatabase: &localerror];
                         [keyset.currentClassCPointer saveToDatabase: &localerror];
 
                         [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];
                         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);
                     } 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;
                         [strongCKKS _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateNewTLKsFailed withError: nil];
                     }
                     return true;
diff --git a/keychain/ckks/CKKSHealTLKSharesOperation.h b/keychain/ckks/CKKSHealTLKSharesOperation.h
new file mode 100644 (file)
index 0000000..5d1e295
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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
diff --git a/keychain/ckks/CKKSHealTLKSharesOperation.m b/keychain/ckks/CKKSHealTLKSharesOperation.m
new file mode 100644 (file)
index 0000000..97d0cb2
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * 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
index 80a7235da55beebede02c564c506339175219dab..e4787234da776346dbc5744ceca15105fd4f52a7 100644 (file)
@@ -21,6 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #import "CKKSSQLDatabaseObject.h"
 #import "CKKSItem.h"
 #import "CKKSMirrorEntry.h"
 #import "CKKSSQLDatabaseObject.h"
 #import "CKKSItem.h"
 #import "CKKSMirrorEntry.h"
@@ -29,7 +31,7 @@
 
 #ifndef CKKSIncomingQueueEntry_h
 #define CKKSIncomingQueueEntry_h
 
 #ifndef CKKSIncomingQueueEntry_h
 #define CKKSIncomingQueueEntry_h
-#if OCTAGON
+
 
 #import <CloudKit/CloudKit.h>
 
 
 #import <CloudKit/CloudKit.h>
 
index 39a711a5ed21d34737c202805412f6f34ad9c5a1..acd12e9c3ee2f94fdb2087c060cf12f87aec43d4 100644 (file)
@@ -21,6 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #include <AssertMacros.h>
 
 #import <Foundation/Foundation.h>
 #include <AssertMacros.h>
 
 #import <Foundation/Foundation.h>
@@ -31,8 +33,6 @@
 #include <securityd/SecDbItem.h>
 #include <securityd/SecItemSchema.h>
 
 #include <securityd/SecDbItem.h>
 #include <securityd/SecItemSchema.h>
 
-#if OCTAGON
-
 #import <CloudKit/CloudKit.h>
 #import "CKKSIncomingQueueEntry.h"
 #import "CKKSItemEncrypter.h"
 #import <CloudKit/CloudKit.h>
 #import "CKKSIncomingQueueEntry.h"
 #import "CKKSItemEncrypter.h"
index e840c536ed0578c14faf1d7afa0cf0a615b42c4e..dd192ba14203889464ce6456f0d501fadc073999 100644 (file)
@@ -75,7 +75,7 @@
                     return;
                 }
 
                     return;
                 }
 
-                [strongCKKS dispatchSyncWithAccountQueue:^bool{
+                [strongCKKS dispatchSyncWithAccountKeys:^bool{
                     strongCKKS.latestManifest = [CKKSManifest latestTrustedManifestForZone:strongCKKS.zoneName error:&error];
                     if (error) {
                         strongSelf.error = error;
                     strongCKKS.latestManifest = [CKKSManifest latestTrustedManifestForZone:strongCKKS.zoneName error:&error];
                     if (error) {
                         strongSelf.error = error;
         return;
     }
 
         return;
     }
 
-    [ckks dispatchSyncWithAccountQueue: ^bool{
+    [ckks dispatchSyncWithAccountKeys: ^bool{
         if(self.cancelled) {
             ckksnotice("ckksincoming", ckks, "CKKSIncomingQueueOperation cancelled, quitting");
             return false;
         if(self.cancelled) {
             ckksnotice("ckksincoming", ckks, "CKKSIncomingQueueOperation cancelled, quitting");
             return false;
index 8f6e39a1d66dba4148b6a46ed5cf4ab50507c31c..a0aeb0674e4a3e8a3c64fde6242b825e86dc57a7 100644 (file)
@@ -21,6 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #import "keychain/ckks/CKKS.h"
 #import "keychain/ckks/CKKSSQLDatabaseObject.h"
 #import "keychain/ckks/CKKSRecordHolder.h"
 #import "keychain/ckks/CKKS.h"
 #import "keychain/ckks/CKKSSQLDatabaseObject.h"
 #import "keychain/ckks/CKKSRecordHolder.h"
@@ -30,8 +32,6 @@
 #ifndef CKKSItem_h
 #define CKKSItem_h
 
 #ifndef CKKSItem_h
 #define CKKSItem_h
 
-#if OCTAGON
-
 #import <CloudKit/CloudKit.h>
 
 @class CKKSWrappedAESSIVKey;
 #import <CloudKit/CloudKit.h>
 
 @class CKKSWrappedAESSIVKey;
index 00f87879c124232d5c6c74c311d7b384f516d635..c883c189f3d0f2e455cf7e1e50c382d731cd494f 100644 (file)
@@ -21,6 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #include <AssertMacros.h>
 
 #import <Foundation/Foundation.h>
 #include <AssertMacros.h>
 
 #import <Foundation/Foundation.h>
@@ -31,8 +33,6 @@
 #include <securityd/SecDbItem.h>
 #include <securityd/SecItemSchema.h>
 
 #include <securityd/SecDbItem.h>
 #include <securityd/SecItemSchema.h>
 
-#if OCTAGON
-
 #import <CloudKit/CloudKit.h>
 #import <CloudKit/CloudKit_Private.h>
 
 #import <CloudKit/CloudKit.h>
 #import <CloudKit/CloudKit_Private.h>
 
@@ -203,7 +203,7 @@ plaintextPCSServiceIdentifier: (NSNumber*) pcsServiceIdentifier
 
     // subtly improve osversion (but it's okay if that does nothing)
     NSString* finalversion = [platform stringByAppendingString: [osversion stringByReplacingOccurrencesOfString:@"Version" withString:@""]];
 
     // 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 {
 }
 
 - (CKRecord*) updateCKRecord: (CKRecord*) record zoneID: (CKRecordZoneID*) zoneID {
@@ -351,7 +351,7 @@ plaintextPCSServiceIdentifier: (NSNumber*) pcsServiceIdentifier
         if(record) {
             for(NSString* key in record.allKeys) {
                 if([key isEqualToString:@"UUID"] ||
         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] ||
                    [key isEqualToString:SecCKRecordDataKey] ||
                    [key isEqualToString:SecCKRecordWrappedKeyKey] ||
                    [key isEqualToString:SecCKRecordGenerationCountKey] ||
index ce0dedf611bee0eed76c815d2a720f9fc76ed28e..5fe452bf1e3fb122f61800e69132799ed97c6903 100644 (file)
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #import <Foundation/Foundation.h>
 
 #import "keychain/ckks/CKKSItem.h"
 #import "keychain/ckks/CKKSSIV.h"
 
 #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
 
 
 @interface CKKSKey : CKKSItem
 
 // Attempts to unwrap this key via unwrapping its wrapping keys via the key hierarchy.
 - (CKKSAESSIVKey*)unwrapViaKeyHierarchy: (NSError * __autoreleasing *) error;
 
 // 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;
 
 - (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*)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
 
 + (NSDictionary<NSString*,NSNumber*>*)countsByClass:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error;
 @end
index a967f10e6cb199903ce04cbab30c58d536ee4c19..231f2c7c595d6693cf495fe2763b5a6d9fd23323 100644 (file)
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #import "CKKSViewManager.h"
 #import "CKKSKeychainView.h"
 #import "CKKSCurrentKeyPointer.h"
 #import "CKKSKey.h"
 #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>
 
 #include <securityd/SecItemSchema.h>
 #include <Security/SecItem.h>
 #include <Security/SecItemPriv.h>
 
-#if OCTAGON
-
 #include <CloudKit/CloudKit.h>
 #include <CloudKit/CloudKit_Private.h>
 
 #include <CloudKit/CloudKit.h>
 #include <CloudKit/CloudKit_Private.h>
 
     return self.aessivkey;
 }
 
     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];
 - (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 {
 #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,
             NSStringFromClass([self class]),
             self.zoneID.zoneName,
             self.uuid,
             self.keyclass,
             self.state,
-            self.currentkey,
-            &self];
+            self.currentkey];
 }
 
 #pragma mark - CKKSSQLDatabaseObject methods
 }
 
 #pragma mark - CKKSSQLDatabaseObject methods
     return keyCopy;
 }
 
     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
 @end
 
 #endif // OCTAGON
index f9d7de0013f0ace4879fa4740a2403de92606e15..f10395e0c033b0c14a00e234bf2079c955449b37 100644 (file)
@@ -51,6 +51,8 @@
 #import "keychain/ckks/CKKSZone.h"
 #import "keychain/ckks/CKKSZoneChangeFetcher.h"
 #import "keychain/ckks/CKKSNotifier.h"
 #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"
 
 
 #include "CKKS.h"
 
@@ -70,7 +72,9 @@
 @class CKKSOutgoingQueueEntry;
 @class CKKSZoneChangeFetcher;
 
 @class CKKSOutgoingQueueEntry;
 @class CKKSZoneChangeFetcher;
 
-@interface CKKSKeychainView : CKKSZone <CKKSZoneUpdateReceiver, CKKSChangeFetcherErrorOracle> {
+@interface CKKSKeychainView : CKKSZone <CKKSZoneUpdateReceiver,
+                                        CKKSChangeFetcherErrorOracle,
+                                        CKKSPeerUpdateListener> {
     CKKSZoneKeyState* _keyHierarchyState;
 }
 
     CKKSZoneKeyState* _keyHierarchyState;
 }
 
 @property CKKSReencryptOutgoingItemsOperation*    lastReencryptOutgoingItemsOperation;
 @property CKKSScanLocalItemsOperation*            lastScanLocalItemsOperation;
 @property CKKSSynchronizeOperation*               lastSynchronizeOperation;
 @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;
 
 /* 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;
 
 /* 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
 - (instancetype)initWithContainer:     (CKContainer*) container
                              zoneName: (NSString*) zoneName
                        accountTracker:(CKKSCKAccountStateTracker*) accountTracker
                      lockStateTracker:(CKKSLockStateTracker*) lockStateTracker
                      savedTLKNotifier:(CKKSNearFutureScheduler*) savedTLKNotifier
+                         peerProvider:(id<CKKSPeerProvider>)peerProvider
  fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
  fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
+           fetchRecordsOperationClass: (Class<CKKSFetchRecordsOperation>)fetchRecordsOperationClass
+                  queryOperationClass:(Class<CKKSQueryOperation>)queryOperationClass
     modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
       modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
                    apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
     modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
       modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
                    apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
 
 - (CKKSKey*) keyForItem: (SecDbItemRef) item error: (NSError * __autoreleasing *) error;
 
 
 - (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 */
 
 
 /* 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
 
 // 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;
 
 
 - (CKKSSynchronizeOperation*) resyncWithCloud;
 
 // Use these helper methods to make sure those exist.
 - (void) dispatchAsync: (bool (^)(void)) block;
 - (void) dispatchSync: (bool (^)(void)) block;
 // 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 */
 
 
 /* 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;
 
 - (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;
 - (bool)_onQueueUpdateLatestManifestWithError:(NSError**)error;
 
 - (CKKSDeviceStateEntry*)_onqueueCurrentDeviceStateEntry: (NSError* __autoreleasing*)error;
index 0f7416717931d2855d71e785cd755ffb1f1ba110..1906df763fab652d315fbe9105dbe6472a7e4660 100644 (file)
@@ -53,6 +53,7 @@
 #import "CKKSManifest.h"
 #import "CKKSManifestLeafRecord.h"
 #import "CKKSZoneChangeFetcher.h"
 #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/CKKSDeviceStateEntry.h"
 #import "keychain/ckks/CKKSNearFutureScheduler.h"
 #import "keychain/ckks/CKKSCurrentItemPointer.h"
@@ -61,6 +62,8 @@
 #import "keychain/ckks/CKKSLockStateTracker.h"
 #import "keychain/ckks/CKKSNotifier.h"
 #import "keychain/ckks/CloudKitCategories.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>
 
 #include <utilities/SecCFWrappers.h>
 #include <utilities/SecDb.h>
 @property CKKSResultOperation* processIncomingQueueAfterNextUnlockOperation;
 
 @property NSMutableDictionary<NSString*, SecBoolNSErrorCallback>* pendingSyncCallbacks;
 @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
 
 @end
 #endif
 
                        accountTracker:(CKKSCKAccountStateTracker*) accountTracker
                      lockStateTracker:(CKKSLockStateTracker*) lockStateTracker
                      savedTLKNotifier:(CKKSNearFutureScheduler*) savedTLKNotifier
                        accountTracker:(CKKSCKAccountStateTracker*) accountTracker
                      lockStateTracker:(CKKSLockStateTracker*) lockStateTracker
                      savedTLKNotifier:(CKKSNearFutureScheduler*) savedTLKNotifier
+                     peerProvider:(id<CKKSPeerProvider>)peerProvider
  fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
  fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
+           fetchRecordsOperationClass: (Class<CKKSFetchRecordsOperation>)fetchRecordsOperationClass
+                  queryOperationClass:(Class<CKKSQueryOperation>)queryOperationClass
     modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
       modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
                    apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
     modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
       modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
                    apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
                               zoneName:zoneName
                         accountTracker:accountTracker
   fetchRecordZoneChangesOperationClass:fetchRecordZoneChangesOperationClass
                               zoneName:zoneName
                         accountTracker:accountTracker
   fetchRecordZoneChangesOperationClass:fetchRecordZoneChangesOperationClass
+            fetchRecordsOperationClass:fetchRecordsOperationClass
+                   queryOperationClass:queryOperationClass
      modifySubscriptionsOperationClass:modifySubscriptionsOperationClass
        modifyRecordZonesOperationClass:modifyRecordZonesOperationClass
                     apsConnectionClass:apsConnectionClass]) {
      modifySubscriptionsOperationClass:modifySubscriptionsOperationClass
        modifyRecordZonesOperationClass:modifyRecordZonesOperationClass
                     apsConnectionClass:apsConnectionClass]) {
                                                         keepProcessAlive:true
                                                                    block:^{
                                                                        __strong __typeof(self) strongSelf = weakSelf;
                                                         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.
                                                                        [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;
 
         _lockStateTracker = lockStateTracker;
         _savedTLKNotifier = savedTLKNotifier;
+        _currentPeerProvider = peerProvider;
+        [_currentPeerProvider registerForPeerChangeUpdates:self];
 
         _setupSuccessful = false;
 
 
         _setupSuccessful = false;
 
         _keyStateFetchRequested = false;
         _keyStateProcessRequested = 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]
 
         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.
         }
 
         // 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.
             CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state: strongSelf.zoneName];
 
             // Check if we believe we've synced this zone before.
             } else {
                 // Likely a restart of securityd!
 
             } 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]) {
                 strongSelf.keyHierarchyOperationGroup = [CKOperationGroup CKKSGroupWithName:@"restart-setup"];
 
                 if ([CKKSManifest shouldSyncManifests]) {
 
                 NSOperation* initialProcess = nil;
                 if(ckse.lastFetchTime == nil || [ckse.lastFetchTime compare: deadline] == NSOrderedAscending) {
 
                 NSOperation* initialProcess = nil;
                 if(ckse.lastFetchTime == nil || [ckse.lastFetchTime compare: deadline] == NSOrderedAscending) {
-                    initialProcess = [strongSelf fetchAndProcessCKChanges:CKKSFetchBecauseSecuritydRestart];
+                    initialProcess = [strongSelf fetchAndProcessCKChanges:CKKSFetchBecauseSecuritydRestart after:strongSelf.lastFixupOperation];
                 } else {
                 } 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";
                 }
 
                 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.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;
         }];
     }];
             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);
     [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);
     [CKKSDeviceStateEntry deleteAll:self.zoneID error:&localerror];
     if(localerror) {
         ckkserror("ckks", self, "couldn't delete all CKKSDeviceStateEntry: %@", localerror);
         __block NSError* error = nil;
 
         [strongSelf dispatchSync: ^bool{
         __block NSError* error = nil;
 
         [strongSelf dispatchSync: ^bool{
-            [self _onqueueResetLocalData: &error];
+            [strongSelf _onqueueResetLocalData: &error];
             return true;
         }];
 
             return true;
         }];
 
         }
     }];
 
         }
     }];
 
+    // On a reset, all other operations should be cancelled
+    [self cancelAllOperations];
     [resetFollowUp runBeforeGroupFinished:op];
     [self scheduleOperationWithoutDependencies:resetFollowUp];
     return resetFollowUp;
     [resetFollowUp runBeforeGroupFinished:op];
     [self scheduleOperationWithoutDependencies:resetFollowUp];
     return resetFollowUp;
         return nil;
     }
 
         return nil;
     }
 
+    // On a reset, we should cancel all existing operations
+    [self cancelAllOperations];
     CKKSResultOperation* reset = [super beginResetCloudKitZoneOperation];
 
     __weak __typeof(self) weakSelf = self;
     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.
 - (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 = true;
     [self _onqueueAdvanceKeyStateMachineToState: nil withError: nil];
         self.keyStateProcessRequested = false;
 
         self.keyHierarchyOperationGroup = [CKOperationGroup CKKSGroupWithName:@"key-state-reset"];
         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;
     }
 
         return;
     }
 
         } else {
             // Error state: record the error and exit early
             ckkserror("ckkskey", self, "advised of error: coming from state (%@): %@", self.keyHierarchyState, error);
         } 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;
             self.keyHierarchyState = SecCKKSZoneKeyStateError;
             self.keyHierarchyError = error;
+
             return;
         }
     }
             return;
         }
     }
 
         if(!self.keyStateMachineOperation) {
             // We think we're ready. Double check.
 
         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.
                 // 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.
 
             } 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.
 
         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, "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 {
             }
 
         } else {
             [self _onqueueKeyHierarchyFetch];
         }
 
             [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]) {
     } 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];
         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;
             // 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]) {
         }
 
     } else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateWaitForTLK]) {
         ckksnotice("ckkskey", self, "Creating new TLKs didn't work. Attempting to refetch!");
         [self _onqueueKeyHierarchyFetch];
 
         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];
     } else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateNeedFullRefetch]) {
         ckksnotice("ckkskey", self, "Informed of request for full refetch");
         [self _onqueueKeyHierarchyRefetch];
     }
 
     if(self.keyStateMachineOperation) {
     }
 
     if(self.keyStateMachineOperation) {
-
         if(self.keyStateReadyDependency == nil || [self.keyStateReadyDependency isFinished]) {
             ckksnotice("ckkskey", self, "reloading keyStateReadyDependency due to operation %@", 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.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 scheduleOperation: self.keyStateMachineOperation];
         self.keyStateMachineOperation = [[CKKSHealKeyHierarchyOperation alloc] initWithCKKSKeychainView:self 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]) {
     } 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);
 
     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;
 
     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) {
         if(error) {
-            *error = localerror;
+            *error = set.error;
         }
         }
-        return false;
+        return SecCKKSZoneKeyStateUnhealthy;
     }
 
     }
 
+    NSError* localerror = nil;
+
     // keychain being locked is not a fatal error here
     // 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)) {
     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;
         }
         if(error) {
             *error = localerror;
         }
-        return false;
+        return SecCKKSZoneKeyStateUnhealthy;
     } else if(localerror) {
     } 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
     }
     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)) {
     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;
         }
         if(error) {
             *error = localerror;
         }
-        return false;
+        return SecCKKSZoneKeyStateUnhealthy;
     } else if(localerror) {
     } 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
     }
     localerror = nil;
 
     // keychain being locked is a fatal error here, since this is class C
-    [classA loadKeyMaterialFromKeychain:&localerror];
+    [set.classC loadKeyMaterialFromKeychain:&localerror];
     if(localerror) {
     if(localerror) {
-        ckksinfo("ckkskey", self, "Error loading classC(%@): %@", classC, localerror);
+        ckkserror("ckkskey", self, "Error loading classC(%@): %@", set.classC, localerror);
         if(error) {
             *error = 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.
 
     // Got to the bottom? Cool! All keys are present and accounted for.
-    return true;
+    return SecCKKSZoneKeyStateReady;
 }
 
 - (void)_onqueueKeyHierarchyFetch {
 }
 
 - (void)_onqueueKeyHierarchyFetch {
             return;
         }
 
             return;
         }
 
-        [strongSelf dispatchSync: ^bool{
+        [strongSelf dispatchSyncWithAccountKeys: ^bool{
             [strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateFetchComplete withError: nil];
             return true;
         }];
             [strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateFetchComplete withError: nil];
             return true;
         }];
             return;
         }
 
             return;
         }
 
-        [strongSelf dispatchSync: ^bool{
+        [strongSelf dispatchSyncWithAccountKeys: ^bool{
             [strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateFetchComplete withError: nil];
             return true;
         }];
             [strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateFetchComplete withError: nil];
             return true;
         }];
                              rateLimiter: (CKKSRateLimiter*) rateLimiter
                             syncCallback: (SecBoolNSErrorCallback) syncCallback {
     if(!SecCKKSIsEnabled()) {
                              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;
     }
 
         return;
     }
 
     bool proceed = addedSync || deletedSync;
 
     if(!proceed) {
     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;
     }
 
         return;
     }
 
     if(! ([protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleWhenUnlocked] ||
           [protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleAfterFirstUnlock] ||
           [protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleAlways])) {
     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 {
         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(![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 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
                 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);
                 [self _onqueueAdvanceKeyStateMachineToState: nil withError: nil];
 
                 ckksnotice("ckks", self, "Launching scan operation to refind %@", added);
             return true;
         }
 
             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(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;
         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).
         }
 
         // 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];
 
         // Schedule a "view changed" notification
         [self.notifyViewChangedScheduler trigger];
 
                            complete:(void (^) (NSError* operror)) complete
 {
     if(accessGroup == nil || identifier == nil) {
                            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;
         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);
         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;
             complete(error);
             CFReleaseNull(cferror);
             return false;
             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) {
         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);
             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;
                 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.");
         // 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;
         }
             complete(error);
             return false;
         }
         CKKSUpdateCurrentItemPointerOperation* ucipo = [[CKKSUpdateCurrentItemPointerOperation alloc] initWithCKKSKeychainView:self
                                                                                                                 currentPointer:(NSString*)currentIdentifier
                                                                                                                    oldItemUUID:(NSString*)oldItemUUID
         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) {
                                                                                                                    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 {
             } 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(ucipo.error);
         }];
                            complete:(void (^) (NSString* uuid, NSError* operror)) complete
 {
     if(accessGroup == nil || identifier == nil) {
                            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.");
         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;
     }
 
         return;
     }
 
     }
 
     __weak __typeof(self) weakSelf = self;
     }
 
     __weak __typeof(self) weakSelf = self;
-    CKKSResultOperation* getCurrentItem = [CKKSResultOperation operationWithBlock:^{
+    CKKSResultOperation* getCurrentItem = [CKKSResultOperation named:@"get-current-item-pointer" withBlock:^{
         if(fetchAndProcess.error) {
         if(fetchAndProcess.error) {
+            ckksnotice("ckkscurrent", self, "Rejecting current item pointer get since fetch failed: %@", fetchAndProcess.error);
             complete(NULL, fetchAndProcess.error);
             return;
         }
             complete(NULL, fetchAndProcess.error);
             return;
         }
 
             if(!cip.currentItemUUID) {
                 ckkserror("ckkscurrent", strongSelf, "Current item pointer is empty %@", cip);
 
             if(!cip.currentItemUUID) {
                 ckkserror("ckkscurrent", strongSelf, "Current item pointer is empty %@", cip);
-                complete(nil, [NSError errorWithDomain:@"securityd"
+                complete(nil, [NSError errorWithDomain:CKKSErrorDomain
                                                   code:errSecInternalError
                                                   code:errSecInternalError
-                                              userInfo:@{NSLocalizedDescriptionKey: @"Current item pointer is empty"}]);
+                                           description:@"Current item pointer is empty"]);
                 return false;
             }
 
                 return false;
             }
 
+            ckksnotice("ckkscurrent", strongSelf, "Retrieved current item pointer: %@", cip);
             complete(cip.currentItemUUID, NULL);
             complete(cip.currentItemUUID, NULL);
-
             return true;
         }];
     }];
             return true;
         }];
     }];
-    getCurrentItem.name = @"get-current-item-pointer";
 
     [getCurrentItem addNullableDependency:fetchAndProcess];
     [self scheduleOperation: getCurrentItem];
 
     [getCurrentItem addNullableDependency:fetchAndProcess];
     [self scheduleOperation: getCurrentItem];
               [protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleAfterFirstUnlock]) {
         class = SecCKKSKeyClassC;
     } else {
               [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) {
         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;
     }
 
         }
 
         return nil;
     }
 
-    CKKSKey* key = [CKKSKey currentKeyForClass: class zoneID:self.zoneID error:error];
+    NSError* currentKeyError = nil;
+    CKKSKey* key = [CKKSKey currentKeyForClass: class zoneID:self.zoneID error:&currentKeyError];
+    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.
 
     // 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;
     }
 
         return nil;
     }
 
             (CKKSOutgoingQueueOperation*) [self findFirstPendingOperation:self.outgoingQueueOperations
                                                                   ofClass:[CKKSOutgoingQueueOperation class]];
     if(outgoingop) {
             (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(after) {
             [outgoingop addDependency: after];
         }
             if(!outgoingop.ckoperationGroup && ckoperationGroup) {
                 outgoingop.ckoperationGroup = ckoperationGroup;
             } else if(ckoperationGroup) {
             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;
         }
     }
             return outgoingop;
         }
     }
     [op addNullableDependency: self.initialScanOperation];
 
     [self scheduleOperation: op];
     [op addNullableDependency: self.initialScanOperation];
 
     [self scheduleOperation: op];
+    ckksnotice("ckksoutgoing", self, "Scheduled %@", op);
     return op;
 }
 
     return op;
 }
 
     if(incomingop) {
         ckksinfo("ckks", self, "Skipping processIncomingQueue due to at least one pending instance");
         if(after) {
     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]) {
         }
         // check (again) for race condition; if the op has started we need to add another (for the dependency)
         if([incomingop isPending]) {
     return op;
 }
 
     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;
     }
     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";
 
 
     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.
     // 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;
 }
     [self scheduleAccountStatusOperation:op];
     return op;
 }
 }
 
 - (CKKSResultOperation*)fetchAndProcessCKChanges:(CKKSFetchBecause*)because {
 }
 
 - (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(!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]];
 }
     // We fetched some changes; try to process them!
     return [self processIncomingQueue:false after:[self.zoneChangeFetcher requestSuccessfulFetch:because]];
 }
                 return true;
             }
         }
                 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;
     }
 
     return false;
     } else if([recordType isEqual: SecCKRecordIntermediateKeyType]) {
         // TODO: handle in some interesting way
         return true;
     } 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);
     } 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;
     }
         // TODO: actually pass error back up
         return error == nil;
     }
+
     else {
         ckkserror("ckksfetch", self, "unknown record type: %@ %@", recordType, recordID);
         return false;
     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: 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;
     } else if([[record recordType] isEqualToString: SecCKRecordCurrentKeyType]) {
         [self _onqueueCKRecordCurrentKeyPointerChanged:record resync:resync];
         return true;
     [self _onqueueKeyStateMachineRequestProcess];
 }
 
     [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);
 
 - (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) {
     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);
             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 {
         } 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);
     }
 
         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 {
 }
 
 - (void)_onqueueCKRecordCurrentItemPointerChanged:(CKRecord*)record resync:(bool)resync {
         SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
         if(callback) {
             callback(true, nil);
         SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
         if(callback) {
             callback(true, nil);
+            self.pendingSyncCallbacks[oqe.uuid] = nil;
         }
 
         [oqe deleteFromDatabase: &localerror];
         }
 
         [oqe deleteFromDatabase: &localerror];
     SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
     if(callback) {
         callback(false, itemError);
     SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
     if(callback) {
         callback(false, itemError);
+        self.pendingSyncCallbacks[oqe.uuid] = nil;
     }
     NSError* localerror = 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!
 
     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;
 }
 
     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 {
 #pragma mark - Test Support
 
 - (bool) outgoingQueueEmpty: (NSError * __autoreleasing *) error {
     [self.keyStateMachineOperation cancel];
     [self.keyStateReadyDependency cancel];
     [self.zoneChangeFetcher cancel];
     [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];
 
 
     [super cancelAllOperations];
 
             [mutDeviceStates addObject: [obj description]];
         }];
 
             [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" :
         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),
                  @"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),
 
                  @"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),
                  @"zoneSetupOperation":                 stringify(self.zoneSetupOperation),
                  @"viewSetupOperation":                 stringify(self.viewSetupOperation),
                  @"keyStateOperation":                  stringify(self.keyStateMachineOperation),
index 8323614236d60c1e9fa7018bffd8afd7e4f69299..49680e8da0bc6b4afbb7e3004648d82173c3079e 100644 (file)
 }
 
 -(bool)isLockedError:(NSError *)error {
 }
 
 -(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;
 }
 
 
 }
 
 
index ff05f89de928650269918317153988ab2e889cf6..ada18403df6666817165450ef393d5eab04e9f0e 100644 (file)
@@ -1410,7 +1410,7 @@ static NSUInteger LeafBucketIndexForUUID(NSString* uuid)
         CFArrayForEach(peerInfos, ^(const void* peerInfoPtr) {
             SOSPeerInfoRef peerInfo = (SOSPeerInfoRef)peerInfoPtr;
             CFErrorRef blockError = NULL;
         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;
             if (!secPublicKey || error) {
                 CFReleaseNull(blockError);
                 return;
index e0f83a6ea07cfd293ec64f81fa3231b40c0c51de..9b1238dc54007416f3772bde9ac1f55256cf5fc7 100644 (file)
@@ -21,6 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #import "CKKSSQLDatabaseObject.h"
 #import "CKKSItem.h"
 #include <utilities/SecDb.h>
 #import "CKKSSQLDatabaseObject.h"
 #import "CKKSItem.h"
 #include <utilities/SecDb.h>
@@ -29,8 +31,6 @@
 #ifndef CKKSMirrorEntry_h
 #define CKKSMirrorEntry_h
 
 #ifndef CKKSMirrorEntry_h
 #define CKKSMirrorEntry_h
 
-#if OCTAGON
-
 #import <CloudKit/CloudKit.h>
 
 @class CKKSWrappedAESSIVKey;
 #import <CloudKit/CloudKit.h>
 
 @class CKKSWrappedAESSIVKey;
index 7cd9d6fe98407e3325cf136f181025b8ce533472..3609839e127254deaebea67dd628e6cb351d3ef3 100644 (file)
@@ -21,6 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #include <AssertMacros.h>
 
 #import <Foundation/Foundation.h>
 #include <AssertMacros.h>
 
 #import <Foundation/Foundation.h>
@@ -31,7 +33,6 @@
 #include <securityd/SecDbItem.h>
 #include <securityd/SecItemSchema.h>
 
 #include <securityd/SecDbItem.h>
 #include <securityd/SecItemSchema.h>
 
-#if OCTAGON
 
 #import <CloudKit/CloudKit.h>
 #import "CKKSOutgoingQueueEntry.h"
 
 #import <CloudKit/CloudKit.h>
 #import "CKKSOutgoingQueueEntry.h"
index aec9dfaabf8e6b89bf64a606389dc5033a99646b..bdccb8028e4d356a268735cace5ae3e88d3d57ce 100644 (file)
@@ -31,6 +31,8 @@
 
 #if OCTAGON
 
 
 #if OCTAGON
 
+#import "keychain/ckks/CKKSTLKShare.h"
+
 @interface CKKSNewTLKOperation ()
 @property NSBlockOperation* cloudkitModifyOperationFinished;
 @property CKOperationGroup* ckoperationGroup;
 @interface CKKSNewTLKOperation ()
 @property NSBlockOperation* cloudkitModifyOperationFinished;
 @property CKOperationGroup* ckoperationGroup;
@@ -79,7 +81,7 @@
     }
 
     // Synchronous, on some thread. Get back on the CKKS queue for SQL thread-safety.
     }
 
     // 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;
         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!
         }
 
         // 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];
 
         [newTLK       saveKeyMaterialToKeychain: &error];
         [newClassAKey saveKeyMaterialToKeychain: &error];
             return false;
         }
 
             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];
         // 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;
             }
 
                 return;
             }
 
-            [strongCKKS dispatchSync: ^bool{
+            [strongCKKS dispatchSyncWithAccountKeys: ^bool{
                 if(ckerror == nil) {
                     ckksnotice("ckkstlk", strongCKKS, "Completed TLK CloudKit operation");
 
                 if(ckerror == nil) {
                     ckksnotice("ckkstlk", strongCKKS, "Completed TLK CloudKit operation");
 
                         } else if([currentClassCPointer matchesCKRecord: record]) {
                             currentClassCPointer.storedCKRecord = record;
                         }
                         } else if([currentClassCPointer matchesCKRecord: record]) {
                             currentClassCPointer.storedCKRecord = record;
                         }
+
+                        for(CKKSTLKShare* share in tlkShares) {
+                            if([share matchesCKRecord: record]) {
+                                share.storedCKRecord = record;
+                            }
+                        }
                     }
 
                     [newTLK       saveToDatabaseAsOnlyCurrentKeyForClassAndState: &localerror];
                     }
 
                     [newTLK       saveToDatabaseAsOnlyCurrentKeyForClassAndState: &localerror];
 
                     [wrappedOldTLK saveToDatabase: &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);
                     // TLKs are already saved in the local keychain; fire off a backup
                     CKKSNearFutureScheduler* tlkNotifier = strongCKKS.savedTLKNotifier;
                     ckksnotice("ckkstlk", strongCKKS, "triggering new TLK notification: %@", tlkNotifier);
index e0e85c80fd3a3cde679c7ad99b643f6d7eb2cbc9..032e604a7658a9d1bad89d3fe4524bf7ac47d77a 100644 (file)
@@ -39,6 +39,7 @@
 #import "CKKSOutgoingQueueEntry.h"
 #import "CKKSItemEncrypter.h"
 #import "CKKSKey.h"
 #import "CKKSOutgoingQueueEntry.h"
 #import "CKKSItemEncrypter.h"
 #import "CKKSKey.h"
+#import "keychain/ckks/CloudKitCategories.h"
 
 
 @implementation CKKSOutgoingQueueEntry
 
 
 @implementation CKKSOutgoingQueueEntry
@@ -84,7 +85,7 @@
             true) ? YES : NO;
 }
 
             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;
     CFErrorRef cferror = NULL;
     CKKSKey* key = nil;
     NSString* uuid = nil;
 
     NSInteger newGenerationCount = -1;
 
 
     NSInteger newGenerationCount = -1;
 
-
     NSMutableDictionary* objd = nil;
 
     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) {
         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];
 
         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) {
     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]]) {
         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;
     }
 
         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) {
     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.
         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";
     }
 
         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
     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;
         }
         if([existingOQE.action isEqual: SecCKKSActionDelete] && [action isEqual:SecCKKSActionAdd]) {
             actualAction = SecCKKSActionModify;
         }
-
     }
 
     newGenerationCount = ckme ? ckme.item.generationCount : (NSInteger) 0; // TODO: this is wrong
     }
 
     newGenerationCount = ckme ? ckme.item.generationCount : (NSInteger) 0; // TODO: this is wrong
                                   plaintextPCSPublicKey:pcsPublicKey
                              plaintextPCSPublicIdentity:pcsPublicIdentity];
 
                                   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
     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 nil;
     }
 
index 45b61f975ec2a3283b5b5e62bc2a81acf2b5c3cb..96fbe10e472a2e7997f1c44ed0f99b63ceaf0dbe 100644 (file)
@@ -78,7 +78,7 @@
         return;
     }
 
         return;
     }
 
-    [ckks dispatchSyncWithAccountQueue: ^bool{
+    [ckks dispatchSyncWithAccountKeys: ^bool{
         ckks.lastOutgoingQueueOperation = self;
         if(self.cancelled) {
             ckksnotice("ckksoutgoing", ckks, "CKKSOutgoingQueueOperation cancelled, quitting");
         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];
 
                                     // 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];
                                     // 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]) {
                                 } 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) {
                                     }
                                 }
                             } 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]) {
                                 // 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!
 
             [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);
         };
 
         ckksinfo("ckksoutgoing", ckks, "Current keys to update: %@", currentKeysToSave);
             if(!error) {
                 ckksnotice("ckksoutgoing", blockCKKS, "Record upload successful for %@", record.recordID.recordName);
             } else {
             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);
             }
         };
 
             }
         };
 
diff --git a/keychain/ckks/CKKSPeer.h b/keychain/ckks/CKKSPeer.h
new file mode 100644 (file)
index 0000000..dd9f939
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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
diff --git a/keychain/ckks/CKKSPeer.m b/keychain/ckks/CKKSPeer.m
new file mode 100644 (file)
index 0000000..094bb7a
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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
index 3c0d23a36452a47a0fe44df96c9818800a5c35da..d05c869578d9629cda0e1f8f5b246b9aca6e5f07 100644 (file)
@@ -57,7 +57,7 @@
         return;
     }
 
         return;
     }
 
-    [ckks dispatchSync: ^bool{
+    [ckks dispatchSyncWithAccountKeys: ^bool{
         if(self.cancelled) {
             ckksinfo("ckkskey", ckks, "CKKSProcessReceivedKeysOperation cancelled, quitting");
             return false;
         if(self.cancelled) {
             ckksinfo("ckkskey", ckks, "CKKSProcessReceivedKeysOperation cancelled, quitting");
             return false;
         }
 
         // This key is our proposed TLK. Check with the CKKS object.
         }
 
         // 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);
             // 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);
index b044e95915cada038c68dd73bcd39661f61010ba..df6253424ceb394570e60a4ebeff1b49783b5278 100644 (file)
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #include <utilities/SecDb.h>
 #include <securityd/SecDbItem.h>
 
 #ifndef CKKSRecordHolder_h
 #define CKKSRecordHolder_h
 
 #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>
 
 #import "keychain/ckks/CKKSSQLDatabaseObject.h"
 #import <CloudKit/CloudKit.h>
 
index e42bfaa00236f4ec4b6ad9b31a0a8eda17103886..13b2a78fafb04b3277f4be83afedae68c00fc58d 100644 (file)
@@ -21,6 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #include <AssertMacros.h>
 
 #import <Foundation/Foundation.h>
 #include <AssertMacros.h>
 
 #import <Foundation/Foundation.h>
@@ -31,8 +33,6 @@
 #include <securityd/SecDbItem.h>
 #include <securityd/SecItemSchema.h>
 
 #include <securityd/SecDbItem.h>
 #include <securityd/SecItemSchema.h>
 
-#if OCTAGON
-
 #import <CloudKit/CloudKit.h>
 
 
 #import <CloudKit/CloudKit.h>
 
 
diff --git a/keychain/ckks/CKKSResultOperation.h b/keychain/ckks/CKKSResultOperation.h
new file mode 100644 (file)
index 0000000..20fc548
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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
+
diff --git a/keychain/ckks/CKKSResultOperation.m b/keychain/ckks/CKKSResultOperation.m
new file mode 100644 (file)
index 0000000..0adc511
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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
index b47983b05e1c4bd082e0ede744f57f486b12aa3b..5b1b268708afd58f1299423bb8dd44a0bab76163 100644 (file)
@@ -21,6 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #import <Foundation/Foundation.h>
 
 // For AES-SIV 512.
 #import <Foundation/Foundation.h>
 
 // For AES-SIV 512.
@@ -60,3 +62,5 @@
 - (NSData*)decryptData: (NSData*) ciphertext authenticatedData: (NSDictionary<NSString*, NSData*>*) ad error: (NSError * __autoreleasing *) error;
 
 @end
 - (NSData*)decryptData: (NSData*) ciphertext authenticatedData: (NSDictionary<NSString*, NSData*>*) ad error: (NSError * __autoreleasing *) error;
 
 @end
+
+#endif // OCTAGON
index 3097ee94407555351dfcea22ffb64828fd68eae0..90ec111ca938c2483ac589ad31739819454f3bf2 100644 (file)
@@ -21,6 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#if OCTAGON
+
 #define __STDC_WANT_LIB_EXT1__ 1
 #include <string.h>
 
 #define __STDC_WANT_LIB_EXT1__ 1
 #include <string.h>
 
@@ -34,6 +36,8 @@
 #include <corecrypto/ccaes.h>
 #include <corecrypto/ccmode_siv.h>
 
 #include <corecrypto/ccaes.h>
 #include <corecrypto/ccmode_siv.h>
 
+#import "keychain/ckks/CloudKitCategories.h"
+
 @implementation CKKSBaseAESSIVKey
 - (instancetype)init {
     if(self = [super init]) {
 @implementation CKKSBaseAESSIVKey
 - (instancetype)init {
     if(self = [super init]) {
     NSError* localerror = nil;
     bool success = false;
 
     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] = {};
 
     CKKSWrappedAESSIVKey* wrappedKey = nil;
     uint8_t buffer[CKKSWrappedKeySize] = {};
 
@@ -389,3 +403,5 @@ out:
 
 
 @end
 
 
 @end
+
+#endif // OCTAGON
index 205a87756e4fc80220ac3cb51a14fd64a007d7e1..ed859d2de0b2941a5e605268fe5e6c653b294c1c 100644 (file)
 
         NSString * sql = [[NSString alloc] initWithFormat: @"DELETE FROM %@%@;", table, whereClause];
         SecDbPrepare(dbconn, (__bridge CFStringRef) sql, &cferror, ^void (sqlite3_stmt *stmt) {
 
         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]) {
 
             [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) {
 
         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]) {
             [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;
                 }
             }];
 
                 }
             }];
 
index b15449819bfe9919c522b4ab3693bff16cd05a54..065d06a0143b6800d6fdd9d054c071b613b6472c 100644 (file)
@@ -65,7 +65,7 @@
         return;
     }
 
         return;
     }
 
-    [ckks dispatchSyncWithAccountQueue: ^bool{
+    [ckks dispatchSyncWithAccountKeys: ^bool{
         if(self.cancelled) {
             ckksnotice("ckksscan", ckks, "CKKSScanLocalItemsOperation cancelled, quitting");
             return false;
         if(self.cancelled) {
             ckksnotice("ckksscan", ckks, "CKKSScanLocalItemsOperation cancelled, quitting");
             return false;
                         return;
                     }
 
                         return;
                     }
 
-                    ckksnotice("ckksscan", ckks, "Syncing new item: %@ %@", oqe, itemToSave);
+                    ckksnotice("ckksscan", ckks, "Syncing new item: %@", oqe);
                     CFReleaseNull(itemToSave);
 
                     [oqe saveToDatabase: &error];
                     if(error) {
                     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;
                     }
                         self.error = error;
                         return;
                     }
diff --git a/keychain/ckks/CKKSTLKShare.h b/keychain/ckks/CKKSTLKShare.h
new file mode 100644 (file)
index 0000000..2ef3b85
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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
diff --git a/keychain/ckks/CKKSTLKShare.m b/keychain/ckks/CKKSTLKShare.m
new file mode 100644 (file)
index 0000000..fd3b269
--- /dev/null
@@ -0,0 +1,616 @@
+/*
+ * 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
index 4fdb370d73894febeb753b684bc17fae6505c9a4..6ce5057c25148056d4174acee209ddb2a4d56589 100644 (file)
@@ -33,6 +33,7 @@
 - (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*) ckks
                           currentPointer:(NSString*)identifier
                              oldItemUUID:(NSString*)oldItemUUID
 - (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*) ckks
                           currentPointer:(NSString*)identifier
                              oldItemUUID:(NSString*)oldItemUUID
+                             oldItemHash:(NSData*)oldItemHash
                              newItemUUID:(NSString*)newItemUUID
                         ckoperationGroup:(CKOperationGroup*)ckoperationGroup;
 @end
                              newItemUUID:(NSString*)newItemUUID
                         ckoperationGroup:(CKOperationGroup*)ckoperationGroup;
 @end
index 2e2e1d204fd68c6d3829ac5a5d4833eedf8dab42..1d14471769d00a7324febccf50061b69f4c0384e 100644 (file)
@@ -29,6 +29,7 @@
 #import "keychain/ckks/CKKSCurrentItemPointer.h"
 #import "keychain/ckks/CKKSUpdateCurrentItemPointerOperation.h"
 #import "keychain/ckks/CKKSManifest.h"
 #import "keychain/ckks/CKKSCurrentItemPointer.h"
 #import "keychain/ckks/CKKSUpdateCurrentItemPointerOperation.h"
 #import "keychain/ckks/CKKSManifest.h"
+#import "keychain/ckks/CloudKitCategories.h"
 
 #import <CloudKit/CloudKit.h>
 
 
 #import <CloudKit/CloudKit.h>
 
@@ -38,6 +39,7 @@
 
 @property NSString* currentPointerIdentifier;
 @property NSString* oldCurrentItemUUID;
 
 @property NSString* currentPointerIdentifier;
 @property NSString* oldCurrentItemUUID;
+@property NSData* oldCurrentItemHash;
 @property NSString* currentItemUUID;
 @end
 
 @property NSString* currentItemUUID;
 @end
 
@@ -46,6 +48,7 @@
 - (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*) ckks
                           currentPointer:(NSString*)identifier
                              oldItemUUID:(NSString*)oldItemUUID
 - (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*) ckks
                           currentPointer:(NSString*)identifier
                              oldItemUUID:(NSString*)oldItemUUID
+                             oldItemHash:(NSData*)oldItemHash
                              newItemUUID:(NSString*)newItemUUID
                         ckoperationGroup:(CKOperationGroup*)ckoperationGroup
 {
                              newItemUUID:(NSString*)newItemUUID
                         ckoperationGroup:(CKOperationGroup*)ckoperationGroup
 {
@@ -54,6 +57,7 @@
 
         _currentPointerIdentifier = identifier;
         _oldCurrentItemUUID = oldItemUUID;
 
         _currentPointerIdentifier = identifier;
         _oldCurrentItemUUID = oldItemUUID;
+        _oldCurrentItemHash = oldItemHash;
         _currentItemUUID = newItemUUID;
         _ckoperationGroup = ckoperationGroup;
     }
         _currentItemUUID = newItemUUID;
         _ckoperationGroup = ckoperationGroup;
     }
     CKKSKeychainView* ckks = self.ckks;
     if(!ckks) {
         ckkserror("ckkscurrent", ckks, "no CKKS object");
     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;
 
         return;
     }
 
     __weak __typeof(self) weakSelf = self;
 
-    [ckks dispatchSyncWithAccountQueue:^bool {
+    [ckks dispatchSyncWithAccountKeys:^bool {
         if(self.cancelled) {
             ckksnotice("ckksscan", ckks, "CKKSUpdateCurrentItemPointerOperation cancelled, quitting");
             return false;
         if(self.cancelled) {
             ckksnotice("ckksscan", ckks, "CKKSUpdateCurrentItemPointerOperation cancelled, quitting");
             return false;
@@ -81,8 +87,9 @@
         // Ensure that there's no pending pointer update
         CKKSCurrentItemPointer* cipPending = [CKKSCurrentItemPointer tryFromDatabase:self.currentPointerIdentifier state:SecCKKSProcessedStateRemote zoneID:ckks.zoneID error:&error];
         if(cipPending) {
         // 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;
         }
             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) {
         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!
                 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);
         } 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.
             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]) {
         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]) {
         }
         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) {
         }
 
         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);
         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;
         }
             self.error = error;
             return false;
         }
             __strong __typeof(strongSelf.ckks) strongCKKS = strongSelf.ckks;
             if(!strongSelf || !strongCKKS) {
                 ckkserror("ckkscurrent", strongCKKS, "received callback for released object");
             __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;
             }
                 [strongCKKS scheduleOperation: modifyComplete];
                 return;
             }
index b112b5273ebfa910c23e735c0a542cbad505c125..0a78ec8443f1a2bb4f2051f2d48dbd3b270e7490 100644 (file)
@@ -23,6 +23,7 @@
 
 #if OCTAGON
 
 
 #if OCTAGON
 
+#include <utilities/SecInternalReleasePriv.h>
 #import "keychain/ckks/CKKSKeychainView.h"
 #import "keychain/ckks/CKKSUpdateDeviceStateOperation.h"
 #import "keychain/ckks/CKKSCurrentKeyPointer.h"
 #import "keychain/ckks/CKKSKeychainView.h"
 #import "keychain/ckks/CKKSUpdateDeviceStateOperation.h"
 #import "keychain/ckks/CKKSCurrentKeyPointer.h"
@@ -79,7 +80,7 @@
         return;
     }
 
         return;
     }
 
-    [ckks dispatchSyncWithAccountQueue:^bool {
+    [ckks dispatchSyncWithAccountKeys:^bool {
         NSError* error = nil;
 
         CKKSDeviceStateEntry* cdse = [ckks _onqueueCurrentDeviceStateEntry:&error];
         NSError* error = nil;
 
         CKKSDeviceStateEntry* cdse = [ckks _onqueueCurrentDeviceStateEntry:&error];
         if(self.rateLimit) {
             NSDate* lastUpdate = cdse.storedCKRecord.modificationDate;
 
         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];
             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) {
             NSDate* deadline = [[NSCalendar currentCalendar] dateByAddingComponents:offset toDate:now options:0];
 
             if(lastUpdate == nil || [lastUpdate compare: deadline] == NSOrderedAscending) {
index 699575b9d4542b625a35a6afb7b394f89b2e9ff0..8b071b0d09de328657c67fabbd170253c86dd4c7 100644 (file)
@@ -34,6 +34,7 @@
 #import "keychain/ckks/CKKSRateLimiter.h"
 #import "keychain/ckks/CKKSNotifier.h"
 #import "keychain/ckks/CKKSCondition.h"
 #import "keychain/ckks/CKKSRateLimiter.h"
 #import "keychain/ckks/CKKSNotifier.h"
 #import "keychain/ckks/CKKSCondition.h"
+#import "keychain/ckks/CKKSPeer.h"
 #endif
 
 @class CKKSKeychainView, CKKSRateLimiter;
 #endif
 
 @class CKKSKeychainView, CKKSRateLimiter;
@@ -41,7 +42,7 @@
 #if !OCTAGON
 @interface CKKSViewManager : NSObject
 #else
 #if !OCTAGON
 @interface CKKSViewManager : NSObject
 #else
-@interface CKKSViewManager : NSObject <CKKSControlProtocol>
+@interface CKKSViewManager : NSObject <CKKSControlProtocol, CKKSPeerProvider>
 
 @property CKContainer* container;
 @property CKKSCKAccountStateTracker* accountTracker;
 
 @property CKContainer* container;
 @property CKKSCKAccountStateTracker* accountTracker;
 @property NSOperation* zoneStartupDependency;
 
 - (instancetype)initCloudKitWithContainerName: (NSString*) containerName usePCS:(bool)usePCS;
 @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
                                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
     modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
       modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
                    apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
 - (CKKSKeychainView*)restartZone:(NSString*)viewName;
 
 // Returns the viewList for a CKKSViewManager
 - (CKKSKeychainView*)restartZone:(NSString*)viewName;
 
 // Returns the viewList for a CKKSViewManager
-+(NSSet*)viewList;
+-(NSSet*)viewList;
 
 // Notify sbd to re-backup.
 -(void)notifyNewTLKsInKeychain;
 
 // 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
 
 #endif // OCTAGON
 @end
index 7b6b38f5b50a3dbef82e5b0680f8ff32ada70744..436c0d5abb586d0e7692c8bf3a05f3104e2a91c2 100644 (file)
@@ -48,6 +48,9 @@
 #if OCTAGON
 #import <CloudKit/CloudKit.h>
 #import <CloudKit/CloudKit_Private.h>
 #if OCTAGON
 #import <CloudKit/CloudKit.h>
 #import <CloudKit/CloudKit_Private.h>
+
+#import <SecurityFoundation/SFKey.h>
+#import <SecurityFoundation/SFKey_Private.h>
 #endif
 
 @interface CKKSViewManager () <NSXPCListenerDelegate>
 #endif
 
 @interface CKKSViewManager () <NSXPCListenerDelegate>
@@ -56,6 +59,8 @@
 
 // Once you set these, all CKKSKeychainViews created will use them
 @property (readonly) Class<CKKSFetchRecordZoneChangesOperation> fetchRecordZoneChangesOperationClass;
 
 // 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 (readonly) Class<CKKSModifySubscriptionsOperation> modifySubscriptionsOperationClass;
 @property (readonly) Class<CKKSModifyRecordZonesOperation> modifyRecordZonesOperationClass;
 @property (readonly) Class<CKKSAPSConnection> apsConnectionClass;
@@ -67,6 +72,8 @@
 @property NSMutableDictionary<NSString*, SecBoolNSErrorCallback>* pendingSyncCallbacks;
 @property CKKSNearFutureScheduler* savedTLKNotifier;;
 @property NSOperationQueue* operationQueue;
 @property NSMutableDictionary<NSString*, SecBoolNSErrorCallback>* pendingSyncCallbacks;
 @property CKKSNearFutureScheduler* savedTLKNotifier;;
 @property NSOperationQueue* operationQueue;
+
+@property NSMapTable<dispatch_queue_t, id<CKKSPeerUpdateListener>>* peerChangeListeners;
 #endif
 @end
 
 #endif
 @end
 
@@ -77,6 +84,8 @@
     return [self initWithContainerName:containerName
                                 usePCS:usePCS
   fetchRecordZoneChangesOperationClass:[CKFetchRecordZoneChangesOperation class]
     return [self initWithContainerName:containerName
                                 usePCS:usePCS
   fetchRecordZoneChangesOperationClass:[CKFetchRecordZoneChangesOperation class]
+            fetchRecordsOperationClass:[CKFetchRecordsOperation class]
+                   queryOperationClass:[CKQueryOperation class]
      modifySubscriptionsOperationClass:[CKModifySubscriptionsOperation class]
        modifyRecordZonesOperationClass:[CKModifyRecordZonesOperation class]
                     apsConnectionClass:[APSConnection class]
      modifySubscriptionsOperationClass:[CKModifySubscriptionsOperation class]
        modifyRecordZonesOperationClass:[CKModifyRecordZonesOperation class]
                     apsConnectionClass:[APSConnection class]
@@ -88,6 +97,8 @@
 - (instancetype)initWithContainerName: (NSString*) containerName
                                usePCS:(bool)usePCS
  fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
 - (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
     modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
       modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
                    apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
                             setupHold: (NSOperation*) setupHold {
     if(self = [super init]) {
         _fetchRecordZoneChangesOperationClass = fetchRecordZoneChangesOperationClass;
                             setupHold: (NSOperation*) setupHold {
     if(self = [super init]) {
         _fetchRecordZoneChangesOperationClass = fetchRecordZoneChangesOperationClass;
+        _fetchRecordsOperationClass = fetchRecordsOperationClass;
+        _queryOperationClass = queryOperationClass;
         _modifySubscriptionsOperationClass = modifySubscriptionsOperationClass;
         _modifyRecordZonesOperationClass = modifyRecordZonesOperationClass;
         _apsConnectionClass = apsConnectionClass;
         _modifySubscriptionsOperationClass = modifySubscriptionsOperationClass;
         _modifyRecordZonesOperationClass = modifyRecordZonesOperationClass;
         _apsConnectionClass = apsConnectionClass;
 
         _operationQueue = [[NSOperationQueue alloc] init];
 
 
         _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];
 
         _views = [[NSMutableDictionary alloc] init];
         _pendingSyncCallbacks = [[NSMutableDictionary alloc] init];
 
         _listener = [NSXPCListener anonymousListener];
         _listener.delegate = self;
         [_listener resume];
         _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;
 }
     }
     return self;
 }
@@ -181,7 +207,7 @@ dispatch_once_t globalZoneStateQueueOnce;
 }
 
 // Mostly exists to be mocked out.
 }
 
 // Mostly exists to be mocked out.
-+(NSSet*)viewList {
+-(NSSet*)viewList {
     return CFBridgingRelease(SOSViewCopyViewSet(kViewSetCKKS));
 }
 
     return CFBridgingRelease(SOSViewCopyViewSet(kViewSetCKKS));
 }
 
@@ -243,7 +269,10 @@ dispatch_once_t globalZoneStateQueueOnce;
                                                             accountTracker: self.accountTracker
                                                           lockStateTracker: self.lockStateTracker
                                                           savedTLKNotifier: self.savedTLKNotifier
                                                             accountTracker: self.accountTracker
                                                           lockStateTracker: self.lockStateTracker
                                                           savedTLKNotifier: self.savedTLKNotifier
+                                                              peerProvider:self
                                       fetchRecordZoneChangesOperationClass: self.fetchRecordZoneChangesOperationClass
                                       fetchRecordZoneChangesOperationClass: self.fetchRecordZoneChangesOperationClass
+                                                fetchRecordsOperationClass: self.fetchRecordsOperationClass
+                                                       queryOperationClass:self.queryOperationClass
                                          modifySubscriptionsOperationClass: self.modifySubscriptionsOperationClass
                                            modifyRecordZonesOperationClass: self.modifyRecordZonesOperationClass
                                                         apsConnectionClass: self.apsConnectionClass
                                          modifySubscriptionsOperationClass: self.modifySubscriptionsOperationClass
                                            modifyRecordZonesOperationClass: self.modifyRecordZonesOperationClass
                                                         apsConnectionClass: self.apsConnectionClass
@@ -296,7 +325,7 @@ dispatch_once_t globalZoneStateQueueOnce;
     @synchronized(self.views) {
         self.initializeNewZones = true;
 
     @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
         }
         for(NSString* s in viewSet) {
             [self findOrCreateView:s]; // initializes any newly-created views
         }
@@ -409,10 +438,10 @@ dispatch_once_t globalZoneStateQueueOnce;
     CKKSKeychainView* view = [self findView:viewHint];
 
     if(!view) {
     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;
     }
 
         return;
     }
 
@@ -433,10 +462,10 @@ dispatch_once_t globalZoneStateQueueOnce;
 {
     CKKSKeychainView* view = [self findView:viewHint];
     if(!view) {
 {
     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;
     }
 
         return;
     }
 
@@ -485,10 +514,10 @@ dispatch_once_t globalZoneStateQueueOnce;
 -(void)notifyNewTLKsInKeychain {
     // Why two functions here? Limitation of OCMock, unfortunately: can't stub and expect the same method
     secnotice("ckksbackup", "New TLKs have arrived");
 -(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) {
     SOSAccount* account = (__bridge SOSAccount*)SOSKeychainAccountGetSharedAccount();
 
     [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
@@ -528,7 +557,7 @@ dispatch_once_t globalZoneStateQueueOnce;
 #pragma mark - RPCs to manage and report state
 
 - (void)performanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *counter))reply {
 #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 {
 }
 
 - (void)rpcResetLocal:(NSString*)viewName reply: (void(^)(NSError* result)) reply {
@@ -570,7 +599,7 @@ dispatch_once_t globalZoneStateQueueOnce;
     } else {
         secnotice("ckksreset", "Completed rpcResetLocal with error: %@", op.error);
     }
     } 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 {
 }
 
 - (void)rpcResetCloudKit:(NSString*)viewName reply: (void(^)(NSError* result)) reply {
@@ -611,8 +640,8 @@ dispatch_once_t globalZoneStateQueueOnce;
         secnotice("ckksreset", "Completed rpcResetCloudKit");
     } else {
         secnotice("ckksreset", "Completed rpcResetCloudKit with error: %@", op.error);
         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 {
 }
 
 - (void)rpcResync:(NSString*)viewName reply: (void(^)(NSError* result)) reply {
@@ -656,12 +685,35 @@ dispatch_once_t globalZoneStateQueueOnce;
     [op timeout:120*NSEC_PER_SEC];
     [self.operationQueue addOperation:op];
     [op waitUntilFinished];
     [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];
 
 }
 
 - (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);
     NSArray* actualViews = nil;
     if(viewName) {
         secnotice("ckks", "Received a status RPC for zone %@", viewName);
@@ -732,7 +784,7 @@ dispatch_once_t globalZoneStateQueueOnce;
     [blockOp setCompletionBlock:^{
         __strong __typeof(blockOp) strongBlockOp = weakBlockOp;
         [strongBlockOp allDependentsSuccessful];
     [blockOp setCompletionBlock:^{
         __strong __typeof(blockOp) strongBlockOp = weakBlockOp;
         [strongBlockOp allDependentsSuccessful];
-        reply(strongBlockOp.error);
+        reply(CKXPCSuitableError(strongBlockOp.error));
     }];
 
     for(CKKSKeychainView* view in actualViews) {
     }];
 
     for(CKKSKeychainView* view in actualViews) {
@@ -775,7 +827,7 @@ dispatch_once_t globalZoneStateQueueOnce;
     [blockOp setCompletionBlock:^{
         __strong __typeof(blockOp) strongBlockOp = weakBlockOp;
         [strongBlockOp allDependentsSuccessful];
     [blockOp setCompletionBlock:^{
         __strong __typeof(blockOp) strongBlockOp = weakBlockOp;
         [strongBlockOp allDependentsSuccessful];
-        reply(strongBlockOp.error);
+        reply(CKXPCSuitableError(strongBlockOp.error));
     }];
 
     for(CKKSKeychainView* view in actualViews) {
     }];
 
     for(CKKSKeychainView* view in actualViews) {
@@ -792,21 +844,21 @@ dispatch_once_t globalZoneStateQueueOnce;
 {
     NSError* error = nil;
     NSString* sysdiagnose = [[CKKSAnalyticsLogger logger] getSysdiagnoseDumpWithError:&error];
 {
     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;
 }
 
 - (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];
 }
 
 - (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 {
 }
 
 -(void)xpc24HrNotification {
@@ -824,9 +876,153 @@ dispatch_once_t globalZoneStateQueueOnce;
     for(CKKSKeychainView* view in actualViews) {
         ckksnotice("ckks", view, "Starting device state XPC update");
         // Let the update know it should rate-limit itself
     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
 #endif // OCTAGON
 @end
index 9db60dd55bf803c2ee4b95d6eac6b58d6a7508c0..a46deabb4bbd55cc21bf3d59f72cf889c29839d4 100644 (file)
@@ -66,6 +66,8 @@
 
 // Dependencies (for injection)
 @property (readonly) Class<CKKSFetchRecordZoneChangesOperation> fetchRecordZoneChangesOperationClass;
 
 // 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;
 @property (readonly) Class<CKKSModifySubscriptionsOperation> modifySubscriptionsOperationClass;
 @property (readonly) Class<CKKSModifyRecordZonesOperation> modifyRecordZonesOperationClass;
 @property (readonly) Class<CKKSAPSConnection> apsConnectionClass;
@@ -76,6 +78,8 @@
                              zoneName: (NSString*) zoneName
                        accountTracker:(CKKSCKAccountStateTracker*) tracker
  fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
                              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;
     modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
       modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
                    apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass;
index 30790c43a10b49e975e52e1793554ef76b483e58..eee5cdb4799b49f1bd7c2f52e313963a4b4f5478 100644 (file)
@@ -60,6 +60,8 @@
                              zoneName: (NSString*) zoneName
                        accountTracker:(CKKSCKAccountStateTracker*) tracker
  fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
                              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
     modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
       modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
                    apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
@@ -79,6 +81,8 @@
         _accountOperations = [NSHashTable weakObjectsHashTable];
 
         _fetchRecordZoneChangesOperationClass = fetchRecordZoneChangesOperationClass;
         _accountOperations = [NSHashTable weakObjectsHashTable];
 
         _fetchRecordZoneChangesOperationClass = fetchRecordZoneChangesOperationClass;
+        _fetchRecordsOperationClass = fetchRecordsOperationClass;
+        _queryOperationClass = queryOperationClass;
         _modifySubscriptionsOperationClass = modifySubscriptionsOperationClass;
         _modifyRecordZonesOperationClass = modifyRecordZonesOperationClass;
         _apsConnectionClass = apsConnectionClass;
         _modifySubscriptionsOperationClass = modifySubscriptionsOperationClass;
         _modifyRecordZonesOperationClass = modifyRecordZonesOperationClass;
         _apsConnectionClass = apsConnectionClass;
             return;
         }
 
             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];
 
         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];
     };
 
         [strongSelf.operationQueue addOperation: doneOp];
     };
 
index 49dc7878a9e1748b72aaf16ed21367357a8f87f6..41d4d4ff63d755cde00211d20631e83fe8e411dd 100644 (file)
@@ -63,6 +63,9 @@ extern CKKSFetchBecause* const CKKSFetchBecauseTesting;
 - (CKKSResultOperation*)requestSuccessfulFetch:(CKKSFetchBecause*)why;
 - (CKKSResultOperation*)requestSuccessfulResyncFetch:(CKKSFetchBecause*)why;
 
 - (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
 
 -(void)cancel;
 @end
 
index 40a797dd53e2045cc9094cd34139f56d0cc65b5c..551f870eddbace59bf8973c68b0b4f21f1a92c98 100644 (file)
@@ -55,6 +55,8 @@ CKKSFetchBecause* const CKKSFetchBecauseTesting = (CKKSFetchBecause*) @"testing"
 @property CKKSResultOperation* successfulFetchDependency;
 
 @property CKKSNearFutureScheduler* fetchScheduler;
 @property CKKSResultOperation* successfulFetchDependency;
 
 @property CKKSNearFutureScheduler* fetchScheduler;
+
+@property CKKSResultOperation* holdOperation;
 @end
 
 @implementation CKKSZoneChangeFetcher
 @end
 
 @implementation CKKSZoneChangeFetcher
@@ -157,6 +159,7 @@ CKKSFetchBecause* const CKKSFetchBecauseTesting = (CKKSFetchBecause*) @"testing"
     CKOperationGroup* operationGroup = [CKOperationGroup CKKSGroupWithName: [[lastFetchReasons sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES]]] componentsJoinedByString:@","]];
 
     CKKSFetchAllRecordZoneChangesOperation* fetchAllChanges = [[CKKSFetchAllRecordZoneChangesOperation alloc] initWithCKKSKeychainView: ckks ckoperationGroup:operationGroup];
     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;
 
     fetchAllChanges.resync = self.newResyncRequests;
     self.newResyncRequests = false;
 
@@ -245,6 +248,10 @@ CKKSFetchBecause* const CKKSFetchBecauseTesting = (CKKSFetchBecause*) @"testing"
     self.successfulFetchDependency = dep;
 }
 
     self.successfulFetchDependency = dep;
 }
 
+- (void)holdFetchesUntil:(CKKSResultOperation*)holdOperation {
+    self.holdOperation = holdOperation;
+}
+
 -(void)cancel {
     [self.fetchScheduler cancel];
 }
 -(void)cancel {
     [self.fetchScheduler cancel];
 }
index 28d913d659a64aa53bc8fa1f69653aeced059a23..274d3dc909d20e0aaf8c0fc37fdc7877997c17e3 100644 (file)
@@ -31,6 +31,7 @@
 #if OCTAGON
 
 #import <CloudKit/CloudKit.h>
 #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,
 
 /*
  * This class hold the state for a particular zone: has the zone been created, have we subscribed to it,
@@ -56,6 +57,8 @@
 @property NSData* encodedChangeToken;
 @property NSDate* lastFetchTime;
 
 @property NSData* encodedChangeToken;
 @property NSDate* lastFetchTime;
 
+@property CKKSFixup lastFixup;
+
 @property CKKSRateLimiter* rateLimiter;
 @property NSData* encodedRateLimiter;
 
 @property CKKSRateLimiter* rateLimiter;
 @property NSData* encodedRateLimiter;
 
 + (instancetype) fromDatabase: (NSString*) ckzone error: (NSError * __autoreleasing *) error;
 + (instancetype) tryFromDatabase: (NSString*) ckzone error: (NSError * __autoreleasing *) error;
 
 + (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;
 
 - (CKServerChangeToken*) getChangeToken;
 - (void) setChangeToken: (CKServerChangeToken*) token;
index a433aff5a3eb8fe3f9a31cea351db3676e4d03b3..f8e808fed7d4f85572b3b1a0428fa179b6c6880d 100644 (file)
 #import <CloudKit/CloudKit.h>
 #import "CKKSZoneStateEntry.h"
 #import "keychain/ckks/CKKSRateLimiter.h"
 #import <CloudKit/CloudKit.h>
 #import "CKKSZoneStateEntry.h"
 #import "keychain/ckks/CKKSRateLimiter.h"
+#import "keychain/ckks/CKKSFixups.h"
 
 
 @implementation CKKSZoneStateEntry
 
 
 
 @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;
     if(self = [super init]) {
         _ckzone = ckzone;
         _ckzonecreated = ckzonecreated;
         _ckzonesubscribed = ckzonesubscribed;
         _encodedChangeToken = changetoken;
         _lastFetchTime = lastFetch;
+        _lastFixup = lastFixup;
 
         self.encodedRateLimiter = encodedRateLimiter;
     }
 
         self.encodedRateLimiter = encodedRateLimiter;
     }
@@ -65,8 +74,9 @@
             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.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 {
 }
 
 + (instancetype) state: (NSString*) ckzone {
     }
 
     if(!ret) {
     }
 
     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;
 }
     }
     return ret;
 }
 }
 
 + (NSArray<NSString*>*) sqlColumns {
 }
 
 + (NSArray<NSString*>*) sqlColumns {
-    return @[@"ckzone", @"ckzonecreated", @"ckzonesubscribed", @"changetoken", @"lastfetch", @"ratelimiter"];
+    return @[@"ckzone", @"ckzonecreated", @"ckzonesubscribed", @"changetoken", @"lastfetch", @"ratelimiter", @"lastFixup"];
 }
 
 - (NSDictionary<NSString*,NSString*>*) whereClauseToFindSelf {
 }
 
 - (NSDictionary<NSString*,NSString*>*) whereClauseToFindSelf {
          @"ckzonesubscribed": [NSNumber numberWithBool:self.ckzonesubscribed],
          @"changetoken": CKKSNilToNSNull([self.encodedChangeToken base64EncodedStringWithOptions:0]),
          @"lastfetch": CKKSNilToNSNull(self.lastFetchTime ? [dateFormat stringFromDate: self.lastFetchTime] : nil),
          @"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"]]
                                                    [[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]
             ];
 }
                                    encodedRateLimiter: [row[@"ratelimiter"] isEqual: [NSNull null]] ? nil : [[NSData alloc] initWithBase64EncodedString: row[@"ratelimiter"] options:0]
             ];
 }
index 9c98c2fd02e72e9aec2886e4e0955e0b1f72f23d..25d0a49935ec624ff5799f9bb54f538b94b44885 100644 (file)
@@ -25,6 +25,7 @@
 
 #import <Foundation/Foundation.h>
 #import <CloudKit/CloudKit.h>
 
 #import <Foundation/Foundation.h>
 #import <CloudKit/CloudKit.h>
+#import <CloudKit/CloudKit_Private.h>
 
 @interface CKOperationGroup (CKKS)
 +(instancetype) CKKSGroupWithName:(NSString*)name;
 
 @interface CKOperationGroup (CKKS)
 +(instancetype) CKKSGroupWithName:(NSString*)name;
 
 @interface NSError (CKKS)
 
 
 @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
 
 // 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
 #endif // OCTAGON
index 64be426567db3ee00849d0b3eb7924fda613a41c..c4185f4a623288542c5865bdd4d021dc07747fff 100644 (file)
 
 @implementation NSError (CKKS)
 
 
 @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) {
 -(bool) ckksIsCKErrorRecordChangedError {
     NSDictionary<CKRecordID*,NSError*>* partialErrors = self.userInfo[CKPartialErrorsByItemIDKey];
     if([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorPartialFailure && partialErrors) {
 
 @end
 
 
 @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
 #endif //OCTAGON
index d3bb1b3fa21f1eb0d176603f251afd3cfc2835d1..39fb4a2ce02b6af06f0767db99d119aac4780ee2 100644 (file)
@@ -87,6 +87,43 @@ NS_ASSUME_NONNULL_BEGIN
 @interface CKFetchRecordZoneChangesOperation () <CKKSFetchRecordZoneChangesOperation>;
 @end
 
 @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;
 /* APSConnection */
 @protocol CKKSAPSConnection <NSObject>
 + (instancetype)alloc;
diff --git a/keychain/ckks/NSOperationCategories.h b/keychain/ckks/NSOperationCategories.h
new file mode 100644 (file)
index 0000000..1255446
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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
+
diff --git a/keychain/ckks/NSOperationCategories.m b/keychain/ckks/NSOperationCategories.m
new file mode 100644 (file)
index 0000000..8d9725d
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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
diff --git a/keychain/ckks/proto/CKKSSerializedKey.proto b/keychain/ckks/proto/CKKSSerializedKey.proto
new file mode 100644 (file)
index 0000000..c8d9f39
--- /dev/null
@@ -0,0 +1,13 @@
+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;
+}
diff --git a/keychain/ckks/proto/source/CKKSSerializedKey.h b/keychain/ckks/proto/source/CKKSSerializedKey.h
new file mode 100644 (file)
index 0000000..01359ee
--- /dev/null
@@ -0,0 +1,44 @@
+// 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
+
diff --git a/keychain/ckks/proto/source/CKKSSerializedKey.m b/keychain/ckks/proto/source/CKKSSerializedKey.m
new file mode 100644 (file)
index 0000000..3935d3f
--- /dev/null
@@ -0,0 +1,193 @@
+// 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
+
diff --git a/keychain/ckks/tests/CKKSAESSIVEncryptionTests.m b/keychain/ckks/tests/CKKSAESSIVEncryptionTests.m
new file mode 100644 (file)
index 0000000..e1cecaf
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+ * 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
index 42d5af380e408e8f433ebeea41a4f08195f07857..007d5eacfadf95d3eca39c3e1f973da2893ce97d 100644 (file)
@@ -91,6 +91,8 @@
     CKKSViewManager* manager = [[CKKSViewManager alloc] initWithContainerName:containerName
                                                                        usePCS:SecCKKSContainerUsePCS
                                          fetchRecordZoneChangesOperationClass:[CKFetchRecordZoneChangesOperation class]
     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]
                                             modifySubscriptionsOperationClass:[CKModifySubscriptionsOperation class]
                                               modifyRecordZonesOperationClass:[CKModifyRecordZonesOperation class]
                                                            apsConnectionClass:[APSConnection class]
diff --git a/keychain/ckks/tests/CKKSEncryptionTests.m b/keychain/ckks/tests/CKKSEncryptionTests.m
deleted file mode 100644 (file)
index 1b2d007..0000000
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * 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
index 42ca79d6bba49e0dcf893df06a5a3698c731d07c..9c0afc62a687cf7a8a83873db89a0ef326cfcd40 100644 (file)
@@ -59,7 +59,9 @@ static NSString* tablePath = nil;
 {
     NSString* schema = @"CREATE table test (test_column INTEGER);";
     SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
 {
     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];
 }
     XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:tablePath]);
     [sqlTable close];
 }
@@ -68,7 +70,9 @@ static NSString* tablePath = nil;
 {
     NSString* schema = @"CREATE table test (test_column INTEGER);";
     SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
 {
     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)}];
 
     [sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(1)}];
     [sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(2)}];
@@ -82,6 +86,44 @@ static NSString* tablePath = nil;
     XCTAssertTrue([[sqlTable selectAllFrom:@"test" where:nil bindings:nil] count] == 0);
 }
 
     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
 @end
 
 @interface CKKSAnalyticsLoggerTests : CloudKitKeychainSyncingTestsBase
@@ -103,7 +145,7 @@ static NSString* tablePath = nil;
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
 
     NSError* error = nil;
     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(json, @"failed to generate logging json");
     XCTAssertNil(error, @"encourntered error getting logging json: %@", error);
 
@@ -111,6 +153,27 @@ static NSString* tablePath = nil;
     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, @"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
 }
 
 - (void)testSplunkDefaultTopicNameExists
@@ -173,6 +236,7 @@ static NSString* tablePath = nil;
     [[[self.keychainView waitForFetchAndIncomingQueueProcessing] completionHandlerDidRunCondition] wait:4 * NSEC_PER_SEC];
 
     NSDictionary* extraValues = [[CKKSAnalyticsLogger logger] extraValuesToUploadToServer];
     [[[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]);
     XCTAssertTrue([extraValues[@"keychain-TLKs"] boolValue]);
     XCTAssertTrue([extraValues[@"keychain-inSyncA"] boolValue]);
     XCTAssertTrue([extraValues[@"keychain-inSyncC"] boolValue]);
@@ -188,7 +252,7 @@ static NSString* tablePath = nil;
 
     NSData* json = nil;
     NSError* error = nil;
 
     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);
 }
     XCTAssertNotNil(json, @"Failed to get JSON after logging nil event");
     XCTAssertNil(error, @"Got error when grabbing JSON after logging nil event: %@", error);
 }
index fb0aa2e5fd59d068dc704f4d8638a043bed5d608..3a2e1688d582856e310adf688e69d335c74054a6 100644 (file)
 
     [super setUp];
 
 
     [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);
     // 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];
     // 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"];
     
     [self addGenericPassword:@"data" account:@"first"];
     [self addGenericPassword:@"data" account:@"second"];
     // Wait for uploads to happen
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
     [self waitForCKModifications];
     // 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];
     
     NSArray* items = [self mirrorItemsForExistingItems];
     _egoManifest = [CKKSEgoManifest newManifestForZone:self.keychainZoneID.zoneName withItems:items peerManifestIDs:@[] currentItems:@{} error:&error];
     return query;
 }
 
     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;
 {
     if (![CKKSManifest shouldSyncManifests]) {
         return;
index 1eab4c61a681498c78944cdd24f7b16fe21d3282..9ac3c81c792a3c6ca9c1cf41aecbfa20d595c4af 100644 (file)
@@ -24,6 +24,7 @@
 #import <XCTest/XCTest.h>
 
 #import "keychain/ckks/CKKSGroupOperation.h"
 #import <XCTest/XCTest.h>
 
 #import "keychain/ckks/CKKSGroupOperation.h"
+#import "keychain/ckks/CKKSCondition.h"
 
 // Helper Operations
 @interface CKKSResultCancelOperation : CKKSResultOperation
 
 // Helper Operations
 @interface CKKSResultCancelOperation : CKKSResultOperation
     [super tearDown];
 }
 
     [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;
 - (void)testResultOperation {
     CKKSResultOperation* op = [[CKKSResultOperation alloc] init];
     __weak __typeof(op) weakOp = op;
     XCTAssertNil(group.error, "Group operation: no error");
 }
 
     XCTAssertNil(group.error, "Group operation: no error");
 }
 
-- (void)testGroupOperationCancel {
+- (void)testGroupOperationSubOperationCancel {
     CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
 
     CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
     CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
 
     CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
 
     XCTAssertNil(op1.error, "First operation: no error");
     XCTAssertNil(op2.error, "Second operation: no error");
 
     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");
 }
 
     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];
 
 - (void)testGroupOperationTimeout {
     CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
 
     CKKSResultOperation* never = [[CKKSResultOperation alloc] init];
     [group addDependency: never];
 
     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];
     [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");
     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 {
 }
 
 - (void)testGroupOperationError {
     XCTAssertNil(group.error, "Group operation: no error");
 }
 
     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
 @end
index a69569297a3e50265120ebc20a1185a9b21ef1dc..0538f6707bcd74ebb183d0e2e64aa766a91bd05e 100644 (file)
 
 #import "keychain/ckks/tests/MockCloudKit.h"
 
 
 #import "keychain/ckks/tests/MockCloudKit.h"
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 #include <Security/SecureObjectSync/SOSCloudCircle.h>
 #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>
 #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;
 @interface CloudKitKeychainSyncingSOSIntegrationTests : CloudKitKeychainSyncingMockXCTest
 
 @property CKRecordZoneID*      engramZoneID;
 @property FakeCKZone*          healthZone;
 @property (readonly) ZoneKeys* healthZoneKeys;
 
 @property FakeCKZone*          healthZone;
 @property (readonly) ZoneKeys* healthZoneKeys;
 
+@property CKRecordZoneID*      applepayZoneID;
+@property CKKSKeychainView*    applepayView;
+@property FakeCKZone*          applepayZone;
+@property (readonly) ZoneKeys* applepayZoneKeys;
 
 @end
 
 
 @end
 
     self.healthZone = [[FakeCKZone alloc] initZone: self.healthZoneID];
     self.zones[self.healthZoneID] = self.healthZone;
     self.healthView = [[CKKSViewManager manager] findView:@"Health"];
     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 {
 }
 
 - (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 cancelAllOperations];
     [self.engramView waitUntilAllOperationsAreFinished];
+    self.engramView = nil;
 
     [self.manateeView cancelAllOperations];
     [self.manateeView waitUntilAllOperationsAreFinished];
 
     [self.manateeView cancelAllOperations];
     [self.manateeView waitUntilAllOperationsAreFinished];
+    self.manateeView = nil;
 
     [self.autoUnlockView cancelAllOperations];
     [self.autoUnlockView waitUntilAllOperationsAreFinished];
 
     [self.autoUnlockView cancelAllOperations];
     [self.autoUnlockView waitUntilAllOperationsAreFinished];
+    self.autoUnlockView = nil;
 
     [self.healthView cancelAllOperations];
     [self.healthView waitUntilAllOperationsAreFinished];
 
     [self.healthView cancelAllOperations];
     [self.healthView waitUntilAllOperationsAreFinished];
+    self.healthView = nil;
+
+    [self.applepayView cancelAllOperations];
+    [self.applepayView waitUntilAllOperationsAreFinished];
+    self.applepayView = nil;
 
     [super tearDown];
 }
 
     [super tearDown];
 }
     [self createAndSaveFakeKeyHierarchy: self.manateeZoneID];
     [self createAndSaveFakeKeyHierarchy: self.autoUnlockZoneID];
     [self createAndSaveFakeKeyHierarchy: self.healthZoneID];
     [self createAndSaveFakeKeyHierarchy: self.manateeZoneID];
     [self createAndSaveFakeKeyHierarchy: self.autoUnlockZoneID];
     [self createAndSaveFakeKeyHierarchy: self.healthZoneID];
+    [self createAndSaveFakeKeyHierarchy: self.applepayZoneID];
 }
 
 -(void)testAddEngramManateeItems {
 }
 
 -(void)testAddEngramManateeItems {
     [self startCKKSSubsystem];
 
     XCTestExpectation* healthChanged = [self expectChangeForView:self.healthZoneID.zoneName];
     [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;
 
     XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
     pcsChanged.inverted = YES;
 
     [self waitForExpectations:@[pcsChanged] timeout:0.2];
 }
 
     [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.
 
 -(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.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
 }
 
 -(NSString*)fileForStorage
     NSArray<NSData *>* icloudidentities = piggydata[@"idents"];
     NSArray<NSDictionary *>* tlks = piggydata[@"tlk"];
 
     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");
 
     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.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];
 }
 -(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];
 }
 -(void)deleteTLKMaterialsFromKeychain{
     [self deleteTLKMaterialFromKeychain: self.engramZoneID];
     [self deleteTLKMaterialFromKeychain: self.manateeZoneID];
     [self deleteTLKMaterialFromKeychain: self.autoUnlockZoneID];
     [self deleteTLKMaterialFromKeychain: self.healthZoneID];
+    [self deleteTLKMaterialFromKeychain: self.applepayZoneID];
 }
 
 -(void)waitForKeyHierarchyReadinesses {
 }
 
 -(void)waitForKeyHierarchyReadinesses {
     [self.engramView waitForKeyHierarchyReadiness];
     [self.autoUnlockView waitForKeyHierarchyReadiness];
     [self.healthView waitForKeyHierarchyReadiness];
     [self.engramView waitForKeyHierarchyReadiness];
     [self.autoUnlockView waitForKeyHierarchyReadiness];
     [self.healthView waitForKeyHierarchyReadiness];
+    [self.applepayView waitForKeyHierarchyReadiness];
 }
 
 -(void)testAcceptExistingAndUsePiggyKeyHierarchy {
 }
 
 -(void)testAcceptExistingAndUsePiggyKeyHierarchy {
index fe958b566e5f57bce58ec9fc5308fe198ca7fe37..d3fa83b44ca9ef75309fd47c2d94c946cda00675 100644 (file)
 }
 
 -(void)testCKKSZoneStateEntrySQL {
 }
 
 -(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];
 
                                                       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];
 
                                                            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");
                                                                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)");
     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 {
 }
 
 - (void)testGroupBy {
diff --git a/keychain/ckks/tests/CKKSServerValidationRecoveryTests.m b/keychain/ckks/tests/CKKSServerValidationRecoveryTests.m
new file mode 100644 (file)
index 0000000..4dc08d1
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * 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
diff --git a/keychain/ckks/tests/CKKSTLKSharingEncryptionTests.m b/keychain/ckks/tests/CKKSTLKSharingEncryptionTests.m
new file mode 100644 (file)
index 0000000..a61d499
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * 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
diff --git a/keychain/ckks/tests/CKKSTLKSharingTests.m b/keychain/ckks/tests/CKKSTLKSharingTests.m
new file mode 100644 (file)
index 0000000..71ce090
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ * 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
index ec3659ff2f2527d6b5487c7c9497a09251f709d1..2cf4f790af02c651c2bebcaa987b6b8eece817b9 100644 (file)
@@ -41,8 +41,9 @@
 #import "keychain/ckks/CKKSViewManager.h"
 #import "keychain/ckks/CKKSZoneStateEntry.h"
 
 #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"
 
 #import "keychain/ckks/tests/CKKSTests.h"
 #import "keychain/ckks/tests/CKKSTests+API.h"
 
     [self waitForExpectationsWithTimeout:5.0 handler:nil];
 }
 
     [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
 - (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
     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);
         [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);
 }
 
     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;
 -(void)testResetCloudKitZoneWhileLoggedOut {
     // We're "logged in to" cloudkit but not in circle.
     self.circleStatus = kSOSCCNotInCircle;
     // Spin up CKKS subsystem.
     [self startCKKSSubsystem];
 
     // 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];
 
     CKRecord* ckr = [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-F9C5-481E-98AC-5A507ACB2D85"];
     [self.keychainZone addToZone: ckr];
 
     self.circleStatus = kSOSCCInCircle;
     [self.accountStateTracker notifyCircleStatusChangeAndWaitForSignal];
 
     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);
 
     [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);
 }
 
     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
 @end
 
 #endif // OCTAGON
index f73f4c8dacf4e689227da5f3619808175e87f398..fe852f6d6023b8d9732286ba4254d06d495ba5e7 100644 (file)
 
     [self waitForExpectations:@[keychainChanged] timeout:1];
 
 
     [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");
 
     [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];
 
     // Still no current pointer.
     [self fetchCurrentPointerExpectingError:false];
 
     [self.keychainView waitForFetchAndIncomingQueueProcessing];
 
     [self waitForExpectations:@[keychainChanged] timeout:1];
     [self.keychainView waitForFetchAndIncomingQueueProcessing];
 
     [self waitForExpectations:@[keychainChanged] timeout:1];
-
     [self fetchCurrentPointer:false persistentRef:persistentRef];
 
     [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();
 }
 
     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
 @end
 
 #endif // OCTAGON
index d43b2c895022d80e6a09343b147818d4338a4f98..d0d7b3b0d12b797153cd900617bad9ddbc2021c0 100644 (file)
@@ -75,9 +75,6 @@
     self.keychainZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"keychain" ownerName:CKCurrentUserDefaultName];
     self.keychainZone = [[FakeCKZone alloc] initZone: self.keychainZoneID];
 
     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");
 
     // Wait for the ViewManager to be brought up
     XCTAssertEqual(0, [self.injectedManager.completedSecCKKSInitialize wait:4*NSEC_PER_SEC], "No timeout waiting for SecCKKSInitialize");
 
@@ -99,6 +96,9 @@
     NSDictionary* status = [self.keychainView status];
     (void)status;
 
     NSDictionary* status = [self.keychainView status];
     (void)status;
 
+    [self.keychainView cancelAllOperations];
+    [self.keychainView waitUntilAllOperationsAreFinished];
+
     self.keychainView = nil;
     self.keychainZoneID = nil;
 
     self.keychainView = nil;
     self.keychainZoneID = nil;
 
 
 - (void)testUploadInitialKeyHierarchy {
     // Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
 
 - (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];
 
     // Spin up CKKS subsystem.
     [self startCKKSSubsystem];
     }
 
     // After unlock, the key hierarchy should be created.
     }
 
     // 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];
 
     self.aksLockState = false;
     [self.lockStateTracker recheck];
 
 - (void)testUploadAndUseKeyHierarchy {
     // Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
 
 - (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];
 
 
     [self startCKKSSubsystem];
 
 
 - (void)testUploadInitialKeyHierarchyTriggersBackup {
     // Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
 
 - (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)
 
     // 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);
 
     // Spin up CKKS subsystem.
     [self startCKKSSubsystem];
 
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
-    OCMVerifyAllWithDelay(mockVM, 10);
-
-    [mockVM stopMocking];
+    OCMVerifyAllWithDelay(self.mockCKKSViewManager, 10);
 }
 
 - (void)testAcceptExistingKeyHierarchy {
 }
 
 - (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)
 - (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 putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
     [self saveTLKMaterialToKeychain:self.keychainZoneID];
 
     [self.lockStateTracker recheck];
 
     id partialKVMock = OCMPartialMock(self.keychainView);
     [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];
 
     // 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");
 
     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]];
 
     [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);
 }
 
     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
 
 - (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 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];
 
     // 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.
 
 - (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];
 
     // 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)
 
     // 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];
 
     // Let the horses loose
     [self startCKKSSubsystem];
 
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
 
 
     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.
 }
 
 - (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];
 
 
     [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];
     // 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"
     [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.
     // 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];
 
     [self.keychainView waitForKeyHierarchyReadiness];
     [self startCKKSSubsystem];
 
     // The CKKS subsystem should figure out the issue, and fix it.
     [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.keychainView waitForKeyHierarchyReadiness];
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
     [self startCKKSSubsystem];
 
     // The CKKS subsystem should figure out the issue, and fix it.
     [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.keychainView waitForKeyHierarchyReadiness];
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
     [self startCKKSSubsystem];
 
     // The CKKS subsystem should figure out the issue, and fix it.
     [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.keychainView waitForKeyHierarchyReadiness];
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
     [self startCKKSSubsystem];
 
     // The CKKS subsystem should figure out the issue, and fix it.
     [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.keychainView waitForKeyHierarchyReadiness];
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
     [self startCKKSSubsystem];
 
     // The CKKS subsystem should figure out the issue, and fix it.
     [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.keychainView waitForKeyHierarchyReadiness];
 
     [self startCKKSSubsystem];
 
     // The CKKS subsystem should figure out the issue, and fix it.
     [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.keychainView waitForKeyHierarchyReadiness];
 
 
 - (void)testRecoverFromDeletedKeysNewItem {
     // Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
 
 - (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];
 
     [self startCKKSSubsystem];
     [self.keychainView waitForKeyHierarchyReadiness];
 
 - (void)testRecoverFromDeletedKeysReceive {
     // Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
 
 - (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];
 
     [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.
     // 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 startCKKSSubsystem];
     [self.keychainView waitForKeyHierarchyReadiness];
     [self.keychainView waitForFetchAndIncomingQueueProcessing];
     [self.keychainView waitForOperationsOfClass:[CKKSHealKeyHierarchyOperation class]];
 
     [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 {
 }
 
 - (void)testRecoverFromBadCurrentKeyPointer {
     [self startCKKSSubsystem];
 
     // The CKKS subsystem should figure out the issue, and fix it.
     [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);
 }
 
 
     [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];
 - (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.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];
     [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);
 }
 
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
 }
 
-- (void)testRecoverFromCloudKitZoneNotFound {
+- (void)testRecoverFromCloudKitZoneNotFoundZoneDeletionSuccess {
     // Test starts with nothing in database, but one in our fake CloudKit.
     [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
 
     // 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 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.
     // 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:@{}];
     NSError* zoneNotFoundError = [[CKPrettyError alloc] initWithDomain:CKErrorDomain
                                                                   code:CKErrorZoneNotFound
                                                               userInfo:@{}];
                                                   userInfo:@{CKPartialErrorsByItemIDKey: @{self.keychainZoneID:zoneNotFoundError}}];
     [self.keychainZone failNextFetchWith:error];
 
                                                   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];
     [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);
 }
 
     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;
 - (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.
     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];
 
     self.circleStatus = kSOSCCInCircle;
     [self.accountStateTracker notifyCircleStatusChangeAndWaitForSignal];
 
 - (void)testCloudKitLogoutLogin {
     // Test starts with nothing in database. We expect some sort of TLK/key hierarchy upload.
 
 - (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];
 
 
     [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.
     // 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"]];
 
     // 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];
 
             }
            runAfterModification:nil];
 
-    [self.keychainView updateDeviceState:false ckoperationGroup:nil];
+    [self.keychainView updateDeviceState:false waitForKeyHierarchyInitialization:2*NSEC_PER_SEC ckoperationGroup:nil];
 
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
 }
 
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
 }
             }
            runAfterModification:nil];
 
             }
            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
     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!
     [op waitUntilFinished];
 
     // But not rate-limiting works just fine!
                          zoneID:self.keychainZoneID
             checkModifiedRecord:nil
            runAfterModification:nil];
                          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];
 
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
     [op waitUntilFinished];
 
                          zoneID:self.keychainZoneID
             checkModifiedRecord:nil
            runAfterModification:nil];
                          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, 12);
     [op waitUntilFinished];
 }
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
 
     // Check that an immediate rate-limited retry doesn't upload anything
     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];
 }
 
     [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.
 
 - (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");
 
     [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]}
 
     __weak __typeof(self) weakSelf = self;
     [self expectCKModifyRecords: @{SecCKRecordDeviceStateType: [NSNumber numberWithInt:1]}
             }
            runAfterModification:nil];
 
             }
            runAfterModification:nil];
 
-    [self.keychainView updateDeviceState:false ckoperationGroup:nil];
+    [self.keychainView updateDeviceState:false waitForKeyHierarchyInitialization:500*NSEC_PER_MSEC ckoperationGroup:nil];
 
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
 }
 
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
 }
 
     [self startCKKSSubsystem];
     XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:8*NSEC_PER_SEC], "CKKS entered waitfortlk");
 
     [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");
 
     // 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]}
 
     __weak __typeof(self) weakSelf = self;
     [self expectCKModifyRecords: @{SecCKRecordDeviceStateType: [NSNumber numberWithInt:1]}
             }
            runAfterModification:nil];
 
             }
            runAfterModification:nil];
 
-    [self.keychainView updateDeviceState:false ckoperationGroup:nil];
+    [self.keychainView updateDeviceState:false waitForKeyHierarchyInitialization:500*NSEC_PER_MSEC ckoperationGroup:nil];
 
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
 }
 
     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");
     // 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]}
 
     __weak __typeof(self) weakSelf = self;
     [self expectCKModifyRecords: @{SecCKRecordDeviceStateType: [NSNumber numberWithInt:1]}
             }
            runAfterModification:nil];
 
             }
            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];
     OCMVerifyAllWithDelay(self.mockDatabase, 8);
 
     [op waitUntilFinished];
diff --git a/keychain/ckks/tests/CloudKitKeychainSyncingFixupTests.m b/keychain/ckks/tests/CloudKitKeychainSyncingFixupTests.m
new file mode 100644 (file)
index 0000000..33004bf
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * 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
index 46ee617aab9d6d95b0dded9acdc3921c8eee069e..65a39adcefb92679a51b62badd94521d910ddff2 100644 (file)
@@ -22,6 +22,7 @@
  */
 #import "CloudKitMockXCTest.h"
 #import "keychain/ckks/CKKS.h"
  */
 #import "CloudKitMockXCTest.h"
 #import "keychain/ckks/CKKS.h"
+#import "keychain/ckks/CKKSControl.h"
 #import "keychain/ckks/CKKSCurrentKeyPointer.h"
 
 @class CKKSKey;
 #import "keychain/ckks/CKKSCurrentKeyPointer.h"
 
 @class CKKSKey;
@@ -29,6 +30,8 @@
 
 @interface ZoneKeys : CKKSCurrentKeySet
 @property CKKSKey* rolledTLK;
 
 @interface ZoneKeys : CKKSCurrentKeySet
 @property CKKSKey* rolledTLK;
+
+- (instancetype)initLoadingRecordsFromZone:(FakeCKZone*)zone;
 @end
 
 /*
 @end
 
 /*
 
 @interface CloudKitKeychainSyncingMockXCTest : CloudKitMockXCTest
 
 
 @interface CloudKitKeychainSyncingMockXCTest : CloudKitMockXCTest
 
+@property CKKSControl* ckksControl;
+
 @property id mockCKKSKey;
 
 @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
 @property NSMutableDictionary<CKRecordZoneID*, ZoneKeys*>* keys;
 
 // Pass in an oldTLK to wrap it to the new TLK; otherwise, pass nil
 - (NSMutableDictionary*)SOSPiggyBackCopyFromKeychain;
 - (NSMutableArray<NSData *>*) SOSPiggyICloudIdentities;
 
 - (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)
 - (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;
 
 // 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
 @end
index 3dfcbe9edae3731a3dcc800697082a8e13b9d1e9..1aaeecce66c67fda1db3b5dc82d70f3c1b01c4a8 100644 (file)
 #import "keychain/ckks/CKKSViewManager.h"
 #import "keychain/ckks/CKKSZoneStateEntry.h"
 #import "keychain/ckks/CKKSManifest.h"
 #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"
 #import "Security/SecureObjectSync/SOSAccount.h"
+#pragma clang diagnostic pop
+
 @implementation ZoneKeys
 @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 {
 @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);
     (void)[CKKSManifest shouldSyncManifests]; // perfrom initialization
     SecCKKSSetSyncManifests(false);
     SecCKKSSetEnforceManifests(false);
                                                stashTLK:[OCMArg anyObjectRef]
                                                   error:[OCMArg anyObjectRef]]
              ).andCall(self, @selector(handleLockSaveKeyMaterialToKeychain:stashTLK:error:));
                                                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 {
 }
 
 - (void)tearDown {
 
     [super tearDown];
     self.keys = nil;
 
     [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 {
 }
 
 - (void)createClassCItemAndWaitForUpload:(CKRecordZoneID*)zoneID account:(NSString*)account {
     self.keys[zoneID] = nil;
 
     CKKSKey* oldTLK = zonekeys.tlk;
     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];
 
     [self createFakeKeyHierarchy: zoneID oldTLK:oldTLK];
     [self putFakeKeyHierarchyInCloudKit: zoneID];
     XCTAssertNil( (__bridge NSError*)cferror, @"no error with transaction");
     CFReleaseNull(cferror);
 }
     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;
 {
     SOSFullPeerInfoRef result = NULL;
     SecKeyRef publicKey = NULL;
@@ -247,10 +330,13 @@ static SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(CFStringRef name, SecKey
     *outSigningKey = GeneratePermanentFullECKey(256, name, error);
     
     *outOctagonSigningKey = GeneratePermanentFullECKey(384, name, error);
     *outSigningKey = GeneratePermanentFullECKey(256, name, error);
     
     *outOctagonSigningKey = GeneratePermanentFullECKey(384, name, error);
-    
+    *outOctagonEncryptionKey = GeneratePermanentFullECKey(384, name, error);
+
     gestalt = SOSCreatePeerGestaltFromName(name);
     
     gestalt = SOSCreatePeerGestaltFromName(name);
     
-    result = SOSFullPeerInfoCreate(NULL, gestalt, NULL, *outSigningKey, *outOctagonSigningKey, error);
+    result = SOSFullPeerInfoCreate(NULL, gestalt, NULL, *outSigningKey,
+                                   *outOctagonSigningKey, *outOctagonEncryptionKey,
+                                   error);
     
     CFReleaseNull(gestalt);
     CFReleaseNull(publicKey);
     
     CFReleaseNull(gestalt);
     CFReleaseNull(publicKey);
@@ -261,9 +347,10 @@ static SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(CFStringRef name, SecKey
 {
     SecKeyRef signingKey = NULL;
     SecKeyRef octagonSigningKey = NULL;
 {
     SecKeyRef signingKey = NULL;
     SecKeyRef octagonSigningKey = NULL;
+    SecKeyRef octagonEncryptionKey = NULL;
     NSMutableArray<NSData *>* icloudidentities = [NSMutableArray array];
 
     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)
     
     NSData *data = CFBridgingRelease(SOSPeerInfoCopyData(SOSFullPeerInfoGetPeerInfo(fpi), NULL));
     if (data)
@@ -271,7 +358,8 @@ static SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(CFStringRef name, SecKey
     
     CFReleaseNull(signingKey);
     CFReleaseNull(octagonSigningKey);
     
     CFReleaseNull(signingKey);
     CFReleaseNull(octagonSigningKey);
-    
+    CFReleaseNull(octagonEncryptionKey);
+
     return icloudidentities;
 }
 static CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name)
     return icloudidentities;
 }
 static CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name)
@@ -343,20 +431,86 @@ static CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name)
     XCTAssertNil(error, @"Saved Class C material to keychain");
 }
 
     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];
 - (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:
 }
 
 // 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],
 
     __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
         deletedRecordTypeCounts:nil
                          zoneID:zoneID
             checkModifiedRecord:nil
@@ -404,9 +558,105 @@ static CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name)
                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");
                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{
 - (void)checkNoCKKSData: (CKKSKeychainView*) view {
     // Test that there are no items in the database
     [view dispatchSync:^bool{
index b5dd2435e8664a12cfd9ab0b7ee45cf8e4daaf60..ea3519f0349238dfd0c9d1fe07bc865b251a2d8b 100644 (file)
@@ -48,6 +48,8 @@
 @property id mockFakeCKModifyRecordZonesOperation;
 @property id mockFakeCKModifySubscriptionsOperation;
 @property id mockFakeCKFetchRecordZoneChangesOperation;
 @property id mockFakeCKModifyRecordZonesOperation;
 @property id mockFakeCKModifySubscriptionsOperation;
 @property id mockFakeCKFetchRecordZoneChangesOperation;
+@property id mockFakeCKFetchRecordsOperation;
+@property id mockFakeCKQueryOperation;
 
 @property id mockAccountStateTracker;
 
 
 @property id mockAccountStateTracker;
 
@@ -70,6 +72,7 @@
 @property NSBlockOperation* ckaccountHoldOperation;
 
 @property NSBlockOperation* ckModifyHoldOperation;
 @property NSBlockOperation* ckaccountHoldOperation;
 
 @property NSBlockOperation* ckModifyHoldOperation;
+@property NSBlockOperation* ckFetchHoldOperation;
 
 @property bool silentFetchesAllowed;
 
 
 @property bool silentFetchesAllowed;
 
 
 - (void)expectCKDeleteItemRecords: (NSUInteger) expectedNumberOfRecords zoneID: (CKRecordZoneID*) zoneID;
 
 
 - (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
 
 - (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 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;
 
 // 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;
 
 // 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
 #endif // OCTAGON
 @end
index d965ac99fde60178e9033f759ed5e302363915ea..e0af138d62753774c49f0502b2cff8618dc1d260 100644 (file)
     self.mockFakeCKFetchRecordZoneChangesOperation = OCMClassMock([FakeCKFetchRecordZoneChangesOperation class]);
     OCMStub([self.mockFakeCKFetchRecordZoneChangesOperation ckdb]).andReturn(self.zones);
 
     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;
     OCMStub([self.mockDatabase addOperation: [OCMArg checkWithBlock:^BOOL(id obj) {
         __strong __typeof(self) strongSelf = weakSelf;
         BOOL matches = NO;
                 matches = YES;
 
                 FakeCKFetchRecordZoneChangesOperation *frzco = (FakeCKFetchRecordZoneChangesOperation *)obj;
                 matches = YES;
 
                 FakeCKFetchRecordZoneChangesOperation *frzco = (FakeCKFetchRecordZoneChangesOperation *)obj;
+                [frzco addNullableDependency:strongSelf.ckFetchHoldOperation];
                 [strongSelf.operationQueue addOperation: frzco];
             }
         }
         return matches;
     }]]);
 
                 [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.testZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName];
 
     }];
     self.ckksHoldOperation.name = @"ckks-hold";
 
     }];
     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]);
 
     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];
 
 
     [CKKSViewManager resetManager:false setTo:self.injectedManager];
 
 }
 
 -(void)expectCKFetch {
 }
 
 -(void)expectCKFetch {
+    [self expectCKFetchAndRunBeforeFinished: nil];
+}
+
+-(void)expectCKFetchAndRunBeforeFinished: (void (^)())blockAfterFetch {
     // Create an object for the block to retain and modify
     BoolHolder* runAlready = [[BoolHolder alloc] init];
 
     // Create an object for the block to retain and modify
     BoolHolder* runAlready = [[BoolHolder alloc] init];
 
             runAlready.state = true;
 
             FakeCKFetchRecordZoneChangesOperation *frzco = (FakeCKFetchRecordZoneChangesOperation *)obj;
             runAlready.state = true;
 
             FakeCKFetchRecordZoneChangesOperation *frzco = (FakeCKFetchRecordZoneChangesOperation *)obj;
+            frzco.blockAfterFetch = blockAfterFetch;
+            [frzco addNullableDependency: strongSelf.ckFetchHoldOperation];
             [strongSelf.operationQueue addOperation: frzco];
         }
         return matches;
     }]];
 }
 
             [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)startCKKSSubsystem {
     [self startCKAccountStatusMock];
     [self startCKKSSubsystemOnly];
 }
 
 -(void)holdCloudKitModifications {
 }
 
 -(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.");
     }];
     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)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* 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
         deletedRecordTypeCounts:nil
                          zoneID:zoneID
             checkModifiedRecord:nil
 
     if(SecCKKSIsEnabled()) {
         // Ensure we don't have any blocking operations
 
     if(SecCKKSIsEnabled()) {
         // Ensure we don't have any blocking operations
+        self.accountStatus = CKAccountStatusNoAccount;
         [self startCKKSSubsystem];
 
         [self waitForCKModifications];
         [self startCKKSSubsystem];
 
         [self waitForCKModifications];
     [self.injectedManager cancelPendingOperations];
     [CKKSViewManager resetManager:true setTo:nil];
     self.injectedManager = nil;
     [self.injectedManager cancelPendingOperations];
     [CKKSViewManager resetManager:true setTo:nil];
     self.injectedManager = nil;
+    [self.mockCKKSViewManager stopMocking];
+    self.mockCKKSViewManager = nil;
 
     [self.mockAccountStateTracker stopMocking];
     self.mockAccountStateTracker = nil;
 
     [self.mockAccountStateTracker stopMocking];
     self.mockAccountStateTracker = nil;
     [self.mockLockStateTracker stopMocking];
     self.mockLockStateTracker = nil;
 
     [self.mockLockStateTracker stopMocking];
     self.mockLockStateTracker = nil;
 
-    [self.mockCKKSViewManager stopMocking];
-    self.mockCKKSViewManager = nil;
-
     [self.mockFakeCKModifyRecordZonesOperation stopMocking];
     self.mockFakeCKModifyRecordZonesOperation = nil;
 
     [self.mockFakeCKModifyRecordZonesOperation stopMocking];
     self.mockFakeCKModifyRecordZonesOperation = nil;
 
     [self.mockFakeCKFetchRecordZoneChangesOperation stopMocking];
     self.mockFakeCKFetchRecordZoneChangesOperation = 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;
 
     [self.mockDatabase stopMocking];
     self.mockDatabase = nil;
 
     return key;
 }
 
     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
 
 
 @end
 
index e64c10bb028eed0f6e5a5da947d12c07b0fe8817..9b96a083b62aeb48cb1e6839cd26c8c001abc9d0 100644 (file)
@@ -43,7 +43,6 @@ typedef NSMutableDictionary<CKRecordZoneID*, FakeCKZone*> FakeCKDatabase;
 +(FakeCKDatabase*) ckdb;
 @end
 
 +(FakeCKDatabase*) ckdb;
 @end
 
-
 @interface FakeCKModifySubscriptionsOperation : NSBlockOperation <CKKSModifySubscriptionsOperation>
 @property (nullable) NSError* subscriptionError;
 @property (nonatomic, nullable) NSMutableArray<CKSubscription *> *subscriptionsSaved;
 @interface FakeCKModifySubscriptionsOperation : NSBlockOperation <CKKSModifySubscriptionsOperation>
 @property (nullable) NSError* subscriptionError;
 @property (nonatomic, nullable) NSMutableArray<CKSubscription *> *subscriptionsSaved;
@@ -54,6 +53,15 @@ typedef NSMutableDictionary<CKRecordZoneID*, FakeCKZone*> FakeCKDatabase;
 
 @interface FakeCKFetchRecordZoneChangesOperation : NSOperation <CKKSFetchRecordZoneChangesOperation>
 +(FakeCKDatabase*) ckdb;
 
 @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
 
 
 @end
 
 
index ea44543f9f45b9a82148615e17b3226a55810d0b..8a06f416b0949ac76777e5fc0dfa337df9c758e8 100644 (file)
     }
 
     for(CKRecordZoneID* zoneID in self.recordZoneIDsToDelete) {
     }
 
     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);
 
         self.recordZoneChangeTokensUpdatedBlock(zoneID, zone.currentChangeToken, nil);
         self.recordZoneFetchCompletionBlock(zoneID, zone.currentChangeToken, nil, NO, nil);
+
+        if(self.blockAfterFetch) {
+            self.blockAfterFetch();
+        }
+
         self.fetchRecordZoneChangesCompletionBlock(nil);
     }
 }
         self.fetchRecordZoneChangesCompletionBlock(nil);
     }
 }
 }
 @end
 
 }
 @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
 
 // Do literally nothing
 @implementation FakeAPSConnection
     [self rollChangeToken];
 
     record.etag = [self.currentChangeToken description];
     [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;
 }
     record.modificationDate = [NSDate date];
     self.currentDatabase[record.recordID] = record;
 }
diff --git a/keychain/ckksctl/ckksctl.h b/keychain/ckksctl/ckksctl.h
deleted file mode 100644 (file)
index b7032c6..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-//  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
index 66d41a607b1092f2b2ec1be08852cd3fedf4dd9e..9bb556b680ab8ad57453f03f2e0240ab06940f44 100644 (file)
@@ -10,8 +10,7 @@
 #import <err.h>
 
 #import "keychain/ckks/CKKS.h"
 #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"
 
 
 #include "lib/SecArgParse.h"
 
@@ -89,48 +88,35 @@ static void print_entry(id k, id v, int ind)
     }
 }
 
     }
 }
 
-@interface CKKSControl ()
-@property NSXPCConnection *connection;
+@interface CKKSControlCLI : NSObject
+@property CKKSControl* control;
 @end
 
 @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;
 }
 
 
     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);
 
 {
     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;
         }];
         [counters enumerateKeysAndObjectsUsingBlock:^(NSString * key, NSNumber * obj, BOOL *stop) {
             perfDict[key] = obj;
         }];
+
         dispatch_semaphore_signal(sema);
     }];
 
         dispatch_semaphore_signal(sema);
     }];
 
@@ -147,18 +133,15 @@ static void print_entry(id k, id v, int ind)
     printf("Beginning local reset for %s...\n", view ? [[view description] UTF8String] : "all zones");
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
     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");
 
     if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
         printf("\n\nError: timed out waiting for response\n");
@@ -171,15 +154,11 @@ static void print_entry(id k, id v, int ind)
     printf("Beginning CloudKit reset for %s...\n", view ? [[view description] UTF8String] : "all zones");
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
     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("CloudKit Reset complete.\n");
         } else {
-            printf("Reset error: %s\n", [[result description] UTF8String]);
+            nsprintf(@"Reset error: %@\n", error);
         }
         dispatch_semaphore_signal(sema);
     }];
         }
         dispatch_semaphore_signal(sema);
     }];
@@ -195,15 +174,11 @@ static void print_entry(id k, id v, int ind)
     printf("Beginning resync for %s...\n", view ? [[view description] UTF8String] : "all zones");
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
     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 success.\n");
         } else {
-            printf("resync errored: %s\n", [[result description] UTF8String]);
+            nsprintf(@"resync errored: %@\n", error);
         }
         dispatch_semaphore_signal(sema);
     }];
         }
         dispatch_semaphore_signal(sema);
     }];
@@ -219,10 +194,7 @@ static void print_entry(id k, id v, int ind)
     printf("Getting analytics sysdiagnose....\n");
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
     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);
         }
         if (sysdiagnose && !error) {
             nsprintf(@"Analytics sysdiagnose:\n\n%@", sysdiagnose);
         }
@@ -243,10 +215,7 @@ static void print_entry(id k, id v, int ind)
     printf("Getting analytics json....\n");
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
     
     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]);
         }
         if (json && !error) {
             nsprintf(@"Analytics JSON:\n\n%@", [[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding]);
         }
@@ -267,10 +236,7 @@ static void print_entry(id k, id v, int ind)
     printf("Uploading....\n");
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
     
     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");
         }
         if (success) {
             nsprintf(@"successfully uploaded analytics data");
         }
@@ -286,21 +252,17 @@ static void print_entry(id k, id v, int ind)
     }
 }
 
     }
 }
 
-- (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);
 
     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(error) {
             status[@"error"] = [error description];
         }
 
-        if(result.count == 0u) {
+        if(result.count <= 1u) {
             printf("No CKKS views are active.\n");
         }
 
             printf("No CKKS views are active.\n");
         }
 
@@ -318,28 +280,49 @@ static void print_entry(id k, id v, int ind)
     return status;
 }
 
     return status;
 }
 
-- (void)status_custom: (NSString*) view {
+- (void)printHumanReadableStatus: (NSString*) view {
 #if OCTAGON
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
 #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(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");
         }
 
             printf("No CKKS views are active.\n");
         }
 
-        for(NSDictionary* viewStatus in result) {
+        for(NSDictionary* viewStatus in remainingViews) {
             NSMutableDictionary* status = [viewStatus mutableCopy];
 
             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");
             NSString* viewName = pop(status,@"view");
             NSString* accountStatus = pop(status,@"ckaccountstatus");
             NSString* lockStateTracker = pop(status,@"lockstatetracker");
@@ -364,6 +347,8 @@ static void print_entry(id k, id v, int ind)
             NSDictionary* keys = pop(status,@"keys");
             NSDictionary* ckmirror = pop(status,@"ckmirror");
             NSArray* devicestates = pop(status, @"devicestates");
             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");
 
             NSString* zoneSetupOperation                  = pop(status,@"zoneSetupOperation");
             NSString* viewSetupOperation                  = pop(status,@"viewSetupOperation");
@@ -408,6 +393,8 @@ static void print_entry(id k, id v, int ind)
             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("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]);
             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]);
@@ -446,11 +433,7 @@ static void print_entry(id k, id v, int ind)
 #if OCTAGON
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
 #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(error) {
             printf("Error fetching: %s\n", [[error description] UTF8String]);
         } else {
@@ -470,11 +453,7 @@ static void print_entry(id k, id v, int ind)
 #if OCTAGON
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
 #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 {
         if(error) {
             printf("Error pushing: %s\n", [[error description] UTF8String]);
         } else {
@@ -539,41 +518,39 @@ int main(int argc, char **argv)
     }
 
     @autoreleasepool {
     }
 
     @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;
 
 
         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];
             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) {
         } else if(fetch) {
             [ctl fetch:view];
         } else if(push) {
index 93f27603736bfa13278da62ca7f37fb925444853..57ccd3e0fca53a84c0462ee3c1b148382014f5ec 100644 (file)
@@ -278,6 +278,7 @@ static bool
 _kb_save_bag_to_disk(service_user_record_t * ur, const char * bag_file, void * data, size_t length)
 {
     bool result = false;
 _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);
     int fd = -1;
 
     require(bag_file, done);
@@ -285,9 +286,17 @@ _kb_save_bag_to_disk(service_user_record_t * ur, const char * bag_file, void * d
     _set_thread_credentials(ur);
     require(_kb_verify_create_path(ur), 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;
 
 
     result = true;
 
index 37147d53864933a596edbbaff0a4ea625e65ba89..93fa1641df1edd6bd9096e368a809e61697f5465 100644 (file)
@@ -546,6 +546,17 @@ CSSM_RETURN SecDigestGetData(CSSM_ALGORITHMS alg, CSSM_DATA* digest, const CSSM_
 bool SecCertificateIsValidX(SecCertificateRef certificate, CFAbsoluteTime verifyTime)
     __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_7, __MAC_10_9, __IPHONE_NA, __IPHONE_NA);
 
 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
 #endif /* SEC_OS_OSX */
 
 __END_DECLS
index adf34d4e37043332312027a509848f746ae16dc9..2f70ab0b022fb4177aac01a62873139a2ba7f9fb 100644 (file)
@@ -1487,7 +1487,7 @@ SecPolicyRef SecPolicyCreateAppleExternalDeveloper(void)
 /*!
  @function SecPolicyCreateAppleSoftwareSigning
  @abstract Returns a policy object for verifying the Apple Software Signing certificate.
 /*!
  @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.
  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.
index 00b64c06b3074d740f5744a212551842befb8ca7..3d2828a783e9b8cd66cd525f4645b1a57d278daa 100644 (file)
@@ -27,9 +27,8 @@ OTHER_LDFLAGS_CLOUDKIT_BRIDGE_NO = -framework CloudKit
 OTHER_LDFLAGS_CLOUDKIT_BRIDGE_YES =
 OTHER_LDFLAGS_CLOUDKIT = $(OTHER_LDFLAGS_CLOUDKIT_BRIDGE_$(BRIDGE))
 
 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 =
 
 OTHER_LDFLAGS_SHAREDWEBCREDENTIALS_IOS_NO = -framework SharedWebCredentials
 OTHER_LDFLAGS_SHAREDWEBCREDENTIALS_IOS_YES =
index c0cbe4fba306fc140ca58aaf5c10b5c4e83ad147..6a48d3cb76b81ee24a5df448a4ff919ed2c29c8a 100644 (file)
@@ -7,10 +7,11 @@ HEADER_SEARCH_PATHS = $(PROJECT_DIR) $(PROJECT_DIR)/OSX/libsecurity_keychain/lib
 ARCHS[sdk=macosx*] = $(ARCHS_STANDARD)
 
 #include "xcconfig/PlatformFeatures.xcconfig"
 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
 
 // 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
 
 
 SECURITY_FUZZER_BASE_DIR = /AppleInternal/CoreOS/Fuzzers/Security
 
diff --git a/xcconfig/Version.xcconfig b/xcconfig/Version.xcconfig
new file mode 100644 (file)
index 0000000..a6a747b
--- /dev/null
@@ -0,0 +1,9 @@
+
+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)
index 504ae89ecbd2261d9a7f8d9064574f4aeb96db0a..5c058bc8fc97ff84d494c0d14bc2e4773d2131f3 100644 (file)
@@ -2,6 +2,6 @@
 // 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.
 
 // 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