Merge branch 'charlesmchen/2faVsRegistration'

pull/1/head
Matthew Chen 7 years ago
commit 12bd50c3e4

@ -1 +1 @@
Subproject commit 267e347b1eb97f8fd2e03a881b1a1c0c1de20e0f
Subproject commit 9a4161d91bf218604eefef73a6ba237cc2f3188d

@ -131,6 +131,7 @@
347E0B7B2003CD7500BC2F76 /* OWSBackupImportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 347E0B782003CD7400BC2F76 /* OWSBackupImportViewController.m */; };
3497DBEC1ECE257500DB2605 /* OWSCountryMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = 3497DBEB1ECE257500DB2605 /* OWSCountryMetadata.m */; };
3497DBEF1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3497DBEE1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m */; };
34A55F3720485465002CC6DE /* OWS2FARegistrationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A55F3520485464002CC6DE /* OWS2FARegistrationViewController.m */; };
34A910601FFEB114000C4745 /* OWSBackup.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A9105F1FFEB114000C4745 /* OWSBackup.m */; };
34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B0796B1FCF46B000E248C2 /* MainAppContext.m */; };
34B3F8711E8DF1700035BE1A /* AboutTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8351E8DF1700035BE1A /* AboutTableViewController.m */; };
@ -656,6 +657,8 @@
3497DBEB1ECE257500DB2605 /* OWSCountryMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSCountryMetadata.m; sourceTree = "<group>"; };
3497DBED1ECE2E4700DB2605 /* DomainFrontingCountryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DomainFrontingCountryViewController.h; sourceTree = "<group>"; };
3497DBEE1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DomainFrontingCountryViewController.m; sourceTree = "<group>"; };
34A55F3520485464002CC6DE /* OWS2FARegistrationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWS2FARegistrationViewController.m; sourceTree = "<group>"; };
34A55F3620485464002CC6DE /* OWS2FARegistrationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWS2FARegistrationViewController.h; sourceTree = "<group>"; };
34A9105E1FFEB113000C4745 /* OWSBackup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackup.h; sourceTree = "<group>"; };
34A9105F1FFEB114000C4745 /* OWSBackup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackup.m; sourceTree = "<group>"; };
34B0796B1FCF46B000E248C2 /* MainAppContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainAppContext.m; sourceTree = "<group>"; };
@ -1472,6 +1475,8 @@
34B3F8571E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m */,
34B3F8581E8DF1700035BE1A /* NotificationSettingsViewController.h */,
34B3F8591E8DF1700035BE1A /* NotificationSettingsViewController.m */,
34A55F3620485464002CC6DE /* OWS2FARegistrationViewController.h */,
34A55F3520485464002CC6DE /* OWS2FARegistrationViewController.m */,
345BC30A2047030600257B7C /* OWS2FASettingsViewController.h */,
345BC30B2047030600257B7C /* OWS2FASettingsViewController.m */,
34CCAF391F0C2748004084F4 /* OWSAddToContactViewController.h */,
@ -3041,6 +3046,7 @@
34B3F88F1E8DF1710035BE1A /* RegistrationViewController.m in Sources */,
451166C01FD86B98000739BA /* AccountManager.swift in Sources */,
3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */,
34A55F3720485465002CC6DE /* OWS2FARegistrationViewController.m in Sources */,
34B3F8901E8DF1710035BE1A /* AppSettingsViewController.m in Sources */,
34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */,
34B3F8931E8DF1710035BE1A /* SignalsNavigationController.m in Sources */,

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
@ -34,11 +34,13 @@ class AccountManager: NSObject {
// MARK: registration
@objc func register(verificationCode: String) -> AnyPromise {
return AnyPromise(register(verificationCode: verificationCode))
@objc func register(verificationCode: String,
pin: String?) -> AnyPromise {
return AnyPromise(register(verificationCode: verificationCode, pin: pin))
}
func register(verificationCode: String) -> Promise<Void> {
func register(verificationCode: String,
pin: String?) -> Promise<Void> {
guard verificationCode.count > 0 else {
let error = OWSErrorWithCodeDescription(.userError,
NSLocalizedString("REGISTRATION_ERROR_BLANK_VERIFICATION_CODE",
@ -48,7 +50,7 @@ class AccountManager: NSObject {
Logger.debug("\(self.TAG) registering with signal server")
let registrationPromise: Promise<Void> = firstly {
self.registerForTextSecure(verificationCode: verificationCode)
self.registerForTextSecure(verificationCode: verificationCode, pin: pin)
}.then {
self.syncPushTokens()
}.recover { (error) -> Promise<Void> in
@ -71,11 +73,13 @@ class AccountManager: NSObject {
return registrationPromise
}
private func registerForTextSecure(verificationCode: String) -> Promise<Void> {
private func registerForTextSecure(verificationCode: String,
pin: String?) -> Promise<Void> {
return Promise { fulfill, reject in
self.textSecureAccountManager.verifyAccount(withCode:verificationCode,
success:fulfill,
failure:reject)
self.textSecureAccountManager.verifyAccount(withCode: verificationCode,
pin: pin,
success: fulfill,
failure: reject)
}
}
@ -95,16 +99,16 @@ class AccountManager: NSObject {
func updatePushTokens(pushToken: String, voipToken: String) -> Promise<Void> {
return Promise { fulfill, reject in
self.textSecureAccountManager.registerForPushNotifications(pushToken:pushToken,
voipToken:voipToken,
success:fulfill,
failure:reject)
self.textSecureAccountManager.registerForPushNotifications(pushToken: pushToken,
voipToken: voipToken,
success: fulfill,
failure: reject)
}
}
func registerForManualMessageFetching() -> Promise<Void> {
return Promise { fulfill, reject in
self.textSecureAccountManager.registerForManualMessageFetching(success:fulfill, failure:reject)
self.textSecureAccountManager.registerForManualMessageFetching(success: fulfill, failure: reject)
}
}
@ -119,7 +123,7 @@ class AccountManager: NSObject {
}
if let responseDictionary = responseObject as? [String: AnyObject] {
if let turnServerInfo = TurnServerInfo(attributes:responseDictionary) {
if let turnServerInfo = TurnServerInfo(attributes: responseDictionary) {
return fulfill(turnServerInfo)
}
Logger.error("\(self.TAG) unexpected server response:\(responseDictionary)")

@ -1,12 +1,13 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "CodeVerificationViewController.h"
#import "OWS2FARegistrationViewController.h"
#import "ProfileViewController.h"
#import "Signal-Swift.h"
#import "UIViewController+OWS.h"
#import <PromiseKit/AnyPromise.h>
#import <SignalMessaging/UIViewController+OWS.h>
#import <SignalServiceKit/OWSError.h>
#import <SignalServiceKit/TSAccountManager.h>
#import <SignalServiceKit/TSNetworkManager.h>
@ -65,7 +66,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)viewDidLoad {
[super viewDidLoad];
[self createViews];
[self initializeKeyboardHandlers];
@ -259,28 +260,42 @@ NS_ASSUME_NONNULL_BEGIN
[self startActivityIndicator];
OWSProdInfo([OWSAnalyticsEvents registrationRegisteringCode]);
__weak CodeVerificationViewController *weakSelf = self;
[self.accountManager registerWithVerificationCode:[self validationCodeFromTextField]]
[self.accountManager registerWithVerificationCode:[self validationCodeFromTextField] pin:nil]
.then(^{
OWSProdInfo([OWSAnalyticsEvents registrationRegisteringSubmittedCode]);
DDLogInfo(@"%@ Successfully registered Signal account.", weakSelf.logTag);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf stopActivityIndicator];
[weakSelf vericationWasCompleted];
[weakSelf verificationWasCompleted];
});
})
.catch(^(NSError *_Nonnull error) {
.catch(^(NSError *error) {
DDLogError(@"%@ error: %@, %@, %zd", weakSelf.logTag, [error class], error.domain, error.code);
OWSProdInfo([OWSAnalyticsEvents registrationRegistrationFailed]);
DDLogError(@"%@ error verifying challenge: %@", weakSelf.logTag, error);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf stopActivityIndicator];
[weakSelf presentAlertWithVerificationError:error];
[weakSelf.challengeTextField becomeFirstResponder];
if ([error.domain isEqualToString:OWSSignalServiceKitErrorDomain]
&& error.code == OWSErrorCodeRegistrationMissing2FAPIN) {
CodeVerificationViewController *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
DDLogInfo(@"%@ Showing 2FA registration view.", strongSelf.logTag);
OWS2FARegistrationViewController *viewController = [OWS2FARegistrationViewController new];
viewController.verificationCode = strongSelf.validationCodeFromTextField;
[strongSelf.navigationController pushViewController:viewController animated:YES];
} else {
[weakSelf presentAlertWithVerificationError:error];
[weakSelf.challengeTextField becomeFirstResponder];
}
});
});
}
- (void)vericationWasCompleted
- (void)verificationWasCompleted
{
[ProfileViewController presentForRegistration:self.navigationController];
}

@ -42,7 +42,6 @@
#import "TSInfoMessage.h"
#import "TSInvalidIdentityKeyErrorMessage.h"
#import "UIFont+OWS.h"
#import "UIViewController+OWS.h"
#import "UIViewController+Permissions.h"
#import "ViewControllerUtils.h"
#import <AVFoundation/AVFoundation.h>
@ -68,6 +67,7 @@
#import <SignalMessaging/TSUnreadIndicatorInteraction.h>
#import <SignalMessaging/ThreadUtil.h>
#import <SignalMessaging/UIUtil.h>
#import <SignalMessaging/UIViewController+OWS.h>
#import <SignalServiceKit/ContactsUpdater.h>
#import <SignalServiceKit/MimeTypeUtil.h>
#import <SignalServiceKit/NSDate+OWS.h>

@ -0,0 +1,15 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <SignalMessaging/OWSViewController.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWS2FARegistrationViewController : OWSViewController
@property (nonatomic) NSString *verificationCode;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,290 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWS2FARegistrationViewController.h"
#import "ProfileViewController.h"
#import "Signal-Swift.h"
#import <PromiseKit/AnyPromise.h>
#import <SignalMessaging/UIColor+OWS.h>
#import <SignalMessaging/UIFont+OWS.h>
#import <SignalMessaging/UIView+OWS.h>
#import <SignalMessaging/UIViewController+OWS.h>
#import <SignalServiceKit/OWS2FAManager.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWS2FARegistrationViewController () <UITextFieldDelegate>
@property (nonatomic, readonly) AccountManager *accountManager;
@property (nonatomic) UITextField *pinTextfield;
@property (nonatomic) OWSFlatButton *submitButton;
@end
#pragma mark -
@implementation OWS2FARegistrationViewController
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (!self) {
return self;
}
_accountManager = SignalApp.sharedApp.accountManager;
return self;
}
- (instancetype)init
{
self = [super init];
if (!self) {
return self;
}
_accountManager = SignalApp.sharedApp.accountManager;
return self;
}
#pragma mark - View Lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
[self createContents];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateEnabling];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// If we're using a PIN textfield, select it.
[self.pinTextfield becomeFirstResponder];
}
- (UILabel *)createLabelWithText:(NSString *)text
{
UILabel *label = [UILabel new];
label.textColor = [UIColor blackColor];
label.text = text;
label.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(14.f, 16.f)];
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
label.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:label];
return label;
}
- (void)createPinTextfield
{
self.pinTextfield = [UITextField new];
self.pinTextfield.textColor = [UIColor blackColor];
self.pinTextfield.placeholder
= NSLocalizedString(@"2FA_PIN_DEFAULT_TEXT", @"Text field placeholder when entering a 'two-factor auth pin'.");
self.pinTextfield.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(30.f, 36.f)];
self.pinTextfield.textAlignment = NSTextAlignmentCenter;
self.pinTextfield.keyboardType = UIKeyboardTypeNumberPad;
self.pinTextfield.delegate = self;
self.pinTextfield.secureTextEntry = YES;
self.pinTextfield.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:self.pinTextfield];
}
- (UILabel *)createForgotLink
{
UILabel *label = [UILabel new];
label.textColor = [UIColor ows_materialBlueColor];
NSString *text = NSLocalizedString(
@"REGISTER_2FA_FORGOT_PIN", @"Label for 'I forgot my PIN' link in the 2FA registration view.");
label.attributedText = [[NSAttributedString alloc]
initWithString:text
attributes:@{
NSForegroundColorAttributeName : [UIColor ows_materialBlueColor],
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid)
}];
label.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(14.f, 16.f)];
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
label.textAlignment = NSTextAlignmentCenter;
label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(forgotPinLinkTapped:)]];
[self.view addSubview:label];
return label;
}
- (void)createSubmitButton
{
const CGFloat kSubmitButtonHeight = 47.f;
// NOTE: We use ows_signalBrandBlueColor instead of ows_materialBlueColor
// throughout the onboarding flow to be consistent with the headers.
OWSFlatButton *submitButton =
[OWSFlatButton buttonWithTitle:NSLocalizedString(@"REGISTER_2FA_SUBMIT_BUTTON",
@"Label for 'submit' button in the 2FA registration view.")
font:[OWSFlatButton fontForHeight:kSubmitButtonHeight]
titleColor:[UIColor whiteColor]
backgroundColor:[UIColor ows_signalBrandBlueColor]
target:self
selector:@selector(submitButtonWasPressed)];
self.submitButton = submitButton;
[self.view addSubview:self.submitButton];
[self.submitButton autoSetDimension:ALDimensionHeight toSize:kSubmitButtonHeight];
}
- (CGFloat)hMargin
{
return 20.f;
}
- (void)createContents
{
const CGFloat kVSpacing = 30.f;
NSString *instructionsText = NSLocalizedString(
@"REGISTER_2FA_INSTRUCTIONS", @"Instructions to enter the 'two-factor auth pin' in the 2FA registration view.");
UILabel *instructionsLabel = [self createLabelWithText:instructionsText];
[instructionsLabel autoPinTopToSuperviewWithMargin:kVSpacing];
[instructionsLabel autoPinWidthToSuperviewWithMargin:self.hMargin];
UILabel *createForgotLink = [self createForgotLink];
[createForgotLink autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:instructionsLabel withOffset:5];
[createForgotLink autoPinWidthToSuperviewWithMargin:self.hMargin];
[self createPinTextfield];
[self.pinTextfield autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:createForgotLink withOffset:kVSpacing];
[self.pinTextfield autoPinWidthToSuperviewWithMargin:self.hMargin];
UIView *underscoreView = [UIView new];
underscoreView.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1.f];
[self.view addSubview:underscoreView];
[underscoreView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.pinTextfield withOffset:3];
[underscoreView autoPinWidthToSuperviewWithMargin:self.hMargin];
[underscoreView autoSetDimension:ALDimensionHeight toSize:1.f];
[self createSubmitButton];
[self.submitButton autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:underscoreView withOffset:kVSpacing];
[self.submitButton autoPinWidthToSuperviewWithMargin:self.hMargin];
[self updateEnabling];
}
- (void)updateEnabling
{
[self.submitButton setEnabled:self.hasValidPin];
}
#pragma mark - UITextFieldDelegate
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)insertionText
{
[ViewControllerUtils ows2FAPINTextField:textField
shouldChangeCharactersInRange:range
replacementString:insertionText];
[self updateEnabling];
return NO;
}
#pragma mark - Events
- (void)submitButtonWasPressed
{
OWSAssert(self.hasValidPin);
[self tryToRegister];
}
- (BOOL)hasValidPin
{
return self.pinTextfield.text.length >= kMin2FAPinLength;
}
- (void)tryToRegister
{
OWSAssert(self.hasValidPin);
OWSAssert(self.verificationCode.length > 0);
NSString *pin = self.pinTextfield.text;
OWSAssert(pin.length > 0);
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
__weak OWS2FARegistrationViewController *weakSelf = self;
[ModalActivityIndicatorViewController
presentFromViewController:self
canCancel:NO
backgroundBlock:^(ModalActivityIndicatorViewController *modalActivityIndicator) {
OWSProdInfo([OWSAnalyticsEvents registrationRegisteringCode]);
[self.accountManager registerWithVerificationCode:self.verificationCode pin:pin]
.then(^{
OWSAssertIsOnMainThread();
OWSProdInfo([OWSAnalyticsEvents registrationRegisteringSubmittedCode]);
DDLogInfo(@"%@ Successfully registered Signal account.", weakSelf.logTag);
dispatch_async(dispatch_get_main_queue(), ^{
[modalActivityIndicator dismissWithCompletion:^{
OWSAssertIsOnMainThread();
[weakSelf verificationWasCompleted];
}];
});
})
.catch(^(NSError *error) {
OWSAssertIsOnMainThread();
OWSProdInfo([OWSAnalyticsEvents registrationRegistrationFailed]);
DDLogError(@"%@ error verifying challenge: %@", weakSelf.logTag, error);
dispatch_async(dispatch_get_main_queue(), ^{
[modalActivityIndicator dismissWithCompletion:^{
OWSAssertIsOnMainThread();
[OWSAlerts showAlertWithTitle:NSLocalizedString(
@"REGISTER_2FA_REGISTRATION_FAILED_ALERT_TITLE",
@"Title for alert indicating that attempt to "
@"register with 'two-factor auth' failed.")
message:error.localizedDescription];
[weakSelf.pinTextfield becomeFirstResponder];
}];
});
});
}];
}
- (void)verificationWasCompleted
{
[ProfileViewController presentForRegistration:self.navigationController];
}
- (void)forgotPinLinkTapped:(UIGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateRecognized) {
[OWSAlerts
showAlertWithTitle:nil
message:NSLocalizedString(@"REGISTER_2FA_FORGOT_PIN_ALERT_MESSAGE",
@"Alert message explaining what happens if you forget your 'two-factor auth pin'.")];
}
}
@end
NS_ASSUME_NONNULL_END

@ -113,18 +113,8 @@ NS_ASSUME_NONNULL_BEGIN
{
self.pinTextfield = [UITextField new];
self.pinTextfield.textColor = [UIColor blackColor];
switch (self.mode) {
case OWS2FASettingsMode_SelectPIN:
self.pinTextfield.placeholder = NSLocalizedString(@"ENABLE_2FA_VIEW_SELECT_PIN_DEFAULT_TEXT",
@"Text field placeholder for 'two factor auth pin' when selecting a pin.");
break;
case OWS2FASettingsMode_ConfirmPIN:
self.pinTextfield.placeholder = NSLocalizedString(@"ENABLE_2FA_VIEW_CONFIRM_PIN_DEFAULT_TEXT",
@"Text field placeholder for 'two factor auth pin' when confirming a pin.");
break;
case OWS2FASettingsMode_Status:
OWSFail(@"%@ invalid mode.", self.logTag) break;
}
self.pinTextfield.placeholder
= NSLocalizedString(@"2FA_PIN_DEFAULT_TEXT", @"Text field placeholder when entering a 'two-factor auth pin'.");
self.pinTextfield.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(30.f, 36.f)];
self.pinTextfield.textAlignment = NSTextAlignmentCenter;
self.pinTextfield.keyboardType = UIKeyboardTypeNumberPad;
@ -203,14 +193,12 @@ NS_ASSUME_NONNULL_BEGIN
[self.pinTextfield autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:instructionsLabel withOffset:kVSpacing];
[self.pinTextfield autoPinWidthToSuperviewWithMargin:self.hMargin];
[self.pinTextfield autoHCenterInSuperview];
UIView *underscoreView = [UIView new];
underscoreView.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1.f];
[self.view addSubview:underscoreView];
[underscoreView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.pinTextfield withOffset:3];
[underscoreView autoPinWidthToSuperviewWithMargin:self.hMargin];
[underscoreView autoHCenterInSuperview];
[underscoreView autoSetDimension:ALDimensionHeight toSize:1.f];
[self updateNavigationItems];
@ -269,8 +257,7 @@ NS_ASSUME_NONNULL_BEGIN
// view is pushed on top of this one, not how the "back"
// button looks when this view is visible.
self.navigationItem.backBarButtonItem =
[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"VERIFICATION_BACK_BUTTON",
@"button text for back button on verification view")
[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"BACK_BUTTON", @"button text for back button")
style:UIBarButtonItemStylePlain
target:self
action:@selector(backButtonWasPressed)];
@ -293,41 +280,9 @@ NS_ASSUME_NONNULL_BEGIN
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)insertionText
{
// TODO: ?
const NSUInteger kMaxPinLength = 14;
// * We only want to let the user enter decimal digits.
// * The user should be able to copy and paste freely.
// * Invalid input should be simply ignored.
//
// We accomplish this by being permissive and trying to "take as much of the user
// input as possible".
//
// * Always accept deletes.
// * Ignore invalid input.
// * Take partial input if possible.
NSString *oldText = textField.text;
// Construct the new contents of the text field by:
// 1. Determining the "left" substring: the contents of the old text _before_ the deletion range.
// Filtering will remove non-decimal digit characters.
NSString *left = [oldText substringToIndex:range.location].digitsOnly;
// 2. Determining the "right" substring: the contents of the old text _after_ the deletion range.
NSString *right = [oldText substringFromIndex:range.location + range.length].digitsOnly;
// 3. Determining the "center" substring: the contents of the new insertion text.
NSString *center = insertionText.digitsOnly;
// 4. Construct the "raw" new text by concatenating left, center and right.
NSString *textAfterChange = [[left stringByAppendingString:center] stringByAppendingString:right];
// 5. Ensure we don't exceed the maximum length for a PIN.
if (textAfterChange.length > kMaxPinLength) {
textAfterChange = [textAfterChange substringToIndex:kMaxPinLength];
}
// 6. Construct the final text.
textField.text = textAfterChange;
NSUInteger cursorPositionAfterChange = MIN(left.length + center.length, textAfterChange.length);
UITextPosition *pos =
[textField positionFromPosition:textField.beginningOfDocument offset:(NSInteger)cursorPositionAfterChange];
[textField setSelectedTextRange:[textField textRangeFromPosition:pos toPosition:pos]];
[ViewControllerUtils ows2FAPINTextField:textField
shouldChangeCharactersInRange:range
replacementString:insertionText];
[self updateNavigationItems];
@ -379,8 +334,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)hasValidPin
{
const NSUInteger kMinPinLength = 4;
return self.pinTextfield.text.length >= kMinPinLength;
return self.pinTextfield.text.length >= kMin2FAPinLength;
}
- (void)showEnable2FAWorkUI
@ -470,7 +424,7 @@ NS_ASSUME_NONNULL_BEGIN
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
[self.navigationController popToViewController:self.root2FAViewController animated:NO];
[self.navigationController popToViewController:self.root2FAViewController animated:YES];
}
- (void)backButtonWasPressed

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "ProfileViewController.h"
@ -12,9 +12,9 @@
#import "UIColor+OWS.h"
#import "UIFont+OWS.h"
#import "UIView+OWS.h"
#import "UIViewController+OWS.h"
#import <SignalMessaging/NSString+OWS.h>
#import <SignalMessaging/OWSProfileManager.h>
#import <SignalMessaging/UIViewController+OWS.h>
#import <SignalServiceKit/NSDate+OWS.h>
#import <SignalServiceKit/TSStorageManager.h>

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "NumberUtil.h"
@ -83,7 +83,7 @@
}
+ (NSUInteger)assertConvertIntToNSUInteger:(int)value {
assert(0 <= value);
OWSAssert(0 <= value);
return (NSUInteger)value;
}
@ -93,7 +93,7 @@
}
+ (int)assertConvertNSUIntegerToInt:(NSUInteger)value {
assert(value <= INT32_MAX);
OWSAssert(value <= INT32_MAX);
return (int)value;
}

@ -1,3 +1,6 @@
/* Text field placeholder when entering a 'two-factor auth pin'. */
"2FA_PIN_DEFAULT_TEXT" = "••••••";
/* Button text to dismiss missing contacts permission alert */
"AB_PERMISSION_MISSING_ACTION_NOT_NOW" = "Not Now";
@ -151,6 +154,9 @@
/* action sheet button title to enable built in speaker during a call */
"AUDIO_ROUTE_BUILT_IN_SPEAKER" = "Speaker";
/* button text for back button */
"BACK_BUTTON" = "Back";
/* Message indicating that backup export is complete. */
"BACKUP_EXPORT_COMPLETE_MESSAGE" = "Backup complete.";
@ -274,9 +280,6 @@
/* Accessibilty label for placing call button */
"CALL_LABEL" = "Call";
/* Text shown on call screen in place of remote video */
"CALL_REMOTE_VIDEO_DISABLED" = "Please upgrade to iOS 9 or newer to see remote video.";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "No Answer.";
@ -643,17 +646,14 @@
/* No comment provided by engineer. */
"EMPTY_INBOX_TITLE" = "Squeaky Freaking Clean.";
/* Text field placeholder for 'two factor auth pin' when confirming a pin. */
"ENABLE_2FA_VIEW_CONFIRM_PIN_DEFAULT_TEXT" = "••••••";
/* Indicates that user should confirm their 'two factor auth pin'. */
"ENABLE_2FA_VIEW_CONFIRM_PIN_INSTRUCTIONS" = "Confirm your PIN.";
/* Error indicating that attempt to disable 'two-factor auth' failed. */
"ENABLE_2FA_VIEW_COULD_NOT_DISABLE_2FA" = "Could not disable two-step verification.";
"ENABLE_2FA_VIEW_COULD_NOT_DISABLE_2FA" = "Could not disable Registration Lock.";
/* Error indicating that attempt to enable 'two-factor auth' failed. */
"ENABLE_2FA_VIEW_COULD_NOT_ENABLE_2FA" = "Could not enable two-step verification.";
"ENABLE_2FA_VIEW_COULD_NOT_ENABLE_2FA" = "Could not enable Registration Lock.";
/* Label for the 'enable two-factor auth' item in the settings view */
"ENABLE_2FA_VIEW_DISABLE_2FA" = "Disable";
@ -667,20 +667,17 @@
/* Error indicating that the entered 'two-factor auth PINs' do not match. */
"ENABLE_2FA_VIEW_PIN_DOES_NOT_MATCH" = "Pin does not match.";
/* Text field placeholder for 'two factor auth pin' when selecting a pin. */
"ENABLE_2FA_VIEW_SELECT_PIN_DEFAULT_TEXT" = "******";
/* Indicates that user should select a 'two factor auth pin'. */
"ENABLE_2FA_VIEW_SELECT_PIN_INSTRUCTIONS" = "Enter a PIN which you'll be asked for when you register your phone number with Signal. Your PIN must have at least 4 digits.";
"ENABLE_2FA_VIEW_SELECT_PIN_INSTRUCTIONS" = "Enter a Registration Lock PIN. You will be asked to enter this PIN the next time you register this phone number with Signal.";
/* Indicates that user has 'two factor auth pin' disabled. */
"ENABLE_2FA_VIEW_STATUS_DISABLED_INSTRUCTIONS" = "For added security, enable two-step verification, which will require a PIN when registering your phone number with Signal again.";
"ENABLE_2FA_VIEW_STATUS_DISABLED_INSTRUCTIONS" = "For increased security, enable a Registration Lock PIN that will be required in order to register this phone number with Signal again.";
/* Indicates that user has 'two factor auth pin' enabled. */
"ENABLE_2FA_VIEW_STATUS_ENABLED_INSTRUCTIONS" = "Two-step verification is enabled. You'll need to enter your PIN when registering your phone number with Signal again.";
"ENABLE_2FA_VIEW_STATUS_ENABLED_INSTRUCTIONS" = "Registration Lock is enabled. You'll need to enter your PIN when registering your phone number with Signal again.";
/* Title for the 'enable two factor auth PIN' views. */
"ENABLE_2FA_VIEW_TITLE" = "Two-Step Verification";
"ENABLE_2FA_VIEW_TITLE" = "Registration Lock";
/* Call setup status label */
"END_CALL_RESPONDER_IS_BUSY" = "Busy.";
@ -1383,6 +1380,21 @@
/* No comment provided by engineer. */
"RATING_TITLE" = "Support Signal!";
/* Label for 'I forgot my PIN' link in the 2FA registration view. */
"REGISTER_2FA_FORGOT_PIN" = "I forgot my PIN.";
/* Alert message explaining what happens if you forget your 'two-factor auth pin'. */
"REGISTER_2FA_FORGOT_PIN_ALERT_MESSAGE" = "Registration of this phone number will be possible without your Registration Lock PIN after 7 days have passed since the phone number was last active on Signal.";
/* Instructions to enter the 'two-factor auth pin' in the 2FA registration view. */
"REGISTER_2FA_INSTRUCTIONS" = "This phone number has Registration Lock enabled. Please enter the Registration Lock PIN.\n\nYour Registration Lock PIN is separate from the automated verification code that was sent to your phone during the last step.";
/* Title for alert indicating that attempt to register with 'two-factor auth' failed. */
"REGISTER_2FA_REGISTRATION_FAILED_ALERT_TITLE" = "Registration Failed";
/* Label for 'submit' button in the 2FA registration view. */
"REGISTER_2FA_SUBMIT_BUTTON" = "Submit";
/* No comment provided by engineer. */
"REGISTER_CONTACTS_WELCOME" = "Welcome!";

@ -6,6 +6,9 @@
NS_ASSUME_NONNULL_BEGIN
extern const NSUInteger kMin2FAPinLength;
extern const NSUInteger kMax2FAPinLength;
@interface ViewControllerUtils : NSObject
- (instancetype)init NS_UNAVAILABLE;
@ -18,6 +21,10 @@ NS_ASSUME_NONNULL_BEGIN
replacementString:(NSString *)insertionText
countryCode:(NSString *)countryCode;
+ (void)ows2FAPINTextField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)insertionText;
+ (NSString *)examplePhoneNumberForCountryCode:(NSString *)countryCode callingCode:(NSString *)callingCode;
@end

@ -12,7 +12,8 @@
NS_ASSUME_NONNULL_BEGIN
#pragma mark -
const NSUInteger kMin2FAPinLength = 4;
const NSUInteger kMax2FAPinLength = 16;
@implementation ViewControllerUtils
@ -74,6 +75,44 @@ NS_ASSUME_NONNULL_BEGIN
[textField setSelectedTextRange:[textField textRangeFromPosition:pos toPosition:pos]];
}
+ (void)ows2FAPINTextField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)insertionText
{
// * We only want to let the user enter decimal digits.
// * The user should be able to copy and paste freely.
// * Invalid input should be simply ignored.
//
// We accomplish this by being permissive and trying to "take as much of the user
// input as possible".
//
// * Always accept deletes.
// * Ignore invalid input.
// * Take partial input if possible.
NSString *oldText = textField.text;
// Construct the new contents of the text field by:
// 1. Determining the "left" substring: the contents of the old text _before_ the deletion range.
// Filtering will remove non-decimal digit characters.
NSString *left = [oldText substringToIndex:range.location].digitsOnly;
// 2. Determining the "right" substring: the contents of the old text _after_ the deletion range.
NSString *right = [oldText substringFromIndex:range.location + range.length].digitsOnly;
// 3. Determining the "center" substring: the contents of the new insertion text.
NSString *center = insertionText.digitsOnly;
// 4. Construct the "raw" new text by concatenating left, center and right.
NSString *textAfterChange = [[left stringByAppendingString:center] stringByAppendingString:right];
// 5. Ensure we don't exceed the maximum length for a PIN.
if (textAfterChange.length > kMax2FAPinLength) {
textAfterChange = [textAfterChange substringToIndex:kMax2FAPinLength];
}
// 6. Construct the final text.
textField.text = textAfterChange;
NSUInteger cursorPositionAfterChange = MIN(left.length + center.length, textAfterChange.length);
UITextPosition *pos =
[textField positionFromPosition:textField.beginningOfDocument offset:(NSInteger)cursorPositionAfterChange];
[textField setSelectedTextRange:[textField textRangeFromPosition:pos toPosition:pos]];
}
+ (NSString *)examplePhoneNumberForCountryCode:(NSString *)countryCode callingCode:(NSString *)callingCode
{
OWSAssert(countryCode.length > 0);

@ -28,14 +28,12 @@ import Foundation
}
@objc
public class func showAlert(withTitle title: String, message: String) {
public class func showAlert(withTitle title: String?, message: String) {
self.showAlert(withTitle: title, message: message, buttonTitle: nil)
}
@objc
public class func showAlert(withTitle title: String, message: String? = nil, buttonTitle: String? = nil, buttonAction: ((UIAlertAction) -> Void)? = nil) {
assert(title.count > 0)
public class func showAlert(withTitle title: String?, message: String? = nil, buttonTitle: String? = nil, buttonAction: ((UIAlertAction) -> Void)? = nil) {
let actionTitle = buttonTitle ?? NSLocalizedString("OK", comment: "")
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)

@ -80,6 +80,7 @@ extern NSString *const kNSNotificationName_LocalNumberDidChange;
+ (void)rerequestVoiceWithSuccess:(void (^)(void))successBlock failure:(void (^)(NSError *error))failureBlock;
- (void)verifyAccountWithCode:(NSString *)verificationCode
pin:(nullable NSString *)pin
success:(void (^)(void))successBlock
failure:(void (^)(NSError *error))failureBlock;

@ -309,7 +309,7 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling
TSAccountManager *manager = [self sharedInstance];
NSString *number = manager.phoneNumberAwaitingVerification;
assert(number);
OWSAssert(number);
[self registerWithPhoneNumber:number success:successBlock failure:failureBlock smsVerification:YES];
}
@ -319,7 +319,7 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling
TSAccountManager *manager = [self sharedInstance];
NSString *number = manager.phoneNumberAwaitingVerification;
assert(number);
OWSAssert(number);
[self registerWithPhoneNumber:number success:successBlock failure:failureBlock smsVerification:NO];
}
@ -340,6 +340,7 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling
}
- (void)verifyAccountWithCode:(NSString *)verificationCode
pin:(nullable NSString *)pin
success:(void (^)(void))successBlock
failure:(void (^)(NSError *error))failureBlock
{
@ -347,12 +348,13 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling
NSString *signalingKey = [[self class] generateNewSignalingKeyToken];
NSString *phoneNumber = self.phoneNumberAwaitingVerification;
assert(signalingKey);
assert(authToken);
assert(phoneNumber);
OWSAssert(signalingKey);
OWSAssert(authToken);
OWSAssert(phoneNumber);
TSVerifyCodeRequest *request = [[TSVerifyCodeRequest alloc] initWithVerificationCode:verificationCode
forNumber:phoneNumber
pin:pin
signalingKey:signalingKey
authKey:authToken];
@ -392,6 +394,13 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling
failureBlock(userError);
break;
}
case 423: {
DDLogError(@"%@ 2FA PIN required: %ld", self.logTag, error.code);
NSError *error = OWSErrorWithCodeDescription(
OWSErrorCodeRegistrationMissing2FAPIN, @"Registration missing 2FA PIN.");
failureBlock(error);
break;
}
default: {
DDLogError(@"%@ verifying code failed with unhandled error: %@", self.logTag, error);
failureBlock(error);

@ -1,20 +1,21 @@
//
// TSVerifyCodeRequest.h
// TextSecureKit
//
// Created by Frederic Jacobs on 14/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "TSRequest.h"
NS_ASSUME_NONNULL_BEGIN
@interface TSVerifyCodeRequest : TSRequest
- (TSRequest *)initWithVerificationCode:(NSString *)verificationCode
forNumber:(NSString *)phoneNumber
pin:(nullable NSString *)pin
signalingKey:(NSString *)signalingKey
authKey:(NSString *)authKey;
@property (nonatomic, readonly) NSString *numberToValidate;
@end
NS_ASSUME_NONNULL_END

@ -7,20 +7,30 @@
#import "TSAttributes.h"
#import "TSConstants.h"
NS_ASSUME_NONNULL_BEGIN
@implementation TSVerifyCodeRequest
- (TSRequest *)initWithVerificationCode:(NSString *)verificationCode
forNumber:(NSString *)phoneNumber
pin:(nullable NSString *)pin
signalingKey:(NSString *)signalingKey
authKey:(NSString *)authKey {
authKey:(NSString *)authKey
{
self = [super
initWithURL:[NSURL URLWithString:[NSString
stringWithFormat:@"%@/code/%@", textSecureAccountsAPI, verificationCode]]];
_numberToValidate = phoneNumber;
self.parameters =
[TSAttributes attributesWithSignalingKey:signalingKey serverAuthToken:authKey manualMessageFetching:NO];
NSMutableDictionary *parameters =
[[TSAttributes attributesWithSignalingKey:signalingKey serverAuthToken:authKey manualMessageFetching:NO]
mutableCopy];
if (pin) {
OWSAssert(pin.length > 0);
parameters[@"pin"] = pin;
}
self.parameters = parameters;
[self setHTTPMethod:@"PUT"];
@ -28,3 +38,5 @@
}
@end
NS_ASSUME_NONNULL_END

@ -272,8 +272,8 @@ const NSUInteger kAES256_KeyByteLength = 32;
#pragma mark methods which use AES CBC
+ (NSData *)decryptAppleMessagePayload:(NSData *)payload withSignalingKey:(NSString *)signalingKeyString {
assert(payload);
assert(signalingKeyString);
OWSAssert(payload);
OWSAssert(signalingKeyString);
unsigned char version[1];
unsigned char iv[16];

@ -28,7 +28,8 @@ typedef NS_ENUM(NSInteger, OWSErrorCode) {
OWSErrorCodeCouldNotWriteAttachmentData = 777409,
OWSErrorCodeMessageDeletedBeforeSent = 777410,
OWSErrorCodeDatabaseConversionFatalError = 777411,
OWSErrorCodeMoveFileToSharedDataContainerError = 777412
OWSErrorCodeMoveFileToSharedDataContainerError = 777412,
OWSErrorCodeRegistrationMissing2FAPIN = 777413
};
extern NSString *const OWSErrorRecipientIdentifierKey;

Loading…
Cancel
Save