diff --git a/.gitmodules b/.gitmodules index a6a3d32c4..98f43d18a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "RadarSDKIndoors"] path = RadarSDKIndoors url = https://github.com/radarlabs/radar-sdk-ios-indoors.git +[submodule "RadarSDKFraud"] + path = RadarSDKFraud + url = https://github.com/radarlabs/radar-lib-fraud-ios.git + branch = initial-setup diff --git a/RadarSDK.xcodeproj/project.pbxproj b/RadarSDK.xcodeproj/project.pbxproj index 57870a328..b630cc313 100644 --- a/RadarSDK.xcodeproj/project.pbxproj +++ b/RadarSDK.xcodeproj/project.pbxproj @@ -76,6 +76,7 @@ 01F810702AF0119800BD7088 /* RadarVerifiedDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 01F8106F2AF0119800BD7088 /* RadarVerifiedDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 01F99CFB2965C182004E8CF3 /* RadarConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 01F99CFA2965C182004E8CF3 /* RadarConfig.h */; }; 01F99CFD2965C1C4004E8CF3 /* RadarConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 01F99CFC2965C1C4004E8CF3 /* RadarConfig.m */; }; + 4B5BB8D72E7C79AF004C98C9 /* RadarSDKFraudProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B5BB8D62E7C79AF004C98C9 /* RadarSDKFraudProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; 532FC304277A790600989279 /* Radar+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 532FC303277A783900989279 /* Radar+Internal.h */; }; 53B3B26B23EE41B400080818 /* context.json in Resources */ = {isa = PBXBuildFile; fileRef = 53B3B26A23EE41B400080818 /* context.json */; }; 53CCD783275E579800F79CC8 /* RadarLogBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 53CCD782275E579800F79CC8 /* RadarLogBuffer.m */; }; @@ -208,6 +209,7 @@ 01F8106F2AF0119800BD7088 /* RadarVerifiedDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RadarVerifiedDelegate.h; sourceTree = ""; }; 01F99CFA2965C182004E8CF3 /* RadarConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RadarConfig.h; sourceTree = ""; }; 01F99CFC2965C1C4004E8CF3 /* RadarConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RadarConfig.m; sourceTree = ""; }; + 4B5BB8D62E7C79AF004C98C9 /* RadarSDKFraudProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RadarSDKFraudProtocol.h; sourceTree = ""; }; 532FC303277A783900989279 /* Radar+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Radar+Internal.h"; sourceTree = ""; }; 5343FFD923E38BA300808D93 /* RadarContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RadarContext.m; sourceTree = ""; }; 53B3B26A23EE41B400080818 /* context.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = context.json; sourceTree = ""; }; @@ -422,6 +424,7 @@ 96A5A0D827AD9F7F007B960B /* Include */ = { isa = PBXGroup; children = ( + 4B5BB8D62E7C79AF004C98C9 /* RadarSDKFraudProtocol.h */, FE9417172E1C2958008ECBEB /* RadarIndoorsProtocol.h */, 828D1A552E295FD400663787 /* RadarTripOrder.h */, 96A5A0F527AD9F7F007B960B /* Radar.h */, @@ -678,6 +681,7 @@ buildActionMask = 2147483647; files = ( 96A5A0F727AD9F7F007B960B /* RadarEvent.h in Headers */, + 4B5BB8D72E7C79AF004C98C9 /* RadarSDKFraudProtocol.h in Headers */, 96A5A0CD27AD9F41007B960B /* RadarRegion+Internal.h in Headers */, 0195143A2BD081630031ABA2 /* RadarVerifiedLocationToken+Internal.h in Headers */, 96A5A0FD27AD9F7F007B960B /* RadarUser.h in Headers */, diff --git a/RadarSDK/Include/RadarSDKFraudProtocol.h b/RadarSDK/Include/RadarSDKFraudProtocol.h new file mode 100644 index 000000000..20c59d2c4 --- /dev/null +++ b/RadarSDK/Include/RadarSDKFraudProtocol.h @@ -0,0 +1,29 @@ +// +// RadarSDKFraudProtocol.h +// RadarSDK +// +// Copyright © 2025 Radar Labs, Inc. All rights reserved. +// + +#import +#import +#import "Radar.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^RadarIPChangeCallback)(NSString *reason); +typedef void (^RadarFraudPayloadCallback)(RadarStatus status, NSString *_Nullable payload); + +@protocol RadarSDKFraudProtocol + ++ (instancetype)sharedInstance; + +- (void)getFraudPayloadWithLocation:(CLLocation *_Nullable)location nonce:(NSString *_Nonnull)nonce completionHandler:(RadarFraudPayloadCallback)completionHandler; + +- (void)startIPMonitoringWithCallback:(RadarIPChangeCallback)callback; + +- (void)stopIPMonitoring; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RadarSDK/RadarAPIClient.h b/RadarSDK/RadarAPIClient.h index 0fed1ca11..1d26af8cf 100644 --- a/RadarSDK/RadarAPIClient.h +++ b/RadarSDK/RadarAPIClient.h @@ -89,10 +89,7 @@ typedef void (^_Nonnull RadarSyncLogsAPICompletionHandler)(RadarStatus status); beacons:(NSArray *_Nullable)beacons indoorScan:(NSString *_Nullable)indoorScan verified:(BOOL)verified - attestationString:(NSString *_Nullable)attestationString - keyId:(NSString *_Nullable)keyId - attestationError:(NSString *_Nullable)attestationError - encrypted:(BOOL)encrypted + fraudPayload:(NSString *_Nullable)fraudPayload expectedCountryCode:(NSString *_Nullable)expectedCountryCode expectedStateCode:(NSString *_Nullable)expectedStateCode reason:(NSString *_Nullable)reason diff --git a/RadarSDK/RadarAPIClient.m b/RadarSDK/RadarAPIClient.m index 048abf30c..846849fe3 100644 --- a/RadarSDK/RadarAPIClient.m +++ b/RadarSDK/RadarAPIClient.m @@ -37,6 +37,7 @@ #import "RadarNotificationHelper.h" #import "Radar-Swift.h" #import +#import "RadarSDKFraudProtocol.h" @implementation RadarAPIClient @@ -199,10 +200,7 @@ - (void)trackWithLocation:(CLLocation *_Nonnull)location beacons:beacons indoorScan:indoorScan verified:NO - attestationString:nil - keyId:nil - attestationError:nil - encrypted:NO + fraudPayload:nil expectedCountryCode:nil expectedStateCode:nil reason:nil @@ -218,10 +216,7 @@ - (void)trackWithLocation:(CLLocation *_Nonnull)location beacons:(NSArray *_Nullable)beacons indoorScan:(NSString *_Nullable)indoorScan verified:(BOOL)verified - attestationString:(NSString *_Nullable)attestationString - keyId:(NSString *_Nullable)keyId - attestationError:(NSString *_Nullable)attestationError - encrypted:(BOOL)encrypted + fraudPayload:(NSString *_Nullable)fraudPayload expectedCountryCode:(NSString * _Nullable)expectedCountryCode expectedStateCode:(NSString * _Nullable)expectedStateCode reason:(NSString * _Nullable)reason @@ -298,20 +293,7 @@ - (void)trackWithLocation:(CLLocation *_Nonnull)location } else { params[@"xPlatformType"] = @"Native"; } - NSMutableArray *fraudFailureReasons = [NSMutableArray new]; - if (@available(iOS 15.0, *)) { - CLLocationSourceInformation *sourceInformation = location.sourceInformation; - if (sourceInformation) { - if (sourceInformation.isSimulatedBySoftware) { - params[@"mocked"] = @(YES); - [fraudFailureReasons addObject:@"fraud_mocked_from_mock_provider"]; - } - if (sourceInformation.isProducedByAccessory) { - [fraudFailureReasons addObject:@"fraud_mocked_produced_by_accessory"]; - } - } - } - + RadarTripOptions *tripOptions = Radar.getTripOptions; if (tripOptions) { @@ -351,15 +333,6 @@ - (void)trackWithLocation:(CLLocation *_Nonnull)location params[@"verified"] = @(verified); if (verified) { - params[@"attestationString"] = attestationString; - params[@"keyId"] = keyId; - params[@"attestationError"] = attestationError; - params[@"encrypted"] = @(encrypted); - BOOL jailbroken = [[RadarVerificationManager sharedInstance] isJailbroken]; - params[@"compromised"] = @(jailbroken); - if (jailbroken) { - [fraudFailureReasons addObject:@"fraud_compromised_jailbroken"]; - } if (expectedCountryCode) { params[@"expectedCountryCode"] = expectedCountryCode; } @@ -372,14 +345,11 @@ - (void)trackWithLocation:(CLLocation *_Nonnull)location if (transactionId) { params[@"transactionId"] = transactionId; } - if (UIScreen.mainScreen.isCaptured) { - [fraudFailureReasons addObject:@"fraud_sharing_capturing_screen"]; - } - NSString *kDeviceId = [[RadarVerificationManager sharedInstance] kDeviceId]; - if (kDeviceId) { - params[@"kDeviceId"] = kDeviceId; + if (fraudPayload) { + params[@"fraudPayload"] = fraudPayload; } } + params[@"appId"] = [[NSBundle mainBundle] bundleIdentifier]; NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; if (appName) { @@ -425,15 +395,13 @@ - (void)trackWithLocation:(CLLocation *_Nonnull)location if (options.usePressure || options.useMotion) { params[@"locationMetadata"] = locationMetadata; } - - params[@"fraudFailureReasons"] = fraudFailureReasons; if (anonymous) { [[RadarAPIClient sharedInstance] getConfigForUsage:@"track" - verified:verified - completionHandler:^(RadarStatus status, RadarConfig *_Nullable config){ + verified:verified + completionHandler:^(RadarStatus status, RadarConfig *_Nullable config){ - }]; + }]; } if (sdkConfiguration.useNotificationDiff) { @@ -457,16 +425,14 @@ - (void)trackWithLocation:(CLLocation *_Nonnull)location [[RadarAPIClient sharedInstance] makeTrackRequestWithParams:params options:options stopped:stopped - location:location - source:source - verified:verified - publishableKey:publishableKey - notificationsRemaining:@[] - locationMetadata:locationMetadata - completionHandler:completionHandler]; + location:location + source:source + verified:verified + publishableKey:publishableKey + notificationsRemaining:@[] + locationMetadata:locationMetadata + completionHandler:completionHandler]; } - - } - (void)makeTrackRequestWithParams:(NSDictionary *)params diff --git a/RadarSDK/RadarUtils.h b/RadarSDK/RadarUtils.h index 9e7ca1d37..787be0bc4 100644 --- a/RadarSDK/RadarUtils.h +++ b/RadarSDK/RadarUtils.h @@ -28,7 +28,6 @@ typedef NS_ENUM(NSInteger, RadarConnectionType) { + (NSString *)deviceId; + (NSString *)deviceType; + (NSString *)deviceMake; -+ (BOOL)isSimulator; + (BOOL)locationBackgroundMode; + (NSString *)locationAuthorization; + (NSString *)locationAccuracyAuthorization; diff --git a/RadarSDK/RadarUtils.m b/RadarSDK/RadarUtils.m index f092f5430..7bf88609a 100644 --- a/RadarSDK/RadarUtils.m +++ b/RadarSDK/RadarUtils.m @@ -147,14 +147,6 @@ + (NSDictionary *)appInfo { } -+ (BOOL)isSimulator { -#if TARGET_OS_SIMULATOR - return YES; -#else - return NO; -#endif -} - + (BOOL)locationBackgroundMode { NSArray *backgroundModes = [NSBundle mainBundle].infoDictionary[@"UIBackgroundModes"]; return backgroundModes && [backgroundModes containsObject:@"location"]; diff --git a/RadarSDK/RadarVerificationManager.h b/RadarSDK/RadarVerificationManager.h index 38ad5f999..afb6395e7 100644 --- a/RadarSDK/RadarVerificationManager.h +++ b/RadarSDK/RadarVerificationManager.h @@ -14,8 +14,6 @@ NS_ASSUME_NONNULL_BEGIN @property (assign, nonatomic) BOOL started; -typedef void (^_Nullable RadarVerificationCompletionHandler)(NSString *_Nullable attestationString, NSString *_Nullable keyId, NSString *_Nullable attestationError); - + (instancetype)sharedInstance; - (void)trackVerifiedWithCompletionHandler:(RadarTrackVerifiedCompletionHandler _Nullable)completionHandler; - (void)trackVerifiedWithBeacons:(BOOL)beacons desiredAccuracy:(RadarTrackingOptionsDesiredAccuracy)desiredAccuracy reason:(NSString *_Nullable)reason transactionId:(NSString *_Nullable)transactionId completionHandler:(RadarTrackVerifiedCompletionHandler _Nullable)completionHandler; @@ -24,9 +22,6 @@ typedef void (^_Nullable RadarVerificationCompletionHandler)(NSString *_Nullable - (void)getVerifiedLocationTokenWithBeacons:(BOOL)beacons desiredAccuracy:(RadarTrackingOptionsDesiredAccuracy)desiredAccuracy completionHandler:(RadarTrackVerifiedCompletionHandler _Nullable)completionHandler; - (void)clearVerifiedLocationToken; - (void)setExpectedJurisdictionWithCountryCode:(NSString *)countryCode stateCode:(NSString *)stateCode; -- (void)getAttestationWithNonce:(NSString *)nonce completionHandler:(RadarVerificationCompletionHandler)completionHandler; -- (BOOL)isJailbroken; -- (NSString *_Nullable)kDeviceId; @end diff --git a/RadarSDK/RadarVerificationManager.m b/RadarSDK/RadarVerificationManager.m index 13bb42e62..386cc31ae 100644 --- a/RadarSDK/RadarVerificationManager.m +++ b/RadarSDK/RadarVerificationManager.m @@ -6,11 +6,7 @@ // Copyright © 2023 Radar Labs, Inc. All rights reserved. // -#import -#import #import -#import -#import @import Network; #import "RadarVerificationManager.h" @@ -23,27 +19,15 @@ #import "RadarLogger.h" #import "RadarState.h" #import "RadarUtils.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include +#import "RadarSDKFraudProtocol.h" @interface RadarVerificationManager () @property (assign, nonatomic) NSTimeInterval startedInterval; @property (assign, nonatomic) BOOL startedBeacons; -@property (strong, nonatomic) NSTimer *intervalTimer; -@property (nonatomic, retain) nw_path_monitor_t monitor; @property (strong, nonatomic) RadarVerifiedLocationToken *lastToken; @property (assign, nonatomic) NSTimeInterval lastTokenSystemUptime; @property (assign, nonatomic) BOOL lastTokenBeacons; -@property (strong, nonatomic) NSString *lastIPs; @property (copy, nonatomic) NSString *expectedCountryCode; @property (copy, nonatomic) NSString *expectedStateCode; @@ -112,53 +96,74 @@ - (void)trackVerifiedWithBeacons:(BOOL)beacons desiredAccuracy:(RadarTrackingOpt return; } - [self getAttestationWithNonce:config.nonce - completionHandler:^(NSString *_Nullable attestationString, NSString *_Nullable keyId, NSString *_Nullable attestationError) { + Class RadarSDKFraud = NSClassFromString(@"RadarSDKFraud"); + if (!RadarSDKFraud) { + [RadarUtils runOnMainThread:^{ + [[RadarDelegateHolder sharedInstance] didFailWithStatus:RadarStatusErrorUnknown]; + + if (completionHandler) { + // todo: add a new error type for missing modules? + completionHandler(RadarStatusErrorUnknown, nil); + } + }]; + return; + } + + + [[RadarSDKFraud sharedInstance] getFraudPayloadWithLocation:location nonce:config.nonce completionHandler:^(RadarStatus status, NSString *_Nullable fraudPayload) { + if (status != RadarStatusSuccess) { + [RadarUtils runOnMainThread:^{ + [[RadarDelegateHolder sharedInstance] didFailWithStatus:status]; + + if (completionHandler) { + completionHandler(status, nil); + } + }]; + return; + } + void (^callTrackAPI)(NSArray *_Nullable) = ^(NSArray *_Nullable beacons) { - [[RadarAPIClient sharedInstance] - trackWithLocation:location - stopped:RadarState.stopped - foreground:[RadarUtils foreground] - source:RadarLocationSourceForegroundLocation - replayed:NO - beacons:beacons - indoorScan:nil - verified:YES - attestationString:attestationString - keyId:keyId - attestationError:attestationError - encrypted:NO - expectedCountryCode:self.expectedCountryCode - expectedStateCode:self.expectedStateCode - reason:reason - transactionId:transactionId - completionHandler:^(RadarStatus status, NSDictionary *_Nullable res, NSArray *_Nullable events, - RadarUser *_Nullable user, NSArray *_Nullable nearbyGeofences, - RadarConfig *_Nullable config, RadarVerifiedLocationToken *_Nullable token) { - if (status == RadarStatusSuccess && config != nil) { - [[RadarLocationManager sharedInstance] updateTrackingFromMeta:config.meta]; + [[RadarAPIClient sharedInstance] + trackWithLocation:location + stopped:RadarState.stopped + foreground:[RadarUtils foreground] + source:RadarLocationSourceForegroundLocation + replayed:NO + beacons:beacons + indoorScan:nil + verified:YES + fraudPayload:fraudPayload + expectedCountryCode:self.expectedCountryCode + expectedStateCode:self.expectedStateCode + reason:reason + transactionId:transactionId + completionHandler:^(RadarStatus status, NSDictionary *_Nullable res, NSArray *_Nullable events, + RadarUser *_Nullable user, NSArray *_Nullable nearbyGeofences, + RadarConfig *_Nullable config, RadarVerifiedLocationToken *_Nullable token) { + if (status == RadarStatusSuccess && config != nil) { + [[RadarLocationManager sharedInstance] updateTrackingFromMeta:config.meta]; + } + + if (token) { + self.lastToken = token; + self.lastTokenSystemUptime = [NSProcessInfo processInfo].systemUptime; + self.lastTokenBeacons = lastTokenBeacons; + } + + [RadarUtils runOnMainThread:^{ + if (status != RadarStatusSuccess) { + [[RadarDelegateHolder sharedInstance] didFailWithStatus:status]; } - if (token) { - self.lastToken = token; - self.lastTokenSystemUptime = [NSProcessInfo processInfo].systemUptime; - self.lastTokenBeacons = lastTokenBeacons; + if (completionHandler) { + completionHandler(status, token); } - - [RadarUtils runOnMainThread:^{ - if (status != RadarStatusSuccess) { - [[RadarDelegateHolder sharedInstance] didFailWithStatus:status]; - } - - if (completionHandler) { - completionHandler(status, token); - } }]; }]; }; - - if (beacons) { - [[RadarAPIClient sharedInstance] + + if (beacons) { + [[RadarAPIClient sharedInstance] searchBeaconsNear:location radius:1000 limit:10 @@ -252,47 +257,21 @@ - (void)callTrackVerifiedWithReason:(NSString *)reason { } - (void)startTrackingVerifiedWithInterval:(NSTimeInterval)interval beacons:(BOOL)beacons { + Class RadarSDKFraud = NSClassFromString(@"RadarSDKFraud"); + if (!RadarSDKFraud) { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Skipping startTrackingVerified: RadarSDKFraud submodule not available"]; + return; + } + [self stopTrackingVerified]; - + self.started = YES; self.startedInterval = interval; self.startedBeacons = beacons; - if (!_monitor) { - _monitor = nw_path_monitor_create(); - - nw_path_monitor_set_queue(_monitor, dispatch_get_main_queue()); - - nw_path_monitor_set_update_handler(_monitor, ^(nw_path_t path) { - if (nw_path_get_status(path) == nw_path_status_satisfied) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Network connected"]; - } else { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Network disconnected"]; - } - - NSString *ips = [self getIPs]; - BOOL changed = NO; - - if (!self.lastIPs) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"First time getting IPs | ips = %@", ips]]; - changed = NO; - } else if (!ips || [ips isEqualToString:@"error"]) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"Error getting IPs | ips = %@", ips]]; - changed = YES; - } else if (![ips isEqualToString:self.lastIPs]) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"IPs changed | ips = %@; lastIPs = %@", ips, self.lastIPs]]; - changed = YES; - } else { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"IPs unchanged"]; - } - self.lastIPs = ips; - - if (changed) { - [self callTrackVerifiedWithReason:@"ip_change"]; - } - }); - nw_path_monitor_start(_monitor); - } + [[RadarSDKFraud sharedInstance] startIPMonitoringWithCallback:^(NSString *reason) { + [self callTrackVerifiedWithReason:reason]; + }]; if ([self isLastTokenValid]) { [self scheduleNextIntervalWithLastToken]; @@ -302,11 +281,16 @@ - (void)startTrackingVerifiedWithInterval:(NSTimeInterval)interval beacons:(BOOL } - (void)stopTrackingVerified { + Class RadarSDKFraud = NSClassFromString(@"RadarSDKFraud"); + if (!RadarSDKFraud) { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Skipping stopTrackingVerified: RadarSDKFraud submodule not available"]; + return; + } + self.started = NO; - if (_monitor) { - nw_path_monitor_cancel(_monitor); - } + // Stop IP monitoring in RadarSDKFraud + [[RadarSDKFraud sharedInstance] stopIPMonitoring]; [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(intervalFired) object:nil]; } @@ -355,350 +339,5 @@ - (void)setExpectedJurisdictionWithCountryCode:(NSString *)countryCode stateCode self.expectedStateCode = stateCode; } -- (void)getAttestationWithNonce:(NSString *)nonce completionHandler:(RadarVerificationCompletionHandler)completionHandler { - if (@available(iOS 14.0, *)) { - DCAppAttestService *service = [DCAppAttestService sharedService]; - - if (!service.isSupported) { - completionHandler(nil, nil, @"Service unsupported"); - - return; - } - - if (!nonce) { - completionHandler(nil, nil, @"Missing nonce"); - - return; - } - - [service generateKeyWithCompletionHandler:^(NSString *_Nullable keyId, NSError *_Nullable error) { - if (error) { - completionHandler(nil, nil, error.localizedDescription); - - return; - } - - NSData *clientData = [nonce dataUsingEncoding:NSUTF8StringEncoding]; - NSMutableData *clientDataHash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH]; - CC_SHA256([clientData bytes], (CC_LONG)[clientData length], [clientDataHash mutableBytes]); - - [service attestKey:keyId - clientDataHash:clientDataHash - completionHandler:^(NSData *_Nullable attestationObject, NSError *_Nullable error) { - NSString *assertionString = [attestationObject base64EncodedStringWithOptions:0]; - - completionHandler(assertionString, keyId, nil); - }]; - }]; - } else { - completionHandler(nil, nil, @"OS unsupported"); - } -} - -// inspired by https://github.com/securing/IOSSecuritySuite -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wundeclared-selector" -- (BOOL)isJailbroken { - BOOL jailbroken = NO; - - NSError *error = nil; - NSFileManager *fileManager = [NSFileManager defaultManager]; - - // URL scheme check - NSArray *suspiciousURLSchemes = @[ - @"undecimus://", - @"sileo://", - @"zbra://", - @"filza://" - ]; - for (NSString *urlScheme in suspiciousURLSchemes) { - NSURL *url = [NSURL URLWithString:urlScheme]; - if ([[UIApplication sharedApplication] canOpenURL:url]) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Jailbreak check failed: URL scheme check"]; - return YES; - } - } - - // file check - NSMutableArray *suspiciousFiles = [NSMutableArray arrayWithArray:@[ - @"/var/mobile/Library/Preferences/ABPattern", - @"/usr/lib/ABDYLD.dylib", - @"/usr/lib/ABSubLoader.dylib", - @"/usr/sbin/frida-server", - @"/etc/apt/sources.list.d/electra.list", - @"/etc/apt/sources.list.d/sileo.sources", - @"/.bootstrapped_electra", - @"/usr/lib/libjailbreak.dylib", - @"/jb/lzma", - @"/.cydia_no_stash", - @"/.installed_unc0ver", - @"/jb/offsets.plist", - @"/usr/share/jailbreak/injectme.plist", - @"/etc/apt/undecimus/undecimus.list", - @"/var/lib/dpkg/info/mobilesubstrate.md5sums", - @"/Library/MobileSubstrate/MobileSubstrate.dylib", - @"/jb/jailbreakd.plist", - @"/jb/amfid_payload.dylib", - @"/jb/libjailbreak.dylib", - @"/usr/libexec/cydia/firmware.sh", - @"/var/lib/cydia", - @"/etc/apt", - @"/private/var/lib/apt", - @"/private/var/Users/", - @"/var/log/apt", - @"/Applications/Cydia.app", - @"/private/var/stash", - @"/private/var/lib/apt/", - @"/private/var/lib/cydia", - @"/private/var/cache/apt/", - @"/private/var/log/syslog", - @"/private/var/tmp/cydia.log", - @"/Applications/Icy.app", - @"/Applications/MxTube.app", - @"/Applications/RockApp.app", - @"/Applications/blackra1n.app", - @"/Applications/SBSettings.app", - @"/Applications/FakeCarrier.app", - @"/Applications/WinterBoard.app", - @"/Applications/IntelliScreen.app", - @"/private/var/mobile/Library/SBSettings/Themes", - @"/Library/MobileSubstrate/CydiaSubstrate.dylib", - @"/System/Library/LaunchDaemons/com.ikey.bbot.plist", - @"/Library/MobileSubstrate/DynamicLibraries/Veency.plist", - @"/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist", - @"/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist", - @"/Applications/Sileo.app", - @"/var/binpack", - @"/Library/PreferenceBundles/LibertyPref.bundle", - @"/Library/PreferenceBundles/ShadowPreferences.bundle", - @"/Library/PreferenceBundles/ABypassPrefs.bundle", - @"/Library/PreferenceBundles/FlyJBPrefs.bundle", - @"/Library/PreferenceBundles/Cephei.bundle", - @"/Library/PreferenceBundles/SubstitutePrefs.bundle", - @"/Library/PreferenceBundles/libhbangprefs.bundle", - @"/usr/lib/libhooker.dylib", - @"/usr/lib/libsubstitute.dylib", - @"/usr/lib/substrate", - @"/usr/lib/TweakInject", - @"/var/binpack/Applications/loader.app", - @"/Applications/FlyJB.app", - @"/Applications/Zebra.app", - @"/Library/BawAppie/ABypass", - @"/Library/MobileSubstrate/DynamicLibraries/SSLKillSwitch2.plist", - @"/Library/MobileSubstrate/DynamicLibraries/PreferenceLoader.plist", - @"/Library/MobileSubstrate/DynamicLibraries/PreferenceLoader.dylib", - @"/Library/MobileSubstrate/DynamicLibraries", - @"/var/mobile/Library/Preferences/me.jjolano.shadow.plist" - ]]; - if (![RadarUtils isSimulator]) { - [suspiciousFiles addObjectsFromArray:@[ - @"/bin/bash", - @"/usr/sbin/sshd", - @"/usr/libexec/ssh-keysign", - @"/bin/sh", - @"/etc/ssh/sshd_config", - @"/usr/libexec/sftp-server", - @"/usr/bin/ssh" - ]]; - } - for (NSString *file in suspiciousFiles) { - if ([fileManager fileExistsAtPath:file]) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Jailbreak check failed: File check"]; - jailbroken = YES; - } - struct stat statStruct; - if (stat([file UTF8String], &statStruct) == 0) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Jailbreak check failed: File check"]; - jailbroken = YES; - } - } - - // fork check - if (![RadarUtils isSimulator]) { - void *handle = dlopen(NULL, RTLD_LAZY); - pid_t (*forkFunction)(void) = dlsym(handle, "fork"); - if (forkFunction == NULL) { - dlclose(handle); - } - pid_t forkResult = forkFunction(); - if (forkResult >= 0) { - if (forkResult > 0) { - kill(forkResult, SIGTERM); - } - dlclose(handle); - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Jailbreak check failed: Fork check"]; - jailbroken = YES; - } - dlclose(handle); - } - - // directory check - NSArray *suspiciousDirs = @[ - @"/", - @"/root/", - @"/private/", - @"/jb/" - ]; - for (NSString *dir in suspiciousDirs) { - NSString *path = [dir stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]]; - [@"RadarSDK" writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&error]; - if (!error) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Jailbreak check failed: Directory check"]; - [fileManager removeItemAtPath:path error:nil]; - jailbroken = YES; - } - } - - // symlink check - NSArray *suspiciousSymlinks = @[ - @"/var/lib/undecimus/apt", - @"/Applications", - @"/Library/Ringtones", - @"/Library/Wallpaper", - @"/usr/arm-apple-darwin9", - @"/usr/include", - @"/usr/libexec", - @"/usr/share" - ]; - for (NSString *symlink in suspiciousSymlinks) { - NSString *result = [fileManager destinationOfSymbolicLinkAtPath:symlink error:&error]; - if (result != nil && ![result isEqualToString:@""]) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Jailbreak check failed: Symlink check"]; - jailbroken = YES; - } - } - - // dylib check - NSArray *suspiciousDylibs = @[ - @"SubstrateLoader.dylib", - @"SSLKillSwitch2.dylib", - @"SSLKillSwitch.dylib", - @"MobileSubstrate.dylib", - @"TweakInject.dylib", - @"CydiaSubstrate", - @"cynject", - @"CustomWidgetIcons", - @"PreferenceLoader", - @"RocketBootstrap", - @"WeeLoader", - @"/.file", - @"libhooker", - @"SubstrateInserter", - @"SubstrateBootstrap", - @"ABypass", - @"FlyJB", - @"Substitute", - @"Cephei", - @"Electra", - @"AppSyncUnified-FrontBoard.dylib", - @"Shadow", - @"FridaGadget", - @"frida", - @"libcycript" - ]; - NSUInteger imageCount = _dyld_image_count(); - for (uint32_t i = 0; i < imageCount; i++) { - const char *imageNameCStr = _dyld_get_image_name(i); - NSString *imageName = [NSString stringWithUTF8String:imageNameCStr]; - for (NSString *dylib in suspiciousDylibs) { - NSRange range = [imageName rangeOfString:dylib options:NSCaseInsensitiveSearch]; - if (range.location != NSNotFound) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Jailbreak check failed: Dylib check"]; - jailbroken = YES; - } - } - } - - // class check - Class shadowRulesetClass = objc_getClass("ShadowRuleset"); - if (shadowRulesetClass != nil) { - SEL selector = @selector(internalDictionary); - if (class_getInstanceMethod(shadowRulesetClass, selector) != NULL) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Jailbreak check failed: Class check"]; - jailbroken = YES; - } - } - - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Jailbreak check passed"]; - - return jailbroken; -} - -- (NSString *)getIPs { - NSMutableArray *ips = [NSMutableArray new]; - struct ifaddrs *interfaces = NULL; - struct ifaddrs *temp_addr = NULL; - int success = 0; - success = getifaddrs(&interfaces); - if (success == 0) { - temp_addr = interfaces; - while(temp_addr != NULL) { - if(temp_addr->ifa_addr->sa_family == AF_INET) { - NSString *ip = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]; - [ips addObject:ip]; - } - temp_addr = temp_addr->ifa_next; - } - } - freeifaddrs(interfaces); - return (ips.count > 0) ? [ips componentsJoinedByString:@","] : @"error"; - -} - -- (NSString *)kDeviceId { - NSString *key = @"com.radar.kDeviceId"; - NSString *service = @"com.radar"; - - @try { - NSDictionary *query = @{ - (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, - (__bridge id)kSecAttrService: service, - (__bridge id)kSecAttrAccount: key, - (__bridge id)kSecReturnData: @YES, - (__bridge id)kSecAttrSynchronizable: @NO - }; - - CFTypeRef result = NULL; - OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); - if (status == errSecSuccess && result) { - NSData *data = (__bridge_transfer NSData *)result; - NSString *kDeviceId = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - if (kDeviceId.length > 0) { - return kDeviceId; - } - } else if (status != errSecItemNotFound) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"Error reading from keychain | status = %d", (int)status]]; - } - - NSString *kDeviceId = [RadarUtils deviceId]; - if (!kDeviceId || kDeviceId.length == 0) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Error getting deviceId"]; - - return nil; - } - - NSData *data = [kDeviceId dataUsingEncoding:NSUTF8StringEncoding]; - NSDictionary *attributes = @{ - (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, - (__bridge id)kSecAttrService: service, - (__bridge id)kSecAttrAccount: key, - (__bridge id)kSecValueData: data, - (__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAfterFirstUnlock, - (__bridge id)kSecAttrSynchronizable: @NO - }; - OSStatus addStatus = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL); - if (addStatus != errSecSuccess) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"Error saving to keychain | addStatus = %d", (int)addStatus]]; - - return nil; - } - - return kDeviceId; - } @catch (NSException *exception) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"Error accessing keychain | exception = %@", exception]]; - - return nil; - } -} @end diff --git a/RadarSDKFraud b/RadarSDKFraud new file mode 160000 index 000000000..e0a8dd9e0 --- /dev/null +++ b/RadarSDKFraud @@ -0,0 +1 @@ +Subproject commit e0a8dd9e0f4252ce1dd671b13c7c33bcd9097f01 diff --git a/RadarSDKTests/RadarSDKTests.m b/RadarSDKTests/RadarSDKTests.m index fb6a977e5..de3afeeb7 100644 --- a/RadarSDKTests/RadarSDKTests.m +++ b/RadarSDKTests/RadarSDKTests.m @@ -129,17 +129,6 @@ - (void)assertTripOk:(RadarTrip *)trip { XCTAssertEqual(trip.status, RadarTripStatusStarted); } -#define AssertFraudOk(fraud) [self assertFraudOk:fraud] -- (void)assertFraudOk:(RadarFraud *)fraud { - XCTAssertNotNil(fraud); - XCTAssertTrue(fraud.passed); - XCTAssertTrue(fraud.bypassed); - XCTAssertTrue(fraud.proxy); - XCTAssertTrue(fraud.mocked); - XCTAssertTrue(fraud.compromised); - XCTAssertTrue(fraud.jumped); -} - #define AssertUserOk(user) [self assertUserOk:user] - (void)assertUserOk:(RadarUser *)user { XCTAssertNotNil(user); @@ -160,7 +149,6 @@ - (void)assertUserOk:(RadarUser *)user { AssertChainsOk(user.topChains); XCTAssertNotEqual(user.source, RadarLocationSourceUnknown); AssertTripOk(user.trip); - AssertFraudOk(user.fraud); } #define AssertEventsOk(events) [self assertEventsOk:events] diff --git a/RadarSDKTests/RadarTestUtils.h b/RadarSDKTests/RadarTestUtils.h index 9dfd0fed2..33b4f7dc3 100644 --- a/RadarSDKTests/RadarTestUtils.h +++ b/RadarSDKTests/RadarTestUtils.h @@ -13,7 +13,6 @@ #import "RadarState.h" #import "RadarUtils.h" #import "RadarTripOptions.h" -#import "RadarVerificationManager.h" NS_ASSUME_NONNULL_BEGIN diff --git a/RadarSDKTests/RadarTestutils.m b/RadarSDKTests/RadarTestutils.m index 4f5c687ad..777f2c3ae 100644 --- a/RadarSDKTests/RadarTestutils.m +++ b/RadarSDKTests/RadarTestutils.m @@ -147,11 +147,6 @@ + (NSMutableDictionary *)createTrackParamWithLocation:(CLLocation *_Nonnull)loca params[@"keyId"] = keyId; params[@"attestationError"] = attestationError; params[@"encrypted"] = @(encrypted); - BOOL jailbroken = [[RadarVerificationManager sharedInstance] isJailbroken]; - params[@"compromised"] = @(jailbroken); - if (jailbroken) { - [fraudFailureReasons addObject:@"fraud_compromised_jailbroken"]; - } if (expectedCountryCode) { params[@"expectedCountryCode"] = expectedCountryCode; }