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 - (NSString* _Nullable)machineID:(NSError**)error
70 AKAnisetteProvisioningController* anisetteController = [[AKAnisetteProvisioningController alloc] init];
71 NSError* localError = nil;
72 AKAnisetteData* anisetteData = [anisetteController anisetteDataWithError:&localError];
74 secnotice("authkit", "Unable to fetch data: %@", localError);
81 NSString* machineID = anisetteData.machineID;
83 secnotice("authkit", "Anisette data does not have machineID");
85 [SecABC triggerAutoBugCaptureWithType:@"AuthKit" subType:@"missingMID"];
86 *error = [NSError errorWithDomain:OctagonErrorDomain
87 code:OTAuthKitMachineIDMissing
88 description:@"Anisette data does not have machineID"];
93 secnotice("authkit", "fetched current machine ID as: %@", machineID);
98 - (void)fetchCurrentDeviceList:(void (^)(NSSet<NSString*>* _Nullable machineIDs, NSError* _Nullable error))complete
100 AKDeviceListRequestContext* context = [[AKDeviceListRequestContext alloc] init];
101 if (context == nil) {
102 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
103 code:OTAuthKitAKDeviceListRequestContextClass
104 description:@"can't get AKDeviceListRequestContextClass"];
105 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
106 complete(nil, error);
109 NSError *authKitError = nil;
110 context.altDSID = [self primaryiCloudAccountAltDSID:&authKitError];
111 if (context.altDSID == NULL) {
112 secnotice("authkit", "Failed to get primary account AltDSID: %@", authKitError);
113 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
114 code:OTAuthKitPrimaryAccountHaveNoDSID
115 description:@"Can't get primary AltDSID"
116 underlying:authKitError];
117 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
118 [SecABC triggerAutoBugCaptureWithType:@"AuthKit" subType:@"missingAltDSID"];
119 complete(nil, error);
123 AKAppleIDAuthenticationController *authController = [[AKAppleIDAuthenticationController alloc] init];
124 if(authController == nil) {
125 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
126 code:OTAuthKitNoAuthenticationController
127 description:@"can't get authController"];
128 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
129 complete(nil, error);
133 [authController fetchDeviceListWithContext:context completion:^(NSArray<AKRemoteDevice *> *deviceList, NSError *error) {
135 NSMutableSet *mids = [[NSMutableSet alloc] init];
137 for (AKRemoteDevice *device in deviceList) {
138 [mids addObject:device.machineId];
141 secnotice("authkit", "Current machine ID list: %@", mids);
142 complete(mids, error);
143 [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventAuthKitDeviceList];
146 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
147 secnotice("authkit", "received no device list: %@", error);
148 complete(nil, error);
153 - (void)registerNotification:(id<OTAuthKitAdapterNotifier>)newNotifier
155 if (self.notifiers == nil) {
156 self.notifiers = [[CKKSListenerCollection<OTAuthKitAdapterNotifier> alloc] initWithName:@"otauthkitadapter-notifiers"];
157 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyAKDeviceList:) name:AKDeviceListChangedNotification object:nil];
159 [self.notifiers registerListener:newNotifier];
162 - (void)notifyAKDeviceList:(NSNotification* _Nullable)notification
164 AKDeviceListDeltaMessagePayload *payload = nil;
165 NSDictionary *userInfo = nil;
166 if (notification != nil) {
167 userInfo = [notification userInfo];
168 if (userInfo != nil) {
169 payload = [[AKDeviceListDeltaMessagePayload alloc] initWithResponseBody:userInfo];
173 secnotice("authkit", "received notifyAKDeviceList: %@, read payload: %@",
174 notification.userInfo,
175 // Logging the payload logs an address, so clean it up here.
176 payload ? @"YES" : @"NO");
178 [self.notifiers iterateListeners:^(id<OTAuthKitAdapterNotifier> listener) {
179 NSString* altDSID = payload.altDSID;
180 NSArray<NSString*>* machineIDs = payload.machineIDs;
182 if (altDSID == nil || machineIDs == nil || machineIDs.count == 0) {
183 secnotice("authkit", "partial push or no machine IDs in list; treating as incomplete");
184 [listener incompleteNotificationOfMachineIDListChange];
187 switch (payload.operation) {
188 case AKDeviceListDeltaOperationAdd:
189 [listener machinesAdded:machineIDs altDSID:altDSID];
192 case AKDeviceListDeltaOperationRemove:
193 [listener machinesRemoved:machineIDs altDSID:altDSID];
196 case AKDeviceListDeltaOperationUnknown:
200 [listener incompleteNotificationOfMachineIDListChange];