Censorship circumvention with attachments v2

This commit is contained in:
Michael Kirk 2019-04-17 19:56:44 -06:00
parent e7daaa834f
commit 76abaef2c6
7 changed files with 88 additions and 31 deletions

View File

@ -494,11 +494,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error);
TSAttachmentPointer *attachmentPointer = job.attachmentPointer;
AFHTTPSessionManager *manager = self.cdnSessionManager;
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
[manager.requestSerializer setValue:OWSMimeTypeApplicationOctetStream forHTTPHeaderField:@"Content-Type"];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSString *urlPath = [NSString stringWithFormat:@"attachments/%llu", attachmentPointer.serverId];
NSURL *url = [[NSURL alloc] initWithString:urlPath relativeToURL:manager.baseURL];
@ -527,6 +523,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error);
URLString:url.absoluteString
parameters:nil
error:&serializationError];
[request setValue:OWSMimeTypeApplicationOctetStream forHTTPHeaderField:@"Content-Type"];
if (serializationError) {
return failureHandler(serializationError);
}

View File

@ -144,6 +144,15 @@ NS_ASSUME_NONNULL_BEGIN
return [TSRequest requestWithUrl:[NSURL URLWithString:@"v2/attachments/form/upload"] method:@"GET" parameters:@{}];
}
+ (TSRequest *)attachmentRequestWithAttachmentId:(UInt64)attachmentId
{
OWSAssertDebug(attachmentId > 0);
NSString *path = [NSString stringWithFormat:@"%@/%llu", textSecureAttachmentsAPI, attachmentId];
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
}
+ (TSRequest *)availablePreKeysCountRequest
{
NSString *path = [NSString stringWithFormat:@"%@", textSecureKeysAPI];
@ -207,15 +216,15 @@ NS_ASSUME_NONNULL_BEGIN
+ (TSRequest *)updateAttributesRequest
{
NSString *path = [textSecureAccountsAPI stringByAppendingString:textSecureAttributesAPI];
NSString *authKey = self.tsAccountManager.serverAuthToken;
OWSAssertDebug(authKey.length > 0);
NSString *_Nullable pin = [self.ows2FAManager pinCode];
NSDictionary<NSString *, id> *accountAttributes = [self accountAttributesWithPin:pin authKey:authKey];
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:accountAttributes];
return [TSRequest requestWithUrl:[NSURL URLWithString:textSecureAttributesAPI]
method:@"PUT"
parameters:accountAttributes];
}
+ (TSRequest *)unregisterAccountRequest
@ -426,7 +435,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssertDebug(authUsername.length > 0);
OWSAssertDebug(authPassword.length > 0);
NSString *path = [NSString stringWithFormat:@"%@/v1/attestation/%@", contactDiscoveryURL, enclaveId];
NSString *path = [NSString stringWithFormat:@"v1/attestation/%@", enclaveId];
TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path]
method:@"PUT"
parameters:@{
@ -435,6 +444,8 @@ NS_ASSUME_NONNULL_BEGIN
}];
request.authUsername = authUsername;
request.authPassword = authPassword;
request.customHost = contactDiscoveryURL;
request.customCensorshipCircumventionPrefix = contactDiscoveryCensorshipPrefix;
// Don't bother with the default cookie store;
// these cookies are ephemeral.
@ -455,7 +466,7 @@ NS_ASSUME_NONNULL_BEGIN
authPassword:(NSString *)authPassword
cookies:(NSArray<NSHTTPCookie *> *)cookies
{
NSString *path = [NSString stringWithFormat:@"%@/v1/discovery/%@", contactDiscoveryURL, enclaveId];
NSString *path = [NSString stringWithFormat:@"v1/discovery/%@", enclaveId];
TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path]
method:@"PUT"
@ -469,6 +480,8 @@ NS_ASSUME_NONNULL_BEGIN
request.authUsername = authUsername;
request.authPassword = authPassword;
request.customHost = contactDiscoveryURL;
request.customCensorshipCircumventionPrefix = contactDiscoveryCensorshipPrefix;
// Don't bother with the default cookie store;
// these cookies are ephemeral.
@ -484,7 +497,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (TSRequest *)remoteAttestationAuthRequest
{
NSString *path = @"/v1/directory/auth";
NSString *path = @"v1/directory/auth";
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
}
@ -506,7 +519,7 @@ NS_ASSUME_NONNULL_BEGIN
}
parameters = @{ @"reason": limitedReason };
}
NSString *path = [NSString stringWithFormat:@"/v1/directory/feedback-v3/%@", status];
NSString *path = [NSString stringWithFormat:@"v1/directory/feedback-v3/%@", status];
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:parameters];
}
@ -514,7 +527,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (TSRequest *)udSenderCertificateRequest
{
NSString *path = @"/v1/certificate/delivery";
NSString *path = @"v1/certificate/delivery";
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
}

View File

@ -12,6 +12,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) BOOL shouldHaveAuthorizationHeaders;
@property (atomic, nullable) NSString *authUsername;
@property (atomic, nullable) NSString *authPassword;
@property (atomic, nullable) NSString *customHost;
@property (atomic, nullable) NSString *customCensorshipCircumventionPrefix;
@property (nonatomic, readonly) NSDictionary<NSString *, id> *parameters;

View File

@ -103,6 +103,35 @@ dispatch_queue_t NetworkManagerQueue()
password:request.authPassword];
}
// Most of TSNetwork requests are destined for the Signal Service.
// When we are domain fronting, we have to target a different host and add a path prefix.
// For common Signal-Service requests the host/path-prefix logic is handled by the
// sessionManager.
//
// However, for CDS requests, we need to:
// With CC enabled, use the service fronting Hostname but a custom path-prefix
// With CC disabled, use the custom directory host, and no path-prefix
NSString *requestURLString;
if (self.signalService.isCensorshipCircumventionActive && request.customCensorshipCircumventionPrefix.length > 0) {
// All fronted requests go through the same host
NSURL *customBaseURL = [self.signalService.domainFrontBaseURL
URLByAppendingPathComponent:request.customCensorshipCircumventionPrefix];
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if (![customBaseURL.absoluteString hasSuffix:@"/"]) {
customBaseURL = [customBaseURL URLByAppendingPathComponent:@""];
}
OWSAssertDebug(customBaseURL);
requestURLString = [NSURL URLWithString:request.URL.absoluteString relativeToURL:customBaseURL].absoluteString;
} else if (request.customHost) {
NSURL *customBaseURL = [NSURL URLWithString:request.customHost];
OWSAssertDebug(customBaseURL);
requestURLString = [NSURL URLWithString:request.URL.absoluteString relativeToURL:customBaseURL].absoluteString;
} else {
// requests for the signal-service (with or without censorship circumvention)
requestURLString = request.URL.absoluteString;
}
OWSAssertDebug(requestURLString.length > 0);
// Honor the request's headers.
for (NSString *headerField in request.allHTTPHeaderFields) {
NSString *headerValue = request.allHTTPHeaderFields[headerField];
@ -110,27 +139,21 @@ dispatch_queue_t NetworkManagerQueue()
}
if ([request.HTTPMethod isEqualToString:@"GET"]) {
[self.sessionManager GET:request.URL.absoluteString
[self.sessionManager GET:requestURLString
parameters:request.parameters
progress:nil
success:success
failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"POST"]) {
[self.sessionManager POST:request.URL.absoluteString
[self.sessionManager POST:requestURLString
parameters:request.parameters
progress:nil
success:success
failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"PUT"]) {
[self.sessionManager PUT:request.URL.absoluteString
parameters:request.parameters
success:success
failure:failure];
[self.sessionManager PUT:requestURLString parameters:request.parameters success:success failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"DELETE"]) {
[self.sessionManager DELETE:request.URL.absoluteString
parameters:request.parameters
success:success
failure:failure];
[self.sessionManager DELETE:requestURLString parameters:request.parameters success:success failure:failure];
} else {
OWSLogError(@"Trying to perform HTTP operation with unknown verb: %@", request.HTTPMethod);
}

View File

@ -12,7 +12,7 @@ extern NSString *const kNSNotificationName_IsCensorshipCircumventionActiveDidCha
@interface OWSSignalService : NSObject
/// For uploading avatar assets.
/// For uploading and downloading avatar assets and attachments
@property (nonatomic, readonly) AFHTTPSessionManager *CDNSessionManager;
+ (instancetype)sharedInstance;
@ -27,6 +27,9 @@ extern NSString *const kNSNotificationName_IsCensorshipCircumventionActiveDidCha
@property (atomic) BOOL isCensorshipCircumventionManuallyDisabled;
@property (atomic, nullable) NSString *manualCensorshipCircumventionCountryCode;
/// should only be accessed if censorship circumvention is active.
@property (nonatomic, readonly) NSURL *domainFrontBaseURL;
/// For interacting with the Signal Service
- (AFHTTPSessionManager *)buildSignalServiceSessionManager;

View File

@ -172,6 +172,13 @@ NSString *const kNSNotificationName_IsCensorshipCircumventionActiveDidChange =
}
}
- (NSURL *)domainFrontBaseURL
{
OWSAssertDebug(self.isCensorshipCircumventionActive);
OWSCensorshipConfiguration *censorshipConfiguration = [self buildCensorshipConfiguration];
return censorshipConfiguration.domainFrontBaseURL;
}
- (AFHTTPSessionManager *)buildSignalServiceSessionManager
{
if (self.isCensorshipCircumventionActive) {
@ -204,9 +211,11 @@ NSString *const kNSNotificationName_IsCensorshipCircumventionActiveDidChange =
(OWSCensorshipConfiguration *)censorshipConfiguration
{
NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration;
NSURL *frontingURL = censorshipConfiguration.domainFrontBaseURL;
NSURL *baseURL = [frontingURL URLByAppendingPathComponent:serviceCensorshipPrefix];
AFHTTPSessionManager *sessionManager =
[[AFHTTPSessionManager alloc] initWithBaseURL:censorshipConfiguration.domainFrontBaseURL
sessionConfiguration:sessionConf];
[[AFHTTPSessionManager alloc] initWithBaseURL:baseURL sessionConfiguration:sessionConf];
sessionManager.securityPolicy = censorshipConfiguration.domainFrontSecurityPolicy;
@ -255,9 +264,10 @@ NSString *const kNSNotificationName_IsCensorshipCircumventionActiveDidChange =
{
NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration;
NSURL *frontingURL = censorshipConfiguration.domainFrontBaseURL;
NSURL *baseURL = [frontingURL URLByAppendingPathComponent:cdnCensorshipPrefix];
AFHTTPSessionManager *sessionManager =
[[AFHTTPSessionManager alloc] initWithBaseURL:censorshipConfiguration.domainFrontBaseURL
sessionConfiguration:sessionConf];
[[AFHTTPSessionManager alloc] initWithBaseURL:baseURL sessionConfiguration:sessionConf];
sessionManager.securityPolicy = censorshipConfiguration.domainFrontSecurityPolicy;

View File

@ -33,6 +33,11 @@ typedef NS_ENUM(NSInteger, TSWhisperMessageType) {
#define textSecureCDNReflectorHost @"europe-west1-signal-cdn-reflector.cloudfunctions.net"
#define contactDiscoveryURL @"https://api.directory.signal.org"
#define kUDTrustRoot @"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"
#define serviceCensorshipPrefix @"service"
#define cdnCensorshipPrefix @"cdn"
#define contactDiscoveryCensorshipPrefix @"directory"
#define USING_PRODUCTION_SERVICE
//#else
@ -41,17 +46,21 @@ typedef NS_ENUM(NSInteger, TSWhisperMessageType) {
//#define textSecureWebSocketAPI @"wss://textsecure-service-staging.whispersystems.org/v1/websocket/"
//#define textSecureServerURL @"https://textsecure-service-staging.whispersystems.org/"
//#define textSecureCDNServerURL @"https://cdn-staging.signal.org"
//#define textSecureServiceReflectorHost @"meek-signal-service-staging.appspot.com";
//#define textSecureCDNReflectorHost @"meek-signal-cdn-staging.appspot.com";
//#define textSecureServiceReflectorHost @"europe-west1-signal-cdn-reflector.cloudfunctions.net";
//#define textSecureCDNReflectorHost @"europe-west1-signal-cdn-reflector.cloudfunctions.net";
//#define contactDiscoveryURL @"https://api-staging.directory.signal.org"
//#define kUDTrustRoot @"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx"
//
//#define serviceCensorshipPrefix @"service-staging"
//#define cdnCensorshipPrefix @"cdn-staging"
//#define contactDiscoveryCensorshipPrefix @"directory-staging"
//#endif
BOOL IsUsingProductionService(void);
#define textSecureAccountsAPI @"v1/accounts"
#define textSecureAttributesAPI @"/attributes/"
#define textSecureAttributesAPI @"v1/accounts/attributes/"
#define textSecureMessagesAPI @"v1/messages/"
#define textSecureKeysAPI @"v2/keys"
@ -64,7 +73,7 @@ BOOL IsUsingProductionService(void);
#define textSecureProfileAPIFormat @"v1/profile/%@"
#define textSecureSetProfileNameAPIFormat @"v1/profile/name/%@"
#define textSecureProfileAvatarFormAPI @"v1/profile/form/avatar"
#define textSecure2FAAPI @"/v1/accounts/pin"
#define textSecure2FAAPI @"v1/accounts/pin"
#define SignalApplicationGroup @"group.org.whispersystems.signal.group"