2
0

Migration of saved passwords.

[ADDED]     Migrate saved passwords when master password changes.
[IMPROVED]  UI improvements to apps.
This commit is contained in:
Maarten Billemont 2012-09-01 22:14:57 +02:00
parent d75ec5c689
commit e2bf8cefa2
11 changed files with 112 additions and 53 deletions

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit ff1e29fc3a4c2ee7b08eb686d06f29a11466bbe5
Subproject commit 552cf5ffc9445362f8f91357f4f7a034b2c390a3

View File

@ -28,7 +28,9 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
inf(@"Found key in keychain for: %@", user.userID);
else {
user.saveKey = NO;
[user.managedObjectContext performBlockAndWait:^{
user.saveKey = NO;
}];
inf(@"No key found in keychain for: %@", user.userID);
}
@ -58,7 +60,9 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery(user)];
if (result == noErr || result == errSecItemNotFound) {
user.saveKey = NO;
[user.managedObjectContext performBlockAndWait:^{
user.saveKey = NO;
}];
if (result == noErr) {
inf(@"Removed key from keychain for: %@", user.userID);
@ -82,14 +86,68 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password {
assert(!password || ![NSThread isMainThread]); // If we need to computing a key, this operation shouldn't be on the main thread.
MPKey *tryKey = nil;
// Method 1: When the user has no keyID set, set a new key from the given master password.
if (!user.keyID) {
if ([password length])
if ((tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
user.keyID = tryKey.keyID;
[user.managedObjectContext performBlockAndWait:^{
user.keyID = tryKey.keyID;
}];
// Migrate existing elements.
MPKey *recoverKey = nil;
PearlAlert *activityAlert = [PearlAlert showActivityWithTitle:PearlString(@"Migrating %d sites...", [user.elements count])];
for (MPElementEntity *element in user.elements) {
if (element.type & MPElementTypeClassStored && ![element contentUsingKey:tryKey]) {
id content = nil;
if (recoverKey)
content = [element contentUsingKey:recoverKey];
while (!content) {
__block NSString *masterPassword = nil;
dispatch_group_t recoverPasswordGroup = dispatch_group_create();
dispatch_group_enter(recoverPasswordGroup);
[PearlAlert showAlertWithTitle:@"Enter Old Master Password"
message:PearlString(@"Your old master password is required to migrate the stored password for %@", element.name)
viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil
tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try {
if (buttonIndex_ == [alert_ cancelButtonIndex])
// Don't Migrate
return;
masterPassword = [alert_ textFieldAtIndex:0].text;
}
@finally {
dispatch_group_leave(recoverPasswordGroup);
}
} cancelTitle:@"Don't Migrate" otherTitles:@"Migrate", nil];
dispatch_group_wait(recoverPasswordGroup, DISPATCH_TIME_FOREVER);
if (!masterPassword)
// Don't Migrate
break;
recoverKey = [element.algorithm keyForPassword:masterPassword ofUserNamed:user.name];
content = [element contentUsingKey:recoverKey];
}
if (!content)
// Don't Migrate
break;
[element.managedObjectContext performBlockAndWait:^{
[element setContent:content usingKey:tryKey];
}];
}
}
[[MPAppDelegate_Shared get] saveContext];
[activityAlert dismissAlert];
}
}
@ -152,9 +210,11 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
}
user.lastUsed = [NSDate date];
self.activeUser = user;
self.activeUser.requiresExplicitMigration = NO;
[user.managedObjectContext performBlockAndWait:^{
user.lastUsed = [NSDate date];
self.activeUser = user;
self.activeUser.requiresExplicitMigration = NO;
}];
[[MPAppDelegate_Shared get] saveContext];
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationSignedIn object:self];

View File

@ -27,6 +27,9 @@
@property (assign) BOOL requiresExplicitMigration;
@property (readonly) id<MPAlgorithm> algorithm;
- (id)contentUsingKey:(MPKey *)key;
- (void)setContent:(id)content usingKey:(MPKey *)key;
- (NSUInteger)use;
- (NSString *)exportContent;
- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)contentProtectionKey usingKey:(MPKey *)key2;

View File

@ -84,7 +84,32 @@
- (id)content {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Content implementation missing." userInfo:nil];
MPKey *key = [MPAppDelegate get].key;
if (!key)
return nil;
assert([key.keyID isEqualToData:self.user.keyID]);
return [self contentUsingKey:key];
}
- (void)setContent:(id)content {
MPKey *key = [MPAppDelegate get].key;
if (!key)
return;
assert([key.keyID isEqualToData:self.user.keyID]);
[self setContent:content usingKey:key];
}
- (id)contentUsingKey:(MPKey *)key {
Throw(@"Content retrieval implementation missing for: %@", [self class]);
}
- (void)setContent:(id)content usingKey:(MPKey *)key {
Throw(@"Content assignment implementation missing for: %@", [self class]);
}
- (NSString *)exportContent {
@ -139,11 +164,7 @@
self.counter_ = @(aCounter);
}
- (id)content {
MPKey *key = [MPAppDelegate get].key;
if (!key)
return nil;
- (id)contentUsingKey:(MPKey *)key {
if (!(self.type & MPElementTypeClassGenerated)) {
err(@"Corrupt element: %@, type: %d is not in MPElementTypeClassGenerated", self.name, self.type);
@ -156,6 +177,7 @@
return [self.algorithm generateContentForElement:self usingKey:key];
}
@end
@implementation MPElementStoredEntity (MP)
@ -168,28 +190,9 @@
matches:nil];
}
- (id)content {
MPKey *key = [MPAppDelegate get].key;
if (!key)
return nil;
return [self contentUsingKey:key];
}
- (void)setContent:(id)content {
MPKey *key = [MPAppDelegate get].key;
if (!key)
return;
[self setContent:content usingKey:key];
}
- (id)contentUsingKey:(MPKey *)key {
assert(self.type & MPElementTypeClassStored);
assert([key.keyID isEqualToData:self.user.keyID]);
NSData *encryptedContent;
if (self.type & MPElementFeatureDevicePrivate)
@ -201,6 +204,9 @@
if ([encryptedContent length])
decryptedContent = [self decryptContent:encryptedContent usingKey:key];
if (!decryptedContent)
return nil;
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
}

View File

@ -241,15 +241,7 @@
if (!importedSitesData)
return;
PearlAlert *activityAlert = [PearlAlert showAlertWithTitle:@"Importing" message:@"\n\n"
viewStyle:UIAlertViewStyleDefault initAlert:
^(UIAlertView *alert, UITextField *firstField) {
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityIndicator.center = CGPointMake(140, 90);
[activityIndicator startAnimating];
[alert addSubview:activityIndicator];
}
tappedButtonBlock:nil cancelTitle:nil otherTitles:nil];
PearlAlert *activityAlert = [PearlAlert showActivityWithTitle:@"Importing"];
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {

View File

@ -96,9 +96,7 @@
viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger vcIndex = [self.pageVCs indexOfObject:viewController];
UIPageViewController *vc = [self.pageVCs objectAtIndex:(vcIndex + 1) % self.pageVCs.count];
return vc;
return [self.pageVCs objectAtIndex:(vcIndex + 1) % self.pageVCs.count];
}
- (void)viewDidUnload {

View File

@ -1167,11 +1167,11 @@ Pink fluffy door frame.</string>
<placeholder placeholderIdentifier="IBFirstResponder" id="hkm-U7-Dm7" userLabel="First Responder" sceneMemberID="firstResponder"/>
<viewController storyboardIdentifier="MPAppViewController_0" id="yIx-9U-bOF" customClass="MPAppViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Yea-s2-C8N">
<rect key="frame" x="0.0" y="0.0" width="305" height="399"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<rect key="frame" x="0.0" y="0.0" width="305" height="400"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Wrz-tq-o1S">
<rect key="frame" x="0.0" y="0.0" width="305" height="399"/>
<rect key="frame" x="0.0" y="0.0" width="305" height="400"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<state key="normal" image="page-gorillas.png">
@ -1186,7 +1186,7 @@ Pink fluffy door frame.</string>
</connections>
</button>
<view contentMode="scaleToFill" id="JN1-cA-6yZ">
<rect key="frame" x="0.0" y="330" width="305" height="60"/>
<rect key="frame" x="0.0" y="331" width="305" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Gorillas At Large On Metropolis Rooftops" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="Ypg-Yc-UK3">
@ -1632,7 +1632,7 @@ You could use the word wall for inspiration in finding a memorable master passw
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="page-gorillas.png" id="QQT-37-azo">
<rect key="frame" x="0.0" y="38" width="305" height="399"/>
<rect key="frame" x="0.0" y="38" width="305" height="400"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="drq-47-KK9">
@ -2021,11 +2021,11 @@ You could use the word wall for inspiration in finding a memorable master passw
<placeholder placeholderIdentifier="IBFirstResponder" id="LHv-Mk-8Kp" userLabel="First Responder" sceneMemberID="firstResponder"/>
<viewController storyboardIdentifier="MPAppViewController_1" id="vOg-Xq-hKm" customClass="MPAppViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="jzQ-Oa-Gdj">
<rect key="frame" x="0.0" y="0.0" width="305" height="399"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<rect key="frame" x="0.0" y="0.0" width="305" height="400"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="x6i-3e-0Rb">
<rect key="frame" x="0.0" y="0.0" width="305" height="399"/>
<rect key="frame" x="0.0" y="0.0" width="305" height="400"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<state key="normal" image="page-deblock.png">
@ -2040,7 +2040,7 @@ You could use the word wall for inspiration in finding a memorable master passw
</connections>
</button>
<view contentMode="scaleToFill" id="AC5-4y-ftd">
<rect key="frame" x="0.0" y="330" width="305" height="60"/>
<rect key="frame" x="0.0" y="331" width="305" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Block Destruction Puzzle" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="11H-1K-20G">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 KiB

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 597 KiB

After

Width:  |  Height:  |  Size: 598 KiB