mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Merge branch 'charlesmchen/productionizeCDS2'
This commit is contained in:
commit
0298c35841
|
@ -48,6 +48,7 @@ static const long SGX_XFRM_RESERVED = 0xFFFFFFFFFFFFFFF8L;
|
|||
{
|
||||
ByteParser *_Nullable parser = [[ByteParser alloc] initWithData:quoteData littleEndian:YES];
|
||||
|
||||
// NOTE: This version is separate from and does _NOT_ match the signature body entity version.
|
||||
uint16_t version = parser.nextShort;
|
||||
if (version < 1 || version > 2) {
|
||||
OWSFailDebug(@"unexpected quote version: %d", (int)version);
|
||||
|
|
|
@ -7,6 +7,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@class ECKeyPair;
|
||||
@class OWSAES256Key;
|
||||
|
||||
@interface RemoteAttestationAuth : NSObject
|
||||
|
||||
@property (nonatomic, readonly) NSString *username;
|
||||
@property (nonatomic, readonly) NSString *password;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface RemoteAttestationKeys : NSObject
|
||||
|
||||
@property (nonatomic, readonly) ECKeyPair *keyPair;
|
||||
|
@ -18,22 +27,25 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface RemoteAttestation : NSObject
|
||||
|
||||
@property (nonatomic, readonly) RemoteAttestationKeys *keys;
|
||||
@property (nonatomic, readonly) NSArray<NSHTTPCookie *> *cookies;
|
||||
@property (nonatomic, readonly) NSData *requestId;
|
||||
@property (nonatomic, readonly) NSString *enclaveId;
|
||||
@property (nonatomic, readonly) NSString *authUsername;
|
||||
@property (nonatomic, readonly) NSString *authToken;
|
||||
@property (nonatomic, readonly) RemoteAttestationAuth *auth;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface ContactDiscoveryService : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
+ (instancetype)sharedService;
|
||||
+ (instancetype)shared;
|
||||
|
||||
- (void)testService;
|
||||
- (void)performRemoteAttestationWithSuccess:(void (^)(RemoteAttestation *_Nonnull remoteAttestation))successHandler
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RemoteAttestationAuth : NSObject
|
||||
@interface RemoteAttestationAuth ()
|
||||
|
||||
@property (nonatomic) NSString *username;
|
||||
@property (nonatomic) NSString *password;
|
||||
|
@ -152,16 +152,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@implementation RemoteAttestation
|
||||
|
||||
- (NSString *)authUsername
|
||||
{
|
||||
return self.auth.username;
|
||||
}
|
||||
|
||||
- (NSString *)password
|
||||
{
|
||||
return self.auth.password;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
@ -171,6 +161,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (nonatomic) NSData *isvEnclaveQuoteBody;
|
||||
@property (nonatomic) NSString *isvEnclaveQuoteStatus;
|
||||
@property (nonatomic) NSString *timestamp;
|
||||
@property (nonatomic) NSNumber *version;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -200,6 +191,16 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return valueString;
|
||||
}
|
||||
|
||||
- (nullable NSNumber *)numberForKey:(NSString *)key
|
||||
{
|
||||
NSNumber *_Nullable value = self[key];
|
||||
if (![value isKindOfClass:[NSNumber class]]) {
|
||||
OWSFailDebug(@"couldn't parse number for key: %@", key);
|
||||
return nil;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
- (nullable NSData *)base64DataForKey:(NSString *)key
|
||||
{
|
||||
NSString *_Nullable valueString = self[key];
|
||||
|
@ -237,7 +238,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@implementation ContactDiscoveryService
|
||||
|
||||
+ (instancetype)sharedService {
|
||||
+ (instancetype)shared
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
static id sharedInstance = nil;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
@ -338,7 +340,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
{
|
||||
ECKeyPair *keyPair = [Curve25519 generateKeyPair];
|
||||
|
||||
// TODO:
|
||||
NSString *enclaveId = @"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9";
|
||||
|
||||
TSRequest *request = [OWSRequestFactory remoteAttestationRequest:keyPair
|
||||
|
@ -529,6 +530,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSFailDebug(@"isvEnclaveQuoteBody has unexpected length.");
|
||||
return NO;
|
||||
}
|
||||
// NOTE: This version is separate from and does _NOT_ match the CDS quote version.
|
||||
const NSUInteger kSignatureBodyVersion = 3;
|
||||
if (![signatureBodyEntity.version isEqual:@(kSignatureBodyVersion)]) {
|
||||
OWSFailDebug(@"signatureBodyEntity has unexpected version.");
|
||||
return NO;
|
||||
}
|
||||
if (quoteData.length < kQuoteBodyComparisonLength) {
|
||||
OWSFailDebug(@"quoteData has unexpected length.");
|
||||
return NO;
|
||||
|
@ -541,9 +548,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return NO;
|
||||
}
|
||||
|
||||
// TODO: Before going to production, remove GROUP_OUT_OF_DATE.
|
||||
if (![@"OK" isEqualToString:signatureBodyEntity.isvEnclaveQuoteStatus]
|
||||
&& ![@"GROUP_OUT_OF_DATE" isEqualToString:signatureBodyEntity.isvEnclaveQuoteStatus]) {
|
||||
if (![@"OK" isEqualToString:signatureBodyEntity.isvEnclaveQuoteStatus]) {
|
||||
OWSFailDebug(@"invalid isvEnclaveQuoteStatus: %@.", signatureBodyEntity.isvEnclaveQuoteStatus);
|
||||
return NO;
|
||||
}
|
||||
|
@ -603,11 +608,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSFailDebug(@"could not parse signature isvEnclaveQuoteStatus.");
|
||||
return nil;
|
||||
}
|
||||
NSNumber *_Nullable version = [jsonDict numberForKey:@"version"];
|
||||
if (!version) {
|
||||
OWSFailDebug(@"could not parse signature version.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
SignatureBodyEntity *result = [SignatureBodyEntity new];
|
||||
result.isvEnclaveQuoteBody = isvEnclaveQuoteBody;
|
||||
result.isvEnclaveQuoteStatus = isvEnclaveQuoteStatus;
|
||||
result.timestamp = timestamp;
|
||||
result.version = version;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -643,8 +654,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSFailDebug(@"enclave ids do not match.");
|
||||
return NO;
|
||||
}
|
||||
// TODO: Reverse this condition in production.
|
||||
if (!quote.isDebugQuote) {
|
||||
if (quote.isDebugQuote) {
|
||||
OWSFailDebug(@"quote has invalid isDebugQuote value.");
|
||||
return NO;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ import Foundation
|
|||
@objc(OWSLegacyContactDiscoveryOperation)
|
||||
class LegacyContactDiscoveryBatchOperation: OWSOperation {
|
||||
|
||||
private let isCDSEnabled = false
|
||||
|
||||
@objc
|
||||
var registeredRecipientIds: Set<String>
|
||||
|
||||
|
@ -85,9 +83,6 @@ class LegacyContactDiscoveryBatchOperation: OWSOperation {
|
|||
|
||||
// Called at most one time.
|
||||
override func didSucceed() {
|
||||
guard isCDSEnabled else {
|
||||
return
|
||||
}
|
||||
// Compare against new CDS service
|
||||
let modernCDSOperation = CDSOperation(recipientIdsToLookup: self.recipientIdsToLookup)
|
||||
let cdsFeedbackOperation = CDSFeedbackOperation(legacyRegisteredRecipientIds: self.registeredRecipientIds)
|
||||
|
@ -270,8 +265,8 @@ class CDSBatchOperation: OWSOperation {
|
|||
cryptIv: encryptionResult.initializationVector,
|
||||
cryptMac: encryptionResult.authTag,
|
||||
enclaveId: remoteAttestation.enclaveId,
|
||||
authUsername: remoteAttestation.authUsername,
|
||||
authPassword: remoteAttestation.authToken,
|
||||
authUsername: remoteAttestation.auth.username,
|
||||
authPassword: remoteAttestation.auth.password,
|
||||
cookies: remoteAttestation.cookies)
|
||||
|
||||
self.networkManager.makeRequest(request,
|
||||
|
|
|
@ -282,8 +282,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSAssertDebug(authUsername.length > 0);
|
||||
OWSAssertDebug(authPassword.length > 0);
|
||||
|
||||
NSString *path =
|
||||
[NSString stringWithFormat:@"https://api.contact-discovery.acton-signal.org/v1/attestation/%@", enclaveId];
|
||||
NSString *path = [NSString stringWithFormat:@"%@/v1/attestation/%@", contactDiscoveryURL, enclaveId];
|
||||
TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path]
|
||||
method:@"PUT"
|
||||
parameters:@{
|
||||
|
@ -293,6 +292,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
request.authUsername = authUsername;
|
||||
request.authPassword = authPassword;
|
||||
|
||||
// Don't bother with the default cookie store;
|
||||
// these cookies are ephemeral.
|
||||
[request setHTTPShouldHandleCookies:NO];
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
|
@ -306,8 +309,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
authPassword:(NSString *)authPassword
|
||||
cookies:(NSArray<NSHTTPCookie *> *)cookies
|
||||
{
|
||||
NSString *path =
|
||||
[NSString stringWithFormat:@"https://api.contact-discovery.acton-signal.org/v1/discovery/%@", enclaveId];
|
||||
NSString *path = [NSString stringWithFormat:@"%@/v1/discovery/%@", contactDiscoveryURL, enclaveId];
|
||||
|
||||
TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path]
|
||||
method:@"PUT"
|
||||
|
@ -322,11 +324,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
request.authUsername = authUsername;
|
||||
request.authPassword = authPassword;
|
||||
|
||||
NSDictionary<NSString *, NSString *> *cookieHeaders = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
|
||||
for (NSString *cookieHeader in cookieHeaders) {
|
||||
NSString *cookieValue = cookieHeaders[cookieHeader];
|
||||
[request setValue:cookieValue forHTTPHeaderField:cookieHeader];
|
||||
}
|
||||
// Don't bother with the default cookie store;
|
||||
// these cookies are ephemeral.
|
||||
[request setHTTPShouldHandleCookies:NO];
|
||||
// Set the cookie header.
|
||||
OWSAssertDebug(request.allHTTPHeaderFields.count == 0);
|
||||
[request setAllHTTPHeaderFields:[NSHTTPCookie requestHeaderFieldsWithCookies:cookies]];
|
||||
|
||||
return request;
|
||||
}
|
||||
|
|
|
@ -117,6 +117,17 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
|
|||
password:request.authPassword];
|
||||
}
|
||||
|
||||
// Honor the request's preferences about default cookie handling.
|
||||
//
|
||||
// Default is YES.
|
||||
sessionManager.requestSerializer.HTTPShouldHandleCookies = request.HTTPShouldHandleCookies;
|
||||
|
||||
// Honor the request's headers.
|
||||
for (NSString *headerField in request.allHTTPHeaderFields) {
|
||||
NSString *headerValue = request.allHTTPHeaderFields[headerField];
|
||||
[sessionManager.requestSerializer setValue:headerValue forHTTPHeaderField:headerField];
|
||||
}
|
||||
|
||||
if ([request.HTTPMethod isEqualToString:@"GET"]) {
|
||||
[sessionManager GET:request.URL.absoluteString
|
||||
parameters:request.parameters
|
||||
|
@ -178,6 +189,7 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
|
|||
[curlComponents addObject:@"--data-ascii"];
|
||||
[curlComponents addObject:[NSString stringWithFormat:@"'%@'", jsonBody]];
|
||||
}
|
||||
// TODO: Add support for cookies.
|
||||
[curlComponents addObject:task.originalRequest.URL.absoluteString];
|
||||
NSString *curlCommand = [curlComponents componentsJoinedByString:@" "];
|
||||
OWSLogVerbose(@"curl for failed request: %@", curlCommand);
|
||||
|
@ -224,10 +236,6 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
|
|||
|
||||
error.isRetryable = NO;
|
||||
|
||||
// TODO distinguish CDS requests. we don't want a bad CDS request to trigger "Signal deauth" logic.
|
||||
// also, shouldn't this be under 403, not 400?
|
||||
[TSAccountManager.sharedInstance setIsDeregistered:YES];
|
||||
|
||||
failureBlock(task, error);
|
||||
break;
|
||||
}
|
||||
|
@ -236,6 +244,7 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
|
|||
networkError.debugDescription,
|
||||
request);
|
||||
error.isRetryable = NO;
|
||||
[self deregisterAfterAuthErrorIfNecessary:task statusCode:statusCode];
|
||||
failureBlock(task, error);
|
||||
break;
|
||||
}
|
||||
|
@ -243,6 +252,7 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
|
|||
OWSLogError(
|
||||
@"The server returned an authentication failure: %@, %@", networkError.debugDescription, request);
|
||||
error.isRetryable = NO;
|
||||
[self deregisterAfterAuthErrorIfNecessary:task statusCode:statusCode];
|
||||
failureBlock(task, error);
|
||||
break;
|
||||
}
|
||||
|
@ -300,6 +310,19 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
|
|||
};
|
||||
}
|
||||
|
||||
+ (void)deregisterAfterAuthErrorIfNecessary:(NSURLSessionDataTask *)task statusCode:(NSInteger)statusCode
|
||||
{
|
||||
OWSLogVerbose(@"Invalid auth: %@", task.originalRequest.allHTTPHeaderFields);
|
||||
|
||||
// Distinguish CDS requests.
|
||||
// We don't want a bad CDS request to trigger "Signal deauth" logic.
|
||||
if ([task.originalRequest.URL.absoluteString hasPrefix:textSecureServerURL]) {
|
||||
[TSAccountManager.sharedInstance setIsDeregistered:YES];
|
||||
} else {
|
||||
OWSLogWarn(@"Ignoring %d for URL: %@", (int)statusCode, task.originalRequest.URL.absoluteString);
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSError *)errorWithHTTPCode:(NSInteger)code
|
||||
description:(NSString *)description
|
||||
failureReason:(NSString *)failureReason
|
||||
|
|
|
@ -33,6 +33,7 @@ typedef NS_ENUM(NSInteger, TSWhisperMessageType) {
|
|||
// Use same reflector for service and CDN
|
||||
#define textSecureServiceReflectorHost @"textsecure-service-reflected.whispersystems.org"
|
||||
#define textSecureCDNReflectorHost @"textsecure-service-reflected.whispersystems.org"
|
||||
#define contactDiscoveryURL @"https://api.directory.signal.org"
|
||||
|
||||
//#else
|
||||
//
|
||||
|
@ -42,6 +43,7 @@ typedef NS_ENUM(NSInteger, TSWhisperMessageType) {
|
|||
//#define textSecureCDNServerURL @"https://cdn-staging.signal.org"
|
||||
//#define textSecureServiceReflectorHost @"meek-signal-service-staging.appspot.com";
|
||||
//#define textSecureCDNReflectorHost @"meek-signal-cdn-staging.appspot.com";
|
||||
//#define contactDiscoveryURL @"https://api-staging.directory.signal.org"
|
||||
//
|
||||
//#endif
|
||||
|
||||
|
|
Loading…
Reference in a new issue