[FIXED] GenericPassword class keychain items should have both an Account and a Service attribute. [FIXED] OS X: Logout when key is saved.
159 lines
5.9 KiB
Objective-C
159 lines
5.9 KiB
Objective-C
//
|
|
// MPAppDelegate.m
|
|
// MasterPassword
|
|
//
|
|
// Created by Maarten Billemont on 24/11/11.
|
|
// Copyright (c) 2011 Lyndir. All rights reserved.
|
|
//
|
|
|
|
#import "MPConfig.h"
|
|
#import "MPAppDelegate_Key.h"
|
|
#import "MPElementEntity.h"
|
|
|
|
@implementation MPAppDelegate_Shared (Key)
|
|
|
|
static NSDictionary *keyQuery() {
|
|
|
|
static NSDictionary *MPKeyQuery = nil;
|
|
if (!MPKeyQuery)
|
|
MPKeyQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
|
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
@"Saved Master Password", (__bridge id)kSecAttrService,
|
|
@"default", (__bridge id)kSecAttrAccount,
|
|
nil]
|
|
matches:nil];
|
|
|
|
return MPKeyQuery;
|
|
}
|
|
|
|
static NSDictionary *keyIDQuery() {
|
|
|
|
static NSDictionary *MPKeyIDQuery = nil;
|
|
if (!MPKeyIDQuery)
|
|
MPKeyIDQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
|
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
@"Master Password Check", (__bridge id)kSecAttrService,
|
|
@"default", (__bridge id)kSecAttrAccount,
|
|
nil]
|
|
matches:nil];
|
|
|
|
return MPKeyIDQuery;
|
|
}
|
|
|
|
- (void)forgetKey {
|
|
|
|
inf(@"Deleting key and ID from keychain.");
|
|
if ([PearlKeyChain deleteItemForQuery:keyQuery()] != errSecItemNotFound)
|
|
inf(@"Removed key from keychain.");
|
|
if ([PearlKeyChain deleteItemForQuery:keyIDQuery()] != errSecItemNotFound)
|
|
inf(@"Removed key ID from keychain.");
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self];
|
|
#ifdef TESTFLIGHT_SDK_VERSION
|
|
[TestFlight passCheckpoint:MPTestFlightCheckpointMPForgotten];
|
|
#endif
|
|
}
|
|
|
|
- (IBAction)signOut:(id)sender {
|
|
|
|
[MPConfig get].saveKey = [NSNumber numberWithBool:NO];
|
|
[self updateKey:nil];
|
|
}
|
|
|
|
- (void)loadStoredKey {
|
|
|
|
if ([[MPConfig get].saveKey boolValue]) {
|
|
// Key is stored in keychain. Load it.
|
|
[self updateKey:[PearlKeyChain dataOfItemForQuery:keyQuery()]];
|
|
inf(@"Looking for key in keychain: %@.", self.key? @"found": @"missing");
|
|
} else {
|
|
// Key should not be stored in keychain. Delete it.
|
|
if ([PearlKeyChain deleteItemForQuery:keyQuery()] != errSecItemNotFound)
|
|
inf(@"Removed key from keychain.");
|
|
#ifdef TESTFLIGHT_SDK_VERSION
|
|
[TestFlight passCheckpoint:MPTestFlightCheckpointMPUnstored];
|
|
#endif
|
|
}
|
|
}
|
|
|
|
- (BOOL)tryMasterPassword:(NSString *)tryPassword {
|
|
|
|
if (![tryPassword length])
|
|
return NO;
|
|
|
|
NSData *tryKey = keyForPassword(tryPassword);
|
|
NSData *tryKeyID = keyIDForKey(tryKey);
|
|
NSData *keyID = [PearlKeyChain dataOfItemForQuery:keyIDQuery()];
|
|
inf(@"Key ID known? %@.", keyID? @"YES": @"NO");
|
|
if (keyID)
|
|
// A key ID is known -> a password is set.
|
|
// Make sure the user's entered password matches it.
|
|
if (![keyID isEqual:tryKeyID]) {
|
|
wrn(@"Key ID mismatch. Expected: %@, answer: %@.", [keyID encodeHex], [tryKeyID encodeHex]);
|
|
|
|
#ifdef TESTFLIGHT_SDK_VERSION
|
|
[TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch];
|
|
#endif
|
|
return NO;
|
|
}
|
|
|
|
#ifdef TESTFLIGHT_SDK_VERSION
|
|
[TestFlight passCheckpoint:MPTestFlightCheckpointMPEntered];
|
|
#endif
|
|
|
|
[self updateKey:tryKey];
|
|
return YES;
|
|
}
|
|
|
|
- (void)updateKey:(NSData *)key {
|
|
|
|
if (self.key != key) {
|
|
self.key = key;
|
|
|
|
if (key)
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeySet object:self];
|
|
else
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyUnset object:self];
|
|
}
|
|
|
|
if (self.key) {
|
|
self.keyID = keyIDForKey(self.key);
|
|
|
|
NSData *existingKeyID = [PearlKeyChain dataOfItemForQuery:keyIDQuery()];
|
|
if (![existingKeyID isEqualToData:self.keyID]) {
|
|
inf(@"Updating key ID in keychain.");
|
|
[PearlKeyChain addOrUpdateItemForQuery:keyIDQuery()
|
|
withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
self.keyID, (__bridge id)kSecValueData,
|
|
#if TARGET_OS_IPHONE
|
|
kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible,
|
|
#endif
|
|
nil]];
|
|
}
|
|
if ([[MPConfig get].saveKey boolValue]) {
|
|
NSData *existingKey = [PearlKeyChain dataOfItemForQuery:keyQuery()];
|
|
if (![existingKey isEqualToData:self.key]) {
|
|
inf(@"Updating key in keychain.");
|
|
[PearlKeyChain addOrUpdateItemForQuery:keyQuery()
|
|
withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
self.key, (__bridge id)kSecValueData,
|
|
#if TARGET_OS_IPHONE
|
|
kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible,
|
|
#endif
|
|
nil]];
|
|
}
|
|
}
|
|
|
|
#ifdef TESTFLIGHT_SDK_VERSION
|
|
[TestFlight passCheckpoint:MPTestFlightCheckpointSetKey];
|
|
#endif
|
|
}
|
|
}
|
|
|
|
- (NSData *)keyWithLength:(NSUInteger)keyLength {
|
|
|
|
return [self.key subdataWithRange:NSMakeRange(0, MIN(keyLength, self.key.length))];
|
|
}
|
|
|
|
@end
|