2
0

Fancy master password input screen.

[ADDED]     Fancy master password input screen.
[FIXED]     Key size of stored passwords.
[FIXED]     Several UI fixes.
[FIXED]     The counter wasn't correctly added to the cipherKey.
[IMPROVED]  Site style improvements.
[UPDATED]   Site algorithm explanation update.
This commit is contained in:
Maarten Billemont
2012-02-25 15:30:23 +01:00
parent ad9c52896d
commit 039ec9b082
29 changed files with 572 additions and 169 deletions

View File

@@ -26,6 +26,8 @@
- (void)showGuide;
- (void)loadKeyPhrase;
- (void)forgetKeyPhrase;
- (NSData *)keyPhraseWithLength:(NSUInteger)keyLength;
- (BOOL)tryMasterPassword:(NSString *)tryPassword;
@end

View File

@@ -20,7 +20,6 @@
+ (NSDictionary *)keyPhraseQuery;
+ (NSDictionary *)keyPhraseHashQuery;
- (void)forgetKeyPhrase;
- (void)loadStoredKeyPhrase;
- (void)askKeyPhrase;
@@ -202,6 +201,11 @@
dbg(@"Deleting master key phrase and hash from key chain.");
[KeyChain deleteItemForQuery:[MPAppDelegate keyPhraseQuery]];
[KeyChain deleteItemForQuery:[MPAppDelegate keyPhraseHashQuery]];
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self];
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPForgotten];
#endif
}
[self loadKeyPhrase];
@@ -234,62 +238,7 @@
- (void)askKeyPhrase {
dispatch_async(dispatch_get_main_queue(), ^{
NSData *keyPhraseHash = [KeyChain dataOfItemForQuery:[MPAppDelegate keyPhraseHashQuery]];
dbg(@"Key phrase hash %@.", keyPhraseHash? @"known": @"NOT known");
[AlertViewController showAlertWithTitle:@"Master Password"
message:keyPhraseHash? @"Unlock with your master password:": @"Choose your master password:"
viewStyle:UIAlertViewStyleSecureTextInput
tappedButtonBlock:
^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
exit(0);
NSString *answer = [alert textFieldAtIndex:0].text;
if (![answer length]) {
// User didn't enter a key phrase.
[AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
message:@"No master password entered."
viewStyle:UIAlertViewStyleDefault
tappedButtonBlock:
^(UIAlertView *alert, NSInteger buttonIndex) {
exit(0);
} cancelTitle:@"Quit" otherTitles:nil];
}
NSData *answerKeyPhrase = keyPhraseForPassword(answer);
NSData *answerHash = keyPhraseHashForKeyPhrase(answerKeyPhrase);
if (keyPhraseHash)
// A key phrase hash is known -> a key phrase is set.
// Make sure the user's entered key phrase matches it.
if (![keyPhraseHash isEqual:answerHash]) {
dbg(@"Key phrase hash mismatch. Expected: %@, answer: %@.", keyPhraseHash, answerHash);
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch];
#endif
[AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
message:
@"Incorrect master password.\n\n"
@"If you are trying to use the app with a different master password, "
@"flip the 'Change my password' option in Settings."
viewStyle:UIAlertViewStyleDefault
tappedButtonBlock:
^(UIAlertView *alert, NSInteger buttonIndex) {
exit(0);
} cancelTitle:@"Quit" otherTitles:nil];
return;
}
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPAsked];
#endif
self.keyPhrase = answerKeyPhrase;
} cancelTitle:@"Quit" otherTitles:@"Unlock", nil];
});
[self.navigationController presentViewController:[self.navigationController.storyboard instantiateViewControllerWithIdentifier:@"MPUnlockViewController"] animated:NO completion:nil];
}
- (void)applicationWillResignActive:(UIApplication *)application {
@@ -337,10 +286,45 @@
}];
}
- (BOOL)tryMasterPassword:(NSString *)tryPassword {
NSData *keyPhraseHash = [KeyChain dataOfItemForQuery:[MPAppDelegate keyPhraseHashQuery]];
dbg(@"Key phrase hash %@.", keyPhraseHash? @"known": @"NOT known");
if (![tryPassword length])
return NO;
NSData *tryKeyPhrase = keyPhraseForPassword(tryPassword);
NSData *tryKeyPhraseHash = keyPhraseHashForKeyPhrase(tryKeyPhrase);
if (keyPhraseHash)
// A key phrase hash is known -> a key phrase is set.
// Make sure the user's entered key phrase matches it.
if (![keyPhraseHash isEqual:tryKeyPhraseHash]) {
dbg(@"Key phrase hash mismatch. Expected: %@, answer: %@.", keyPhraseHash, tryKeyPhraseHash);
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch];
#endif
return NO;
}
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPAsked];
#endif
self.keyPhrase = tryKeyPhrase;
return YES;
}
- (void)setKeyPhrase:(NSData *)keyPhrase {
_keyPhrase = keyPhrase;
if (keyPhrase)
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeySet object:self];
else
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyUnset object:self];
if (keyPhrase) {
self.keyPhraseHash = keyPhraseHashForKeyPhrase(keyPhrase);
self.keyPhraseHashHex = [self.keyPhraseHash encodeHex];

View File

@@ -46,7 +46,7 @@
- (void)setContent:(id)content {
NSData *encryptedContent = [[content description] encryptWithSymmetricKey:[MPAppDelegate get].keyPhrase
NSData *encryptedContent = [[content description] encryptWithSymmetricKey:[[MPAppDelegate get] keyPhraseWithLength:kCipherKeySize]
usePadding:YES];
if (self.type == MPElementTypeStoredDevicePrivate) {

View File

@@ -114,6 +114,9 @@
}
}];
self.alertBody.text = nil;
self.contentTipEditIcon.alpha = 0;
[super viewDidLoad];
}
@@ -167,7 +170,7 @@
self.contentField.enabled = NO;
if ([self.activeElement isKindOfClass:[MPElementGeneratedEntity class]])
self.passwordCounter.text = [NSString stringWithFormat:@"%d", ((MPElementGeneratedEntity *) self.activeElement).counter];
self.passwordCounter.text = [NSString stringWithFormat:@"%u", ((MPElementGeneratedEntity *) self.activeElement).counter];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSString *description = self.activeElement.description;
@@ -194,7 +197,7 @@
if (hidden) {
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 373);
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 415);
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 416);
[MPConfig get].helpHidden = [NSNumber numberWithBool:YES];
} else {
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 175);
@@ -296,7 +299,7 @@
[self updateAnimated:YES];
// Show new and old password.
if (oldPassword && ![oldPassword isEqualToString:newPassword])
if ([oldPassword length] && ![oldPassword isEqualToString:newPassword])
[self showAlertWithTitle:@"Password Changed!" message:l(@"The password for %@ has changed.\n\n"
@"Don't forget to update the site with your new password! "
@"Your old password was:\n"

View File

@@ -33,30 +33,35 @@ typedef enum {
} MPElementType;
#ifndef PRODUCTION
#define MPTestFlightCheckpointAction @"MPTestFlightCheckpointAction"
#define MPTestFlightCheckpointHelpChapter @"MPTestFlightCheckpointHelpChapter_%@"
#define MPTestFlightCheckpointCopyToPasteboard @"MPTestFlightCheckpointCopyToPasteboard"
#define MPTestFlightCheckpointIncrementPasswordCounter @"MPTestFlightCheckpointIncrementPasswordCounter"
#define MPTestFlightCheckpointEditPassword @"MPTestFlightCheckpointEditPassword"
#define MPTestFlightCheckpointCloseAlert @"MPTestFlightCheckpointCloseAlert"
#define MPTestFlightCheckpointSelectType @"MPTestFlightCheckpointSelectType_%@"
#define MPTestFlightCheckpointSelectElement @"MPTestFlightCheckpointSelectElement"
#define MPTestFlightCheckpointDeleteElement @"MPTestFlightCheckpointDeleteElement"
#define MPTestFlightCheckpointCancelSearch @"MPTestFlightCheckpointCancelSearch"
#define MPTestFlightCheckpointExternalLink @"MPTestFlightCheckpointExternalLink"
#define MPTestFlightCheckpointLaunched @"MPTestFlightCheckpointLaunched"
#define MPTestFlightCheckpointActivated @"MPTestFlightCheckpointActivated"
#define MPTestFlightCheckpointDeactivated @"MPTestFlightCheckpointDeactivated"
#define MPTestFlightCheckpointTerminated @"MPTestFlightCheckpointTerminated"
#define MPTestFlightCheckpointShowGuide @"MPTestFlightCheckpointShowGuide"
#define MPTestFlightCheckpointMPChanged @"MPTestFlightCheckpointMPChanged"
#define MPTestFlightCheckpointMPUnstored @"MPTestFlightCheckpointMPUnstored"
#define MPTestFlightCheckpointMPMismatch @"MPTestFlightCheckpointMPMismatch"
#define MPTestFlightCheckpointMPAsked @"MPTestFlightCheckpointMPAsked"
#define MPTestFlightCheckpointStoreIncompatible @"MPTestFlightCheckpointStoreIncompatible"
#define MPTestFlightCheckpointSetKeyphraseLength @"MPTestFlightCheckpointSetKeyphraseLength_%d"
#define MPTestFlightCheckpointAction @"MPTestFlightCheckpointAction"
#define MPTestFlightCheckpointHelpChapter @"MPTestFlightCheckpointHelpChapter_%@"
#define MPTestFlightCheckpointCopyToPasteboard @"MPTestFlightCheckpointCopyToPasteboard"
#define MPTestFlightCheckpointIncrementPasswordCounter @"MPTestFlightCheckpointIncrementPasswordCounter"
#define MPTestFlightCheckpointEditPassword @"MPTestFlightCheckpointEditPassword"
#define MPTestFlightCheckpointCloseAlert @"MPTestFlightCheckpointCloseAlert"
#define MPTestFlightCheckpointSelectType @"MPTestFlightCheckpointSelectType_%@"
#define MPTestFlightCheckpointSelectElement @"MPTestFlightCheckpointSelectElement"
#define MPTestFlightCheckpointDeleteElement @"MPTestFlightCheckpointDeleteElement"
#define MPTestFlightCheckpointCancelSearch @"MPTestFlightCheckpointCancelSearch"
#define MPTestFlightCheckpointExternalLink @"MPTestFlightCheckpointExternalLink"
#define MPTestFlightCheckpointLaunched @"MPTestFlightCheckpointLaunched"
#define MPTestFlightCheckpointActivated @"MPTestFlightCheckpointActivated"
#define MPTestFlightCheckpointDeactivated @"MPTestFlightCheckpointDeactivated"
#define MPTestFlightCheckpointTerminated @"MPTestFlightCheckpointTerminated"
#define MPTestFlightCheckpointShowGuide @"MPTestFlightCheckpointShowGuide"
#define MPTestFlightCheckpointMPForgotten @"MPTestFlightCheckpointMPForgotten"
#define MPTestFlightCheckpointMPChanged @"MPTestFlightCheckpointMPChanged"
#define MPTestFlightCheckpointMPUnstored @"MPTestFlightCheckpointMPUnstored"
#define MPTestFlightCheckpointMPMismatch @"MPTestFlightCheckpointMPMismatch"
#define MPTestFlightCheckpointMPAsked @"MPTestFlightCheckpointMPAsked"
#define MPTestFlightCheckpointStoreIncompatible @"MPTestFlightCheckpointStoreIncompatible"
#define MPTestFlightCheckpointSetKeyphraseLength @"MPTestFlightCheckpointSetKeyphraseLength_%d"
#endif
#define MPNotificationKeySet @"MPNotificationKeySet"
#define MPNotificationKeyUnset @"MPNotificationKeyUnset"
#define MPNotificationKeyForgotten @"MPNotificationKeyForgotten"
NSData *keyPhraseForPassword(NSString *password);
NSData *keyPhraseHashForPassword(NSString *password);
NSData *keyPhraseHashForKeyPhrase(NSData *keyPhrase);

View File

@@ -10,15 +10,16 @@
#import "MPElementGeneratedEntity.h"
#import "MPElementStoredEntity.h"
#define MP_salt nil
#define MP_N 16384
#define MP_r 8
#define MP_p 1
#define MP_hash PearlDigestSHA256
#define MP_salt nil
#define MP_N 16384
#define MP_r 8
#define MP_p 1
#define MP_dkLen 64
#define MP_hash PearlDigestSHA256
NSData *keyPhraseForPassword(NSString *password) {
return [SCrypt deriveKeyWithLength:64 fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding]
return [SCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding]
usingSalt:MP_salt N:MP_N r:MP_r p:MP_p];
}
NSData *keyPhraseHashForPassword(NSString *password) {
@@ -109,10 +110,11 @@ NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *keyPhra
// Determine the hash whose bytes will be used for calculating a password: md4(name-keyPhrase)
assert(name && keyPhrase);
uint16_t ncounter = htons(counter);
NSData *keyHash = [[NSData dataByConcatenatingWithDelimitor:'-' datas:
[name dataUsingEncoding:NSUTF8StringEncoding],
keyPhrase,
htonl(counter),
[NSData dataWithBytes:&ncounter length:sizeof(ncounter)],
nil] hashWith:PearlDigestSHA1];
const char *keyBytes = keyHash.bytes;

View File

@@ -0,0 +1,21 @@
//
// MBUnlockViewController.h
// MasterPassword
//
// Created by Maarten Billemont on 22/02/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface MPUnlockViewController : UIViewController <UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UIImageView *lock;
@property (weak, nonatomic) IBOutlet UIImageView *spinner;
@property (weak, nonatomic) IBOutlet UITextField *field;
@property (weak, nonatomic) IBOutlet UILabel *messageLabel;
@property (weak, nonatomic) IBOutlet UIView *changeMPView;
- (IBAction)changeMP;
@end

View File

@@ -0,0 +1,198 @@
//
// MBUnlockViewController.m
// MasterPassword
//
// Created by Maarten Billemont on 22/02/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
#import "MPUnlockViewController.h"
#import "MPAppDelegate.h"
typedef enum {
MPLockscreenIdle,
MPLockscreenError,
MPLockscreenSuccess,
MPLockscreenProgress,
} MPLockscreen;
@interface MPUnlockViewController ()
@end
@implementation MPUnlockViewController
@synthesize lock;
@synthesize spinner;
@synthesize field;
@synthesize messageLabel;
@synthesize changeMPView;
- (void)showMessage:(NSString *)message state:(MPLockscreen)state {
__block void(^showMessageAnimation)(void) = ^{
self.lock.alpha = 0.0f;
switch (state) {
case MPLockscreenIdle:
[self.lock setImage:[UIImage imageNamed:@"lockscreen_idle"]];
break;
case MPLockscreenError:
[self.lock setImage:[UIImage imageNamed:@"lockscreen_red"]];
break;
case MPLockscreenSuccess:
[self.lock setImage:[UIImage imageNamed:@"lockscreen_green"]];
break;
case MPLockscreenProgress:
[self.lock setImage:[UIImage imageNamed:@"lockscreen_blue"]];
break;
}
self.lock.alpha = 0.0f;
[UIView animateWithDuration:1.0f animations:^{
self.lock.alpha = 1.0f;
} completion:^(BOOL finished) {
if (finished)
[UIView animateWithDuration:1.0f delay:0 options:UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse animations:^{
self.lock.alpha = 0.5f;
} completion:nil];
}];
[UIView animateWithDuration:0.5f animations:^{
self.messageLabel.alpha = 1.0f;
self.messageLabel.text = message;
}];
};
if (self.messageLabel.alpha)
[UIView animateWithDuration:0.3f animations:^{
self.messageLabel.alpha = 0.0f;
} completion:^(BOOL finished) {
if (finished)
showMessageAnimation();
}];
else
showMessageAnimation();
}
- (void)hideMessage {
[UIView animateWithDuration:0.5f animations:^{
self.messageLabel.alpha = 0.0f;
}];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)viewDidLoad {
self.messageLabel.text = nil;
self.messageLabel.alpha = 0;
self.changeMPView.alpha = 0;
self.spinner.alpha = 0;
self.field.text = nil;
[[NSNotificationCenter defaultCenter] addObserverForName:MPNotificationKeyForgotten
object:nil queue:nil usingBlock:^(NSNotification *note) {
[self.field becomeFirstResponder];
}];
[super viewDidLoad];
}
- (void)viewDidUnload {
[self setSpinner:nil];
[self setField:nil];
[self setMessageLabel:nil];
[self setLock:nil];
[self setChangeMPView:nil];
[super viewDidUnload];
}
- (void)viewWillAppear:(BOOL)animated {
[[UIApplication sharedApplication] setStatusBarHidden:YES
withAnimation:animated? UIStatusBarAnimationSlide: UIStatusBarAnimationNone];
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[[UIApplication sharedApplication] setStatusBarHidden:NO
withAnimation:animated? UIStatusBarAnimationSlide: UIStatusBarAnimationNone];
[super viewWillDisappear:animated];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if ([textField.text length]) {
[textField resignFirstResponder];
return YES;
}
return NO;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@try {
dispatch_async(dispatch_get_main_queue(), ^{
CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
rotate.fromValue = [NSNumber numberWithFloat:0];
rotate.toValue = [NSNumber numberWithFloat:2 * M_PI];
rotate.repeatCount = MAXFLOAT;
rotate.duration = 3.0;
[self.spinner.layer removeAllAnimations];
[self.spinner.layer addAnimation:rotate forKey:@"transform"];
[UIView animateWithDuration:0.3f animations:^{
self.spinner.alpha = 1.0f;
}];
[self showMessage:@"Checking password..." state:MPLockscreenProgress];
});
if ([[MPAppDelegate get] tryMasterPassword:textField.text])
dispatch_async(dispatch_get_main_queue(), ^{
[self showMessage:@"Success!" state:MPLockscreenSuccess];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 1.5f), dispatch_get_main_queue(), ^{
[self dismissModalViewControllerAnimated:YES];
});
});
else
dispatch_async(dispatch_get_main_queue(), ^{
[self showMessage:@"Not valid." state:MPLockscreenError];
[UIView animateWithDuration:0.5f animations:^{
self.changeMPView.alpha = 1.0f;
}];
});
}
@finally {
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.3f animations:^{
self.spinner.alpha = 0.0f;
} completion:^(BOOL finished) {
[self.spinner.layer removeAllAnimations];
}];
});
}
});
}
- (IBAction)changeMP {
[[MPAppDelegate get] forgetKeyPhrase];
}
@end

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="1.0" toolsVersion="1938" systemVersion="11D50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="KZF-fe-y9n">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="1.1" toolsVersion="2177" systemVersion="11D50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="KZF-fe-y9n">
<dependencies>
<development defaultVersion="4200" identifier="xcode"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="933"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="1173"/>
</dependencies>
<scenes>
<!--Type View Controller - Type-->
<scene sceneID="WkW-SR-cr2">
<objects>
<placeholder placeholderIdentifier="IBFirstResponder" id="jZj-N1-rhF" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -52,9 +53,9 @@
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Word-like, 14 characters, contains symbols." lineBreakMode="tailTruncation" minimumFontSize="10" id="6iu-aM-lJA">
<rect key="frame" x="20" y="184" width="280" height="20"/>
<rect key="frame" x="20" y="183" width="280" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
<fontDescription key="fontDescription" name="GillSans-LightItalic" family="Gill Sans" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
@@ -96,9 +97,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Word-like, 8 characters, contains symbols." lineBreakMode="tailTruncation" minimumFontSize="10" id="MI2-JM-j4b">
<rect key="frame" x="20" y="24" width="280" height="20"/>
<rect key="frame" x="20" y="23" width="280" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
<fontDescription key="fontDescription" name="GillSans-LightItalic" family="Gill Sans" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
@@ -129,9 +130,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Word-like, 4 characters." lineBreakMode="tailTruncation" minimumFontSize="10" id="Zi3-26-3iq">
<rect key="frame" x="20" y="24" width="280" height="20"/>
<rect key="frame" x="20" y="23" width="280" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
<fontDescription key="fontDescription" name="GillSans-LightItalic" family="Gill Sans" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
@@ -162,9 +163,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="8 characters." lineBreakMode="tailTruncation" minimumFontSize="10" id="CYQ-D8-vNS">
<rect key="frame" x="20" y="24" width="280" height="20"/>
<rect key="frame" x="20" y="23" width="280" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
<fontDescription key="fontDescription" name="GillSans-LightItalic" family="Gill Sans" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
@@ -195,9 +196,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="4 numbers." lineBreakMode="tailTruncation" minimumFontSize="10" id="5Zm-AH-bAe">
<rect key="frame" x="20" y="24" width="280" height="20"/>
<rect key="frame" x="20" y="23" width="280" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
<fontDescription key="fontDescription" name="GillSans-LightItalic" family="Gill Sans" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
@@ -251,9 +252,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="AES-encrypted, iTunes backup, iCloud sync." lineBreakMode="tailTruncation" minimumFontSize="10" id="vNa-Yq-XIJ">
<rect key="frame" x="20" y="144" width="280" height="20"/>
<rect key="frame" x="20" y="143" width="280" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
<fontDescription key="fontDescription" name="GillSans-LightItalic" family="Gill Sans" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
@@ -284,9 +285,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="AES-encrypted, stays on this device only." lineBreakMode="tailTruncation" minimumFontSize="10" id="6S8-9Y-pzj">
<rect key="frame" x="20" y="24" width="280" height="20"/>
<rect key="frame" x="20" y="23" width="280" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
<fontDescription key="fontDescription" name="GillSans-LightItalic" family="Gill Sans" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
@@ -308,6 +309,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
</objects>
<point key="canvasLocation" x="996" y="182"/>
</scene>
<!--Main View Controller - Master Password-->
<scene sceneID="U26-Zf-euQ">
<objects>
<placeholder placeholderIdentifier="IBFirstResponder" id="mK2-p1-3zC" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -340,7 +342,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<rect key="frame" x="0.0" y="43" width="320" height="175"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="top" image="Content-Backdrop.png" id="enb-OH-DVZ">
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="bottom" image="background.png" id="enb-OH-DVZ">
<rect key="frame" x="0.0" y="0.0" width="320" height="175"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
@@ -373,7 +375,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.50000000000000011" contentMode="left" text="apple.com" lineBreakMode="tailTruncation" minimumFontSize="10" id="gSK-aB-wNI">
<rect key="frame" x="25" y="20" width="270" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<fontDescription key="fontDescription" name="Copperplate" family="Copperplate" pointSize="12"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
<color key="shadowColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
@@ -383,7 +385,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<rect key="frame" x="20" y="46" width="280" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="textColor" red="0.47450980390000003" green="0.86666666670000003" blue="0.98431372549999996" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="Optima-ExtraBlack" family="Optima" pointSize="26"/>
<fontDescription key="fontDescription" name="Copperplate-Bold" family="Copperplate" pointSize="26"/>
<textInputTraits key="textInputTraits" autocorrectionType="no"/>
<connections>
<outlet property="delegate" destination="PQa-Xl-A3x" id="qOM-gq-c6g"/>
@@ -392,7 +394,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.50000000000000011" contentMode="left" text="1" textAlignment="right" lineBreakMode="tailTruncation" minimumFontSize="10" id="Iuf-np-e9C">
<rect key="frame" x="240" y="27" width="40" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Optima-ExtraBlack" family="Optima" pointSize="17"/>
<fontDescription key="fontDescription" name="Copperplate-Bold" family="Copperplate" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
<color key="shadowColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
@@ -415,7 +417,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
</connections>
</button>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="9FS-fS-xH6">
<rect key="frame" x="275" y="18.5" width="37" height="37"/>
<rect key="frame" x="274.5" y="18.5" width="37.5" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<inset key="contentEdgeInsets" minX="5" minY="5" maxX="5" maxY="5"/>
@@ -433,9 +435,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" reversesTitleShadowWhenHighlighted="YES" lineBreakMode="middleTruncation" id="Cei-5z-uWE">
<rect key="frame" x="10" y="115" width="300" height="46"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="15"/>
<size key="titleShadowOffset" width="0.0" height="1"/>
<state key="normal" title="Long" backgroundImage="ui_button_standard_large.png">
<state key="normal" title="Long Password" backgroundImage="ui_button_standard_large.png">
<color key="titleColor" cocoaTouchSystemColor="lightTextColor"/>
<color key="titleShadowColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</state>
@@ -461,7 +463,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="icon_edit.png" id="KEn-n3-qhX">
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="icon_edit.png" id="KEn-n3-qhX">
<rect key="frame" x="48.5" y="6.5" width="24" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
@@ -471,7 +473,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<view clipsSubviews="YES" alpha="0.60000000000000009" contentMode="scaleToFill" id="61G-By-qLB">
<view clipsSubviews="YES" contentMode="scaleToFill" id="61G-By-qLB">
<rect key="frame" x="0.0" y="216" width="320" height="200"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
@@ -601,6 +603,7 @@ L4m3P4sSw0rD</string>
</objects>
<point key="canvasLocation" x="455" y="182"/>
</scene>
<!--Guide View Controller-->
<scene sceneID="Rt1-b4-sUB">
<objects>
<placeholder placeholderIdentifier="IBFirstResponder" id="7yf-G7-kVy" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -669,6 +672,113 @@ L4m3P4sSw0rD</string>
</objects>
<point key="canvasLocation" x="455" y="785"/>
</scene>
<!--Unlock View Controller-->
<scene sceneID="HkH-JR-Fhy">
<objects>
<placeholder placeholderIdentifier="IBFirstResponder" id="OGA-5j-IcQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
<viewController storyboardIdentifier="MPUnlockViewController" modalTransitionStyle="flipHorizontal" id="Nbn-Rv-sP1" customClass="MPUnlockViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="PHH-XC-9QQ">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="top" image="background.png" id="QWe-Gw-rD3">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<view contentMode="scaleToFill" id="OP6-72-eij">
<rect key="frame" x="0.0" y="157" width="320" height="130"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="lock_idle.png" id="tyv-qJ-bKR">
<rect key="frame" x="110" y="15" width="100" height="100"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" alpha="0.0" contentMode="center" image="lock_idle.png" id="Lpf-KA-3CV">
<rect key="frame" x="110" y="15" width="100" height="100"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_spinner.png" id="27q-lX-0vy">
<rect key="frame" x="122" y="28" width="75" height="75"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Checking password..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="oU9-lf-nnJ">
<rect key="frame" x="20" y="115" width="280" height="15"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="Copperplate-Bold" family="Copperplate" pointSize="13"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_textfield.png" id="ivR-Xl-NrT">
<rect key="frame" x="20" y="89" width="280" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<rect key="contentStretch" x="0.25" y="0.25" width="0.49999999999999961" height="0.49999999999999961"/>
</imageView>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="center" clearsOnBeginEditing="YES" minimumFontSize="17" id="rTR-7Q-X8o">
<rect key="frame" x="20" y="89" width="280" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" name="Copperplate" family="Copperplate" pointSize="36"/>
<textInputTraits key="textInputTraits" secureTextEntry="YES"/>
<connections>
<outlet property="delegate" destination="Nbn-Rv-sP1" id="Y0T-cI-gF1"/>
</connections>
</textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Enter your master password:" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="RhX-bA-EhC">
<rect key="frame" x="32" y="61" width="256" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Copperplate" family="Copperplate" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" id="Wu7-Mg-9SD">
<rect key="frame" x="0.0" y="391" width="320" height="89"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Trying to log in with a different master password?" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="vnS-n6-NZI">
<rect key="frame" x="0.0" y="0.0" width="320" height="15"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="Copperplate-Bold" family="Copperplate" pointSize="11"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" reversesTitleShadowWhenHighlighted="YES" lineBreakMode="middleTruncation" id="wad-V1-K3f">
<rect key="frame" x="10" y="23" width="300" height="46"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<size key="titleShadowOffset" width="0.0" height="1"/>
<state key="normal" title="Change master password" backgroundImage="ui_button_standard_large.png">
<color key="titleColor" cocoaTouchSystemColor="lightTextColor"/>
<color key="titleShadowColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="changeMP" destination="Nbn-Rv-sP1" eventType="touchUpInside" id="7RI-hu-SiS"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<nil key="simulatedStatusBarMetrics"/>
<connections>
<outlet property="changeMPView" destination="Wu7-Mg-9SD" id="84H-HT-5ml"/>
<outlet property="field" destination="rTR-7Q-X8o" id="DHg-gg-MVD"/>
<outlet property="lock" destination="Lpf-KA-3CV" id="6cE-2g-4XQ"/>
<outlet property="messageLabel" destination="oU9-lf-nnJ" id="Ahc-hl-KnJ"/>
<outlet property="spinner" destination="27q-lX-0vy" id="jUx-GK-Lgf"/>
</connections>
</viewController>
</objects>
<point key="canvasLocation" x="455" y="1425"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="8r0-wA-Zre">
<objects>
<placeholder placeholderIdentifier="IBFirstResponder" id="Lcz-JH-B5B" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -690,8 +800,9 @@ L4m3P4sSw0rD</string>
</scene>
</scenes>
<resources>
<image name="Content-Backdrop.png" width="480" height="480"/>
<image name="Content-Backdrop.png" width="16" height="16"/>
<image name="Square-bottom.png" width="551" height="58"/>
<image name="background.png" width="480" height="480"/>
<image name="guide_page_0.png" width="320" height="480"/>
<image name="guide_page_1.png" width="320" height="480"/>
<image name="guide_page_2.png" width="320" height="480"/>
@@ -702,6 +813,7 @@ L4m3P4sSw0rD</string>
<image name="icon_cancel.png" width="32" height="32"/>
<image name="icon_edit.png" width="32" height="32"/>
<image name="icon_plus.png" width="32" height="32"/>
<image name="lock_idle.png" width="100" height="100"/>
<image name="tip_alert_black.png" width="235" height="81"/>
<image name="tip_basic_black.png" width="210" height="60"/>
<image name="ui_button_green_large.png" width="300" height="46"/>
@@ -711,6 +823,8 @@ L4m3P4sSw0rD</string>
<image name="ui_list_middle.png" width="300" height="34"/>
<image name="ui_panel_container.png" width="300" height="87"/>
<image name="ui_panel_display.png" width="300" height="86"/>
<image name="ui_spinner.png" width="75" height="75"/>
<image name="ui_textfield.png" width="158" height="34"/>
</resources>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar" statusBarStyle="blackTranslucent"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB