diff --git a/MasterPassword/MPAppDelegate_Key.m b/MasterPassword/MPAppDelegate_Key.m index cc7acd6e..d5be3bc0 100644 --- a/MasterPassword/MPAppDelegate_Key.m +++ b/MasterPassword/MPAppDelegate_Key.m @@ -24,23 +24,23 @@ static NSDictionary *keyQuery() { return MPKeyQuery; } -static NSDictionary *keyHashQuery() { +static NSDictionary *keyIDQuery() { - static NSDictionary *MPKeyHashQuery = nil; - if (!MPKeyHashQuery) - MPKeyHashQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword - attributes:[NSDictionary dictionaryWithObject:@"Master Password Verification" - forKey:(__bridge id)kSecAttrService] - matches:nil]; + static NSDictionary *MPKeyIDQuery = nil; + if (!MPKeyIDQuery) + MPKeyIDQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword + attributes:[NSDictionary dictionaryWithObject:@"Master Password Verification" + forKey:(__bridge id)kSecAttrService] + matches:nil]; - return MPKeyHashQuery; + return MPKeyIDQuery; } - (void)forgetKey { - inf(@"Deleting key and hash from keychain."); + inf(@"Deleting key and ID from keychain."); [PearlKeyChain deleteItemForQuery:keyQuery()]; - [PearlKeyChain deleteItemForQuery:keyHashQuery()]; + [PearlKeyChain deleteItemForQuery:keyIDQuery()]; [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self]; #ifdef TESTFLIGHT_SDK_VERSION @@ -75,14 +75,14 @@ static NSDictionary *keyHashQuery() { return NO; NSData *tryKey = keyForPassword(tryPassword); - NSData *tryKeyHash = keyHashForKey(tryKey); - NSData *keyHash = [PearlKeyChain dataOfItemForQuery:keyHashQuery()]; - inf(@"Key hash known? %@.", keyHash? @"YES": @"NO"); - if (keyHash) - // A key hash is known -> a key is set. - // Make sure the user's entered key matches it. - if (![keyHash isEqual:tryKeyHash]) { - wrn(@"Key ID mismatch. Expected: %@, answer: %@.", [keyHash encodeHex], [tryKeyHash encodeHex]); + 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]; @@ -110,15 +110,14 @@ static NSDictionary *keyHashQuery() { } if (self.key) { - self.keyHash = keyHashForKey(self.key); - self.keyID = [self.keyHash encodeHex]; + self.keyID = keyIDForKey(self.key); - NSData *existingKeyHash = [PearlKeyChain dataOfItemForQuery:keyHashQuery()]; - if (![existingKeyHash isEqualToData:self.keyHash]) { + NSData *existingKeyID = [PearlKeyChain dataOfItemForQuery:keyIDQuery()]; + if (![existingKeyID isEqualToData:self.keyID]) { inf(@"Updating key ID in keychain."); - [PearlKeyChain addOrUpdateItemForQuery:keyHashQuery() + [PearlKeyChain addOrUpdateItemForQuery:keyIDQuery() withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: - self.keyHash, (__bridge id)kSecValueData, + self.keyID, (__bridge id)kSecValueData, #if TARGET_OS_IPHONE kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible, #endif diff --git a/MasterPassword/MPAppDelegate_Shared.h b/MasterPassword/MPAppDelegate_Shared.h index da968675..f898a080 100644 --- a/MasterPassword/MPAppDelegate_Shared.h +++ b/MasterPassword/MPAppDelegate_Shared.h @@ -9,8 +9,7 @@ @interface MPAppDelegate_Shared : PearlAppDelegate @property (strong, nonatomic) NSData *key; -@property (strong, nonatomic) NSData *keyHash; -@property (strong, nonatomic) NSString *keyID; +@property (strong, nonatomic) NSData *keyID; + (MPAppDelegate_Shared *)get; diff --git a/MasterPassword/MPAppDelegate_Shared.m b/MasterPassword/MPAppDelegate_Shared.m index 0e9bb5bc..5acbe0f1 100644 --- a/MasterPassword/MPAppDelegate_Shared.m +++ b/MasterPassword/MPAppDelegate_Shared.m @@ -11,7 +11,6 @@ @implementation MPAppDelegate_Shared @synthesize key; -@synthesize keyHash; @synthesize keyID; + (MPAppDelegate_Shared *)get { diff --git a/MasterPassword/MPAppDelegate_Store.m b/MasterPassword/MPAppDelegate_Store.m index d205cda8..65e136d7 100644 --- a/MasterPassword/MPAppDelegate_Store.m +++ b/MasterPassword/MPAppDelegate_Store.m @@ -265,7 +265,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil; if (!headerPattern || !sitePattern) return MPImportResultInternalError; - NSString *keyID = nil; + NSString *keyIDHex = nil; BOOL headerStarted = NO, headerEnded = NO; NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; NSMutableSet *elementsToDelete = [NSMutableSet set]; @@ -295,7 +295,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil; NSString *key = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]]; NSString *value = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]]; if ([key isEqualToString:@"Key ID"]) { - if (![(keyID = value) isEqualToString:[keyHashForPassword(password) encodeHex]]) + if (![(keyIDHex = value) isEqualToString:[keyIDForPassword(password) encodeHex]]) return MPImportResultInvalidPassword; } @@ -303,7 +303,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil; } if (!headerEnded) continue; - if (!keyID) + if (!keyIDHex) return MPImportResultMalformedInput; if (![importedSiteLine length]) continue; @@ -321,7 +321,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil; NSString *exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]]; // Find existing site. - fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND keyID == %@", name, keyID]; + fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND keyID == %@", name, keyIDHex]; NSArray *existingSites = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error]; if (error) err(@"Couldn't search existing sites: %@", error); @@ -352,11 +352,11 @@ static NSDateFormatter *rfc3339DateFormatter = nil; NSString *exportContent = [siteElements objectAtIndex:4]; // Create new site. - inf(@"Importing site: name=%@, lastUsed=%@, uses=%d, type=%u, keyID=%@", name, lastUsed, uses, type, keyID); + inf(@"Importing site: name=%@, lastUsed=%@, uses=%d, type=%u, keyID=%@", name, lastUsed, uses, type, keyIDHex); MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:ClassNameFromMPElementType(type) inManagedObjectContext:self.managedObjectContext]; element.name = name; - element.keyID = keyID; + element.keyID = [keyIDHex decodeHex]; element.type = type; element.uses = uses; element.lastUsed = [lastUsed timeIntervalSinceReferenceDate]; @@ -386,7 +386,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil; [export appendFormat:@"# \n"]; [export appendFormat:@"##\n"]; [export appendFormat:@"# Version: %@\n", [PearlInfoPlist get].CFBundleVersion]; - [export appendFormat:@"# Key ID: %@\n", self.keyID]; + [export appendFormat:@"# Key ID: %@\n", [self.keyID encodeHex]]; [export appendFormat:@"# Date: %@\n", [rfc3339DateFormatter stringFromDate:[NSDate date]]]; if (showPasswords) [export appendFormat:@"# Passwords: VISIBLE\n"]; diff --git a/MasterPassword/MPElementEntity.h b/MasterPassword/MPElementEntity.h index 4da7d035..24c3d891 100644 --- a/MasterPassword/MPElementEntity.h +++ b/MasterPassword/MPElementEntity.h @@ -13,7 +13,7 @@ @interface MPElementEntity : NSManagedObject @property (nonatomic, retain) NSString *name; -@property (nonatomic, retain) NSString *keyID; +@property (nonatomic, retain) NSData *keyID; @property (nonatomic, assign) int16_t type; @property (nonatomic, assign) int16_t uses; @property (nonatomic, assign) NSTimeInterval lastUsed; diff --git a/MasterPassword/MPTypes.h b/MasterPassword/MPTypes.h index 86caf158..af54ff01 100644 --- a/MasterPassword/MPTypes.h +++ b/MasterPassword/MPTypes.h @@ -77,8 +77,8 @@ typedef enum { #define MPNotificationKeyForgotten @"MPNotificationKeyForgotten" NSData *keyForPassword(NSString *password); -NSData *keyHashForPassword(NSString *password); -NSData *keyHashForKey(NSData *key); +NSData *keyIDForPassword(NSString *password); +NSData *keyIDForKey(NSData *key); NSString *NSStringFromMPElementType(MPElementType type); NSString *ClassNameFromMPElementType(MPElementType type); Class ClassFromMPElementType(MPElementType type); diff --git a/MasterPassword/MPTypes.m b/MasterPassword/MPTypes.m index 714a596f..87c4f619 100644 --- a/MasterPassword/MPTypes.m +++ b/MasterPassword/MPTypes.m @@ -23,15 +23,15 @@ NSData *keyForPassword(NSString *password) { NSData *key = [PearlSCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding] usingSalt:MP_salt N:MP_N r:MP_r p:MP_p]; - - trc(@"Password: %@ derives to key ID: %@", password, [keyHashForKey(key) encodeHex]); + + trc(@"Password: %@ derives to key ID: %@", password, [keyIDForKey(key) encodeHex]); return key; } -NSData *keyHashForPassword(NSString *password) { +NSData *keyIDForPassword(NSString *password) { - return keyHashForKey(keyForPassword(password)); + return keyIDForKey(keyForPassword(password)); } -NSData *keyHashForKey(NSData *key) { +NSData *keyIDForKey(NSData *key) { return [key hashWith:MP_hash]; } @@ -129,29 +129,29 @@ NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *key, in MPTypes_ciphers = [NSDictionary dictionaryWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"ciphers" withExtension:@"plist"]]; - // Determine the hash whose bytes will be used for calculating a password: md4(name-key) + // Determine the seed whose bytes will be used for calculating a password: sha1(name . '\0' . key . '\0' . salt) uint32_t nsalt = htonl(salt); - trc(@"key hash from: %@-%@-%u", name, key, nsalt); - NSData *keyHash = [[NSData dataByConcatenatingWithDelimitor:'-' datas: - [name dataUsingEncoding:NSUTF8StringEncoding], - key, - [NSData dataWithBytes:&nsalt length:sizeof(nsalt)], - nil] hashWith:PearlDigestSHA1]; - trc(@"key hash is: %@", keyHash); - const char *keyBytes = keyHash.bytes; + trc(@"seed from: sha1(%@, %@, %u)", name, key, nsalt); + NSData *seed = [[NSData dataByConcatenatingWithDelimitor:'\0' datas: + [name dataUsingEncoding:NSUTF8StringEncoding], + key, + [NSData dataWithBytes:&nsalt length:sizeof(nsalt)], + nil] hashWith:PearlDigestSHA1]; + trc(@"seed is: %@", seed); + const char *seedBytes = seed.bytes; - // Determine the cipher from the first hash byte. - assert([keyHash length]); + // Determine the cipher from the first seed byte. + assert([seed length]); NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:ClassNameFromMPElementType(type)] valueForKey:NSStringFromMPElementType(type)]; - NSString *cipher = [typeCiphers objectAtIndex:htons(keyBytes[0]) % [typeCiphers count]]; + NSString *cipher = [typeCiphers objectAtIndex:htons(seedBytes[0]) % [typeCiphers count]]; trc(@"type %d, ciphers: %@, selected: %@", type, typeCiphers, cipher); - // Encode the content, character by character, using subsequent hash bytes and the cipher. - assert([keyHash length] >= [cipher length] + 1); + // Encode the content, character by character, using subsequent seed bytes and the cipher. + assert([seed length] >= [cipher length] + 1); NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]]; for (NSUInteger c = 0; c < [cipher length]; ++c) { - uint16_t keyByte = htons(keyBytes[c + 1]); + uint16_t keyByte = htons(seedBytes[c + 1]); NSString *cipherClass = [cipher substringWithRange:NSMakeRange(c, 1)]; NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass]; NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange(keyByte % [cipherClassCharacters length], 1)]; diff --git a/MasterPassword/Mac/MPAppDelegate.m b/MasterPassword/Mac/MPAppDelegate.m index 7c00a897..9407a200 100644 --- a/MasterPassword/Mac/MPAppDelegate.m +++ b/MasterPassword/Mac/MPAppDelegate.m @@ -29,7 +29,6 @@ @synthesize passwordWindow; @synthesize key; -@synthesize keyHash; @synthesize keyID; #pragma GCC diagnostic ignored "-Wfour-char-constants" diff --git a/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword.xcdatamodel/contents b/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword.xcdatamodel/contents index 3934f1a6..0a73de56 100644 --- a/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword.xcdatamodel/contents +++ b/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword.xcdatamodel/contents @@ -1,7 +1,7 @@ - +