Reset from unlock, FAQ improvements.
[ADDED] Ability to reset a master password from the unlock screen. [FIXED] Manually retain objects that live next to a VC in a storyboard within the VC to avoid an OS bug. [FIXED] Visibility of the deleteTip. [ADDED] An index to the FAQ. [IMPROVED] Improved and expanded the FAQ a bit more.
This commit is contained in:
parent
a67d9676ba
commit
d4adafb448
@ -75,10 +75,13 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
|
|
||||||
- (void)signOut {
|
- (void)signOut {
|
||||||
|
|
||||||
self.key = nil;
|
if (self.key)
|
||||||
self.activeUser = nil;
|
self.key = nil;
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationSignedOut object:self];
|
if (self.activeUser) {
|
||||||
|
self.activeUser = nil;
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationSignedOut object:self];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password {
|
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password {
|
||||||
|
@ -18,6 +18,6 @@
|
|||||||
- (void)showGuide;
|
- (void)showGuide;
|
||||||
|
|
||||||
- (void)export;
|
- (void)export;
|
||||||
- (void)changeMP;
|
- (void)changeMasterPasswordFor:(MPUserEntity *)user;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -331,7 +331,7 @@
|
|||||||
[TestFlight passCheckpoint:MPCheckpointDeactivated];
|
[TestFlight passCheckpoint:MPCheckpointDeactivated];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma - mark Behavior
|
#pragma mark - Behavior
|
||||||
|
|
||||||
- (void)checkConfig {
|
- (void)checkConfig {
|
||||||
|
|
||||||
@ -452,7 +452,7 @@
|
|||||||
[self.window.rootViewController presentModalViewController:composer animated:YES];
|
[self.window.rootViewController presentModalViewController:composer animated:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)changeMP {
|
- (void)changeMasterPasswordFor:(MPUserEntity *)user {
|
||||||
|
|
||||||
[PearlAlert showAlertWithTitle:@"Changing Master Password"
|
[PearlAlert showAlertWithTitle:@"Changing Master Password"
|
||||||
message:
|
message:
|
||||||
@ -464,9 +464,9 @@
|
|||||||
if (buttonIndex == [alert cancelButtonIndex])
|
if (buttonIndex == [alert cancelButtonIndex])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
inf(@"Unsetting master password for: %@.", self.activeUser.userID);
|
inf(@"Unsetting master password for: %@.", user.userID);
|
||||||
self.activeUser.keyID = nil;
|
user.keyID = nil;
|
||||||
[self forgetSavedKeyFor:self.activeUser];
|
[self forgetSavedKeyFor:user];
|
||||||
[self signOut];
|
[self signOut];
|
||||||
|
|
||||||
[TestFlight passCheckpoint:MPCheckpointChangeMP];
|
[TestFlight passCheckpoint:MPCheckpointChangeMP];
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
@property (weak, nonatomic) IBOutlet UIView *searchTipContainer;
|
@property (weak, nonatomic) IBOutlet UIView *searchTipContainer;
|
||||||
@property (weak, nonatomic) IBOutlet UIView *actionsTipContainer;
|
@property (weak, nonatomic) IBOutlet UIView *actionsTipContainer;
|
||||||
@property (weak, nonatomic) IBOutlet UIView *typeTipContainer;
|
@property (weak, nonatomic) IBOutlet UIView *typeTipContainer;
|
||||||
|
@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *resetPasswordCounterGesture;
|
||||||
|
|
||||||
@property (copy) void (^contentTipCleanup)(BOOL finished);
|
@property (copy) void (^contentTipCleanup)(BOOL finished);
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
@synthesize searchTipContainer = _searchTipContainer;
|
@synthesize searchTipContainer = _searchTipContainer;
|
||||||
@synthesize actionsTipContainer = _actionsTipContainer;
|
@synthesize actionsTipContainer = _actionsTipContainer;
|
||||||
@synthesize typeTipContainer = _typeTipContainer;
|
@synthesize typeTipContainer = _typeTipContainer;
|
||||||
|
@synthesize resetPasswordCounterGesture = _resetPasswordCounterGesture;
|
||||||
@synthesize contentField = _contentField;
|
@synthesize contentField = _contentField;
|
||||||
@synthesize contentTipCleanup;
|
@synthesize contentTipCleanup;
|
||||||
|
|
||||||
@ -149,6 +150,7 @@
|
|||||||
[self setSearchTipContainer:nil];
|
[self setSearchTipContainer:nil];
|
||||||
[self setActionsTipContainer:nil];
|
[self setActionsTipContainer:nil];
|
||||||
[self setTypeTipContainer:nil];
|
[self setTypeTipContainer:nil];
|
||||||
|
[self setResetPasswordCounterGesture:nil];
|
||||||
[super viewDidUnload];
|
[super viewDidUnload];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@
|
|||||||
|
|
||||||
else
|
else
|
||||||
if (cell == self.changeMPCell)
|
if (cell == self.changeMPCell)
|
||||||
[[MPAppDelegate get] changeMP];
|
[[MPAppDelegate get] changeMasterPasswordFor:[MPAppDelegate get].activeUser];
|
||||||
|
|
||||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,10 @@
|
|||||||
@property (weak, nonatomic) IBOutlet UILabel *deleteTip;
|
@property (weak, nonatomic) IBOutlet UILabel *deleteTip;
|
||||||
@property (weak, nonatomic) IBOutlet UIView *passwordTipView;
|
@property (weak, nonatomic) IBOutlet UIView *passwordTipView;
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *passwordTipLabel;
|
@property (weak, nonatomic) IBOutlet UILabel *passwordTipLabel;
|
||||||
|
@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *targetedUserActionGesture;
|
||||||
|
|
||||||
@property (nonatomic, strong) UIColor *avatarShadowColor;
|
@property (nonatomic, strong) UIColor *avatarShadowColor;
|
||||||
|
|
||||||
- (IBAction)deleteTargetedUser:(UILongPressGestureRecognizer *)sender;
|
- (IBAction)targetedUserAction:(UILongPressGestureRecognizer *)sender;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
@synthesize deleteTip;
|
@synthesize deleteTip;
|
||||||
@synthesize passwordTipView;
|
@synthesize passwordTipView;
|
||||||
@synthesize passwordTipLabel;
|
@synthesize passwordTipLabel;
|
||||||
|
@synthesize targetedUserActionGesture;
|
||||||
@synthesize avatarShadowColor = _avatarShadowColor;
|
@synthesize avatarShadowColor = _avatarShadowColor;
|
||||||
|
|
||||||
|
|
||||||
@ -137,6 +138,7 @@
|
|||||||
[self setDeleteTip:nil];
|
[self setDeleteTip:nil];
|
||||||
[self setPasswordTipView:nil];
|
[self setPasswordTipView:nil];
|
||||||
[self setPasswordTipLabel:nil];
|
[self setPasswordTipLabel:nil];
|
||||||
|
[self setTargetedUserActionGesture:nil];
|
||||||
[super viewDidUnload];
|
[super viewDidUnload];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +358,7 @@
|
|||||||
self.nameLabel.backgroundColor = [UIColor clearColor];
|
self.nameLabel.backgroundColor = [UIColor clearColor];
|
||||||
self.oldNameLabel.center = self.nameLabel.center;
|
self.oldNameLabel.center = self.nameLabel.center;
|
||||||
self.avatarShadowColor = [UIColor lightGrayColor];
|
self.avatarShadowColor = [UIColor lightGrayColor];
|
||||||
self.deleteTip.alpha = [self.avatarToUser count] > 2? 1: 0;
|
self.deleteTip.alpha = 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
MPUserEntity *targetedUser = self.selectedUser;
|
MPUserEntity *targetedUser = self.selectedUser;
|
||||||
@ -590,13 +592,11 @@
|
|||||||
*targetContentOffset = CGPointMake(middleAvatar.center.x - scrollView.bounds.size.width / 2, targetContentOffset->y);
|
*targetContentOffset = CGPointMake(middleAvatar.center.x - scrollView.bounds.size.width / 2, targetContentOffset->y);
|
||||||
|
|
||||||
[self updateLayoutAnimated:NO allowScroll:NO completion:nil];
|
[self updateLayoutAnimated:NO allowScroll:NO completion:nil];
|
||||||
// [self scrollToAvatar:middleAvatar animated:YES];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
|
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
|
||||||
|
|
||||||
[self updateLayoutAnimated:YES allowScroll:YES completion:nil];
|
[self updateLayoutAnimated:YES allowScroll:YES completion:nil];
|
||||||
// [self scrollToAvatar:middleAvatar animated:YES];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||||
@ -606,7 +606,7 @@
|
|||||||
|
|
||||||
#pragma mark - IBActions
|
#pragma mark - IBActions
|
||||||
|
|
||||||
- (IBAction)deleteTargetedUser:(UILongPressGestureRecognizer *)sender {
|
- (IBAction)targetedUserAction:(UILongPressGestureRecognizer *)sender {
|
||||||
|
|
||||||
if (sender.state != UIGestureRecognizerStateBegan)
|
if (sender.state != UIGestureRecognizerStateBegan)
|
||||||
return;
|
return;
|
||||||
@ -617,19 +617,24 @@
|
|||||||
MPUserEntity *targetedUser = [self userForAvatar:[self findTargetedAvatar]];
|
MPUserEntity *targetedUser = [self userForAvatar:[self findTargetedAvatar]];
|
||||||
if (!targetedUser)
|
if (!targetedUser)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[PearlAlert showAlertWithTitle:@"Delete User" message:
|
[PearlAlert showAlertWithTitle:@"Delete Or Reset User"
|
||||||
PearlString(@"Do you want to delete all record of the following user?\n\n%@",
|
message:
|
||||||
targetedUser.name)
|
PearlString(@"Do you want to reset the master password or delete all record of the following user?\n\n%@",
|
||||||
|
targetedUser.name)
|
||||||
viewStyle:UIAlertViewStyleDefault
|
viewStyle:UIAlertViewStyleDefault
|
||||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||||
if (buttonIndex == [alert cancelButtonIndex])
|
if (buttonIndex == [alert cancelButtonIndex])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[[MPAppDelegate get].managedObjectContext deleteObject:targetedUser];
|
if (buttonIndex == [alert firstOtherButtonIndex]) {
|
||||||
[[MPAppDelegate get] saveContext];
|
[[MPAppDelegate get].managedObjectContext deleteObject:targetedUser];
|
||||||
|
[[MPAppDelegate get] saveContext];
|
||||||
[self updateUsers];
|
[self updateUsers];
|
||||||
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Delete", nil];
|
} else if (buttonIndex == [alert firstOtherButtonIndex] + 1) {
|
||||||
|
[[MPAppDelegate get] changeMasterPasswordFor:targetedUser];
|
||||||
|
}
|
||||||
|
|
||||||
|
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Delete", @"Reset", nil];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
@ -734,6 +734,7 @@ L4m3P4sSw0rD</string>
|
|||||||
<outlet property="passwordCounter" destination="Iuf-np-e9C" id="CIm-Mk-nJh"/>
|
<outlet property="passwordCounter" destination="Iuf-np-e9C" id="CIm-Mk-nJh"/>
|
||||||
<outlet property="passwordEdit" destination="9FS-fS-xH6" id="YeB-HF-ZPk"/>
|
<outlet property="passwordEdit" destination="9FS-fS-xH6" id="YeB-HF-ZPk"/>
|
||||||
<outlet property="passwordIncrementer" destination="jec-mu-nPt" id="i9B-lX-zzX"/>
|
<outlet property="passwordIncrementer" destination="jec-mu-nPt" id="i9B-lX-zzX"/>
|
||||||
|
<outlet property="resetPasswordCounterGesture" destination="cZr-Fj-eBw" id="St3-9p-J86"/>
|
||||||
<outlet property="searchDisplayController" destination="P8c-gf-nN3" id="CLs-YI-7NC"/>
|
<outlet property="searchDisplayController" destination="P8c-gf-nN3" id="CLs-YI-7NC"/>
|
||||||
<outlet property="searchResultsController" destination="0QO-2P-OhD" id="xEC-gV-lHp"/>
|
<outlet property="searchResultsController" destination="0QO-2P-OhD" id="xEC-gV-lHp"/>
|
||||||
<outlet property="searchTipContainer" destination="zOR-Du-qRL" id="X7h-Vh-iCE"/>
|
<outlet property="searchTipContainer" destination="zOR-Du-qRL" id="X7h-Vh-iCE"/>
|
||||||
@ -919,9 +920,9 @@ L4m3P4sSw0rD</string>
|
|||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="Tap and hold to delete." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="DBJ-Qi-ZcF">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="Tap and hold to delete or reset." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="DBJ-Qi-ZcF">
|
||||||
<rect key="frame" x="32" y="460" width="256" height="20"/>
|
<rect key="frame" x="20" y="460" width="280" height="20"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
<fontDescription key="fontDescription" name="Copperplate" family="Copperplate" pointSize="12"/>
|
<fontDescription key="fontDescription" name="Copperplate" family="Copperplate" pointSize="12"/>
|
||||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
@ -946,11 +947,12 @@ L4m3P4sSw0rD</string>
|
|||||||
<outlet property="passwordTipView" destination="NvG-0R-eTZ" id="4Mx-TL-yfu"/>
|
<outlet property="passwordTipView" destination="NvG-0R-eTZ" id="4Mx-TL-yfu"/>
|
||||||
<outlet property="passwordView" destination="7cc-yu-i0m" id="WoF-Ab-PPC"/>
|
<outlet property="passwordView" destination="7cc-yu-i0m" id="WoF-Ab-PPC"/>
|
||||||
<outlet property="spinner" destination="27q-lX-0vy" id="CGK-G9-PRI"/>
|
<outlet property="spinner" destination="27q-lX-0vy" id="CGK-G9-PRI"/>
|
||||||
|
<outlet property="targetedUserActionGesture" destination="9WS-yS-aqQ" id="y74-cg-eat"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<pongPressGestureRecognizer allowableMovement="10" minimumPressDuration="0.5" id="9WS-yS-aqQ">
|
<pongPressGestureRecognizer allowableMovement="10" minimumPressDuration="0.5" id="9WS-yS-aqQ">
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="deleteTargetedUser:" destination="Nbn-Rv-sP1" id="cBQ-oQ-c7g"/>
|
<action selector="targetedUserAction:" destination="Nbn-Rv-sP1" id="mgC-0X-heO"/>
|
||||||
</connections>
|
</connections>
|
||||||
</pongPressGestureRecognizer>
|
</pongPressGestureRecognizer>
|
||||||
</objects>
|
</objects>
|
||||||
@ -1372,6 +1374,7 @@ L4m3P4sSw0rD</string>
|
|||||||
<relationship kind="outlet" name="passwordCounter" candidateClass="UILabel"/>
|
<relationship kind="outlet" name="passwordCounter" candidateClass="UILabel"/>
|
||||||
<relationship kind="outlet" name="passwordEdit" candidateClass="UIButton"/>
|
<relationship kind="outlet" name="passwordEdit" candidateClass="UIButton"/>
|
||||||
<relationship kind="outlet" name="passwordIncrementer" candidateClass="UIButton"/>
|
<relationship kind="outlet" name="passwordIncrementer" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="outlet" name="resetPasswordCounterGesture" candidateClass="UILongPressGestureRecognizer"/>
|
||||||
<relationship kind="outlet" name="searchResultsController" candidateClass="MPSearchDelegate"/>
|
<relationship kind="outlet" name="searchResultsController" candidateClass="MPSearchDelegate"/>
|
||||||
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
|
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
|
||||||
<relationship kind="outlet" name="siteName" candidateClass="UILabel"/>
|
<relationship kind="outlet" name="siteName" candidateClass="UILabel"/>
|
||||||
@ -1409,7 +1412,7 @@ L4m3P4sSw0rD</string>
|
|||||||
<class className="MPUnlockViewController" superclassName="UIViewController">
|
<class className="MPUnlockViewController" superclassName="UIViewController">
|
||||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPUnlockViewController.h"/>
|
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPUnlockViewController.h"/>
|
||||||
<relationships>
|
<relationships>
|
||||||
<relationship kind="action" name="deleteTargetedUser:" candidateClass="UILongPressGestureRecognizer"/>
|
<relationship kind="action" name="targetedUserAction:" candidateClass="UILongPressGestureRecognizer"/>
|
||||||
<relationship kind="outlet" name="avatarTemplate" candidateClass="UIButton"/>
|
<relationship kind="outlet" name="avatarTemplate" candidateClass="UIButton"/>
|
||||||
<relationship kind="outlet" name="avatarsView" candidateClass="UIScrollView"/>
|
<relationship kind="outlet" name="avatarsView" candidateClass="UIScrollView"/>
|
||||||
<relationship kind="outlet" name="deleteTip" candidateClass="UILabel"/>
|
<relationship kind="outlet" name="deleteTip" candidateClass="UILabel"/>
|
||||||
@ -1420,6 +1423,7 @@ L4m3P4sSw0rD</string>
|
|||||||
<relationship kind="outlet" name="passwordTipView" candidateClass="UIView"/>
|
<relationship kind="outlet" name="passwordTipView" candidateClass="UIView"/>
|
||||||
<relationship kind="outlet" name="passwordView" candidateClass="UIView"/>
|
<relationship kind="outlet" name="passwordView" candidateClass="UIView"/>
|
||||||
<relationship kind="outlet" name="spinner" candidateClass="UIImageView"/>
|
<relationship kind="outlet" name="spinner" candidateClass="UIImageView"/>
|
||||||
|
<relationship kind="outlet" name="targetedUserActionGesture" candidateClass="UILongPressGestureRecognizer"/>
|
||||||
</relationships>
|
</relationships>
|
||||||
</class>
|
</class>
|
||||||
</classes>
|
</classes>
|
||||||
|
@ -101,8 +101,25 @@
|
|||||||
|
|
||||||
<h2 id="faq">— F.A.Q. —</h2>
|
<h2 id="faq">— F.A.Q. —</h2>
|
||||||
|
|
||||||
<h3>What is this thing?<br />
|
<ol>
|
||||||
How do I use it?</h3>
|
<li><a href="#what" >What is it and how do I use it?</a></li>
|
||||||
|
<li><a href="#why" >Why do I need Master Password?</a></li>
|
||||||
|
<li><a href="#custom" >A password was given to me.</a></li>
|
||||||
|
<li><a href="#loss" >What if I loose my device?</a></li>
|
||||||
|
<li><a href="#device" >Am I dependant on my device?</a></li>
|
||||||
|
<li><a href="#paranoid" >How do I maximize my security?</a></li>
|
||||||
|
<li><a href="#hacked" >A website I use got hacked!</a></li>
|
||||||
|
<li><a href="#forgot" >I forgot my master password!</a></li>
|
||||||
|
<li><a href="#algorithm">How does Master Password work?</a></li>
|
||||||
|
<li><a href="#branded" >Do you offer enterprise solutions?</a></li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h3 id="what">What is Master Password and how do I use it?</h3>
|
||||||
|
<p>
|
||||||
|
Master Password <b>creates secure and unique passwords for you</b>, so you don't have to.<br />
|
||||||
|
The human brain is not well suited for creating secure and random passwords, and it's also terrible at remembering lots of unique passwords.
|
||||||
|
Master Password does the work for you: all you need to do is remember a single long and secure master password to log into the app.
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Begin by entering the name</b> of the thing you want a password for. Naming is entirely up to you, but remember to be consistent.<br />
|
<b>Begin by entering the name</b> of the thing you want a password for. Naming is entirely up to you, but remember to be consistent.<br />
|
||||||
<i>Good names</i> could be:<br />
|
<i>Good names</i> could be:<br />
|
||||||
@ -115,7 +132,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<b>Tap the resulting password</b> to copy it for pasting in a different application or read it to type it in or use it manually elsewhere.
|
<b>Tap the resulting password</b> to copy it for pasting in a different application or read it to type it in or use it manually elsewhere.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p id="why">
|
||||||
The thought behind this application is to secure your online (and offline) life by <b>changing all of your passwords</b>
|
The thought behind this application is to secure your online (and offline) life by <b>changing all of your passwords</b>
|
||||||
to passwords generated by this app.
|
to passwords generated by this app.
|
||||||
</p>
|
</p>
|
||||||
@ -140,8 +157,7 @@
|
|||||||
<i>other</i> site. Nothing is stopping them from trying to log into <i>your</i> GMail, Hotmail or Twitter
|
<i>other</i> site. Nothing is stopping them from trying to log into <i>your</i> GMail, Hotmail or Twitter
|
||||||
accounts using the same password that you used to register an account on their site. Even if you only give
|
accounts using the same password that you used to register an account on their site. Even if you only give
|
||||||
your password to sites you trust, all it takes is for one of those sites to get hacked and lose their
|
your password to sites you trust, all it takes is for one of those sites to get hacked and lose their
|
||||||
passwords database. Those hackers now have all it takes to impersonate you. This is, in fact, so common,
|
passwords database. Those hackers now have all it takes to impersonate you.
|
||||||
that it's one of the main reasons people's accounts are getting hacked nowadays.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Some of you already try to remember unique-ish passwords for different sites. This causes problems too:
|
Some of you already try to remember unique-ish passwords for different sites. This causes problems too:
|
||||||
@ -158,7 +174,7 @@
|
|||||||
or purpose you might need a password for.
|
or purpose you might need a password for.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>I can't change all my passwords.<br />
|
<h3 id="custom">I can't change all my passwords.<br />
|
||||||
Some of them were assigned to me.</h3>
|
Some of them were assigned to me.</h3>
|
||||||
<p>
|
<p>
|
||||||
That's why this application allows you to change the password type to <code>Personal</code> or <code>Device
|
That's why this application allows you to change the password type to <code>Personal</code> or <code>Device
|
||||||
@ -171,15 +187,14 @@
|
|||||||
password to one generated by the app, this is as good as it gets.
|
password to one generated by the app, this is as good as it gets.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>So, what if I lose my device?<br />
|
<h3 id="loss">So, what if I lose my device?<br />
|
||||||
I'm locked out of everything?</h3>
|
I'm locked out of everything?</h3>
|
||||||
<p>
|
<p>
|
||||||
<b>Absolutely not!</b> In fact, generated passwords aren't even stored on your device. No, not in the
|
<b>Absolutely not!</b> In fact, generated passwords aren't even stored on your device. No, not in the
|
||||||
cloud either. They're not stored anywhere! What that basically means is, if you grab the iPhone of a
|
cloud either. They're not stored anywhere! What that basically means is, if you grab the iPhone of a
|
||||||
colleague or friend and open this app on it with your own master password, <i>it'll give you all your
|
colleague or friend and open this app on it, re-create your user and log in, <i>it'll give you all your
|
||||||
generated passwords</i> (don't worry, it's perfectly safe). So, if you lose your iPhone or forget it,
|
generated passwords</i>. So, if you lose your iPhone or forget it, just open the app on your iPad,
|
||||||
just open the app on your iPad, or borrow a friend's phone, and you're back in business. No backups or
|
or borrow a friend's device, and you're back in business. No backups or restores needed.
|
||||||
restores needed.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
This also means that, unlike all those apps that store your passwords or send them off to be stored on the
|
This also means that, unlike all those apps that store your passwords or send them off to be stored on the
|
||||||
@ -187,108 +202,107 @@
|
|||||||
get at your passwords. There's also no cloud service that can be mis-managed or hacked.
|
get at your passwords. There's also no cloud service that can be mis-managed or hacked.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>Great, but that still means I need my phone to get my passwords.</h3>
|
<h3 id="device">Great, but that still means I need my device to get my passwords.</h3>
|
||||||
<p>
|
<p>
|
||||||
Correct. However, remember that usually you'll only need to use this app once for each site. After you log
|
Correct. However, remember that usually you'll only need to use this app once for each site. After you log
|
||||||
into a site once using the password generated by this app, your browser will probably ask you to remember
|
into a site once using the password generated by this app, your browser will probably ask you to remember
|
||||||
the password for the future. Agree to that, and you won't need to bring up your phone again the next time
|
the password for the future. Agree to that, and you won't need to bring up your device again the next time
|
||||||
you log in to the account.
|
you log in to the account.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
There is also a <b>Mac version</b> of Master Password available from the App Store. It allows you to
|
There is also a <b>Mac version</b> of Master Password that will be released on the Mac App Store.
|
||||||
generate any of your passwords without even needing to take out your phone.
|
It allows you to generate any of your passwords without the need to bring out your device.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>I'm paranoid.<br />
|
<h3 id="paranoid">I'm paranoid.<br />
|
||||||
How do I maximize my security?</h3>
|
How do I maximize my security?</h3>
|
||||||
<p>
|
<p>
|
||||||
For starters, make sure you've changed the passwords of all your sites you have accounts for to those
|
The <b>most important</b> aspect to the security of your passwords is your <b>master password</b>. Make sure
|
||||||
generated by this app and make sure that you use this app when registering a new account somewhere, to
|
you've chosen a <em>long and unique master password</em>. Master Password's algorithm makes it exceedingly
|
||||||
determine the password to use for the account.
|
difficult for an attacker to try and guess your master password, but that doesn't make you invulnerable when
|
||||||
|
your master password is short or easy to guess. Ideally, your master password should be <em>longer than 10 characters</em>.
|
||||||
|
<b>An absurd sentence is a great idea</b>, especially if you add non-english or gibberish words to it.
|
||||||
|
Absurd sentences are long and high in entropy, but also particularly easy for the human brain to remember.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
It's also important that you've chosen a long master password. Short master passwords, especially 4-digit
|
Armed with a good master password, your next step is to assign generated passwords to all of your sites.
|
||||||
PIN codes, are trivial to guess by attackers. Using a <b>10-character master password</b> provides
|
By default, Master Password creates passwords that are secure and still easy to copy from your device to a
|
||||||
sufficient entropy to protect against any modern-day attempt at brute-forcing, assuming the password is not
|
computer by keyboard. If you prefer, you can go into Master Password's preferences (using the top-right icon)
|
||||||
based on easily determined facts (names, birth dates, etc.).
|
and change the default password type to <code>Maximum Security</code>. Any new sites will now generate
|
||||||
|
passwords that are even higher in entropy. These types of passwords are nigh impossible for an attacker to
|
||||||
|
brute-force (though a <code>Long Password</code> really is secure enough for most any purpose, see
|
||||||
|
<a href="#hacked">What if a site I use gets hacked?</a>).
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>A better idea yet</b> is to use a pass phrase, ideally <em>an absurd sentence</em>. These are usually
|
Also check out the application's preferences (using the action icon on the top right, select <code>Preferences</code>).
|
||||||
much easier to remember and much harder to guess by attackers.
|
Make sure that <code>Save Password</code> is disabled. Saving your password is a convenience feature that lets your
|
||||||
</p>
|
device save your master password so you don't need to enter it anymore. It also means that if somebody finds your device
|
||||||
<p>
|
somewhere or steals it, the only obstacle between them and your passwords are your device's PIN code (assuming you even
|
||||||
Using the action icon on the top right, select <code>Settings</code> to find some advanced settings for
|
have one set).<br />
|
||||||
the application. Here, you can disable <code>Remember my password</code>. Doing so will force the
|
If you go into <code>Settings</code> from the <code>Preferences</code> page, you'll see some global application settings.
|
||||||
application to <b>ask for your master password each time</b> you open it. That way, when you show your
|
Make sure that <code>Stay logged in</code> is disabled here. If enabled, Master Password will not log you out when you
|
||||||
phone to somebody else after unlocking it, they can't go through your passwords.
|
close the app. Your master password isn't saved on your device, but kept in memory for as long as your device remains
|
||||||
|
powered on. Again, a malignent person can easily get to your passwords if they find your device powered on and logged
|
||||||
|
into Master Password.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>I forgot my master password. What are my options?</h3>
|
<h3 id="hacked">What if a site I use gets hacked?</h3>
|
||||||
|
<p>
|
||||||
|
There have been some high-profile password database leaks lately. LinkedIn, eHarmony, Last.fm, to name a few,
|
||||||
|
have lost millions of people's password hashes. In these cases, attackers have obtained a <q>hash</q> of
|
||||||
|
the passwords of all of these people, which makes it much easier for them to guess their real password.
|
||||||
|
A single sophisticated computer can be used to try about 200 million password combinations per second in an
|
||||||
|
attempt to find the real password behind a hash. That means these millions of people should be really worried
|
||||||
|
about their account's security.<br />
|
||||||
|
However, if your account is protected by a <code>Long Password</code> generated by Master Password, it would
|
||||||
|
take an attacker with ten sophisticated machines multiple lifetimes to find your actual password from a hash.
|
||||||
|
If the attacker knew beforehand that you had used Master Password to generate your password, he could make
|
||||||
|
his approach smarter and ten sophisticated machines would still take more than a year of constantly trying
|
||||||
|
millions of password combinations to find out your actual password.<br />
|
||||||
|
If instead you used a <code>Maximum Security</code> password to protect your account, the time it would take
|
||||||
|
for an attacker to brute-force your password goes completely off the scale: 10,000 sophisticated machines
|
||||||
|
would take up to 312409704477000000 years to try and find your password, even if the attacker knew you're
|
||||||
|
using Master Password.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you're worried anyway or you need a new password for your site for some other reason, tap the password
|
||||||
|
counter button (the plus icon) to instantly create a new password for that site.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Long story short: When a website you use gets hacked and your password hashes are revealed to hackers, this
|
||||||
|
is a big problem for the security of your account, but only if you're <b>not</b> using Master Password.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 id="forgot">I forgot my master password. What are my options?</h3>
|
||||||
<p>
|
<p>
|
||||||
Due to the nature of this app's algorithms and the decisions that were made to protect against brute-force
|
Due to the nature of this app's algorithms and the decisions that were made to protect against brute-force
|
||||||
attacks, it is simply infeasible to recover your master password. If you really can't remember it, your
|
attacks, it is simply infeasible to recover your master password. If you really can't remember it, your
|
||||||
passwords are <b>gone</b>.
|
passwords are <b>gone</b>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Where you go from here is, you log in with a new master password, and for each of your accounts, you go
|
Where you go from here is: on the unlock screen, tap and hold your user. A dialog will pop-up that will allow
|
||||||
through the password recovery procedure (which will usually involve sending a message to your email account)
|
you to reset your master password. Assign a new master password, log in, and for each of your accounts, go
|
||||||
|
through the password recovery procedure (which will usually involve the site sending a mail to your email account)
|
||||||
and reset the passwords of these accounts to passwords generated by your newly chosen master password.<br />
|
and reset the passwords of these accounts to passwords generated by your newly chosen master password.<br />
|
||||||
Just don't forget it again! :-)
|
Now don't forget it again! :-)
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>So how does this thing work internally?</h3>
|
<h3 id="algorithm">So how does this thing work internally?</h3>
|
||||||
<p>
|
<p>
|
||||||
The way Master Password works internally is <i>fully disclosed</i>. The source code for this application
|
The way Master Password works internally is <a href="http://masterpassword.lyndir.com/algorithm.html">fully disclosed</a>.
|
||||||
is also available from <b>GitHub</b>. I invite anyone with a technical background to go through these
|
The source code for this application is also available from <a href="https://github.com/Lyndir/MasterPassword">GitHub</a>.
|
||||||
resources to make certain of the trustworthyness of Master Password.
|
I invite anyone with a technical background to go through these resources to make certain of the trustworthiness of Master Password.
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
This part will likely make sense to you only if you're well versed in computer security jargon. If you're
|
|
||||||
the kind of person who likes to know how the clock ticks before deciding that it can be trusted to keep
|
|
||||||
ticking, read on.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The user chooses a single master password, preferably sufficiently long to harden against brute-force
|
|
||||||
attacks. The application then creates a scrypt key derivative from the user's password. This process
|
|
||||||
takes quite a bit of processing time and memory. It makes brute-forcing the master password
|
|
||||||
<b>far more difficult</b>, to practically infeasible, even for otherwise vulnerable password strings.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
When the user requests a password be generated for a site, the application composes a byte buffer
|
|
||||||
consisting of the site's name (<code>UTF-8</code> encoded), the key derived from the master password,
|
|
||||||
and a password counter, delimited in that order by a NUL byte. The bytes are hashed using the
|
|
||||||
<code>SHA-1</code> algorithm. The bytes resulting from this hashing operation are called the
|
|
||||||
<code>seed</code> in the next steps.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Next, we need the password type that the user has chosen to use for the site. Password types determine the
|
|
||||||
<q>cipher</q> that will be used to encrypt <code>seed</code> into a readable password. For
|
|
||||||
instance, the standard password type <q>Long Password</q> activates one of three pre-set ciphers:
|
|
||||||
<code>CvcvCvcvnoCvcv</code>, <code>CvcvnoCvcvCvcv</code> or <code>CvcvCvcvCvcvno</code>. Which of those
|
|
||||||
will be used, depends on the first of the <code>seed</code> bytes. Take the byte value modulo the amount of
|
|
||||||
pre-set ciphers (in this case, three), and the result tells you which of the three ciphers to use.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Now that we know what cipher to use for building our final password, all that's left is to iterate the
|
|
||||||
cipher, and produce a character of password output for each step. When you iterate the cipher, every
|
|
||||||
character in the cipher represents a set of possible output characters. For instance, a <code>C</code>
|
|
||||||
character in the cipher indicates that we need to choose a capital consonant character. An <code>o</code>
|
|
||||||
character in the cipher indicates that we need to choose an <q>other</q> (symbol) character. Exactly which
|
|
||||||
character to choose in that set for the password output depends on the next byte from <code>seed</code> bytes.
|
|
||||||
Like before, take the next unused <code>seed</code> byte's byte value modulo the amount of characters in the
|
|
||||||
set of possible output characters for the cipher iteration and use the result to choose the output
|
|
||||||
character. Repeat until you've iterated the whole cipher.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The result is a password whose format is dictated by the password type's ciphers and whose exact value is
|
|
||||||
filled in by feeding the algorithm some bytes from a hash operation on the user's givens.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>This stuff is gold.<br />
|
<h3 id="branded">This stuff is gold.<br />
|
||||||
I want one branded for our company.</h3>
|
I want one branded for our company.</h3>
|
||||||
<p>
|
<p>
|
||||||
Contact me directly for enterprise inquiries. I can provide branded clients and enterprise distribution
|
<a href="mailto:masterpassword@lyndir.com">Contact me</a> directly for enterprise inquiries.
|
||||||
if your company is interested in deploying this solution internally.
|
I can provide branded clients and enterprise distribution if your company is interested in deploying this solution internally.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Master Password can also be used as a One-Time Password token generator to secure your infrastructure and client access.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
Loading…
Reference in New Issue
Block a user