diff --git a/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword-iOS.xcodeproj/project.pbxproj index d1dc7470..b6967f90 100644 --- a/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */; }; DA5BFA4F147E415C00F98B1E /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4E147E415C00F98B1E /* CoreData.framework */; }; + DA600C2515054F3A008E9AB6 /* MPAppDelegate_Key.m in Sources */ = {isa = PBXBuildFile; fileRef = DA600C2315054F3A008E9AB6 /* MPAppDelegate_Key.m */; }; + DA600C2815056428008E9AB6 /* MPConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA600C2715056427008E9AB6 /* MPConfig.m */; }; DA672D2F14F92C6B004A189C /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DA672D2E14F92C6B004A189C /* libz.dylib */; }; DA672D3014F9413D004A189C /* libPearl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC77CAD148291A600BCF976 /* libPearl.a */; }; DA95D59D14DF063C008D1B94 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; @@ -61,7 +63,7 @@ DAB8D46015036BCF00CED3BC /* MainStoryboard_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D44215036BCF00CED3BC /* MainStoryboard_iPhone.storyboard */; }; DAB8D46215036BCF00CED3BC /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D44515036BCF00CED3BC /* MasterPassword.entitlements */; }; DAB8D46315036BCF00CED3BC /* MPAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44715036BCF00CED3BC /* MPAppDelegate.m */; }; - DAB8D46415036BCF00CED3BC /* MPConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44915036BCF00CED3BC /* MPConfig.m */; }; + DAB8D46415036BCF00CED3BC /* MPiOSConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44915036BCF00CED3BC /* MPiOSConfig.m */; }; DAB8D46515036BCF00CED3BC /* MPGuideViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44B15036BCF00CED3BC /* MPGuideViewController.m */; }; DAB8D46615036BCF00CED3BC /* MPMainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44D15036BCF00CED3BC /* MPMainViewController.m */; }; DAB8D46715036BCF00CED3BC /* MPSearchDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44F15036BCF00CED3BC /* MPSearchDelegate.m */; }; @@ -841,6 +843,10 @@ DA5BFA4A147E415C00F98B1E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; DA5BFA4E147E415C00F98B1E /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + DA600C2315054F3A008E9AB6 /* MPAppDelegate_Key.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_Key.m; sourceTree = ""; }; + DA600C2415054F3A008E9AB6 /* MPAppDelegate_Key.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_Key.h; sourceTree = ""; }; + DA600C2615056427008E9AB6 /* MPConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MPConfig.h; path = MasterPassword/MPConfig.h; sourceTree = SOURCE_ROOT; }; + DA600C2715056427008E9AB6 /* MPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPConfig.m; path = MasterPassword/MPConfig.m; sourceTree = SOURCE_ROOT; }; DA672D2E14F92C6B004A189C /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; DA95D59C14DF063C008D1B94 /* libInAppSettingsKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libInAppSettingsKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; DA95D5A814DF0691008D1B94 /* IASKAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKAppSettingsViewController.h; sourceTree = ""; }; @@ -891,8 +897,8 @@ DAB8D44515036BCF00CED3BC /* MasterPassword.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = MasterPassword.entitlements; sourceTree = ""; }; DAB8D44615036BCF00CED3BC /* MPAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate.h; sourceTree = ""; }; DAB8D44715036BCF00CED3BC /* MPAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate.m; sourceTree = ""; }; - DAB8D44815036BCF00CED3BC /* MPConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPConfig.h; sourceTree = ""; }; - DAB8D44915036BCF00CED3BC /* MPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPConfig.m; sourceTree = ""; }; + DAB8D44815036BCF00CED3BC /* MPiOSConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPiOSConfig.h; sourceTree = ""; }; + DAB8D44915036BCF00CED3BC /* MPiOSConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPiOSConfig.m; sourceTree = ""; }; DAB8D44A15036BCF00CED3BC /* MPGuideViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGuideViewController.h; sourceTree = ""; }; DAB8D44B15036BCF00CED3BC /* MPGuideViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGuideViewController.m; sourceTree = ""; }; DAB8D44C15036BCF00CED3BC /* MPMainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMainViewController.h; sourceTree = ""; }; @@ -1754,6 +1760,10 @@ children = ( DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */, DAB8D43E15036BCF00CED3BC /* iOS */, + DA600C2415054F3A008E9AB6 /* MPAppDelegate_Key.h */, + DA600C2315054F3A008E9AB6 /* MPAppDelegate_Key.m */, + DA600C2615056427008E9AB6 /* MPConfig.h */, + DA600C2715056427008E9AB6 /* MPConfig.m */, DAB8D45515036BCF00CED3BC /* MPElementStoredEntity.m */, DAB8D45615036BCF00CED3BC /* MPTypes.m */, DAB8D45715036BCF00CED3BC /* MPElementEntity.h */, @@ -1862,8 +1872,8 @@ DAB8D44215036BCF00CED3BC /* MainStoryboard_iPhone.storyboard */, DAB8D44615036BCF00CED3BC /* MPAppDelegate.h */, DAB8D44715036BCF00CED3BC /* MPAppDelegate.m */, - DAB8D44815036BCF00CED3BC /* MPConfig.h */, - DAB8D44915036BCF00CED3BC /* MPConfig.m */, + DAB8D44815036BCF00CED3BC /* MPiOSConfig.h */, + DAB8D44915036BCF00CED3BC /* MPiOSConfig.m */, DAB8D44A15036BCF00CED3BC /* MPGuideViewController.h */, DAB8D44B15036BCF00CED3BC /* MPGuideViewController.m */, DAB8D44C15036BCF00CED3BC /* MPMainViewController.h */, @@ -3675,7 +3685,7 @@ DAB8D45D15036BCF00CED3BC /* MasterPassword.xcdatamodeld in Sources */, DAB8D45F15036BCF00CED3BC /* main.m in Sources */, DAB8D46315036BCF00CED3BC /* MPAppDelegate.m in Sources */, - DAB8D46415036BCF00CED3BC /* MPConfig.m in Sources */, + DAB8D46415036BCF00CED3BC /* MPiOSConfig.m in Sources */, DAB8D46515036BCF00CED3BC /* MPGuideViewController.m in Sources */, DAB8D46615036BCF00CED3BC /* MPMainViewController.m in Sources */, DAB8D46715036BCF00CED3BC /* MPSearchDelegate.m in Sources */, @@ -3685,6 +3695,8 @@ DAB8D46C15036BCF00CED3BC /* MPTypes.m in Sources */, DAB8D46D15036BCF00CED3BC /* MPElementEntity.m in Sources */, DAB8D46E15036BCF00CED3BC /* MPElementGeneratedEntity.m in Sources */, + DA600C2515054F3A008E9AB6 /* MPAppDelegate_Key.m in Sources */, + DA600C2815056428008E9AB6 /* MPConfig.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MasterPassword/MPAppDelegate_Key.h b/MasterPassword/MPAppDelegate_Key.h new file mode 100644 index 00000000..c1689629 --- /dev/null +++ b/MasterPassword/MPAppDelegate_Key.h @@ -0,0 +1,33 @@ +// +// MPAppDelegate_Key.h +// MasterPassword +// +// Created by Maarten Billemont on 24/11/11. +// Copyright (c) 2011 Lyndir. All rights reserved. +// + +#import +#import "MPAppDelegate.h" + +@interface MPAppDelegate () + +@property (strong, nonatomic) NSData *key; +@property (strong, nonatomic) NSData *keyHash; +@property (strong, nonatomic) NSString *keyHashHex; + +@end + +@interface MPAppDelegate (Key) + ++ (MPAppDelegate *)get; + +- (void)loadStoredKey; +- (void)signOut; + +- (BOOL)tryMasterPassword:(NSString *)tryPassword; +- (void)updateKey:(NSData *)key; +- (void)forgetKey; + +- (NSData *)keyWithLength:(NSUInteger)keyLength; + +@end diff --git a/MasterPassword/MPAppDelegate_Key.m b/MasterPassword/MPAppDelegate_Key.m new file mode 100644 index 00000000..79505960 --- /dev/null +++ b/MasterPassword/MPAppDelegate_Key.m @@ -0,0 +1,148 @@ +// +// MPAppDelegate.m +// MasterPassword +// +// Created by Maarten Billemont on 24/11/11. +// Copyright (c) 2011 Lyndir. All rights reserved. +// + +#import "MPAppDelegate_Key.h" + +#import "MPMainViewController.h" +#import "IASKSettingsReader.h" + +@implementation MPAppDelegate (Key) + +static NSDictionary *keyQuery() { + + static NSDictionary *MPKeyQuery = nil; + if (!MPKeyQuery) + MPKeyQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword + attributes:[NSDictionary dictionaryWithObject:@"Master Password Key" + forKey:(__bridge id)kSecAttrService] + matches:nil]; + + return MPKeyQuery; +} + +static NSDictionary *keyHashQuery() { + + static NSDictionary *MPKeyHashQuery = nil; + if (!MPKeyHashQuery) + MPKeyHashQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword + attributes:[NSDictionary dictionaryWithObject:@"Master Password Key Hash" + forKey:(__bridge id)kSecAttrService] + matches:nil]; + + return MPKeyHashQuery; +} + +- (void)forgetKey { + + dbg(@"Deleting master key and hash from key chain."); + [PearlKeyChain deleteItemForQuery:keyQuery()]; + [PearlKeyChain deleteItemForQuery:keyHashQuery()]; + + [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self]; +#ifndef PRODUCTION + [TestFlight passCheckpoint:MPTestFlightCheckpointMPForgotten]; +#endif +} + +- (void)signOut { + + [self updateKey:nil]; +} + +- (void)loadStoredKey { + + if ([[MPiOSConfig get].storeKey boolValue]) { + // Key is stored in keychain. Load it. + dbg(@"Loading key from key chain."); + [self updateKey:[PearlKeyChain dataOfItemForQuery:keyQuery()]]; + dbg(@" -> Key %@.", self.key? @"found": @"NOT found"); + } else { + // Key should not be stored in keychain. Delete it. + dbg(@"Deleting key from key chain."); + [PearlKeyChain deleteItemForQuery:keyQuery()]; +#ifndef PRODUCTION + [TestFlight passCheckpoint:MPTestFlightCheckpointMPUnstored]; +#endif + } +} + ++ (MPAppDelegate *)get { + + return (MPAppDelegate *)[super get]; +} + +- (BOOL)tryMasterPassword:(NSString *)tryPassword { + + NSData *keyHash = [PearlKeyChain dataOfItemForQuery:keyHashQuery()]; + dbg(@"Key hash %@.", keyHash? @"known": @"NOT known"); + + if (![tryPassword length]) + return NO; + + NSData *tryKey = keyForPassword(tryPassword); + NSData *tryKeyHash = keyHashForKey(tryKey); + if (keyHash) + // A key hash is known -> a key is set. + // Make sure the user's entered key matches it. + if (![keyHash isEqual:tryKeyHash]) { + dbg(@"Key phrase hash mismatch. Expected: %@, answer: %@.", keyHash, tryKeyHash); + +#ifndef PRODUCTION + [TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch]; +#endif + return NO; + } + +#ifndef PRODUCTION + [TestFlight passCheckpoint:MPTestFlightCheckpointMPAsked]; +#endif + + [self updateKey:tryKey]; + return YES; +} + +- (void)updateKey:(NSData *)key { + + self.key = key; + + if (key) + [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeySet object:self]; + else + [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyUnset object:self]; + + if (key) { + self.keyHash = keyHashForKey(key); + self.keyHashHex = [self.keyHash encodeHex]; + + dbg(@"Updating key hash to: %@.", self.keyHashHex); + [PearlKeyChain addOrUpdateItemForQuery:keyHashQuery() + withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: + self.keyHash, (__bridge id)kSecValueData, + kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible, + nil]]; + if ([[MPiOSConfig get].storeKey boolValue]) { + dbg(@"Storing key in key chain."); + [PearlKeyChain addOrUpdateItemForQuery:keyQuery() + withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: + key, (__bridge id)kSecValueData, + kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible, + nil]]; + } + +#ifndef PRODUCTION + [TestFlight passCheckpoint:[NSString stringWithFormat:MPTestFlightCheckpointSetKeyphraseLength, key.length]]; +#endif + } +} + +- (NSData *)keyWithLength:(NSUInteger)keyLength { + + return [self.key subdataWithRange:NSMakeRange(0, MIN(keyLength, self.key.length))]; +} + +@end diff --git a/MasterPassword/iOS/MPConfig.h b/MasterPassword/MPConfig.h similarity index 53% rename from MasterPassword/iOS/MPConfig.h rename to MasterPassword/MPConfig.h index 513a43fa..dcd3c99c 100644 --- a/MasterPassword/iOS/MPConfig.h +++ b/MasterPassword/MPConfig.h @@ -9,10 +9,8 @@ @interface MPConfig : PearlConfig @property (nonatomic, retain) NSNumber *dataStoreError; -@property (nonatomic, retain) NSNumber *storeKeyPhrase; -@property (nonatomic, retain) NSNumber *rememberKeyPhrase; -@property (nonatomic, retain) NSNumber *helpHidden; -@property (nonatomic, retain) NSNumber *showQuickStart; +@property (nonatomic, retain) NSNumber *storeKey; +@property (nonatomic, retain) NSNumber *rememberKey; + (MPConfig *)get; diff --git a/MasterPassword/iOS/MPConfig.m b/MasterPassword/MPConfig.m similarity index 67% rename from MasterPassword/iOS/MPConfig.m rename to MasterPassword/MPConfig.m index b914bf16..4f675e71 100644 --- a/MasterPassword/iOS/MPConfig.m +++ b/MasterPassword/MPConfig.m @@ -9,7 +9,7 @@ #import "MPConfig.h" @implementation MPConfig -@dynamic dataStoreError, storeKeyPhrase, rememberKeyPhrase, helpHidden, showQuickStart; +@dynamic dataStoreError, storeKey, rememberKey; - (id)init { @@ -18,10 +18,8 @@ [self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(dataStoreError)), - [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(storeKeyPhrase)), - [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(rememberKeyPhrase)), - [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)), - [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(showQuickStart)), + [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(storeKey)), + [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(rememberKey)), nil]]; return self; diff --git a/MasterPassword/MPElementGeneratedEntity.m b/MasterPassword/MPElementGeneratedEntity.m index baac8c25..b2827d42 100644 --- a/MasterPassword/MPElementGeneratedEntity.m +++ b/MasterPassword/MPElementGeneratedEntity.m @@ -7,7 +7,7 @@ // #import "MPElementGeneratedEntity.h" -#import "MPAppDelegate.h" +#import "MPAppDelegate_Key.h" @implementation MPElementGeneratedEntity @@ -22,7 +22,7 @@ return nil; if (self.type & MPElementTypeClassCalculated) - return MPCalculateContent(self.type, self.name, [MPAppDelegate get].keyPhrase, self.counter); + return MPCalculateContent(self.type, self.name, [MPAppDelegate get].key, self.counter); @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"Unsupported type: %d", self.type] userInfo:nil]; diff --git a/MasterPassword/MPElementStoredEntity.m b/MasterPassword/MPElementStoredEntity.m index befa1476..81b97cfd 100644 --- a/MasterPassword/MPElementStoredEntity.m +++ b/MasterPassword/MPElementStoredEntity.m @@ -7,7 +7,7 @@ // #import "MPElementStoredEntity.h" -#import "MPAppDelegate.h" +#import "MPAppDelegate_Key.h" @interface MPElementStoredEntity () @@ -39,14 +39,14 @@ else encryptedContent = self.contentObject; - NSData *decryptedContent = [encryptedContent decryptWithSymmetricKey:[[MPAppDelegate get] keyPhraseWithLength:PearlCryptKeySize] + NSData *decryptedContent = [encryptedContent decryptWithSymmetricKey:[[MPAppDelegate get] keyWithLength:PearlCryptKeySize] usePadding:YES]; return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding]; } - (void)setContent:(id)content { - NSData *encryptedContent = [[content description] encryptWithSymmetricKey:[[MPAppDelegate get] keyPhraseWithLength:PearlCryptKeySize] + NSData *encryptedContent = [[content description] encryptWithSymmetricKey:[[MPAppDelegate get] keyWithLength:PearlCryptKeySize] usePadding:YES]; if (self.type == MPElementTypeStoredDevicePrivate) { diff --git a/MasterPassword/MPTypes.h b/MasterPassword/MPTypes.h index a8ecb315..b4ad1bf5 100644 --- a/MasterPassword/MPTypes.h +++ b/MasterPassword/MPTypes.h @@ -63,10 +63,10 @@ typedef enum { #define MPNotificationKeyUnset @"MPNotificationKeyUnset" #define MPNotificationKeyForgotten @"MPNotificationKeyForgotten" -NSData *keyPhraseForPassword(NSString *password); -NSData *keyPhraseHashForPassword(NSString *password); -NSData *keyPhraseHashForKeyPhrase(NSData *keyPhrase); +NSData *keyForPassword(NSString *password); +NSData *keyHashForPassword(NSString *password); +NSData *keyHashForKey(NSData *key); NSString *NSStringFromMPElementType(MPElementType type); NSString *ClassNameFromMPElementType(MPElementType type); Class ClassFromMPElementType(MPElementType type); -NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *keyPhrase, uint16_t counter); +NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *key, uint16_t counter); diff --git a/MasterPassword/MPTypes.m b/MasterPassword/MPTypes.m index 8f6c162e..d8b8cb27 100644 --- a/MasterPassword/MPTypes.m +++ b/MasterPassword/MPTypes.m @@ -17,18 +17,18 @@ #define MP_dkLen 64 #define MP_hash PearlDigestSHA256 -NSData *keyPhraseForPassword(NSString *password) { +NSData *keyForPassword(NSString *password) { return [PearlSCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding] usingSalt:MP_salt N:MP_N r:MP_r p:MP_p]; } -NSData *keyPhraseHashForPassword(NSString *password) { +NSData *keyHashForPassword(NSString *password) { - return keyPhraseHashForKeyPhrase(keyPhraseForPassword(password)); + return keyHashForKey(keyForPassword(password)); } -NSData *keyPhraseHashForKeyPhrase(NSData *keyPhrase) { +NSData *keyHashForKey(NSData *key) { - return [keyPhrase hashWith:MP_hash]; + return [key hashWith:MP_hash]; } NSString *NSStringFromMPElementType(MPElementType type) { @@ -100,7 +100,7 @@ NSString *ClassNameFromMPElementType(MPElementType type) { } static NSDictionary *MPTypes_ciphers = nil; -NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *keyPhrase, uint16_t counter) { +NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *key, uint16_t counter) { assert(type & MPElementTypeClassCalculated); @@ -108,12 +108,12 @@ NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *keyPhra MPTypes_ciphers = [NSDictionary dictionaryWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"ciphers" withExtension:@"plist"]]; - // Determine the hash whose bytes will be used for calculating a password: md4(name-keyPhrase) - assert(name && keyPhrase); + // Determine the hash whose bytes will be used for calculating a password: md4(name-key) + assert(name && key); uint16_t ncounter = htons(counter); NSData *keyHash = [[NSData dataByConcatenatingWithDelimitor:'-' datas: [name dataUsingEncoding:NSUTF8StringEncoding], - keyPhrase, + key, [NSData dataWithBytes:&ncounter length:sizeof(ncounter)], nil] hashWith:PearlDigestSHA1]; const char *keyBytes = keyHash.bytes; diff --git a/MasterPassword/Mac/MPAppDelegate.h b/MasterPassword/Mac/MPAppDelegate.h index aa5840b3..0efbe403 100644 --- a/MasterPassword/Mac/MPAppDelegate.h +++ b/MasterPassword/Mac/MPAppDelegate.h @@ -7,6 +7,7 @@ // #import +#import "MPPasswordWindowController.h" @interface MPAppDelegate : NSObject @@ -16,9 +17,13 @@ @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext; +@property (readonly, strong, nonatomic) MPPasswordWindowController *passwordWindow; @property (readonly, strong, nonatomic) NSData *keyPhrase; +@property (readonly, strong, nonatomic) NSString *keyPhraseHashHex; + (MPAppDelegate *)get; ++ (NSManagedObjectModel *)managedObjectModel; ++ (NSManagedObjectContext *)managedObjectContext; - (IBAction)saveAction:(id)sender; - (NSData *)keyPhraseWithLength:(NSUInteger)keyLength; diff --git a/MasterPassword/Mac/MPAppDelegate.m b/MasterPassword/Mac/MPAppDelegate.m index c3347625..e8ca5e1e 100644 --- a/MasterPassword/Mac/MPAppDelegate.m +++ b/MasterPassword/Mac/MPAppDelegate.m @@ -8,12 +8,18 @@ #import "MPAppDelegate.h" -@implementation MPAppDelegate +@interface MPAppDelegate () +@property (readwrite, strong, nonatomic) MPPasswordWindowController *passwordWindow; + +@end + +@implementation MPAppDelegate @synthesize window = _window; @synthesize persistentStoreCoordinator = __persistentStoreCoordinator; @synthesize managedObjectModel = __managedObjectModel; @synthesize managedObjectContext = __managedObjectContext; +@synthesize passwordWindow; @synthesize keyPhrase; + (MPAppDelegate *)get { @@ -21,9 +27,25 @@ return (MPAppDelegate *)[NSApplication sharedApplication].delegate; } -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification -{ - // Insert code here to initialize your application ++ (NSManagedObjectContext *)managedObjectContext { + + return [[self get] managedObjectContext]; +} + ++ (NSManagedObjectModel *)managedObjectModel { + + return [[self get] managedObjectModel]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + +} + +- (void)applicationDidBecomeActive:(NSNotification *)notification { + + if (!self.passwordWindow) + self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"]; + [self.passwordWindow showWindow:self]; } - (NSURL *)applicationFilesDirectory { diff --git a/MasterPassword/Mac/MPPasswordWindowController.h b/MasterPassword/Mac/MPPasswordWindowController.h index 822e9ec4..894304f6 100644 --- a/MasterPassword/Mac/MPPasswordWindowController.h +++ b/MasterPassword/Mac/MPPasswordWindowController.h @@ -8,7 +8,7 @@ #import -@interface MPPasswordWindowController : NSWindowController +@interface MPPasswordWindowController : NSWindowController @property (weak) IBOutlet NSTextField *siteField; @property (weak) IBOutlet NSTextField *contentField; diff --git a/MasterPassword/Mac/MPPasswordWindowController.m b/MasterPassword/Mac/MPPasswordWindowController.m index 9273e8ef..9dc27bb4 100644 --- a/MasterPassword/Mac/MPPasswordWindowController.m +++ b/MasterPassword/Mac/MPPasswordWindowController.m @@ -7,20 +7,58 @@ // #import "MPPasswordWindowController.h" +#import "MPAppDelegate.h" @interface MPPasswordWindowController () +@property (nonatomic, assign) BOOL completingSiteName; + @end @implementation MPPasswordWindowController +@synthesize completingSiteName; @synthesize siteField; @synthesize contentField; - (void)windowDidLoad { - [super windowDidLoad]; - [self.contentField setStringValue:@""]; + [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window queue:nil + usingBlock:^(NSNotification *note) { + [[NSApplication sharedApplication] hide:self]; + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:NSControlTextDidChangeNotification object:self.siteField queue:nil + usingBlock:^(NSNotification *note) { + if (!self.completingSiteName) { + self.completingSiteName = YES; + [[[note userInfo] objectForKey:@"NSFieldEditor"] complete:nil]; + self.completingSiteName = NO; + } + }]; + + [super windowDidLoad]; +} + +- (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index { + + NSString *query = [[control stringValue] substringWithRange:charRange]; + NSFetchRequest *fetchRequest = [MPAppDelegate.managedObjectModel + fetchRequestFromTemplateWithName:@"MPElements" + substitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys: + query, @"query", + [MPAppDelegate get].keyPhraseHashHex, @"mpHashHex", + nil]]; + + return [NSArray arrayWithObjects:@"cow", @"milk", @"hippopotamus", nil]; +} + +- (void)controlTextDidEndEditing:(NSNotification *)obj { + + if (obj.object == self.siteField) { +// NSString *siteName = [self.siteField stringValue]; + +// [self.contentField setStringValue:]; + } } @end diff --git a/MasterPassword/Mac/MPPasswordWindowController.xib b/MasterPassword/Mac/MPPasswordWindowController.xib index d9581527..ecab011e 100644 --- a/MasterPassword/Mac/MPPasswordWindowController.xib +++ b/MasterPassword/Mac/MPPasswordWindowController.xib @@ -36,7 +36,7 @@ NSApplication - 8223 + 8215 2 {{600, 530}, {480, 134}} 611845120 @@ -68,7 +68,7 @@ 13 1044 - Enter site name + Enter site name X _NS:9 YES @@ -98,7 +98,6 @@ 268 {{17, 20}, {446, 64}} - 2 _NS:9 {250, 750} @@ -177,6 +176,14 @@ 39 + + + delegate + + + + 43 + @@ -526,7 +533,7 @@ - 42 + 43 0 diff --git a/MasterPassword/iOS/MPAppDelegate.h b/MasterPassword/iOS/MPAppDelegate.h index 993049ae..cfd9de43 100644 --- a/MasterPassword/iOS/MPAppDelegate.h +++ b/MasterPassword/iOS/MPAppDelegate.h @@ -13,11 +13,7 @@ @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext; @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; -@property (readonly, strong, nonatomic) NSData *keyPhrase; -@property (readonly, strong, nonatomic) NSData *keyPhraseHash; -@property (readonly, strong, nonatomic) NSString *keyPhraseHashHex; -+ (MPAppDelegate *)get; + (NSManagedObjectModel *)managedObjectModel; + (NSManagedObjectContext *)managedObjectContext; @@ -25,10 +21,6 @@ - (NSURL *)applicationDocumentsDirectory; - (void)showGuide; -- (void)loadKeyPhrase:(BOOL)animated; -- (void)signOut; -- (void)forgetKeyPhrase; -- (NSData *)keyPhraseWithLength:(NSUInteger)keyLength; -- (BOOL)tryMasterPassword:(NSString *)tryPassword; +- (void)loadKey:(BOOL)animated; @end diff --git a/MasterPassword/iOS/MPAppDelegate.m b/MasterPassword/iOS/MPAppDelegate.m index e2162884..eba5c7ce 100644 --- a/MasterPassword/iOS/MPAppDelegate.m +++ b/MasterPassword/iOS/MPAppDelegate.m @@ -7,21 +7,14 @@ // #import "MPAppDelegate.h" +#import "MPAppDelegate_Key.h" #import "MPMainViewController.h" #import "IASKSettingsReader.h" @interface MPAppDelegate () -@property (strong, nonatomic) NSData *keyPhrase; -@property (strong, nonatomic) NSData *keyPhraseHash; -@property (strong, nonatomic) NSString *keyPhraseHashHex; - -+ (NSDictionary *)keyPhraseQuery; -+ (NSDictionary *)keyPhraseHashQuery; - -- (void)loadStoredKeyPhrase; -- (void)askKeyPhrase:(BOOL)animated; +- (void)askKey:(BOOL)animated; @end @@ -31,13 +24,13 @@ @synthesize managedObjectContext = __managedObjectContext; @synthesize persistentStoreCoordinator = __persistentStoreCoordinator; -@synthesize keyPhrase = _keyPhrase; -@synthesize keyPhraseHash = _keyPhraseHash; -@synthesize keyPhraseHashHex = _keyPhraseHashHex; +@synthesize key; +@synthesize keyHash; +@synthesize keyHashHex; + (void)initialize { - [MPConfig get]; + [MPiOSConfig get]; #ifdef DEBUG [PearlLogger get].autoprintLevel = PearlLogLevelTrace; @@ -45,30 +38,6 @@ #endif } -+ (NSDictionary *)keyPhraseQuery { - - static NSDictionary *MPKeyPhraseQuery = nil; - if (!MPKeyPhraseQuery) - MPKeyPhraseQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword - attributes:[NSDictionary dictionaryWithObject:@"MasterPassword" - forKey:(__bridge id)kSecAttrService] - matches:nil]; - - return MPKeyPhraseQuery; -} - -+ (NSDictionary *)keyPhraseHashQuery { - - static NSDictionary *MPKeyPhraseHashQuery = nil; - if (!MPKeyPhraseHashQuery) - MPKeyPhraseHashQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword - attributes:[NSDictionary dictionaryWithObject:@"MasterPasswordHash" - forKey:(__bridge id)kSecAttrService] - matches:nil]; - - return MPKeyPhraseHashQuery; -} - - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { #ifndef PRODUCTION @@ -145,14 +114,14 @@ [[NSNotificationCenter defaultCenter] addObserverForName:kIASKAppSettingChanged object:nil queue:nil usingBlock:^(NSNotification *note) { - if ([NSStringFromSelector(@selector(storeKeyPhrase)) + if ([NSStringFromSelector(@selector(storeKey)) isEqualToString:[note.object description]]) { - self.keyPhrase = self.keyPhrase; - [self loadKeyPhrase:YES]; + [self updateKey:self.key]; + [self loadKey:YES]; } - if ([NSStringFromSelector(@selector(forgetKeyPhrase)) + if ([NSStringFromSelector(@selector(forgetKey)) isEqualToString:[note.object description]]) - [self loadKeyPhrase:YES]; + [self loadKey:YES]; }]; #ifndef PRODUCTION @@ -175,10 +144,10 @@ - (void)applicationDidBecomeActive:(UIApplication *)application { - if ([[MPConfig get].showQuickStart boolValue]) + if ([[MPiOSConfig get].showQuickStart boolValue]) [self showGuide]; else - [self loadKeyPhrase:NO]; + [self loadKey:NO]; #ifndef PRODUCTION [TestFlight passCheckpoint:MPTestFlightCheckpointActivated]; @@ -194,78 +163,21 @@ #endif } -- (void)loadKeyPhrase:(BOOL)animated { +- (void)loadKey:(BOOL)animated { - if (self.keyPhrase) + if (self.key) return; - [self loadStoredKeyPhrase]; - if (!self.keyPhrase) { - // Key phrase is not known. Ask user to set/specify it. - dbg(@"Key phrase not known. Will ask user."); - [self askKeyPhrase:animated]; + [self loadStoredKey]; + if (!self.key) { + // Key is not known. Ask user to set/specify it. + dbg(@"Key not known. Will ask user."); + [self askKey:animated]; return; } } -- (void)forgetKeyPhrase { - - dbg(@"Forgetting key phrase."); - [PearlAlert showAlertWithTitle:@"Changing Master Password" - message: - @"This will allow you to log in with a different master password.\n\n" - @"Note that you will only see the sites and passwords for the master password you log in with.\n" - @"If you log in with a different master password, your current sites will be unavailable.\n\n" - @"You can always change back to your current master password later.\n" - @"Your current sites and passwords will then become available again." - viewStyle:UIAlertViewStyleDefault - tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { - if (buttonIndex != [alert cancelButtonIndex]) { - // Key phrase reset. Delete it. - dbg(@"Deleting master key phrase and hash from key chain."); - [PearlKeyChain deleteItemForQuery:[MPAppDelegate keyPhraseQuery]]; - [PearlKeyChain deleteItemForQuery:[MPAppDelegate keyPhraseHashQuery]]; - - [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self]; -#ifndef PRODUCTION - [TestFlight passCheckpoint:MPTestFlightCheckpointMPForgotten]; -#endif - } - - [self loadKeyPhrase:YES]; - -#ifndef PRODUCTION - [TestFlight passCheckpoint:MPTestFlightCheckpointMPChanged]; -#endif - } - cancelTitle:[PearlStrings get].commonButtonAbort - otherTitles:[PearlStrings get].commonButtonContinue, nil]; -} - -- (void)signOut { - - self.keyPhrase = nil; - [self loadKeyPhrase:YES]; -} - -- (void)loadStoredKeyPhrase { - - if ([[MPConfig get].storeKeyPhrase boolValue]) { - // Key phrase is stored in keychain. Load it. - dbg(@"Loading master key phrase from key chain."); - self.keyPhrase = [PearlKeyChain dataOfItemForQuery:[MPAppDelegate keyPhraseQuery]]; - dbg(@" -> Master key phrase %@.", self.keyPhrase? @"found": @"NOT found"); - } else { - // Key phrase should not be stored in keychain. Delete it. - dbg(@"Deleting master key phrase from key chain."); - [PearlKeyChain deleteItemForQuery:[MPAppDelegate keyPhraseQuery]]; -#ifndef PRODUCTION - [TestFlight passCheckpoint:MPTestFlightCheckpointMPUnstored]; -#endif - } -} - -- (void)askKeyPhrase:(BOOL)animated { +- (void)askKey:(BOOL)animated { dispatch_async(dispatch_get_main_queue(), ^{ [self.navigationController presentViewController: @@ -278,8 +190,8 @@ [self saveContext]; - if (![[MPConfig get].rememberKeyPhrase boolValue]) - self.keyPhrase = nil; + if (![[MPiOSConfig get].rememberKey boolValue]) + [self updateKey:nil]; #ifndef PRODUCTION [TestFlight passCheckpoint:MPTestFlightCheckpointDeactivated]; @@ -302,12 +214,12 @@ + (NSManagedObjectContext *)managedObjectContext { - return [(MPAppDelegate *)[UIApplication sharedApplication].delegate managedObjectContext]; + return [[self get] managedObjectContext]; } + (NSManagedObjectModel *)managedObjectModel { - return [(MPAppDelegate *)[UIApplication sharedApplication].delegate managedObjectModel]; + return [[self get] managedObjectModel]; } - (void)saveContext { @@ -319,75 +231,6 @@ }]; } -- (BOOL)tryMasterPassword:(NSString *)tryPassword { - - NSData *keyPhraseHash = [PearlKeyChain 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]; - - dbg(@"Updating master key phrase hash to: %@.", self.keyPhraseHashHex); - [PearlKeyChain addOrUpdateItemForQuery:[MPAppDelegate keyPhraseHashQuery] - withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: - self.keyPhraseHash, (__bridge id)kSecValueData, - kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible, - nil]]; - if ([[MPConfig get].storeKeyPhrase boolValue]) { - dbg(@"Storing master key phrase in key chain."); - [PearlKeyChain addOrUpdateItemForQuery:[MPAppDelegate keyPhraseQuery] - withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: - keyPhrase, (__bridge id)kSecValueData, - kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible, - nil]]; - } - -#ifndef PRODUCTION - [TestFlight passCheckpoint:[NSString stringWithFormat:MPTestFlightCheckpointSetKeyphraseLength, _keyPhrase.length]]; -#endif - } -} - -- (NSData *)keyPhraseWithLength:(NSUInteger)keyLength { - - return [self.keyPhrase subdataWithRange:NSMakeRange(0, MIN(keyLength, self.keyPhrase.length))]; -} - #pragma mark - Core Data stack - (NSManagedObjectModel *)managedObjectModel { diff --git a/MasterPassword/iOS/MPGuideViewController.m b/MasterPassword/iOS/MPGuideViewController.m index 45d48e19..de3f55de 100644 --- a/MasterPassword/iOS/MPGuideViewController.m +++ b/MasterPassword/iOS/MPGuideViewController.m @@ -7,7 +7,7 @@ // #import "MPGuideViewController.h" -#import "MPAppDelegate.h" +#import "MPAppDelegate_Key.h" @implementation MPGuideViewController @synthesize scrollView; @@ -28,14 +28,14 @@ [super viewWillDisappear:animated]; - [MPConfig get].showQuickStart = [NSNumber numberWithBool:NO]; + [MPiOSConfig get].showQuickStart = [NSNumber numberWithBool:NO]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; - [[MPAppDelegate get] loadKeyPhrase:animated]; + [[MPAppDelegate get] loadKey:animated]; } diff --git a/MasterPassword/iOS/MPMainViewController.m b/MasterPassword/iOS/MPMainViewController.m index e8b96062..e76e5ae1 100644 --- a/MasterPassword/iOS/MPMainViewController.m +++ b/MasterPassword/iOS/MPMainViewController.m @@ -7,7 +7,7 @@ // #import "MPMainViewController.h" -#import "MPAppDelegate.h" +#import "MPAppDelegate_Key.h" #import "MPElementGeneratedEntity.h" #import "MPElementStoredEntity.h" #import "IASKAppSettingsViewController.h" @@ -75,7 +75,7 @@ self.searchTipContainer.alpha = 1; }]; - [self setHelpHidden:[[MPConfig get].helpHidden boolValue] animated:animated]; + [self setHelpHidden:[[MPiOSConfig get].helpHidden boolValue] animated:animated]; [self updateAnimated:animated]; } @@ -109,14 +109,14 @@ [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - if (![MPAppDelegate get].keyPhrase) { + if (![MPAppDelegate get].key) { self.activeElement = nil; [self updateAnimated:NO]; } }]; [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - if (![MPAppDelegate get].keyPhrase) { + if (![MPAppDelegate get].key) { self.activeElement = nil; [self updateAnimated:NO]; } @@ -205,11 +205,11 @@ if (hidden) { self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, self.view.bounds.size.height - 44); self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, self.view.bounds.size.height); - [MPConfig get].helpHidden = [NSNumber numberWithBool:YES]; + [MPiOSConfig get].helpHidden = [NSNumber numberWithBool:YES]; } else { self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 175); self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 216); - [MPConfig get].helpHidden = [NSNumber numberWithBool:NO]; + [MPiOSConfig get].helpHidden = [NSNumber numberWithBool:NO]; } }]; } @@ -373,8 +373,11 @@ #else case 4: #endif + { [[MPAppDelegate get] signOut]; + [[MPAppDelegate get] loadKey:YES]; break; + } } #ifndef PRODUCTION diff --git a/MasterPassword/iOS/MPSearchDelegate.m b/MasterPassword/iOS/MPSearchDelegate.m index 52803cbc..e0b033fc 100644 --- a/MasterPassword/iOS/MPSearchDelegate.m +++ b/MasterPassword/iOS/MPSearchDelegate.m @@ -7,7 +7,7 @@ // #import "MPSearchDelegate.h" -#import "MPAppDelegate.h" +#import "MPAppDelegate_Key.h" #import "MPElementGeneratedEntity.h" @interface MPSearchDelegate (Private) @@ -109,12 +109,12 @@ - (void)update { assert(self.query); - assert([MPAppDelegate get].keyPhraseHashHex); + assert([MPAppDelegate get].keyHashHex); NSFetchRequest *fetchRequest = [[MPAppDelegate get].managedObjectModel fetchRequestFromTemplateWithName:@"MPElements" substitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys: self.query, @"query", - [MPAppDelegate get].keyPhraseHashHex, @"mpHashHex", + [MPAppDelegate get].keyHashHex, @"mpHashHex", nil]]; [fetchRequest setSortDescriptors: [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses" ascending:NO]]]; @@ -266,7 +266,7 @@ assert([element isKindOfClass:ClassFromMPElementType(element.type)]); element.name = siteName; - element.mpHashHex = [MPAppDelegate get].keyPhraseHashHex; + element.mpHashHex = [MPAppDelegate get].keyHashHex; dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate didSelectElement:element]; diff --git a/MasterPassword/iOS/MPUnlockViewController.m b/MasterPassword/iOS/MPUnlockViewController.m index 11dbbfd4..79a0f4e2 100644 --- a/MasterPassword/iOS/MPUnlockViewController.m +++ b/MasterPassword/iOS/MPUnlockViewController.m @@ -9,7 +9,7 @@ #import #import "MPUnlockViewController.h" -#import "MPAppDelegate.h" +#import "MPAppDelegate_Key.h" typedef enum { MPLockscreenIdle, @@ -199,7 +199,27 @@ typedef enum { - (IBAction)changeMP { - [[MPAppDelegate get] forgetKeyPhrase]; + dbg(@"Forgetting key phrase."); + [PearlAlert showAlertWithTitle:@"Changing Master Password" + message: + @"This will allow you to log in with a different master password.\n\n" + @"Note that you will only see the sites and passwords for the master password you log in with.\n" + @"If you log in with a different master password, your current sites will be unavailable.\n\n" + @"You can always change back to your current master password later.\n" + @"Your current sites and passwords will then become available again." + viewStyle:UIAlertViewStyleDefault + tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { + if (buttonIndex != [alert cancelButtonIndex]) + [[MPAppDelegate get] forgetKey]; + + [[MPAppDelegate get] loadKey:YES]; + +#ifndef PRODUCTION + [TestFlight passCheckpoint:MPTestFlightCheckpointMPChanged]; +#endif + } + cancelTitle:[PearlStrings get].commonButtonAbort + otherTitles:[PearlStrings get].commonButtonContinue, nil]; } @end diff --git a/MasterPassword/iOS/MPiOSConfig.h b/MasterPassword/iOS/MPiOSConfig.h new file mode 100644 index 00000000..c4c1f8c1 --- /dev/null +++ b/MasterPassword/iOS/MPiOSConfig.h @@ -0,0 +1,18 @@ +// +// MPConfig.h +// MasterPassword +// +// Created by Maarten Billemont on 02/01/12. +// Copyright (c) 2012 Lyndir. All rights reserved. +// + +#import "MPConfig.h" + +@interface MPiOSConfig : MPConfig + +@property (nonatomic, retain) NSNumber *helpHidden; +@property (nonatomic, retain) NSNumber *showQuickStart; + ++ (MPiOSConfig *)get; + +@end diff --git a/MasterPassword/iOS/MPiOSConfig.m b/MasterPassword/iOS/MPiOSConfig.m new file mode 100644 index 00000000..e76121b3 --- /dev/null +++ b/MasterPassword/iOS/MPiOSConfig.m @@ -0,0 +1,32 @@ +// +// MPConfig.m +// MasterPassword +// +// Created by Maarten Billemont on 02/01/12. +// Copyright (c) 2012 Lyndir. All rights reserved. +// + +#import "MPiOSConfig.h" + +@implementation MPiOSConfig +@dynamic helpHidden, showQuickStart; + +- (id)init { + + if(!(self = [super init])) + return self; + + [self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)), + [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(showQuickStart)), + nil]]; + + return self; +} + ++ (MPiOSConfig *)get { + + return (MPiOSConfig *)[super get]; +} + +@end diff --git a/MasterPassword/iOS/MasterPassword-Prefix.pch b/MasterPassword/iOS/MasterPassword-Prefix.pch index b662d968..de240c68 100644 --- a/MasterPassword/iOS/MasterPassword-Prefix.pch +++ b/MasterPassword/iOS/MasterPassword-Prefix.pch @@ -21,4 +21,4 @@ #import "Pearl-Prefix.pch" #import "MPTypes.h" -#import "MPConfig.h" +#import "MPiOSConfig.h"