2  * Copyright (c) 2018 Apple Inc. All Rights Reserved.
 
   4  * @APPLE_LICENSE_HEADER_START@
 
   6  * This file contains Original Code and/or Modifications of Original Code
 
   7  * as defined in and that are subject to the Apple Public Source License
 
   8  * Version 2.0 (the 'License'). You may not use this file except in
 
   9  * compliance with the License. Please obtain a copy of the License at
 
  10  * http://www.opensource.apple.com/apsl/ and read it before using this
 
  13  * The Original Code and all software distributed under the License are
 
  14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 
  15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 
  16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 
  17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 
  18  * Please see the License for the specific language governing rights and
 
  19  * limitations under the License.
 
  21  * @APPLE_LICENSE_HEADER_END@
 
  26 #import "utilities/debugging.h"
 
  27 #import <Security/SecKey.h>
 
  28 #import <Security/SecKeyPriv.h>
 
  30 #import "keychain/ot/OTCuttlefishContext.h"
 
  31 #import "keychain/ot/OTOperationDependencies.h"
 
  32 #import "keychain/ot/OTPrepareOperation.h"
 
  34 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
 
  35 #import "keychain/ot/ObjCImprovements.h"
 
  37 @interface OTPrepareOperation ()
 
  38 @property OTOperationDependencies* deps;
 
  39 @property NSOperation* finishedOp;
 
  42 @implementation OTPrepareOperation
 
  43 @synthesize intendedState = _intendedState;
 
  44 @synthesize nextState = _nextState;
 
  46 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
 
  47                        intendedState:(OctagonState*)intendedState
 
  48                           errorState:(OctagonState*)errorState
 
  49                           deviceInfo:(OTDeviceInformation*)deviceInfo
 
  50                       policyOverride:(TPPolicyVersion* _Nullable)policyOverride
 
  53     if((self = [super init])) {
 
  56         _deviceInfo = deviceInfo;
 
  59         _intendedState = intendedState;
 
  60         _nextState = errorState;
 
  62         _policyOverride = policyOverride;
 
  69     secnotice("octagon", "preparing an identity");
 
  71     self.finishedOp = [[NSOperation alloc] init];
 
  72     [self dependOnBeforeGroupFinished:self.finishedOp];
 
  74     NSString* bottleSalt = nil;
 
  76     NSError *authKitError = nil;
 
  77     NSString *altDSID = [self.deps.authKitAdapter primaryiCloudAccountAltDSID:&authKitError];
 
  83         secnotice("octagon-sos", "AuthKit doesn't know about the altDSID: %@", authKitError);
 
  85         NSError* accountError = nil;
 
  86         OTAccountMetadataClassC* account = [self.deps.stateHolder loadOrCreateAccountMetadata:&accountError];
 
  88         if(account && !accountError) {
 
  89             secnotice("octagon", "retrieved account, altdsid is: %@", account.altDSID);
 
  90             bottleSalt = account.altDSID;
 
  92         if(accountError || !account){
 
  93             secerror("failed to rerieve account object: %@", accountError);
 
  98     // But, if this device is SOS-enabled and SOS is present, use the SOS octagon keys (if present)
 
  99     NSData* signingKeyPersistRef = nil;
 
 100     NSData* encryptionKeyPersistRef = nil;
 
 101     if(self.deps.sosAdapter.sosEnabled) {
 
 102         secnotice("octagon-sos", "Investigating use of Octagon keys from SOS identity");
 
 104         NSError* error = nil;
 
 105         id<CKKSSelfPeer> sosSelf = [self.deps.sosAdapter currentSOSSelf:&error];
 
 107         if(!sosSelf || error) {
 
 108             secnotice("octagon-sos", "Failed to get the current SOS self: %@", error);
 
 110             // Fetch the persistent references for our signing and encryption keys
 
 111             OSStatus status = errSecSuccess;
 
 112             CFDataRef cfSigningKeyPersistRef = NULL;
 
 113             status = SecKeyCopyPersistentRef(sosSelf.signingKey.secKey, &cfSigningKeyPersistRef);
 
 114             if(status != errSecSuccess || !cfSigningKeyPersistRef) {
 
 115                 secnotice("octagon-sos", "Failed to get the persistent ref for our SOS signing key: %d", (int)status);
 
 117                 CFDataRef cfEncryptionKeyPersistRef = NULL;
 
 118                 status = SecKeyCopyPersistentRef(sosSelf.encryptionKey.secKey, &cfEncryptionKeyPersistRef);
 
 119                 if(status != errSecSuccess || !cfEncryptionKeyPersistRef) {
 
 120                     secnotice("octagon-sos", "Failed to get the persistent ref for our SOS encryption key: %d", (int)status);
 
 121                     CFReleaseNull(cfSigningKeyPersistRef);
 
 122                     CFReleaseNull(cfEncryptionKeyPersistRef);
 
 124                     // We only want to use these keys if we successfully have both
 
 125                     signingKeyPersistRef = CFBridgingRelease(cfSigningKeyPersistRef);
 
 126                     encryptionKeyPersistRef = CFBridgingRelease(cfEncryptionKeyPersistRef);
 
 132     NSError* persistError = nil;
 
 133     BOOL persisted = [self.deps.stateHolder persistOctagonJoinAttempt:OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED error:&persistError];
 
 134     if(!persisted || persistError) {
 
 135         secerror("octagon: failed to save 'attempted join' state: %@", persistError);
 
 138     // Note: we pass syncUserControllableViews as FOLLOWING here, with the intention that
 
 139     // it will be set later, when this peer decides who it trusts and accepts their value.
 
 141     [self.deps.cuttlefishXPCWrapper prepareWithContainer:self.deps.containerName
 
 142                                                  context:self.deps.contextID
 
 144                                                machineID:self.deviceInfo.machineID
 
 145                                               bottleSalt:bottleSalt
 
 146                                                 bottleID:[NSUUID UUID].UUIDString
 
 147                                                  modelID:self.deviceInfo.modelID
 
 148                                               deviceName:self.deviceInfo.deviceName
 
 149                                             serialNumber:self.deviceInfo.serialNumber
 
 150                                                osVersion:self.deviceInfo.osVersion
 
 151                                            policyVersion:self.policyOverride
 
 153                                syncUserControllableViews:TPPBPeerStableInfo_UserControllableViewStatus_FOLLOWING
 
 154                              signingPrivKeyPersistentRef:signingKeyPersistRef
 
 155                                  encPrivKeyPersistentRef:encryptionKeyPersistRef
 
 156                                                    reply:^(NSString * _Nullable peerID,
 
 157                                                            NSData * _Nullable permanentInfo,
 
 158                                                            NSData * _Nullable permanentInfoSig,
 
 159                                                            NSData * _Nullable stableInfo,
 
 160                                                            NSData * _Nullable stableInfoSig,
 
 161                                                            TPSyncingPolicy* _Nullable syncingPolicy,
 
 162                                                            NSError * _Nullable error) {
 
 164             [[CKKSAnalytics logger] logResultForEvent:OctagonEventPrepareIdentity hardFailure:true result:error];
 
 166                 secerror("octagon: Error preparing identity: %@", error);
 
 168                 [self runBeforeGroupFinished:self.finishedOp];
 
 170                 secnotice("octagon", "Prepared: %@ %@ %@", peerID, permanentInfo, permanentInfoSig);
 
 171                 self.peerID = peerID;
 
 172                 self.permanentInfo = permanentInfo;
 
 173                 self.permanentInfoSig = permanentInfoSig;
 
 174                 self.stableInfo = stableInfo;
 
 175                 self.stableInfoSig = stableInfoSig;
 
 177                 NSError* localError = nil;
 
 179                 secnotice("octagon-ckks", "New syncing policy: %@ views: %@", syncingPolicy, syncingPolicy.viewList);
 
 181                 BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nullable(OTAccountMetadataClassC * _Nonnull metadata) {
 
 182                     metadata.peerID = peerID;
 
 184                     [metadata setTPSyncingPolicy:syncingPolicy];
 
 186                 } error:&localError];
 
 188                 if(!persisted || localError) {
 
 189                     secnotice("octagon", "Couldn't persist metadata: %@", localError);
 
 190                     self.error = localError;
 
 191                     [self runBeforeGroupFinished:self.finishedOp];
 
 195                 // Let CKKS know of the new policy, so it can spin up
 
 196                 [self.deps.viewManager setCurrentSyncingPolicy:syncingPolicy];
 
 198                 self.nextState = self.intendedState;
 
 199                 [self runBeforeGroupFinished:self.finishedOp];