Prepare key handling logic for sharing with OS X.
[MOVED] Key logic now in a common class extension on MPAppDelegate so it can be shared between iOS and OS X apps. [MOVED] MPConfig for sharing between iOS and OS X apps. [CHANGED] keyphrase -> key.
This commit is contained in:
parent
6bda70920b
commit
02ffa9611a
@ -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 = "<group>"; };
|
||||
DA600C2415054F3A008E9AB6 /* MPAppDelegate_Key.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_Key.h; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
@ -891,8 +897,8 @@
|
||||
DAB8D44515036BCF00CED3BC /* MasterPassword.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = MasterPassword.entitlements; sourceTree = "<group>"; };
|
||||
DAB8D44615036BCF00CED3BC /* MPAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate.h; sourceTree = "<group>"; };
|
||||
DAB8D44715036BCF00CED3BC /* MPAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate.m; sourceTree = "<group>"; };
|
||||
DAB8D44815036BCF00CED3BC /* MPConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPConfig.h; sourceTree = "<group>"; };
|
||||
DAB8D44915036BCF00CED3BC /* MPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPConfig.m; sourceTree = "<group>"; };
|
||||
DAB8D44815036BCF00CED3BC /* MPiOSConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPiOSConfig.h; sourceTree = "<group>"; };
|
||||
DAB8D44915036BCF00CED3BC /* MPiOSConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPiOSConfig.m; sourceTree = "<group>"; };
|
||||
DAB8D44A15036BCF00CED3BC /* MPGuideViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGuideViewController.h; sourceTree = "<group>"; };
|
||||
DAB8D44B15036BCF00CED3BC /* MPGuideViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGuideViewController.m; sourceTree = "<group>"; };
|
||||
DAB8D44C15036BCF00CED3BC /* MPMainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMainViewController.h; sourceTree = "<group>"; };
|
||||
@ -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;
|
||||
};
|
||||
|
33
MasterPassword/MPAppDelegate_Key.h
Normal file
33
MasterPassword/MPAppDelegate_Key.h
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// MPAppDelegate_Key.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#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
|
148
MasterPassword/MPAppDelegate_Key.m
Normal file
148
MasterPassword/MPAppDelegate_Key.m
Normal file
@ -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
|
@ -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;
|
||||
|
@ -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;
|
@ -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];
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "MPPasswordWindowController.h"
|
||||
|
||||
@interface MPAppDelegate : NSObject <NSApplicationDelegate>
|
||||
|
||||
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface MPPasswordWindowController : NSWindowController
|
||||
@interface MPPasswordWindowController : NSWindowController <NSTextFieldDelegate>
|
||||
@property (weak) IBOutlet NSTextField *siteField;
|
||||
@property (weak) IBOutlet NSTextField *contentField;
|
||||
|
||||
|
@ -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
|
||||
|
@ -36,7 +36,7 @@
|
||||
<string key="NSClassName">NSApplication</string>
|
||||
</object>
|
||||
<object class="NSWindowTemplate" id="45434518">
|
||||
<int key="NSWindowStyleMask">8223</int>
|
||||
<int key="NSWindowStyleMask">8215</int>
|
||||
<int key="NSWindowBacking">2</int>
|
||||
<string key="NSWindowRect">{{600, 530}, {480, 134}}</string>
|
||||
<int key="NSWTFlags">611845120</int>
|
||||
@ -68,7 +68,7 @@
|
||||
<double key="NSSize">13</double>
|
||||
<int key="NSfFlags">1044</int>
|
||||
</object>
|
||||
<string key="NSPlaceholderString">Enter site name</string>
|
||||
<string key="NSPlaceholderString">Enter site name X</string>
|
||||
<string key="NSCellIdentifier">_NS:9</string>
|
||||
<reference key="NSControlView" ref="291791585"/>
|
||||
<bool key="NSDrawsBackground">YES</bool>
|
||||
@ -98,7 +98,6 @@
|
||||
<int key="NSvFlags">268</int>
|
||||
<string key="NSFrame">{{17, 20}, {446, 64}}</string>
|
||||
<reference key="NSSuperview" ref="258451033"/>
|
||||
<reference key="NSNextKeyView"/>
|
||||
<int key="NSViewLayerContentsRedrawPolicy">2</int>
|
||||
<string key="NSReuseIdentifierKey">_NS:9</string>
|
||||
<string key="NSAntiCompressionPriority">{250, 750}</string>
|
||||
@ -177,6 +176,14 @@
|
||||
</object>
|
||||
<int key="connectionID">39</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBOutletConnection" key="connection">
|
||||
<string key="label">delegate</string>
|
||||
<reference key="source" ref="291791585"/>
|
||||
<reference key="destination" ref="1001"/>
|
||||
</object>
|
||||
<int key="connectionID">43</int>
|
||||
</object>
|
||||
</array>
|
||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||
<array key="orderedObjects">
|
||||
@ -526,7 +533,7 @@
|
||||
<nil key="activeLocalization"/>
|
||||
<dictionary class="NSMutableDictionary" key="localizations"/>
|
||||
<nil key="sourceID"/>
|
||||
<int key="maxID">42</int>
|
||||
<int key="maxID">43</int>
|
||||
</object>
|
||||
<object class="IBClassDescriber" key="IBDocument.Classes"/>
|
||||
<int key="IBDocument.localizationMode">0</int>
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,9 +373,12 @@
|
||||
#else
|
||||
case 4:
|
||||
#endif
|
||||
{
|
||||
[[MPAppDelegate get] signOut];
|
||||
[[MPAppDelegate get] loadKey:YES];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointAction];
|
||||
|
@ -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];
|
||||
|
@ -9,7 +9,7 @@
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#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
|
||||
|
18
MasterPassword/iOS/MPiOSConfig.h
Normal file
18
MasterPassword/iOS/MPiOSConfig.h
Normal file
@ -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
|
32
MasterPassword/iOS/MPiOSConfig.m
Normal file
32
MasterPassword/iOS/MPiOSConfig.m
Normal file
@ -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
|
@ -21,4 +21,4 @@
|
||||
#import "Pearl-Prefix.pch"
|
||||
|
||||
#import "MPTypes.h"
|
||||
#import "MPConfig.h"
|
||||
#import "MPiOSConfig.h"
|
||||
|
Loading…
Reference in New Issue
Block a user