2 * Copyright (c) 2017 - 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@
25 #import <Foundation/Foundation.h>
26 #import <Foundation/NSXPCConnection_Private.h>
27 #import "keychain/securityd/SOSCloudCircleServer.h"
28 #import <Security/SecureObjectSync/SOSPeerInfo.h>
29 #import <Security/SecureObjectSync/SOSCloudCircleInternal.h>
30 #import <Security/SecureObjectSync/SOSViews.h>
31 #import "keychain/SecureObjectSync/SOSTypes.h"
32 #import "keychain/SecureObjectSync/SOSInternal.h"
33 #import "keychain/SecureObjectSync/SOSAuthKitHelpers.h"
34 #import "OSX/sec/Security/SecItemShim.h"
40 #import "keychain/ckks/CKKS.h"
41 #import "keychain/ot/OTManager.h"
42 #import "keychain/ot/OT.h"
43 #import "keychain/ot/OTControl.h"
44 #import "keychain/ot/OTCuttlefishContext.h"
45 #import "keychain/categories/NSError+UsefulConstructors.h"
46 #import "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h"
47 #import "keychain/SecureObjectSync/SOSControlServer.h"
48 #import "KeychainCircle/PairingChannel.h"
50 #import "SharedMocks/NSXPCConnectionMock.h"
52 #import "SecRemoteDeviceProtocol.h"
53 #import "SecRemoteDevice.h"
55 @interface DevicePairingSimulator : NSObject <DevicePairingProtocol>
56 @property KCPairingChannelContext *remoteVersionContext;
57 @property KCPairingChannel *channel;
58 @property SecRemoteDevice *device;
59 @property (assign) bool initiator;
60 @property (assign) bool haveHandshakeCompleted;
62 - (instancetype)init NS_UNAVAILABLE;
63 - (instancetype)initAsInitiator:(bool)initiator version:(KCPairingChannelContext *)peerVersionContext device:(SecRemoteDevice *)device;
66 @implementation DevicePairingSimulator
68 - (instancetype)initAsInitiator:(bool)initiator version:(KCPairingChannelContext *)peerVersionContext device:(SecRemoteDevice *)device
72 self.remoteVersionContext = peerVersionContext;
73 self.initiator = initiator;
75 self.channel = [[KCPairingChannel alloc] initAsInitiator:initiator version:peerVersionContext];
77 [self.channel setXPCConnectionObject:(NSXPCConnection *)[[NSXPCConnectionMock alloc] initWithRealObject:SOSControlServerInternalClient()]];
84 - (void)exchangePacket:(NSData *)data complete:(void (^)(bool complete, NSData *result, NSError *error))complete
86 os_log(NULL, "[%@] exchangePacket", self.device.name);
88 if (self.haveHandshakeCompleted || self.channel == NULL) {
91 [self.channel exchangePacket:data complete:^void(BOOL handshakeComplete, NSData *packet, NSError *error) {
92 self.haveHandshakeCompleted = handshakeComplete;
93 os_log(NULL, "[%@] exchangePacket:complete: %d", self.device.name, handshakeComplete);
94 complete(handshakeComplete, packet, error);
98 - (void)validateStart:(void(^)(bool result, NSError *error))complete
100 if (self.channel == NULL) {
103 [self.channel validateStart:^(bool result, NSError *error) {
104 complete(result, error);
110 @implementation SecRemoteDevice
113 - (void)setUserCredentials:(NSString *)username password:(NSString *)password complete:(void (^)(bool success, NSError *error))complete
115 CFErrorRef cferror = NULL;
116 bool result = SOSCCSetUserCredentialsAndDSID((__bridge CFStringRef)username,
117 (__bridge CFDataRef)[password dataUsingEncoding:NSUTF8StringEncoding],
118 CFSTR("1"), &cferror);
119 complete(result, (__bridge NSError *)cferror);
120 CFReleaseNull(cferror);
123 - (void)setupSOSCircle:(NSString *)username password:(NSString *)password complete:(void (^)(bool success, NSError *error))complete
125 CFErrorRef cferror = NULL;
126 bool result = SOSCCSetUserCredentialsAndDSID((__bridge CFStringRef)username,
127 (__bridge CFDataRef)[password dataUsingEncoding:NSUTF8StringEncoding],
128 CFSTR("1"), &cferror);
130 result = SOSCCResetToOffering(&cferror);
132 complete(result, (__bridge NSError *)cferror);
133 CFReleaseNull(cferror);
136 - (void)sosCircleStatus:(void(^)(SOSCCStatus status, NSError *error))complete
138 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef __unused returnedValues, CFErrorRef __unused sync_error) {
139 CFErrorRef cferror = NULL;
140 SOSCCStatus status = SOSCCThisDeviceIsInCircle(&cferror);
141 complete(status, (__bridge NSError *)cferror);
142 CFReleaseNull(cferror);
146 - (void)sosCircleStatusNonCached:(void(^)(SOSCCStatus status, NSError *error))complete
148 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef __unused returnedValues, CFErrorRef __unused sync_error) {
149 CFErrorRef cferror = NULL;
150 SOSCCStatus status = SOSCCThisDeviceIsInCircleNonCached(&cferror);
151 complete(status, (__bridge NSError *)cferror);
152 CFReleaseNull(cferror);
157 - (void)sosViewStatus:(NSString *) viewName withCompletion: (void(^)(SOSViewResultCode status, NSError *error))complete
159 CFErrorRef cferror = NULL;
160 SOSViewResultCode status = SOSCCView((__bridge CFStringRef)(viewName), kSOSCCViewQuery, &cferror);
161 complete(status, (__bridge NSError *)cferror);
162 CFReleaseNull(cferror);
166 - (void)sosICKStatus: (void(^)(bool status))complete
168 CFErrorRef cferror = NULL;
169 bool status = SOSCCIsIcloudKeychainSyncing();
171 CFReleaseNull(cferror);
174 - (void)sosPeerID:(void (^)(NSString *))complete
176 CFErrorRef cferror = NULL;
177 CFStringRef peerID = NULL;
178 SOSPeerInfoRef peerInfo = SOSCCCopyMyPeerInfo(&cferror);
180 peerID = SOSPeerInfoGetPeerID(peerInfo);
182 complete((__bridge NSString *)peerID);
183 CFReleaseNull(peerInfo);
186 - (void)sosPeerSerial:(void(^)(NSString * _Nullable peerSerial))complete {
187 CFErrorRef cferror = NULL;
188 CFStringRef peerSerial = NULL;
189 SOSPeerInfoRef peerInfo = SOSCCCopyMyPeerInfo(&cferror);
191 peerSerial = SOSPeerInfoCopySerialNumber(peerInfo);
192 complete((__bridge NSString *)peerSerial);
193 CFReleaseNull(peerSerial);
194 CFReleaseNull(peerInfo);
197 - (void)sosCirclePeerIDs:(void (^)(NSArray<NSString *> *))complete
199 CFErrorRef error = NULL;
200 NSArray *array = CFBridgingRelease(SOSCCCopyConcurringPeerPeerInfo(&error));
201 NSMutableArray<NSString *> *peerIDs = [NSMutableArray new];
204 [array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
205 SOSPeerInfoRef peerInfo = (__bridge SOSPeerInfoRef)obj;
206 NSString *peerID = (__bridge NSString *)SOSPeerInfoGetPeerID(peerInfo);
207 [peerIDs addObject:peerID];
213 - (void)sosRequestToJoin:(void(^)(bool success, NSString *peerID, NSError *error))complete
215 CFErrorRef cferror = NULL;
217 os_log(NULL, "[%@] sosRequestToJoin", self.name);
219 SOSCCStatus status = SOSCCThisDeviceIsInCircle(&cferror);
220 if (status == kSOSCCCircleAbsent) {
221 cferror = CFErrorCreate(NULL, CFSTR("MDCircleAbsent"), 1, NULL);
222 complete(false, NULL, (__bridge NSError *)cferror);
223 CFReleaseNull(cferror);
224 } else if (status == kSOSCCNotInCircle) {
225 CFReleaseNull(cferror);
226 NSString *peerID = NULL;
227 bool result = SOSCCRequestToJoinCircle(&cferror);
229 SOSPeerInfoRef peerInfo = SOSCCCopyMyPeerInfo(&cferror);
231 peerID = (__bridge NSString *)SOSPeerInfoGetPeerID(peerInfo);
233 CFReleaseNull(peerInfo);
234 CFReleaseNull(cferror);
236 complete(result, peerID, (__bridge NSError *)cferror);
237 CFReleaseNull(cferror);
240 cferror = CFErrorCreate(NULL, CFSTR("MDGeneralJoinError"), 1, NULL);
242 complete(false, NULL, (__bridge NSError *)cferror);
243 CFReleaseNull(cferror);
247 - (void)sosLeaveCircle: (void(^)(bool success, NSError *error))complete {
248 CFErrorRef cferror = NULL;
251 os_log(NULL, "[%@] sosLeaveCircle", self.name);
253 SOSCCStatus status = SOSCCThisDeviceIsInCircle(&cferror);
254 if(status == kSOSCCInCircle || status == kSOSCCRequestPending) {
255 retval = SOSCCRemoveThisDeviceFromCircle(&cferror);
257 complete(retval, (__bridge NSError *) cferror);
258 CFReleaseNull(cferror);
262 - (void)sosApprovePeer:(NSString *)peerID complete:(void(^)(BOOL success, NSError *error))complete
264 CFErrorRef cferror = NULL;
265 os_log(NULL, "[%@] sosApprovePeer: %@", self.name, peerID);
266 NSArray *applicants = CFBridgingRelease(SOSCCCopyApplicantPeerInfo(&cferror));
267 if ([applicants count] == 0) {
268 CFReleaseNull(cferror);
269 cferror = CFErrorCreate(NULL, CFSTR("MDNoApplicant"), 1, NULL);
270 complete(false, (__bridge NSError *)cferror);
271 CFReleaseNull(cferror);
274 NSMutableArray *approvedApplicants = [NSMutableArray array];
275 for (id peer in applicants) {
276 SOSPeerInfoRef peerInfo = (__bridge SOSPeerInfoRef)peer;
277 NSString *applicantPeerID = (__bridge NSString *)SOSPeerInfoGetPeerID(peerInfo);
278 if (peerID == NULL || [peerID isEqualToString:applicantPeerID]){
279 [approvedApplicants addObject:(__bridge id)peerInfo];
283 if ([approvedApplicants count]) {
284 result = SOSCCAcceptApplicants((__bridge CFArrayRef)approvedApplicants, &cferror);
286 cferror = CFErrorCreate(NULL, CFSTR("MDNoApplicant"), 1, NULL);
288 complete(result, (__bridge NSError *)cferror);
289 CFReleaseNull(cferror);
292 - (void)sosGhostBust:(SOSAccountGhostBustingOptions)options complete:(void(^)(bool busted, NSError *error))complete {
293 os_log(NULL, "[%@] sosGhostBust", self.name);
294 SOSCCGhostBust(options, ^(bool busted, NSError *error) {
295 os_log(NULL, "[%@] sosGhostBust: %sbusted error: %@", self.name, busted ? "" : "no ", error);
296 complete(busted, error);
300 - (void)sosCircleHash: (void(^)(NSString *data, NSError * _Nullable error))complete
302 NSError *error = NULL;
303 NSString *hash = SOSCCCircleHash(&error);
304 complete(hash, error);
308 - (void)sosWaitForInitialSync:(void(^)(bool success, NSError *error))complete
310 CFErrorRef cferror = NULL;
311 bool success = SOSCCWaitForInitialSync(&cferror);
312 complete(success, (__bridge NSError *)cferror);
313 CFReleaseNull(cferror);
316 - (void)sosEnableAllViews:(void(^)(BOOL success, NSError *error))complete
318 CFMutableSetRef viewsToEnable = SOSViewCopyViewSet(kViewSetAll);
319 CFMutableSetRef viewsToDisable = CFSetCreateMutable(NULL, 0, NULL);
321 bool success = SOSCCViewSet(viewsToEnable, viewsToDisable);
322 CFRelease(viewsToEnable);
323 CFRelease(viewsToDisable);
324 complete(success, NULL);
328 - (void) sosCachedViewBitmask: (void(^)(uint64_t bitmask))complete {
329 uint64_t result = SOSCachedViewBitmask();
333 - (void) deviceInfo:(nonnull void (^)(NSString * _Nullable, NSString * _Nullable, NSError * _Nullable))complete {
334 complete(@"", @"", NULL);
340 - (void)pairingChannelSetup:(bool)initiator pairingContext:(KCPairingChannelContext *)context complete:(void (^)(id<DevicePairingProtocol>, NSError *))complete {
342 DevicePairingSimulator *pairingSim = [[DevicePairingSimulator alloc] initAsInitiator:initiator version:context device:self];
343 complete(pairingSim, nil);
346 // MARK: - Diagnostics
348 - (void)diagnosticsLeaks:(void(^)(bool success, NSString *outout, NSError *error))complete
350 complete(true, NULL, NULL);
353 - (void)diagnosticsCPUUsage:(void(^)(bool success, uint64_t user_usec, uint64_t sys_usec, NSError *error))complete
356 getrusage(RUSAGE_SELF, &usage);
357 uint64_t user_usec = usage.ru_utime.tv_sec * USEC_PER_SEC + usage.ru_utime.tv_usec;
358 uint64_t sys_usec = usage.ru_stime.tv_sec * USEC_PER_SEC + usage.ru_stime.tv_usec;
360 complete(true, user_usec, sys_usec, NULL);
363 - (void)diagnosticsDiskUsage:(void(^)(bool success, uint64_t usage, NSError *error))complete
365 rusage_info_current rusage;
367 if (proc_pid_rusage(getpid(), RUSAGE_INFO_CURRENT, (rusage_info_t *)&rusage) == 0) {
368 complete(true, rusage.ri_logical_writes, NULL);
370 complete(false, 0, NULL);
375 - (void)otReset:(NSString *)altDSID complete:(void (^)(bool success, NSError *_Nullable error))complete
378 OTControl *ot = [self OTControl];
380 [ot resetAndEstablish:nil context:OTDefaultContext altDSID:altDSID resetReason:CuttlefishResetReasonTestGenerated reply:^(NSError * _Nullable error) {
381 complete(error == NULL, error);
384 complete(false, [self octagonNotAvailableError]);
388 - (void)otPeerID:(NSString *)altDSID complete:(void (^)(NSString *peerID, NSError *_Nullable error))complete
391 OTControl *ot = [self OTControl];
392 [ot fetchEgoPeerID:nil context:OTDefaultContext reply:^(NSString * _Nullable peerID, NSError * _Nullable error) {
393 complete(peerID, error);
396 complete(false, [self octagonNotAvailableError]);
401 - (void)otInCircle:(NSString *)altDSID complete:(void (^)(bool inCircle, NSError *_Nullable error))complete
404 OTControl *ot = [self OTControl];
405 OTOperationConfiguration *configuration = [[OTOperationConfiguration alloc] init];
406 [ot fetchCliqueStatus:nil context:OTDefaultContext configuration:configuration reply:^(CliqueStatus cliqueStatus, NSError * _Nullable error) {
407 os_log(NULL, "[%@] otInCircle: clique: %d error: %@", self.name, (int)cliqueStatus, error);
408 complete(cliqueStatus == CliqueStatusIn, error);
411 complete(false, [self octagonNotAvailableError]);
416 //MARK: - Misc helpers
420 - (OTControl *)OTControl
423 return [[OTControl alloc] initWithConnection:(NSXPCConnection *)[[NSXPCConnectionMock alloc] initWithRealObject:[OTManager manager]] sync:true];
425 NSError *error = NULL;
426 return [OTControl controlObject:true error:&error];
432 - (NSError *)octagonNotAvailableError
434 return [NSError errorWithDomain:@"DeviceSimulator" code:1 description:@"no octagon available"];