3 #import "OTAuthKitAdapter.h"
5 #import "utilities/SecCFError.h"
6 #import "keychain/categories/NSError+UsefulConstructors.h"
8 #import <AppleAccount/AppleAccount.h>
9 #import <AppleAccount/AppleAccount_Private.h>
10 #import <AuthKit/AuthKit.h>
11 #import <AuthKit/AuthKit_Private.h>
12 #import <AuthKit/AKDeviceListDeltaMessagePayload.h>
13 #import <Foundation/NSDistributedNotificationCenter.h>
14 #import "keychain/ckks/CKKSListenerCollection.h"
16 #import <AppleAccount/ACAccount+AppleAccount.h>
18 @interface OTAuthKitActualAdapter ()
19 @property CKKSListenerCollection<OTAuthKitAdapterNotifier>* notifiers;
22 @implementation OTAuthKitActualAdapter
24 - (NSString* _Nullable)primaryiCloudAccountAltDSID
26 ACAccountStore *store = [[ACAccountStore alloc] init];
27 ACAccount* primaryAccount = [store aa_primaryAppleAccount];
32 return [primaryAccount aa_altDSID];
35 - (BOOL)accountIsHSA2ByAltDSID:(NSString*)altDSID
39 AKAccountManager *manager = [AKAccountManager sharedInstance];
40 ACAccount *authKitAccount = [manager authKitAccountWithAltDSID:altDSID];
41 AKAppleIDSecurityLevel securityLevel = [manager securityLevelForAccount:authKitAccount];
42 if(securityLevel == AKAppleIDSecurityLevelHSA2) {
45 secnotice("security-authkit", "Security level for altDSID %@ is %lu", altDSID, (unsigned long)securityLevel);
49 - (NSString* _Nullable)machineID:(NSError**)error
51 AKAnisetteProvisioningController* anisetteController = [[AKAnisetteProvisioningController alloc] init];
52 NSError* localError = nil;
53 AKAnisetteData* anisetteData = [anisetteController anisetteDataWithError:&localError];
55 secnotice("authkit", "Unable to fetch data: %@", localError);
62 NSString* machineID = anisetteData.machineID;
64 secnotice("authkit", "Anisette data does not have machineID");
66 // TODO: this is a terrible error
67 *error = [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain
69 description:@"Anisette data does not have machineID"];
74 secnotice("authkit", "fetched current machine ID as: %@", machineID);
79 - (void)fetchCurrentDeviceList:(void (^)(NSSet<NSString*>* _Nullable machineIDs, NSError* _Nullable error))complete
81 ACAccountStore *store = [[ACAccountStore alloc] init];
82 ACAccount* primaryAccount = [store aa_primaryAppleAccount];
83 if(primaryAccount == nil) {
84 secnotice("authkit", "can't get account");
85 complete(nil, [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain
87 description:@"no primary account"]);
91 AKDeviceListRequestContext* context = [[AKDeviceListRequestContext alloc] init];
93 complete(nil, [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain
95 description:@"can't get AKDeviceListRequestContextClass"]);
98 context.altDSID = primaryAccount.aa_altDSID;
100 AKAppleIDAuthenticationController *authController = [[AKAppleIDAuthenticationController alloc] init];
101 if(authController == nil) {
102 complete(nil, [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain
104 description:@"can't get authController"]);
108 [authController fetchDeviceListWithContext:context completion:^(NSArray<AKRemoteDevice *> *deviceList, NSError *error) {
110 NSMutableSet *mids = [[NSMutableSet alloc] init];
112 for (AKRemoteDevice *device in deviceList) {
113 [mids addObject:device.machineId];
116 secnotice("authkit", "Current machine ID list: %@", mids);
117 complete(mids, error);
119 secnotice("authkit", "received no device list: %@", error);
120 complete(nil, error);
125 - (void)registerNotification:(id<OTAuthKitAdapterNotifier>)newNotifier
127 if (self.notifiers == nil) {
128 self.notifiers = [[CKKSListenerCollection<OTAuthKitAdapterNotifier> alloc] initWithName:@"otauthkitadapter-notifiers"];
129 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyAKDeviceList:) name:AKDeviceListChangedNotification object:nil];
131 [self.notifiers registerListener:newNotifier];
134 - (void)notifyAKDeviceList:(NSNotification* _Nullable)notification
136 AKDeviceListDeltaMessagePayload *payload = nil;
137 NSDictionary *userInfo = nil;
138 if (notification != nil) {
139 userInfo = [notification userInfo];
140 if (userInfo != nil) {
141 payload = [[AKDeviceListDeltaMessagePayload alloc] initWithResponseBody:userInfo];
145 secnotice("authkit", "received notifyAKDeviceList: %@, read payload: %@",
146 notification.userInfo,
147 // Logging the payload logs an address, so clean it up here.
148 payload ? @"YES" : @"NO");
150 [self.notifiers iterateListeners:^(id<OTAuthKitAdapterNotifier> listener) {
151 NSString* altDSID = payload.altDSID;
152 NSArray<NSString*>* machineIDs = payload.machineIDs;
154 if (altDSID == nil || machineIDs == nil || machineIDs.count == 0) {
155 secnotice("authkit", "partial push or no machine IDs in list; treating as incomplete");
156 [listener incompleteNotificationOfMachineIDListChange];
159 switch (payload.operation) {
160 case AKDeviceListDeltaOperationAdd:
161 [listener machinesAdded:machineIDs altDSID:altDSID];
164 case AKDeviceListDeltaOperationRemove:
165 [listener machinesRemoved:machineIDs altDSID:altDSID];
168 case AKDeviceListDeltaOperationUnknown:
172 [listener incompleteNotificationOfMachineIDListChange];