2012-03-05 21:19:05 +00:00
|
|
|
//
|
|
|
|
// MPAppDelegate.m
|
|
|
|
// MasterPassword
|
|
|
|
//
|
|
|
|
// Created by Maarten Billemont on 24/11/11.
|
|
|
|
// Copyright (c) 2011 Lyndir. All rights reserved.
|
|
|
|
//
|
|
|
|
|
2012-06-10 06:21:41 +00:00
|
|
|
#import <Crashlytics/Crashlytics.h>
|
2012-05-11 20:45:05 +00:00
|
|
|
#import "MPAppDelegate_Key.h"
|
2012-06-05 22:59:09 +00:00
|
|
|
#import "MPAppDelegate_Store.h"
|
2012-06-11 21:39:50 +00:00
|
|
|
#import "LocalyticsSession.h"
|
2012-03-05 21:19:05 +00:00
|
|
|
|
2012-05-11 20:45:05 +00:00
|
|
|
@implementation MPAppDelegate_Shared (Key)
|
2012-03-05 21:19:05 +00:00
|
|
|
|
2012-06-04 09:27:02 +00:00
|
|
|
static NSDictionary *keyQuery(MPUserEntity *user) {
|
2012-06-07 22:40:30 +00:00
|
|
|
|
2012-06-04 09:27:02 +00:00
|
|
|
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
|
|
|
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
2012-06-08 21:46:13 +00:00
|
|
|
@"Saved Master Password", (__bridge id)kSecAttrService,
|
|
|
|
user.name, (__bridge id)kSecAttrAccount,
|
|
|
|
nil]
|
|
|
|
matches:nil];
|
2012-03-05 21:19:05 +00:00
|
|
|
}
|
|
|
|
|
2012-06-07 22:40:30 +00:00
|
|
|
- (NSData *)loadSavedKeyFor:(MPUserEntity *)user {
|
|
|
|
|
|
|
|
NSData *key = [PearlKeyChain dataOfItemForQuery:keyQuery(user)];
|
|
|
|
if (key)
|
2012-06-14 19:56:54 +00:00
|
|
|
inf(@"Found key in keychain for: %@", user.userID);
|
2012-06-07 22:40:30 +00:00
|
|
|
|
|
|
|
else {
|
|
|
|
user.saveKey = NO;
|
2012-06-14 19:56:54 +00:00
|
|
|
inf(@"No key found in keychain for: %@", user.userID);
|
2012-06-04 09:27:02 +00:00
|
|
|
}
|
2012-06-07 22:40:30 +00:00
|
|
|
|
|
|
|
return key;
|
2012-03-05 21:19:05 +00:00
|
|
|
}
|
|
|
|
|
2012-06-07 22:40:30 +00:00
|
|
|
- (void)storeSavedKeyFor:(MPUserEntity *)user {
|
2012-05-13 17:50:40 +00:00
|
|
|
|
2012-06-07 22:40:30 +00:00
|
|
|
if (user.saveKey) {
|
|
|
|
NSData *existingKey = [PearlKeyChain dataOfItemForQuery:keyQuery(user)];
|
2012-03-05 21:19:05 +00:00
|
|
|
|
2012-06-07 22:40:30 +00:00
|
|
|
if (![existingKey isEqualToData:self.key]) {
|
2012-06-14 19:56:54 +00:00
|
|
|
inf(@"Saving key in keychain for: %@", user.userID);
|
|
|
|
|
2012-06-07 22:40:30 +00:00
|
|
|
[PearlKeyChain addOrUpdateItemForQuery:keyQuery(user)
|
|
|
|
withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
2012-06-08 21:46:13 +00:00
|
|
|
self.key, (__bridge id)kSecValueData,
|
|
|
|
#if TARGET_OS_IPHONE
|
|
|
|
kSecAttrAccessibleWhenUnlockedThisDeviceOnly, (__bridge id)kSecAttrAccessible,
|
|
|
|
#endif
|
|
|
|
nil]];
|
2012-06-07 22:40:30 +00:00
|
|
|
}
|
2012-03-05 21:19:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-07 22:40:30 +00:00
|
|
|
- (void)forgetSavedKeyFor:(MPUserEntity *)user {
|
|
|
|
|
|
|
|
OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery(user)];
|
|
|
|
if (result == noErr || result == errSecItemNotFound) {
|
|
|
|
user.saveKey = NO;
|
|
|
|
|
|
|
|
if (result == noErr) {
|
2012-06-14 19:56:54 +00:00
|
|
|
inf(@"Removed key from keychain for: %@", user.userID);
|
2012-06-07 22:40:30 +00:00
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self];
|
2012-05-11 20:45:05 +00:00
|
|
|
#ifdef TESTFLIGHT_SDK_VERSION
|
2012-06-11 21:39:50 +00:00
|
|
|
[TestFlight passCheckpoint:MPCheckpointForgetSavedKey];
|
2012-05-04 16:54:58 +00:00
|
|
|
#endif
|
2012-03-05 21:19:05 +00:00
|
|
|
}
|
2012-06-05 22:59:09 +00:00
|
|
|
}
|
2012-06-07 22:40:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)signOut {
|
|
|
|
|
2012-06-08 21:46:13 +00:00
|
|
|
self.key = nil;
|
2012-06-07 22:40:30 +00:00
|
|
|
self.activeUser = nil;
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationSignedOut object:self];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password {
|
|
|
|
|
|
|
|
NSData *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 = keyForPassword(password, user.name))) {
|
|
|
|
user.keyID = keyIDForKey(tryKey);
|
|
|
|
[[MPAppDelegate_Shared get] saveContext];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method 2: Depending on the user's saveKey, load or remove the key from the keychain.
|
|
|
|
if (!user.saveKey)
|
2012-06-08 21:46:13 +00:00
|
|
|
// Key should not be stored in keychain. Delete it.
|
2012-06-07 22:40:30 +00:00
|
|
|
[self forgetSavedKeyFor:user];
|
|
|
|
|
2012-06-08 21:46:13 +00:00
|
|
|
else
|
|
|
|
if (!tryKey) {
|
|
|
|
// Key should be saved in keychain. Load it.
|
|
|
|
if ((tryKey = [self loadSavedKeyFor:user]))
|
|
|
|
if (![user.keyID isEqual:keyIDForKey(tryKey)]) {
|
|
|
|
// Loaded password doesn't match user's keyID. Forget saved password: it is incorrect.
|
2012-06-14 19:56:54 +00:00
|
|
|
inf(@"Saved password doesn't match keyID for: %@", user.userID);
|
|
|
|
|
2012-06-08 21:46:13 +00:00
|
|
|
tryKey = nil;
|
|
|
|
[self forgetSavedKeyFor:user];
|
|
|
|
}
|
|
|
|
}
|
2012-06-07 22:40:30 +00:00
|
|
|
|
|
|
|
// Method 3: Check the given master password string.
|
|
|
|
if (!tryKey) {
|
|
|
|
if ([password length])
|
|
|
|
if ((tryKey = keyForPassword(password, user.name)))
|
|
|
|
if (![user.keyID isEqual:keyIDForKey(tryKey)]) {
|
2012-06-14 19:56:54 +00:00
|
|
|
inf(@"Key derived from password doesn't match keyID for: %@", user.userID);
|
2012-06-07 22:40:30 +00:00
|
|
|
|
2012-06-14 19:56:54 +00:00
|
|
|
tryKey = nil;
|
2012-06-07 22:40:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No more methods left, fail if key still not known.
|
2012-06-14 19:56:54 +00:00
|
|
|
if (!tryKey) {
|
|
|
|
inf(@"Login failed for: %@", user.userID);
|
|
|
|
|
|
|
|
#ifdef TESTFLIGHT_SDK_VERSION
|
|
|
|
[TestFlight passCheckpoint:MPCheckpointSignInFailed];
|
|
|
|
#endif
|
|
|
|
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointSignInFailed attributes:nil];
|
|
|
|
|
2012-06-07 22:40:30 +00:00
|
|
|
return NO;
|
2012-06-14 19:56:54 +00:00
|
|
|
}
|
|
|
|
inf(@"Logged in: %@", user.userID);
|
2012-06-07 22:40:30 +00:00
|
|
|
|
|
|
|
if (![self.key isEqualToData:tryKey]) {
|
2012-06-04 09:27:02 +00:00
|
|
|
self.key = tryKey;
|
2012-06-07 22:40:30 +00:00
|
|
|
[self storeSavedKeyFor:user];
|
2012-06-04 09:27:02 +00:00
|
|
|
}
|
2012-06-07 22:40:30 +00:00
|
|
|
|
2012-06-10 06:21:41 +00:00
|
|
|
@try {
|
2012-06-14 19:56:54 +00:00
|
|
|
if ([[MPiOSConfig get].sendInfo boolValue]) {
|
|
|
|
[TestFlight addCustomEnvironmentInformation:user.userID forKey:@"username"];
|
|
|
|
[[Crashlytics sharedInstance] setObjectValue:user.userID forKey:@"username"];
|
2012-06-10 06:21:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
@catch (id exception) {
|
|
|
|
err(@"While setting username: %@", exception);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-08 21:46:13 +00:00
|
|
|
user.lastUsed = [NSDate date];
|
2012-06-04 09:27:02 +00:00
|
|
|
self.activeUser = user;
|
2012-06-07 22:40:30 +00:00
|
|
|
[[MPAppDelegate_Shared get] saveContext];
|
2012-03-05 21:19:05 +00:00
|
|
|
|
2012-06-07 22:40:30 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationSignedIn object:self];
|
|
|
|
#ifdef TESTFLIGHT_SDK_VERSION
|
2012-06-11 21:39:50 +00:00
|
|
|
[TestFlight passCheckpoint:MPCheckpointSignedIn];
|
2012-03-05 21:43:20 +00:00
|
|
|
#endif
|
2012-06-11 21:39:50 +00:00
|
|
|
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointSignedIn
|
|
|
|
attributes:nil];
|
2012-03-05 21:19:05 +00:00
|
|
|
|
2012-06-07 22:40:30 +00:00
|
|
|
return YES;
|
2012-06-04 09:27:02 +00:00
|
|
|
}
|
|
|
|
|
2012-03-05 21:19:05 +00:00
|
|
|
- (NSData *)keyWithLength:(NSUInteger)keyLength {
|
2012-06-07 22:40:30 +00:00
|
|
|
|
2012-03-05 21:19:05 +00:00
|
|
|
return [self.key subdataWithRange:NSMakeRange(0, MIN(keyLength, self.key.length))];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|