diff --git a/node_modules/react-native/Libraries/Network/RCTHTTPRequestHandler.mm b/node_modules/react-native/Libraries/Network/RCTHTTPRequestHandler.mm index 76131fa..59804aa 100644 --- a/node_modules/react-native/Libraries/Network/RCTHTTPRequestHandler.mm +++ b/node_modules/react-native/Libraries/Network/RCTHTTPRequestHandler.mm @@ -55,6 +55,59 @@ - (BOOL)canHandleRequest:(NSURLRequest *)request return [schemes containsObject:request.URL.scheme.lowercaseString]; } +-(NSURLCredential *)getUrlCredential:(NSURLAuthenticationChallenge *)challenge path:(NSString *)path password:(NSString *)password +{ + NSString *authMethod = [[challenge protectionSpace] authenticationMethod]; + SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; + + if ([authMethod isEqualToString:NSURLAuthenticationMethodServerTrust] || path == nil || password == nil) { + return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + } else if (path && password) { + NSMutableArray *policies = [NSMutableArray array]; + [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)challenge.protectionSpace.host)]; + SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); + + SecTrustResultType result; + SecTrustEvaluate(serverTrust, &result); + + if (![[NSFileManager defaultManager] fileExistsAtPath:path]) + { + return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + } + + NSData *p12data = [NSData dataWithContentsOfFile:path]; + NSDictionary* options = @{ (id)kSecImportExportPassphrase:password }; + CFArrayRef rawItems = NULL; + OSStatus status = SecPKCS12Import((__bridge CFDataRef)p12data, + (__bridge CFDictionaryRef)options, + &rawItems); + + if (status != noErr) { + return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + } + + NSArray* items = (NSArray*)CFBridgingRelease(rawItems); + NSDictionary* firstItem = nil; + if ((status == errSecSuccess) && ([items count]>0)) { + firstItem = items[0]; + } + + SecIdentityRef identity = (SecIdentityRef)CFBridgingRetain(firstItem[(id)kSecImportItemIdentity]); + SecCertificateRef certificate = NULL; + if (identity) { + SecIdentityCopyCertificate(identity, &certificate); + if (certificate) { CFRelease(certificate); } + } + + NSMutableArray *certificates = [[NSMutableArray alloc] init]; + [certificates addObject:CFBridgingRelease(certificate)]; + + return [NSURLCredential credentialWithIdentity:identity certificates:certificates persistence:NSURLCredentialPersistenceNone]; + } + + return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; +} + - (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request withDelegate:(id)delegate { @@ -177,4 +230,21 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didComp [delegate URLRequest:task didCompleteWithError:error]; } +-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler { + + NSString *host = challenge.protectionSpace.host; + NSDictionary *clientSSL = [[[NSUserDefaults alloc] initWithSuiteName:@"group.ios.chat.rocket"] objectForKey:host]; + + NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + + if (clientSSL != (id)[NSNull null]) { + NSString *path = [clientSSL objectForKey:@"path"]; + NSString *password = [clientSSL objectForKey:@"password"]; + credential = [self getUrlCredential:challenge path:path password:password]; + } + + completionHandler(NSURLSessionAuthChallengeUseCredential, credential); +} + + @end diff --git a/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m b/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m index 6f1e5e8..f268d93 100644 --- a/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m +++ b/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m @@ -479,6 +479,29 @@ - (void)didConnect [self _readHTTPHeader]; } +- (void)setClientSSL:(NSString *)path password:(NSString *)password options:(NSMutableDictionary *)options; +{ + if ([[NSFileManager defaultManager] fileExistsAtPath:path]) + { + NSData *pkcs12data = [[NSData alloc] initWithContentsOfFile:path]; + NSDictionary* certOptions = @{ (id)kSecImportExportPassphrase:password }; + CFArrayRef keyref = NULL; + OSStatus sanityChesk = SecPKCS12Import((__bridge CFDataRef)pkcs12data, + (__bridge CFDictionaryRef)certOptions, + &keyref); + if (sanityChesk == noErr) { + CFDictionaryRef identityDict = CFArrayGetValueAtIndex(keyref, 0); + SecIdentityRef identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity); + SecCertificateRef cert = NULL; + OSStatus status = SecIdentityCopyCertificate(identityRef, &cert); + if (!status) { + NSArray *certificates = [[NSArray alloc] initWithObjects:(__bridge id)identityRef, (__bridge id)cert, nil]; + [options setObject:certificates forKey:(NSString *)kCFStreamSSLCertificates]; + } + } + } +} + - (void)_initializeStreams; { assert(_url.port.unsignedIntValue <= UINT32_MAX); @@ -516,6 +539,15 @@ - (void)_initializeStreams; RCTLogInfo(@"SocketRocket: In debug mode. Allowing connection to any root cert"); #endif + // SSL Pinning + NSDictionary *clientSSL = [[[NSUserDefaults alloc] initWithSuiteName:@"group.ios.chat.rocket"] objectForKey:host]; + if (clientSSL != (id)[NSNull null]) { + NSString *path = [clientSSL objectForKey:@"path"]; + NSString *password = [clientSSL objectForKey:@"password"]; + + [self setClientSSL:path password:password options:SSLOptions]; + } + [_outputStream setProperty:SSLOptions forKey:(__bridge id)kCFStreamPropertySSLSettings]; } @@ -595,6 +627,7 @@ - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; } } + [self.delegate webSocket:self didCloseWithCode:code reason:reason wasClean:YES]; [self _sendFrameWithOpcode:RCTSROpCodeConnectionClose data:payload]; }); } diff --git a/node_modules/react-native/React/Modules/RCTRedBox.m b/node_modules/react-native/React/Modules/RCTRedBox.m index 7ed4900..bb85402 100644 --- a/node_modules/react-native/React/Modules/RCTRedBox.m +++ b/node_modules/react-native/React/Modules/RCTRedBox.m @@ -161,7 +161,7 @@ - (instancetype)initWithFrame:(CGRect)frame - (NSInteger)bottomSafeViewHeight { if (@available(iOS 11.0, *)) { - return [UIApplication sharedApplication].delegate.window.safeAreaInsets.bottom; + return RCTSharedApplication().delegate.window.safeAreaInsets.bottom; } else { return 0; } diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java index cf5ca40..262f22a 100644 --- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java +++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java @@ -91,7 +91,7 @@ public class AndroidInfoModule extends ReactContextBaseJavaModule { private Boolean isRunningScreenshotTest() { try { - Class.forName("android.support.test.rule.ActivityTestRule"); + Class.forName("androidx.test.rule.ActivityTestRule"); return true; } catch (ClassNotFoundException ignored) { return false;