Created
August 20, 2020 13:48
-
-
Save knightsc/758783181e41a986fceea6901b8853e3 to your computer and use it in GitHub Desktop.
AKNativeAnisetteService.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// AKNativeAnisetteService.m | |
// akd | |
// | |
// Created by Scott Knight on 5/10/19. | |
// Copyright © 2019 Scott Knight. All rights reserved. | |
// | |
#import <AuthKit/AuthKit.h> | |
#import "AKNativeAnisetteService.h" | |
#import "AKClient.h" | |
#import "AKADIProxy.h" | |
#import "AKURLBagService.h" | |
dispatch_queue_t gAnisetteQueue; | |
@implementation AKNativeAnisetteService | |
+ (unsigned long long)activeAnisetteDSIDWithEnvironment:(unsigned long long)env { | |
if (env == 0) { | |
return 0xfffffffffffffffe; | |
} | |
if (env <= 2) { | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_DEBUG)) { | |
os_log_debug(_AKTrafficLogSubsystem(), "Using QA Anisette DSID."); | |
} | |
return 0xfffffffffffffffd; | |
} else if (env == 3) { | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_DEBUG)) { | |
os_log_debug(_AKTrafficLogSubsystem(), "Using QA2 Anisette DSID."); | |
} | |
return 0xfffffffffffffffc; | |
} else { | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_DEBUG)) { | |
os_log_debug(_AKTrafficLogSubsystem(), "Using some other non-prod Anisette DSID."); | |
} | |
return 0xfffffffffffffffb; | |
} | |
} | |
+ (unsigned long long)lastKnownActiveAnisetteDSID { | |
unsigned long long env = [[AKURLBag sharedBag] lastKnownIDMSEnvironment]; | |
return [self activeAnisetteDSIDWithEnvironment:env]; | |
} | |
+ (void)initialize { | |
if (gAnisetteQueue== nil) { | |
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_autorelease_frequency(NULL, | |
DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM); | |
gAnisetteQueue = dispatch_queue_create("com.apple.akd.anisette", attr); | |
} | |
} | |
- (instancetype)initWithClient:(AKClient *)client { | |
self = [super init]; | |
if (self) { | |
_client = client; | |
} | |
return self; | |
} | |
- (void)_signRequestWithProvisioningHeaders:(NSMutableURLRequest *)request { | |
[request ak_addClientInfoHeader]; | |
[request ak_addClientTimeHeader]; | |
[request ak_addDeviceMLBHeader]; | |
[request ak_addDeviceROMHeader]; | |
[request ak_addDeviceSerialNumberHeader]; | |
[request ak_addDeviceUDIDHeader]; | |
[request ak_addLocalUserUUIDHashHeader]; | |
if (self->_client.name.length > 0) { | |
[request ak_addClientApp:self->_client.name]; | |
} | |
} | |
- (NSURLRequest *)_createSyncURLRequestWithMID:(NSData *)mid SRM:(NSData *)srm routingInfo:(unsigned long long)routingInfo { | |
NSMutableURLRequest *result = nil; | |
NSURL *syncURL = [[AKURLBag sharedBag] syncAnisetteURL]; | |
if (!syncURL) { | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKLogSystem(), "Nil value for syncAnisetteURL!"); | |
} | |
} else { | |
result = [NSMutableURLRequest requestWithURL:syncURL]; | |
[result setHTTPMethod:@"POST"]; | |
[self _signRequestWithProvisioningHeaders:result]; | |
NSMutableDictionary *body = [[NSMutableDictionary alloc] init]; | |
if (srm != nil) { | |
body[@"srm"] = [srm base64EncodedStringWithOptions:0]; | |
} | |
if (mid != nil) { | |
body[@"X-Apple-I-MD-M"] = [mid base64EncodedStringWithOptions:0]; | |
} | |
if (routingInfo != 0) { | |
body[@"X-Apple-I-MD-RINF"] = [NSNumber numberWithUnsignedLongLong:routingInfo]; | |
} | |
[result ak_setBodyWithParameters:@{@"Header":@{}, @"Request":body}]; | |
} | |
return result; | |
} | |
- (NSURLRequest *)_createProvisioningEndURLRequestWithCPIM:(NSData *)cpim { | |
NSMutableURLRequest *result = nil; | |
NSURL *endURL = [[AKURLBag sharedBag] endProvisioningURL]; | |
if (!endURL) { | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKLogSystem(), "Nil value for endProvisioningURL!"); | |
} | |
} else { | |
result = [NSMutableURLRequest requestWithURL:endURL]; | |
[result setHTTPMethod:@"POST"]; | |
[self _signRequestWithProvisioningHeaders:result]; | |
NSMutableDictionary *body = [[NSMutableDictionary alloc] init]; | |
if (cpim != nil) { | |
body[@"cpim"] = [cpim base64EncodedStringWithOptions:0]; | |
} | |
[result ak_setBodyWithParameters:@{@"Header":@{}, @"Request":body}]; | |
} | |
return result; | |
} | |
- (id)_createProvisioningStartURLRequest { | |
NSMutableURLRequest *result = nil; | |
NSURL *startURL = [[AKURLBag sharedBag] startProvisioningURL]; | |
if (!startURL) { | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKLogSystem(), "Nil value for startProvisioningURL!"); | |
} | |
} else { | |
result = [NSMutableURLRequest requestWithURL:startURL]; | |
[result setHTTPMethod:@"POST"]; | |
[self _signRequestWithProvisioningHeaders:result]; | |
[result ak_setBodyWithParameters:@{@"Header":@{}, @"Request":@{}}]; | |
} | |
return result; | |
} | |
- (void)fetchAnisetteDataAndProvisionIfNecessary:(BOOL)provision withCompletion:(void (^)(AKAnisetteData *, NSError *))completion { | |
dispatch_async(gAnisetteQueue, ^{ | |
NSError *error = nil; | |
unsigned long long dsid = [self.class lastKnownActiveAnisetteDSID]; | |
AKAnisetteData *anisetteData = [self _unsafe_anisetteDataWithRoutingInfoForDSID:dsid withError:&error]; | |
NSError *underlyingError = [[error userInfo] objectForKeyedSubscript:NSUnderlyingErrorKey]; | |
if (anisetteData == nil && [underlyingError code] == -45061) { | |
// I would assume -45061 indicates there is nothing yet provisioned for this machine | |
// in turn we attempt to provisionn if requested | |
if (provision) { | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_DEBUG)) { | |
os_log_debug(_AKTrafficLogSubsystem(), "Client requested that we attempt provisioning..."); | |
} | |
[self _unsafe_provisionAnisetteWithCompletion:^(BOOL succeeded, NSError *error) { | |
if (!succeeded) { | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKTrafficLogSubsystem(), "Provisioning failed. No Anisette for you today! Error: %@", error); | |
} | |
completion(nil, error); | |
} else { | |
dispatch_async(gAnisetteQueue, ^{ | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_DEBUG)) { | |
os_log_debug(_AKTrafficLogSubsystem(), "Provisioning succeeded"); | |
} | |
NSError *newError = nil; | |
unsigned long long dsid = [AKNativeAnisetteService lastKnownActiveAnisetteDSID]; | |
AKAnisetteData *newData = [self _unsafe_anisetteDataWithRoutingInfoForDSID:dsid withError:&newError]; | |
if (newError) { | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKTrafficLogSubsystem(), "Unable to get Anisette data even after provisioning was attempted. Error: %@", newError); | |
} | |
} | |
completion(newData, newError); | |
}); | |
} | |
}]; | |
} else { | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_DEBUG)) { | |
os_log_debug(_AKTrafficLogSubsystem(), "Skipping Anisette provisioning, per client request."); | |
} | |
completion(nil, error); | |
} | |
} else { | |
completion(anisetteData, error); | |
} | |
}); | |
} | |
- (AKAnisetteData *)_unsafe_anisetteDataWithRoutingInfoForDSID:(unsigned long long)dsid withError:(NSError **)error { | |
AKAnisetteData *anisetteData = nil; | |
unsigned long long routingInfo; | |
int result = [AKADIProxy getIDMSRoutingInfo:&routingInfo forDSID:dsid]; | |
if (result != 0) { | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKTrafficLogSubsystem(), "ADIGetIDMSRouting failed! Error: %@", @(result)); | |
} | |
if (error) { | |
*error = [NSError ak_wrappedAnisetteError:result]; | |
} | |
} else { | |
anisetteData = [self _unsafe_anisetteDataForDSID:dsid withError:error]; | |
anisetteData.routingInfo = routingInfo; | |
} | |
return anisetteData; | |
} | |
- (AKAnisetteData *)_unsafe_anisetteDataForDSID:(unsigned long long)dsid withError:(NSError **)error { | |
AKAnisetteData *anisetteData = nil; | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_DEBUG)) { | |
os_log_debug(_AKTrafficLogSubsystem(), "Looking for Anisette data..."); | |
} | |
char *mid; | |
unsigned int midSize; | |
char *otp; | |
unsigned int otpSize; | |
int ret = [AKADIProxy requestOTPForDSID:dsid outMID:&mid outMIDSize:&midSize outOTP:&otp outOTPSize:&otpSize]; | |
if (ret != 0) { | |
if (ret == -45061) { | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKTrafficLogSubsystem(), "The DSID %@ is not provisioned for Anisette.", @(dsid)); | |
} | |
} else { | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKTrafficLogSubsystem(), "ADIOTPRequest failed. Error: %@", @(ret)); | |
} | |
} | |
if (error) { | |
*error = [NSError ak_wrappedAnisetteError:ret]; | |
} | |
} else { | |
NSData *midData = [[NSData alloc] initWithBytesNoCopy:mid length:midSize freeWhenDone:NO]; | |
NSData *otpData = [[NSData alloc] initWithBytesNoCopy:otp length:otpSize freeWhenDone:NO]; | |
NSString *midString = [midData base64EncodedStringWithOptions:0x0]; | |
NSString *otpString = [otpData base64EncodedStringWithOptions:0x0]; | |
if (os_log_type_enabled(_AKTrafficLogSubsystem(), OS_LOG_TYPE_DEBUG)) { | |
os_log_debug(_AKTrafficLogSubsystem(), "Anisette Info: mid - %@ otp - %@", midString, otpString); | |
} | |
if (mid) { | |
[AKADIProxy dispose:mid]; | |
} | |
if (otp) { | |
[AKADIProxy dispose:otp]; | |
} | |
anisetteData = [[AKAnisetteData alloc] init]; | |
anisetteData.machineID = midString; | |
anisetteData.oneTimePassword = otpString; | |
} | |
return anisetteData; | |
} | |
- (void)provisionAnisetteWithCompletion:(void (^)(BOOL, NSError *))completion { | |
dispatch_async(gAnisetteQueue, ^{ | |
[self _unsafe_provisionAnisetteWithCompletion:completion]; | |
}); | |
} | |
- (void)_unsafe_provisionAnisetteWithCompletion:(void (^)(BOOL, NSError *))completion { | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_DEFAULT)) { | |
os_log(_AKLogSystem(), "Kicking off Anisette provisioning...."); | |
} | |
[[AKURLBagService sharedBagService] clearSessionCache]; | |
AKURLBag *bag = [AKURLBag sharedBag]; | |
unsigned long long dsid = [self.class activeAnisetteDSIDWithEnvironment:[bag IDMSEnvironment]]; | |
int ret = [AKADIProxy isMachineProvisioned:dsid]; | |
if (ret != -45061) { | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKLogSystem(), "Anisette is already provisioned!"); | |
} | |
NSError *error = [NSError errorWithDomain:@"AKAnisetteError" code:-8006 userInfo:nil]; | |
completion(NO, error); | |
return; | |
} | |
NSURLRequest *request = [self _createProvisioningStartURLRequest]; | |
if (request == nil) { | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKLogSystem(), "Unable to create start-provisioning URL request!"); | |
} | |
NSError *error = [NSError errorWithDomain:@"AKAnisetteError" code:-8005 userInfo:nil]; | |
completion(NO, error); | |
return; | |
} | |
dispatch_semaphore_t sem = dispatch_semaphore_create(0); | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_DEBUG)) { | |
os_log_debug(_AKLogSystem(), "Starting provisioning with headers: %@", request.allHTTPHeaderFields); | |
} | |
__block NSDictionary *responseData = nil; | |
AKURLSession *session = [AKURLSession sharedAnisetteFreeURLSession]; | |
[session beginDataTaskWithRequest:request completionHandler:^(NSData *data, NSHTTPURLResponse *response, NSError *error) { | |
if (error) { | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKLogSystem(), "Start provisioning request failed! Error: %@", error); | |
} | |
} else { | |
if (![response isKindOfClass:[NSHTTPURLResponse class]]) { | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKLogSystem(), "Unexpected response class! %@", response); | |
} | |
} else { | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_DEFAULT)) { | |
os_log(_AKLogSystem(), "Start provisioning response code: %@", @(response.statusCode)); | |
} | |
if (data != nil && response.statusCode != 200) { | |
NSString *body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_DEFAULT)) { | |
os_log(_AKLogSystem(), "Start provisioning response body: %@", body); | |
} | |
} | |
if (data != nil) { | |
responseData = [NSDictionary ak_dictionaryWithResponseData:data]; | |
} | |
} | |
} | |
dispatch_semaphore_signal(sem); | |
}]; | |
dispatch_semaphore_wait(sem, -1); | |
if (responseData == nil) { | |
// error we didn't get the result from the server | |
NSError *error = [NSError errorWithDomain:@"AKAnisetteError" code:-8007 userInfo:nil]; | |
completion(NO, error); | |
return; | |
} | |
// get spim from results | |
// int ret = AKADIProxy startProvisioningWithDSID:dsid SPIM CPIM | |
// if (ret != 0) { | |
// ADIProvisioningStart failed. Error: %@ | |
// NSError *error = [NSERror ak_wrappedAnisetteError:ret] | |
// completion(NO, error) | |
// return; | |
// } | |
// | |
// NSURLRequest *endRequest = _createProvisioningEndURLRequestWithCPIM | |
// AKURLSession *session = [AKURLSession sharedAnisetteFreeURLSession]; | |
// [session beginDataTaskWithRequest:request completionHandler:^(NSData *data, NSHTTPURLResponse *response, NSError *error) { | |
// }]; | |
} | |
- (void)syncAnisetteWithSIMData:(NSData *)data completion:(void (^)(BOOL, NSError *))completion { | |
dispatch_async(gAnisetteQueue, ^{ | |
// TODO | |
// this is another long one | |
}); | |
} | |
- (void)eraseAnisetteWithCompletion:(void (^)(BOOL, NSError *))completion { | |
dispatch_async(gAnisetteQueue, ^{ | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_DEFAULT)) { | |
os_log(_AKLogSystem(), "Erasing Anisette provisioning data..."); | |
} | |
AKURLBag *bag = [AKURLBag sharedBag]; | |
unsigned long long dsid = [self.class activeAnisetteDSIDWithEnvironment:[bag IDMSEnvironment]]; | |
int ret = [AKADIProxy eraseProvisioningForDSID:dsid]; | |
if (ret != 0) { | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_ERROR)) { | |
os_log_error(_AKLogSystem(), "Erase failed! Error: %@", @(ret)); | |
} | |
completion(NO, [NSError ak_wrappedAnisetteError:ret]); | |
} else { | |
if (os_log_type_enabled(_AKLogSystem(), OS_LOG_TYPE_DEFAULT)) { | |
os_log(_AKLogSystem(), "Erasing Anisette data succeeded!"); | |
} | |
completion(YES, nil); | |
} | |
}); | |
} | |
- (void)legacyAnisetteDataForDSID:(NSString *)dsid withCompletion:(void (^)(AKAnisetteData *, NSError *))completion { | |
dispatch_async(gAnisetteQueue, ^{ | |
unsigned long long ldsid; | |
NSScanner *scanner = [NSScanner scannerWithString:dsid]; | |
BOOL found = [scanner scanUnsignedLongLong:&ldsid]; | |
if (!found) { | |
NSError *error = [NSError errorWithDomain:@"AKAnisetteError" code:-8003 userInfo:nil]; | |
completion(nil, error); | |
return; | |
} | |
NSError *error = nil; | |
AKAnisetteData *anisetteData = [self _unsafe_anisetteDataForDSID:ldsid withError:&error]; | |
completion(anisetteData, error); | |
}); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment