Merge branch 'mkirk/cds-feedback-specifics' into release/2.36.0

pull/2/head
Michael Kirk 6 years ago
commit cd13be64fe

@ -1,12 +1,13 @@
// //
// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // Copyright (c) 2019 Open Whisper Systems. All rights reserved.
// //
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
extern NSErrorUserInfoKey const ContactDiscoveryServiceErrorKey_Reason;
extern NSErrorDomain const ContactDiscoveryServiceErrorDomain; extern NSErrorDomain const ContactDiscoveryServiceErrorDomain;
typedef NS_ERROR_ENUM(ContactDiscoveryServiceErrorDomain, ContactDiscoveryServiceError){ typedef NS_ERROR_ENUM(ContactDiscoveryServiceErrorDomain, ContactDiscoveryServiceError){
ContactDiscoveryServiceErrorAttestationFailed = 100, ContactDiscoveryServiceErrorAttestationFailed = 100, ContactDiscoveryServiceErrorAssertionError = 101
}; };
@class ECKeyPair; @class ECKeyPair;

@ -18,8 +18,16 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
NSErrorUserInfoKey const ContactDiscoveryServiceErrorKey_Reason = @"ContactDiscoveryServiceErrorKey_Reason";
NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.ContactDiscoveryService"; NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.ContactDiscoveryService";
NSError *ContactDiscoveryServiceErrorMakeWithReason(NSInteger code, NSString *reason)
{
return [NSError errorWithDomain:ContactDiscoveryServiceErrorDomain
code:code
userInfo:@{ ContactDiscoveryServiceErrorKey_Reason : reason }];
}
@interface RemoteAttestationAuth () @interface RemoteAttestationAuth ()
@property (nonatomic) NSString *username; @property (nonatomic) NSString *username;
@ -53,17 +61,21 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
+ (nullable RemoteAttestationKeys *)keysForKeyPair:(ECKeyPair *)keyPair + (nullable RemoteAttestationKeys *)keysForKeyPair:(ECKeyPair *)keyPair
serverEphemeralPublic:(NSData *)serverEphemeralPublic serverEphemeralPublic:(NSData *)serverEphemeralPublic
serverStaticPublic:(NSData *)serverStaticPublic serverStaticPublic:(NSData *)serverStaticPublic
error:(NSError **)error
{ {
if (!keyPair) { if (!keyPair) {
OWSFailDebug(@"Missing keyPair"); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"Missing keyPair");
return nil; return nil;
} }
if (serverEphemeralPublic.length < 1) { if (serverEphemeralPublic.length < 1) {
OWSFailDebug(@"Invalid serverEphemeralPublic"); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"Invalid serverEphemeralPublic");
return nil; return nil;
} }
if (serverStaticPublic.length < 1) { if (serverStaticPublic.length < 1) {
OWSFailDebug(@"Invalid serverStaticPublic"); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"Invalid serverStaticPublic");
return nil; return nil;
} }
RemoteAttestationKeys *keys = [RemoteAttestationKeys new]; RemoteAttestationKeys *keys = [RemoteAttestationKeys new];
@ -71,6 +83,8 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
keys.serverEphemeralPublic = serverEphemeralPublic; keys.serverEphemeralPublic = serverEphemeralPublic;
keys.serverStaticPublic = serverStaticPublic; keys.serverStaticPublic = serverStaticPublic;
if (![keys deriveKeys]) { if (![keys deriveKeys]) {
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"failed to derive keys");
return nil; return nil;
} }
return keys; return keys;
@ -350,16 +364,22 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
[[TSNetworkManager sharedManager] makeRequest:request [[TSNetworkManager sharedManager] makeRequest:request
success:^(NSURLSessionDataTask *task, id responseJson) { success:^(NSURLSessionDataTask *task, id responseJson) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *_Nullable error;
RemoteAttestation *_Nullable attestation = [self parseAttestationResponseJson:responseJson RemoteAttestation *_Nullable attestation = [self parseAttestationResponseJson:responseJson
response:task.response response:task.response
keyPair:keyPair keyPair:keyPair
enclaveId:enclaveId enclaveId:enclaveId
auth:auth]; auth:auth
error:&error];
if (!attestation) { if (!attestation) {
NSError *error = [NSError errorWithDomain:ContactDiscoveryServiceErrorDomain if (!error) {
code:ContactDiscoveryServiceErrorAttestationFailed OWSFailDebug(@"error was unexpectedly nil");
userInfo:nil]; error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAssertionError,
@"failure when parsing attestation - no reason given");
} else {
OWSFailDebug(@"error with attestation: %@", error);
}
error.isRetryable = NO; error.isRetryable = NO;
failureHandler(error); failureHandler(error);
return; return;
@ -378,6 +398,7 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
keyPair:(ECKeyPair *)keyPair keyPair:(ECKeyPair *)keyPair
enclaveId:(NSString *)enclaveId enclaveId:(NSString *)enclaveId
auth:(RemoteAttestationAuth *)auth auth:(RemoteAttestationAuth *)auth
error:(NSError **)error
{ {
OWSAssertDebug(responseJson); OWSAssertDebug(responseJson);
OWSAssertDebug(response); OWSAssertDebug(response);
@ -385,78 +406,96 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
OWSAssertDebug(enclaveId.length > 0); OWSAssertDebug(enclaveId.length > 0);
if (![response isKindOfClass:[NSHTTPURLResponse class]]) { if (![response isKindOfClass:[NSHTTPURLResponse class]]) {
OWSFailDebug(@"unexpected response type."); *error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAssertionError, @"unexpected response type.");
return nil; return nil;
} }
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSArray<NSHTTPCookie *> *cookies = NSArray<NSHTTPCookie *> *cookies =
[NSHTTPCookie cookiesWithResponseHeaderFields:httpResponse.allHeaderFields forURL:httpResponse.URL]; [NSHTTPCookie cookiesWithResponseHeaderFields:httpResponse.allHeaderFields forURL:httpResponse.URL];
if (cookies.count < 1) { if (cookies.count < 1) {
OWSFailDebug(@"couldn't parse cookie."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse cookie.");
return nil; return nil;
} }
if (![responseJson isKindOfClass:[NSDictionary class]]) { if (![responseJson isKindOfClass:[NSDictionary class]]) {
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"invalid json response");
return nil; return nil;
} }
NSDictionary *responseDict = responseJson; NSDictionary *responseDict = responseJson;
NSData *_Nullable serverEphemeralPublic = NSData *_Nullable serverEphemeralPublic =
[responseDict base64DataForKey:@"serverEphemeralPublic" expectedLength:32]; [responseDict base64DataForKey:@"serverEphemeralPublic" expectedLength:32];
if (!serverEphemeralPublic) { if (!serverEphemeralPublic) {
OWSFailDebug(@"couldn't parse serverEphemeralPublic."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse serverEphemeralPublic.");
return nil; return nil;
} }
NSData *_Nullable serverStaticPublic = [responseDict base64DataForKey:@"serverStaticPublic" expectedLength:32]; NSData *_Nullable serverStaticPublic = [responseDict base64DataForKey:@"serverStaticPublic" expectedLength:32];
if (!serverStaticPublic) { if (!serverStaticPublic) {
OWSFailDebug(@"couldn't parse serverStaticPublic."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse serverStaticPublic.");
return nil; return nil;
} }
NSData *_Nullable encryptedRequestId = [responseDict base64DataForKey:@"ciphertext"]; NSData *_Nullable encryptedRequestId = [responseDict base64DataForKey:@"ciphertext"];
if (!encryptedRequestId) { if (!encryptedRequestId) {
OWSFailDebug(@"couldn't parse encryptedRequestId."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encryptedRequestId.");
return nil; return nil;
} }
NSData *_Nullable encryptedRequestIv = [responseDict base64DataForKey:@"iv" expectedLength:12]; NSData *_Nullable encryptedRequestIv = [responseDict base64DataForKey:@"iv" expectedLength:12];
if (!encryptedRequestIv) { if (!encryptedRequestIv) {
OWSFailDebug(@"couldn't parse encryptedRequestIv."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encryptedRequestIv.");
return nil; return nil;
} }
NSData *_Nullable encryptedRequestTag = [responseDict base64DataForKey:@"tag" expectedLength:16]; NSData *_Nullable encryptedRequestTag = [responseDict base64DataForKey:@"tag" expectedLength:16];
if (!encryptedRequestTag) { if (!encryptedRequestTag) {
OWSFailDebug(@"couldn't parse encryptedRequestTag."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encryptedRequestTag.");
return nil; return nil;
} }
NSData *_Nullable quoteData = [responseDict base64DataForKey:@"quote"]; NSData *_Nullable quoteData = [responseDict base64DataForKey:@"quote"];
if (!quoteData) { if (!quoteData) {
OWSFailDebug(@"couldn't parse quote data."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse quote data.");
return nil; return nil;
} }
NSString *_Nullable signatureBody = [responseDict stringForKey:@"signatureBody"]; NSString *_Nullable signatureBody = [responseDict stringForKey:@"signatureBody"];
if (![signatureBody isKindOfClass:[NSString class]]) { if (![signatureBody isKindOfClass:[NSString class]]) {
OWSFailDebug(@"couldn't parse signatureBody."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse signatureBody.");
return nil; return nil;
} }
NSData *_Nullable signature = [responseDict base64DataForKey:@"signature"]; NSData *_Nullable signature = [responseDict base64DataForKey:@"signature"];
if (!signature) { if (!signature) {
OWSFailDebug(@"couldn't parse signature."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse signature.");
return nil; return nil;
} }
NSString *_Nullable encodedCertificates = [responseDict stringForKey:@"certificates"]; NSString *_Nullable encodedCertificates = [responseDict stringForKey:@"certificates"];
if (![encodedCertificates isKindOfClass:[NSString class]]) { if (![encodedCertificates isKindOfClass:[NSString class]]) {
OWSFailDebug(@"couldn't parse encodedCertificates."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encodedCertificates.");
return nil; return nil;
} }
NSString *_Nullable certificates = [encodedCertificates stringByRemovingPercentEncoding]; NSString *_Nullable certificates = [encodedCertificates stringByRemovingPercentEncoding];
if (!certificates) { if (!certificates) {
OWSFailDebug(@"couldn't parse certificates."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse certificates.");
return nil; return nil;
} }
RemoteAttestationKeys *_Nullable keys = [RemoteAttestationKeys keysForKeyPair:keyPair RemoteAttestationKeys *_Nullable keys = [RemoteAttestationKeys keysForKeyPair:keyPair
serverEphemeralPublic:serverEphemeralPublic serverEphemeralPublic:serverEphemeralPublic
serverStaticPublic:serverStaticPublic]; serverStaticPublic:serverStaticPublic
if (!keys) { error:error];
OWSFailDebug(@"couldn't derive keys."); if (!keys || *error != nil) {
if (*error == nil) {
OWSFailDebug(@"missing error specifics");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"Couldn't derive keys. No reason given");
}
return nil; return nil;
} }
@ -470,20 +509,28 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
encryptedRequestTag:encryptedRequestTag encryptedRequestTag:encryptedRequestTag
keys:keys]; keys:keys];
if (!requestId) { if (!requestId) {
OWSFailDebug(@"couldn't decrypt request id."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't decrypt request id.");
return nil; return nil;
} }
if (![self verifyServerQuote:quote keys:keys enclaveId:enclaveId]) { if (![self verifyServerQuote:quote keys:keys enclaveId:enclaveId]) {
OWSFailDebug(@"couldn't verify quote."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAttestationFailed, @"couldn't verify quote.");
return nil; return nil;
} }
if (![self verifyIasSignatureWithCertificates:certificates if (![self verifyIasSignatureWithCertificates:certificates
signatureBody:signatureBody signatureBody:signatureBody
signature:signature signature:signature
quoteData:quoteData]) { quoteData:quoteData
OWSFailDebug(@"couldn't verify ias signature."); error:error]) {
if (*error == nil) {
OWSFailDebug(@"missing error specifics");
*error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAssertionError,
@"verifyIasSignatureWithCertificates failed. No reason given");
}
return nil; return nil;
} }
@ -503,61 +550,71 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
signatureBody:(NSString *)signatureBody signatureBody:(NSString *)signatureBody
signature:(NSData *)signature signature:(NSData *)signature
quoteData:(NSData *)quoteData quoteData:(NSData *)quoteData
error:(NSError **)error
{ {
OWSAssertDebug(certificates.length > 0); OWSAssertDebug(certificates.length > 0);
OWSAssertDebug(signatureBody.length > 0); OWSAssertDebug(signatureBody.length > 0);
OWSAssertDebug(signature.length > 0); OWSAssertDebug(signature.length > 0);
OWSAssertDebug(quoteData); OWSAssertDebug(quoteData);
NSError *error; NSError *signingError;
CDSSigningCertificate *_Nullable certificate = CDSSigningCertificate *_Nullable certificate =
[CDSSigningCertificate parseCertificateFromPem:certificates error:&error]; [CDSSigningCertificate parseCertificateFromPem:certificates error:&signingError];
if (error) { if (signingError) {
OWSFailDebug(@"error when parsing signing certificate. %@", error.localizedDescription); *error = signingError;
return NO; return NO;
} }
if (!certificate) { if (!certificate) {
OWSFailDebug(@"could not parse signing certificate."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"could not parse signing certificate.");
return NO; return NO;
} }
if (![certificate verifySignatureOfBody:signatureBody signature:signature]) { if (![certificate verifySignatureOfBody:signatureBody signature:signature]) {
OWSFailDebug(@"could not verify signature."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAttestationFailed, @"could not verify signature.");
return NO; return NO;
} }
SignatureBodyEntity *_Nullable signatureBodyEntity = [self parseSignatureBodyEntity:signatureBody]; SignatureBodyEntity *_Nullable signatureBodyEntity = [self parseSignatureBodyEntity:signatureBody];
if (!signatureBodyEntity) { if (!signatureBodyEntity) {
OWSFailDebug(@"could not parse signature body."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"could not parse signature body.");
return NO; return NO;
} }
// Compare the first N bytes of the quote data with the signed quote body. // Compare the first N bytes of the quote data with the signed quote body.
const NSUInteger kQuoteBodyComparisonLength = 432; const NSUInteger kQuoteBodyComparisonLength = 432;
if (signatureBodyEntity.isvEnclaveQuoteBody.length < kQuoteBodyComparisonLength) { if (signatureBodyEntity.isvEnclaveQuoteBody.length < kQuoteBodyComparisonLength) {
OWSFailDebug(@"isvEnclaveQuoteBody has unexpected length."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"isvEnclaveQuoteBody has unexpected length.");
return NO; return NO;
} }
// NOTE: This version is separate from and does _NOT_ match the CDS quote version. // NOTE: This version is separate from and does _NOT_ match the CDS quote version.
const NSUInteger kSignatureBodyVersion = 3; const NSUInteger kSignatureBodyVersion = 3;
if (![signatureBodyEntity.version isEqual:@(kSignatureBodyVersion)]) { if (![signatureBodyEntity.version isEqual:@(kSignatureBodyVersion)]) {
OWSFailDebug(@"signatureBodyEntity has unexpected version."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"signatureBodyEntity has unexpected version.");
return NO; return NO;
} }
if (quoteData.length < kQuoteBodyComparisonLength) { if (quoteData.length < kQuoteBodyComparisonLength) {
OWSFailDebug(@"quoteData has unexpected length."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"quoteData has unexpected length.");
return NO; return NO;
} }
NSData *isvEnclaveQuoteBodyForComparison = NSData *isvEnclaveQuoteBodyForComparison =
[signatureBodyEntity.isvEnclaveQuoteBody subdataWithRange:NSMakeRange(0, kQuoteBodyComparisonLength)]; [signatureBodyEntity.isvEnclaveQuoteBody subdataWithRange:NSMakeRange(0, kQuoteBodyComparisonLength)];
NSData *quoteDataForComparison = [quoteData subdataWithRange:NSMakeRange(0, kQuoteBodyComparisonLength)]; NSData *quoteDataForComparison = [quoteData subdataWithRange:NSMakeRange(0, kQuoteBodyComparisonLength)];
if (![isvEnclaveQuoteBodyForComparison ows_constantTimeIsEqualToData:quoteDataForComparison]) { if (![isvEnclaveQuoteBodyForComparison ows_constantTimeIsEqualToData:quoteDataForComparison]) {
OWSFailDebug(@"isvEnclaveQuoteBody and quoteData do not match."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAttestationFailed, @"isvEnclaveQuoteBody and quoteData do not match.");
return NO; return NO;
} }
if (![@"OK" isEqualToString:signatureBodyEntity.isvEnclaveQuoteStatus]) { if (![@"OK" isEqualToString:signatureBodyEntity.isvEnclaveQuoteStatus]) {
OWSFailDebug(@"invalid isvEnclaveQuoteStatus: %@.", signatureBodyEntity.isvEnclaveQuoteStatus); NSString *reason =
[NSString stringWithFormat:@"invalid isvEnclaveQuoteStatus: %@", signatureBodyEntity.isvEnclaveQuoteStatus];
*error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAttestationFailed, reason);
return NO; return NO;
} }
@ -567,7 +624,8 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
[dateFormatter setDateFormat:@"yyy-MM-dd'T'HH:mm:ss.SSSSSS"]; [dateFormatter setDateFormat:@"yyy-MM-dd'T'HH:mm:ss.SSSSSS"];
NSDate *timestampDate = [dateFormatter dateFromString:signatureBodyEntity.timestamp]; NSDate *timestampDate = [dateFormatter dateFromString:signatureBodyEntity.timestamp];
if (!timestampDate) { if (!timestampDate) {
OWSFailDebug(@"could not parse signature body timestamp."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"could not parse signature body timestamp.");
return NO; return NO;
} }
@ -581,7 +639,8 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
BOOL isExpired = [now isAfterDate:timestampDatePlus1Day]; BOOL isExpired = [now isAfterDate:timestampDatePlus1Day];
if (isExpired) { if (isExpired) {
OWSFailDebug(@"Signature is expired."); *error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAttestationFailed, @"Signature is expired.");
return NO; return NO;
} }

@ -1,5 +1,5 @@
// //
// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // Copyright (c) 2019 Open Whisper Systems. All rights reserved.
// //
import Foundation import Foundation
@ -400,11 +400,11 @@ class CDSBatchOperation: OWSOperation {
class CDSFeedbackOperation: OWSOperation { class CDSFeedbackOperation: OWSOperation {
enum FeedbackResult: String { enum FeedbackResult {
case ok case ok
case mismatch case mismatch
case attestationError = "attestation-error" case attestationError(reason: String)
case unexpectedError = "unexpected-error" case unexpectedError(reason: String)
} }
private let legacyRegisteredRecipientIds: Set<String> private let legacyRegisteredRecipientIds: Set<String>
@ -455,10 +455,22 @@ class CDSFeedbackOperation: OWSOperation {
case ContactDiscoveryError.serverError, ContactDiscoveryError.clientError: case ContactDiscoveryError.serverError, ContactDiscoveryError.clientError:
// Server already has this information, no need submit feedback // Server already has this information, no need submit feedback
self.reportSuccess() self.reportSuccess()
case ContactDiscoveryServiceError.attestationFailed: case let cdsError as ContactDiscoveryServiceError:
self.makeRequest(result: .attestationError) let reason = cdsError.reason
switch cdsError.code {
case .assertionError:
self.makeRequest(result: .unexpectedError(reason: "CDS assertionError: \(reason ?? "unknown")"))
case .attestationFailed:
self.makeRequest(result: .attestationError(reason: "CDS attestationFailed: \(reason ?? "unknown")"))
}
case ContactDiscoveryError.assertionError(let assertionDescription):
self.makeRequest(result: .unexpectedError(reason: "assertionError: \(assertionDescription)"))
case ContactDiscoveryError.parseError(description: let parseErrorDescription):
self.makeRequest(result: .unexpectedError(reason: "parseError: \(parseErrorDescription)"))
default: default:
self.makeRequest(result: .unexpectedError) let nsError = error as NSError
let reason = "unexpectedError code:\(nsError.code)"
self.makeRequest(result: .unexpectedError(reason: reason))
} }
return return
@ -474,7 +486,18 @@ class CDSFeedbackOperation: OWSOperation {
} }
func makeRequest(result: FeedbackResult) { func makeRequest(result: FeedbackResult) {
let request = OWSRequestFactory.cdsFeedbackRequest(result: result.rawValue) let reason: String?
switch result {
case .ok:
reason = nil
case .mismatch:
reason = nil
case .attestationError(let attestationErrorReason):
reason = attestationErrorReason
case .unexpectedError(let unexpectedErrorReason):
reason = unexpectedErrorReason
}
let request = OWSRequestFactory.cdsFeedbackRequest(status: result.statusPath, reason: reason)
self.networkManager.makeRequest(request, self.networkManager.makeRequest(request,
success: { _, _ in self.reportSuccess() }, success: { _, _ in self.reportSuccess() },
failure: { _, error in self.reportError(error) }) failure: { _, error in self.reportError(error) })
@ -488,3 +511,24 @@ extension Array {
} }
} }
} }
extension CDSFeedbackOperation.FeedbackResult {
var statusPath: String {
switch self {
case .ok:
return "ok"
case .mismatch:
return "mismatch"
case .attestationError:
return "attestation-error"
case .unexpectedError:
return "unexpected-error"
}
}
}
extension ContactDiscoveryServiceError {
var reason: String? {
return userInfo[ContactDiscoveryServiceErrorKey_Reason] as? String
}
}

@ -1,5 +1,5 @@
// //
// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // Copyright (c) 2019 Open Whisper Systems. All rights reserved.
// //
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -102,7 +102,8 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
cookies:(NSArray<NSHTTPCookie *> *)cookies; cookies:(NSArray<NSHTTPCookie *> *)cookies;
+ (TSRequest *)remoteAttestationAuthRequest; + (TSRequest *)remoteAttestationAuthRequest;
+ (TSRequest *)cdsFeedbackRequestWithResult:(NSString *)result NS_SWIFT_NAME(cdsFeedbackRequest(result:)); + (TSRequest *)cdsFeedbackRequestWithStatus:(NSString *)status
reason:(nullable NSString *)reason NS_SWIFT_NAME(cdsFeedbackRequest(status:reason:));
#pragma mark - UD #pragma mark - UD

@ -1,5 +1,5 @@
// //
// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // Copyright (c) 2019 Open Whisper Systems. All rights reserved.
// //
#import "OWSRequestFactory.h" #import "OWSRequestFactory.h"
@ -490,10 +490,26 @@ NS_ASSUME_NONNULL_BEGIN
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
} }
+ (TSRequest *)cdsFeedbackRequestWithResult:(NSString *)result + (TSRequest *)cdsFeedbackRequestWithStatus:(NSString *)status
reason:(nullable NSString *)reason
{ {
NSString *path = [NSString stringWithFormat:@"/v1/directory/feedback/%@", result];
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:@{}]; NSDictionary<NSString *, NSString *> *parameters;
if (reason == nil) {
parameters = @{};
} else {
const NSUInteger kServerReasonLimit = 1000;
NSString *limitedReason;
if (reason.length < kServerReasonLimit) {
limitedReason = reason;
} else {
OWSFailDebug(@"failure: reason should be under 1000");
limitedReason = [reason substringToIndex:kServerReasonLimit - 1];
}
parameters = @{ @"reason": limitedReason };
}
NSString *path = [NSString stringWithFormat:@"/v1/directory/feedback-v2/%@", status];
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:parameters];
} }
#pragma mark - UD #pragma mark - UD

Loading…
Cancel
Save