mirror of https://github.com/oxen-io/session-ios
Censorship circumvention in Egypt and UAE
* domain fronting * non-websocket message fetching * alternate pinning policy for google hosted reflector server // FREEBIEpull/1/head
parent
b1ebfa9873
commit
78515377b1
@ -0,0 +1,14 @@
|
||||
// Created by Michael Kirk on 12/19/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "TSRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSAcknowledgeMessageDeliveryRequest : TSRequest
|
||||
|
||||
- (instancetype)initWithSource:(NSString *)source timestamp:(UInt64)timestamp;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,23 @@
|
||||
// Created by Michael Kirk on 12/19/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSAcknowledgeMessageDeliveryRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSAcknowledgeMessageDeliveryRequest
|
||||
|
||||
- (instancetype)initWithSource:(NSString *)source timestamp:(UInt64)timestamp
|
||||
{
|
||||
NSString *path = [NSString stringWithFormat:@"v1/messages/%@/%llu", source, timestamp];
|
||||
NSURL *url = [NSURL URLWithString:path];
|
||||
|
||||
self = [super initWithURL:url];
|
||||
self.HTTPMethod = @"DELETE";
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,12 @@
|
||||
// Created by Michael Kirk on 12/19/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "TSRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSGetMessagesRequest : TSRequest
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,18 @@
|
||||
// Created by Michael Kirk on 12/19/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSGetMessagesRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSGetMessagesRequest
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
NSURL *url = [NSURL URLWithString:@"v1/messages"];
|
||||
return [super initWithURL:url];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,16 @@
|
||||
// Created by Michael Kirk on 12/20/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSStorageManager;
|
||||
|
||||
@interface OWSCensorshipConfiguration : NSObject
|
||||
|
||||
- (NSString *)frontingHost;
|
||||
- (NSString *)reflectorHost;
|
||||
- (BOOL)isCensoredPhoneNumber:(NSString *)e164PhonNumber;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,57 @@
|
||||
// Created by Michael Kirk on 12/20/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSCensorshipConfiguration.h"
|
||||
#import "TSStorageManager.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const OWSCensorshipConfigurationFrontingHost = @"https://google.com";
|
||||
NSString *const OWSCensorshipConfigurationReflectorHost = @"signal-reflector-meek.appspot.com";
|
||||
|
||||
@implementation OWSCensorshipConfiguration
|
||||
|
||||
- (NSString *)frontingHost
|
||||
{
|
||||
return OWSCensorshipConfigurationFrontingHost;
|
||||
}
|
||||
|
||||
- (NSString *)reflectorHost
|
||||
{
|
||||
return OWSCensorshipConfigurationReflectorHost;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)censoredCountryCodes
|
||||
{
|
||||
// Reports of censorship in:
|
||||
// Egypt
|
||||
// UAE
|
||||
return @[@"+20",
|
||||
@"+971"];
|
||||
}
|
||||
|
||||
- (BOOL)isCensoredPhoneNumber:(NSString *)e164PhonNumber
|
||||
{
|
||||
for (NSString *countryCode in self.censoredCountryCodes) {
|
||||
if ([e164PhonNumber hasPrefix:countryCode]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,17 @@
|
||||
// Created by Michael Kirk on 12/20/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSStorageManager;
|
||||
@class TSAccountManager;
|
||||
@class AFHTTPSessionManager;
|
||||
|
||||
@interface OWSSignalService : NSObject
|
||||
|
||||
@property (nonatomic, readonly) BOOL isCensored;
|
||||
@property (nonatomic, readonly) AFHTTPSessionManager *HTTPSessionManager;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,138 @@
|
||||
// Created by Michael Kirk on 12/20/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSSignalService.h"
|
||||
#import "OWSCensorshipConfiguration.h"
|
||||
#import "OWSHTTPSecurityPolicy.h"
|
||||
#import "TSConstants.h"
|
||||
#import "TSAccountManager.h"
|
||||
#import <AFNetworking/AFHTTPSessionManager.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSSignalService ()
|
||||
|
||||
@property (nonatomic, readonly, strong) OWSCensorshipConfiguration *censorshipConfiguration;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSSignalService
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_censorshipConfiguration = [OWSCensorshipConfiguration new];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isCensored
|
||||
{
|
||||
NSString *localNumber = [TSAccountManager localNumber];
|
||||
|
||||
if (localNumber) {
|
||||
return [self.censorshipConfiguration isCensoredPhoneNumber:localNumber];
|
||||
} else {
|
||||
DDLogError(@"no known phone number to check for censorship.");
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (AFHTTPSessionManager *)HTTPSessionManager
|
||||
{
|
||||
if (self.isCensored) {
|
||||
DDLogInfo(@"%@ using reflector HTTPSessionManager", self.tag);
|
||||
return self.reflectorHTTPSessionManager;
|
||||
} else {
|
||||
DDLogDebug(@"%@ using default HTTPSessionManager", self.tag);
|
||||
return self.defaultHTTPSessionManager;
|
||||
}
|
||||
}
|
||||
|
||||
- (AFHTTPSessionManager *)defaultHTTPSessionManager
|
||||
{
|
||||
NSURL *baseURL = [[NSURL alloc] initWithString:textSecureServerURL];
|
||||
NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration;
|
||||
AFHTTPSessionManager *sessionManager =
|
||||
[[AFHTTPSessionManager alloc] initWithBaseURL:baseURL sessionConfiguration:sessionConf];
|
||||
|
||||
sessionManager.securityPolicy = [OWSHTTPSecurityPolicy sharedPolicy];
|
||||
sessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
|
||||
sessionManager.responseSerializer = [AFJSONResponseSerializer serializer];
|
||||
|
||||
return sessionManager;
|
||||
}
|
||||
|
||||
- (AFHTTPSessionManager *)reflectorHTTPSessionManager
|
||||
{
|
||||
// Target fronting domain
|
||||
NSURL *baseURL = [[NSURL alloc] initWithString:self.censorshipConfiguration.frontingHost];
|
||||
NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration;
|
||||
AFHTTPSessionManager *sessionManager =
|
||||
[[AFHTTPSessionManager alloc] initWithBaseURL:baseURL sessionConfiguration:sessionConf];
|
||||
|
||||
sessionManager.securityPolicy = [[self class] googlePinningPolicy];
|
||||
|
||||
sessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
|
||||
[sessionManager.requestSerializer setValue:self.censorshipConfiguration.reflectorHost forHTTPHeaderField:@"Host"];
|
||||
|
||||
sessionManager.responseSerializer = [AFJSONResponseSerializer serializer];
|
||||
|
||||
return sessionManager;
|
||||
}
|
||||
|
||||
#pragma mark - Google Pinning Policy
|
||||
|
||||
/**
|
||||
* We use the Google Pinning Policy when connecting to our censorship circumventing reflector,
|
||||
* which is hosted on Google.
|
||||
*/
|
||||
+ (AFSecurityPolicy *)googlePinningPolicy {
|
||||
static AFSecurityPolicy *securityPolicy = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSError *error;
|
||||
NSString *path = [NSBundle.mainBundle pathForResource:@"GIAG2" ofType:@"crt"];
|
||||
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
||||
@throw [NSException
|
||||
exceptionWithName:@"Missing server certificate"
|
||||
reason:[NSString stringWithFormat:@"Missing signing certificate for service googlePinningPolicy"]
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
NSData *googleCertData = [NSData dataWithContentsOfFile:path options:0 error:&error];
|
||||
if (!googleCertData) {
|
||||
if (error) {
|
||||
@throw [NSException exceptionWithName:@"OWSSignalServiceHTTPSecurityPolicy" reason:@"Couln't read google pinning cert" userInfo:nil];
|
||||
} else {
|
||||
NSString *reason = [NSString stringWithFormat:@"Reading google pinning cert faile with error: %@", error];
|
||||
@throw [NSException exceptionWithName:@"OWSSignalServiceHTTPSecurityPolicy" reason:reason userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
NSSet<NSData *> *certificates = [NSSet setWithObject:googleCertData];
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:certificates];
|
||||
});
|
||||
return securityPolicy;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
Binary file not shown.
Loading…
Reference in New Issue