From e2bf8cefa25e46228801f8f57af4ebe12b94b734 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Sat, 1 Sep 2012 22:14:57 +0200 Subject: [PATCH] Migration of saved passwords. [ADDED] Migrate saved passwords when master password changes. [IMPROVED] UI improvements to apps. --- External/Pearl | 2 +- MasterPassword/MPAppDelegate_Key.m | 72 ++++++++++++++++-- MasterPassword/MPEntities.h | 3 + MasterPassword/MPEntities.m | 56 ++++++++------ MasterPassword/iOS/MPAppDelegate.m | 10 +-- MasterPassword/iOS/MPAppsViewController.m | 4 +- .../iOS/MainStoryboard_iPhone.storyboard | 18 ++--- Resources/page-deblock.png | Bin 125080 -> 124941 bytes Resources/page-deblock@2x.png | Bin 419572 -> 420827 bytes Resources/page-gorillas.png | Bin 165989 -> 165766 bytes Resources/page-gorillas@2x.png | Bin 611694 -> 612878 bytes 11 files changed, 112 insertions(+), 53 deletions(-) diff --git a/External/Pearl b/External/Pearl index ff1e29fc..552cf5ff 160000 --- a/External/Pearl +++ b/External/Pearl @@ -1 +1 @@ -Subproject commit ff1e29fc3a4c2ee7b08eb686d06f29a11466bbe5 +Subproject commit 552cf5ffc9445362f8f91357f4f7a034b2c390a3 diff --git a/MasterPassword/MPAppDelegate_Key.m b/MasterPassword/MPAppDelegate_Key.m index 445c2c9c..7af9c83e 100644 --- a/MasterPassword/MPAppDelegate_Key.m +++ b/MasterPassword/MPAppDelegate_Key.m @@ -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]; diff --git a/MasterPassword/MPEntities.h b/MasterPassword/MPEntities.h index 787582d8..b0829fb7 100644 --- a/MasterPassword/MPEntities.h +++ b/MasterPassword/MPEntities.h @@ -27,6 +27,9 @@ @property (assign) BOOL requiresExplicitMigration; @property (readonly) id 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; diff --git a/MasterPassword/MPEntities.m b/MasterPassword/MPEntities.m index b5a8066b..61db0acb 100644 --- a/MasterPassword/MPEntities.m +++ b/MasterPassword/MPEntities.m @@ -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]; } diff --git a/MasterPassword/iOS/MPAppDelegate.m b/MasterPassword/iOS/MPAppDelegate.m index b7364a85..0524debb 100644 --- a/MasterPassword/iOS/MPAppDelegate.m +++ b/MasterPassword/iOS/MPAppDelegate.m @@ -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) { diff --git a/MasterPassword/iOS/MPAppsViewController.m b/MasterPassword/iOS/MPAppsViewController.m index e9850ecd..68a1a06f 100644 --- a/MasterPassword/iOS/MPAppsViewController.m +++ b/MasterPassword/iOS/MPAppsViewController.m @@ -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 { diff --git a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard index 57c611b6..5ee5cdd2 100644 --- a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard +++ b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard @@ -1167,11 +1167,11 @@ Pink fluffy door frame. - - + + - +