3 #import "OTAuthKitAdapter.h"
4 #import "OTConstants.h"
6 #import "utilities/SecCFError.h"
7 #import "keychain/categories/NSError+UsefulConstructors.h"
9 #import <AppleAccount/AppleAccount.h>
10 #import <AppleAccount/AppleAccount_Private.h>
11 #import <AuthKit/AuthKit.h>
12 #import <AuthKit/AuthKit_Private.h>
13 #import <AuthKit/AKDeviceListDeltaMessagePayload.h>
14 #import <Foundation/NSDistributedNotificationCenter.h>
15 #import "keychain/ckks/CKKSListenerCollection.h"
16 #import "keychain/ckks/CKKSAnalytics.h"
18 #include "utilities/SecABC.h"
20 #import <AppleAccount/ACAccount+AppleAccount.h>
22 @interface OTAuthKitActualAdapter ()
23 @property CKKSListenerCollection<OTAuthKitAdapterNotifier>* notifiers;
26 @implementation OTAuthKitActualAdapter
28 - (NSString* _Nullable)primaryiCloudAccountAltDSID:(NSError **)error
30 ACAccountStore *store = [[ACAccountStore alloc] init];
31 ACAccount* primaryAccount = [store aa_primaryAppleAccount];
33 secnotice("authkit", "No primary account");
35 *error = [NSError errorWithDomain:OctagonErrorDomain
36 code:OTAuthKitNoPrimaryAccount
37 description:@"No primary account"];
42 NSString *altDSID = [primaryAccount aa_altDSID];
43 if (altDSID == NULL) {
44 secnotice("authkit", "No altDSID on primary account");
46 *error = [NSError errorWithDomain:OctagonErrorDomain
47 code:OTAuthKitPrimaryAccountHaveNoDSID
48 description:@"No altdsid on primary account"];
54 - (BOOL)accountIsHSA2ByAltDSID:(NSString*)altDSID
58 AKAccountManager *manager = [AKAccountManager sharedInstance];
59 ACAccount *authKitAccount = [manager authKitAccountWithAltDSID:altDSID];
60 AKAppleIDSecurityLevel securityLevel = [manager securityLevelForAccount:authKitAccount];
61 if(securityLevel == AKAppleIDSecurityLevelHSA2) {
64 secnotice("security-authkit", "Security level for altDSID %@ is %lu", altDSID, (unsigned long)securityLevel);
68 - (BOOL)accountIsDemoAccount:(NSError**)error
70 NSError* localError = nil;
71 NSString* altDSID = [self primaryiCloudAccountAltDSID:&localError];
74 secerror("octagon-authkit:could not retrieve altDSID");
77 secerror("octagon-authkit: hit an error retrieving altDSID: %@", localError);
84 AKAccountManager *manager = [AKAccountManager sharedInstance];
85 ACAccount *authKitAccount = [manager authKitAccountWithAltDSID:altDSID];
86 BOOL isDemo = [manager demoAccountForAccount:authKitAccount];
88 secnotice("security-authkit", "Account with altDSID %@ is a demo account: %@", altDSID, isDemo ? @"true" : @"false");
93 - (NSString* _Nullable)machineID:(NSError**)error
95 AKAnisetteProvisioningController* anisetteController = [[AKAnisetteProvisioningController alloc] init];
96 NSError* localError = nil;
97 AKAnisetteData* anisetteData = [anisetteController anisetteDataWithError:&localError];
99 secnotice("authkit", "Unable to fetch data: %@", localError);
106 NSString* machineID = anisetteData.machineID;
108 secnotice("authkit", "Anisette data does not have machineID");
110 [SecABC triggerAutoBugCaptureWithType:@"AuthKit" subType:@"missingMID"];
111 *error = [NSError errorWithDomain:OctagonErrorDomain
112 code:OTAuthKitMachineIDMissing
113 description:@"Anisette data does not have machineID"];
118 secnotice("authkit", "fetched current machine ID as: %@", machineID);
123 - (void)fetchCurrentDeviceList:(void (^)(NSSet<NSString*>* _Nullable machineIDs, NSError* _Nullable error))complete
125 AKDeviceListRequestContext* context = [[AKDeviceListRequestContext alloc] init];
126 if (context == nil) {
127 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
128 code:OTAuthKitAKDeviceListRequestContextClass
129 description:@"can't get AKDeviceListRequestContextClass"];
130 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
131 complete(nil, error);
134 NSError *authKitError = nil;
135 context.altDSID = [self primaryiCloudAccountAltDSID:&authKitError];
136 if (context.altDSID == NULL) {
137 secnotice("authkit", "Failed to get primary account AltDSID: %@", authKitError);
138 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
139 code:OTAuthKitPrimaryAccountHaveNoDSID
140 description:@"Can't get primary AltDSID"
141 underlying:authKitError];
142 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
143 [SecABC triggerAutoBugCaptureWithType:@"AuthKit" subType:@"missingAltDSID"];
144 complete(nil, error);
148 AKAppleIDAuthenticationController *authController = [[AKAppleIDAuthenticationController alloc] init];
149 if(authController == nil) {
150 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
151 code:OTAuthKitNoAuthenticationController
152 description:@"can't get authController"];
153 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
154 complete(nil, error);
158 [authController fetchDeviceListWithContext:context completion:^(NSArray<AKRemoteDevice *> *deviceList, NSError *error) {
160 NSMutableSet *mids = [[NSMutableSet alloc] init];
162 for (AKRemoteDevice *device in deviceList) {
163 [mids addObject:device.machineId];
166 secnotice("authkit", "Current machine ID list: %@", mids);
167 complete(mids, error);
168 [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventAuthKitDeviceList];
171 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
172 secnotice("authkit", "received no device list: %@", error);
173 complete(nil, error);
178 - (void)registerNotification:(id<OTAuthKitAdapterNotifier>)newNotifier
180 if (self.notifiers == nil) {
181 self.notifiers = [[CKKSListenerCollection<OTAuthKitAdapterNotifier> alloc] initWithName:@"otauthkitadapter-notifiers"];
182 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyAKDeviceList:) name:AKDeviceListChangedNotification object:nil];
184 [self.notifiers registerListener:newNotifier];
187 - (void)notifyAKDeviceList:(NSNotification* _Nullable)notification
189 AKDeviceListDeltaMessagePayload *payload = nil;
190 NSDictionary *userInfo = nil;
191 if (notification != nil) {
192 userInfo = [notification userInfo];
193 if (userInfo != nil) {
194 payload = [[AKDeviceListDeltaMessagePayload alloc] initWithResponseBody:userInfo];
198 secnotice("authkit", "received notifyAKDeviceList: %@, read payload: %@",
199 notification.userInfo,
200 // Logging the payload logs an address, so clean it up here.
201 payload ? @"YES" : @"NO");
203 [self.notifiers iterateListeners:^(id<OTAuthKitAdapterNotifier> listener) {
204 NSString* altDSID = payload.altDSID;
205 NSArray<NSString*>* machineIDs = payload.machineIDs;
207 if (altDSID == nil || machineIDs == nil || machineIDs.count == 0) {
208 secnotice("authkit", "partial push or no machine IDs in list; treating as incomplete");
209 [listener incompleteNotificationOfMachineIDListChange];
212 switch (payload.operation) {
213 case AKDeviceListDeltaOperationAdd:
214 [listener machinesAdded:machineIDs altDSID:altDSID];
217 case AKDeviceListDeltaOperationRemove:
218 [listener machinesRemoved:machineIDs altDSID:altDSID];
221 case AKDeviceListDeltaOperationUnknown:
225 [listener incompleteNotificationOfMachineIDListChange];