2
0

Key hash -> key ID.

This commit is contained in:
Maarten Billemont 2012-05-12 19:24:29 +02:00
parent 301366f1f1
commit f7e64fe4b8
9 changed files with 55 additions and 59 deletions

View File

@ -24,23 +24,23 @@ static NSDictionary *keyQuery() {
return MPKeyQuery; return MPKeyQuery;
} }
static NSDictionary *keyHashQuery() { static NSDictionary *keyIDQuery() {
static NSDictionary *MPKeyHashQuery = nil; static NSDictionary *MPKeyIDQuery = nil;
if (!MPKeyHashQuery) if (!MPKeyIDQuery)
MPKeyHashQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword MPKeyIDQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword
attributes:[NSDictionary dictionaryWithObject:@"Master Password Verification" attributes:[NSDictionary dictionaryWithObject:@"Master Password Verification"
forKey:(__bridge id)kSecAttrService] forKey:(__bridge id)kSecAttrService]
matches:nil]; matches:nil];
return MPKeyHashQuery; return MPKeyIDQuery;
} }
- (void)forgetKey { - (void)forgetKey {
inf(@"Deleting key and hash from keychain."); inf(@"Deleting key and ID from keychain.");
[PearlKeyChain deleteItemForQuery:keyQuery()]; [PearlKeyChain deleteItemForQuery:keyQuery()];
[PearlKeyChain deleteItemForQuery:keyHashQuery()]; [PearlKeyChain deleteItemForQuery:keyIDQuery()];
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self]; [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self];
#ifdef TESTFLIGHT_SDK_VERSION #ifdef TESTFLIGHT_SDK_VERSION
@ -75,14 +75,14 @@ static NSDictionary *keyHashQuery() {
return NO; return NO;
NSData *tryKey = keyForPassword(tryPassword); NSData *tryKey = keyForPassword(tryPassword);
NSData *tryKeyHash = keyHashForKey(tryKey); NSData *tryKeyID = keyIDForKey(tryKey);
NSData *keyHash = [PearlKeyChain dataOfItemForQuery:keyHashQuery()]; NSData *keyID = [PearlKeyChain dataOfItemForQuery:keyIDQuery()];
inf(@"Key hash known? %@.", keyHash? @"YES": @"NO"); inf(@"Key ID known? %@.", keyID? @"YES": @"NO");
if (keyHash) if (keyID)
// A key hash is known -> a key is set. // A key ID is known -> a password is set.
// Make sure the user's entered key matches it. // Make sure the user's entered password matches it.
if (![keyHash isEqual:tryKeyHash]) { if (![keyID isEqual:tryKeyID]) {
wrn(@"Key ID mismatch. Expected: %@, answer: %@.", [keyHash encodeHex], [tryKeyHash encodeHex]); wrn(@"Key ID mismatch. Expected: %@, answer: %@.", [keyID encodeHex], [tryKeyID encodeHex]);
#ifdef TESTFLIGHT_SDK_VERSION #ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch]; [TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch];
@ -110,15 +110,14 @@ static NSDictionary *keyHashQuery() {
} }
if (self.key) { if (self.key) {
self.keyHash = keyHashForKey(self.key); self.keyID = keyIDForKey(self.key);
self.keyID = [self.keyHash encodeHex];
NSData *existingKeyHash = [PearlKeyChain dataOfItemForQuery:keyHashQuery()]; NSData *existingKeyID = [PearlKeyChain dataOfItemForQuery:keyIDQuery()];
if (![existingKeyHash isEqualToData:self.keyHash]) { if (![existingKeyID isEqualToData:self.keyID]) {
inf(@"Updating key ID in keychain."); inf(@"Updating key ID in keychain.");
[PearlKeyChain addOrUpdateItemForQuery:keyHashQuery() [PearlKeyChain addOrUpdateItemForQuery:keyIDQuery()
withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
self.keyHash, (__bridge id)kSecValueData, self.keyID, (__bridge id)kSecValueData,
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible,
#endif #endif

View File

@ -9,8 +9,7 @@
@interface MPAppDelegate_Shared : PearlAppDelegate @interface MPAppDelegate_Shared : PearlAppDelegate
@property (strong, nonatomic) NSData *key; @property (strong, nonatomic) NSData *key;
@property (strong, nonatomic) NSData *keyHash; @property (strong, nonatomic) NSData *keyID;
@property (strong, nonatomic) NSString *keyID;
+ (MPAppDelegate_Shared *)get; + (MPAppDelegate_Shared *)get;

View File

@ -11,7 +11,6 @@
@implementation MPAppDelegate_Shared @implementation MPAppDelegate_Shared
@synthesize key; @synthesize key;
@synthesize keyHash;
@synthesize keyID; @synthesize keyID;
+ (MPAppDelegate_Shared *)get { + (MPAppDelegate_Shared *)get {

View File

@ -265,7 +265,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
if (!headerPattern || !sitePattern) if (!headerPattern || !sitePattern)
return MPImportResultInternalError; return MPImportResultInternalError;
NSString *keyID = nil; NSString *keyIDHex = nil;
BOOL headerStarted = NO, headerEnded = NO; BOOL headerStarted = NO, headerEnded = NO;
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSMutableSet *elementsToDelete = [NSMutableSet set]; NSMutableSet *elementsToDelete = [NSMutableSet set];
@ -295,7 +295,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
NSString *key = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]]; NSString *key = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]];
NSString *value = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]]; NSString *value = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]];
if ([key isEqualToString:@"Key ID"]) { if ([key isEqualToString:@"Key ID"]) {
if (![(keyID = value) isEqualToString:[keyHashForPassword(password) encodeHex]]) if (![(keyIDHex = value) isEqualToString:[keyIDForPassword(password) encodeHex]])
return MPImportResultInvalidPassword; return MPImportResultInvalidPassword;
} }
@ -303,7 +303,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
} }
if (!headerEnded) if (!headerEnded)
continue; continue;
if (!keyID) if (!keyIDHex)
return MPImportResultMalformedInput; return MPImportResultMalformedInput;
if (![importedSiteLine length]) if (![importedSiteLine length])
continue; continue;
@ -321,7 +321,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
NSString *exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]]; NSString *exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
// Find existing site. // 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]; NSArray *existingSites = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error) if (error)
err(@"Couldn't search existing sites: %@", error); err(@"Couldn't search existing sites: %@", error);
@ -352,11 +352,11 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
NSString *exportContent = [siteElements objectAtIndex:4]; NSString *exportContent = [siteElements objectAtIndex:4];
// Create new site. // 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) MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:ClassNameFromMPElementType(type)
inManagedObjectContext:self.managedObjectContext]; inManagedObjectContext:self.managedObjectContext];
element.name = name; element.name = name;
element.keyID = keyID; element.keyID = [keyIDHex decodeHex];
element.type = type; element.type = type;
element.uses = uses; element.uses = uses;
element.lastUsed = [lastUsed timeIntervalSinceReferenceDate]; element.lastUsed = [lastUsed timeIntervalSinceReferenceDate];
@ -386,7 +386,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
[export appendFormat:@"# \n"]; [export appendFormat:@"# \n"];
[export appendFormat:@"##\n"]; [export appendFormat:@"##\n"];
[export appendFormat:@"# Version: %@\n", [PearlInfoPlist get].CFBundleVersion]; [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]]]; [export appendFormat:@"# Date: %@\n", [rfc3339DateFormatter stringFromDate:[NSDate date]]];
if (showPasswords) if (showPasswords)
[export appendFormat:@"# Passwords: VISIBLE\n"]; [export appendFormat:@"# Passwords: VISIBLE\n"];

View File

@ -13,7 +13,7 @@
@interface MPElementEntity : NSManagedObject @interface MPElementEntity : NSManagedObject
@property (nonatomic, retain) NSString *name; @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 type;
@property (nonatomic, assign) int16_t uses; @property (nonatomic, assign) int16_t uses;
@property (nonatomic, assign) NSTimeInterval lastUsed; @property (nonatomic, assign) NSTimeInterval lastUsed;

View File

@ -77,8 +77,8 @@ typedef enum {
#define MPNotificationKeyForgotten @"MPNotificationKeyForgotten" #define MPNotificationKeyForgotten @"MPNotificationKeyForgotten"
NSData *keyForPassword(NSString *password); NSData *keyForPassword(NSString *password);
NSData *keyHashForPassword(NSString *password); NSData *keyIDForPassword(NSString *password);
NSData *keyHashForKey(NSData *key); NSData *keyIDForKey(NSData *key);
NSString *NSStringFromMPElementType(MPElementType type); NSString *NSStringFromMPElementType(MPElementType type);
NSString *ClassNameFromMPElementType(MPElementType type); NSString *ClassNameFromMPElementType(MPElementType type);
Class ClassFromMPElementType(MPElementType type); Class ClassFromMPElementType(MPElementType type);

View File

@ -24,14 +24,14 @@ NSData *keyForPassword(NSString *password) {
NSData *key = [PearlSCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding] NSData *key = [PearlSCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding]
usingSalt:MP_salt N:MP_N r:MP_r p:MP_p]; 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; 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]; 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" MPTypes_ciphers = [NSDictionary dictionaryWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"ciphers"
withExtension:@"plist"]]; 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); uint32_t nsalt = htonl(salt);
trc(@"key hash from: %@-%@-%u", name, key, nsalt); trc(@"seed from: sha1(%@, %@, %u)", name, key, nsalt);
NSData *keyHash = [[NSData dataByConcatenatingWithDelimitor:'-' datas: NSData *seed = [[NSData dataByConcatenatingWithDelimitor:'\0' datas:
[name dataUsingEncoding:NSUTF8StringEncoding], [name dataUsingEncoding:NSUTF8StringEncoding],
key, key,
[NSData dataWithBytes:&nsalt length:sizeof(nsalt)], [NSData dataWithBytes:&nsalt length:sizeof(nsalt)],
nil] hashWith:PearlDigestSHA1]; nil] hashWith:PearlDigestSHA1];
trc(@"key hash is: %@", keyHash); trc(@"seed is: %@", seed);
const char *keyBytes = keyHash.bytes; const char *seedBytes = seed.bytes;
// Determine the cipher from the first hash byte. // Determine the cipher from the first seed byte.
assert([keyHash length]); assert([seed length]);
NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:ClassNameFromMPElementType(type)] NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:ClassNameFromMPElementType(type)]
valueForKey:NSStringFromMPElementType(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); trc(@"type %d, ciphers: %@, selected: %@", type, typeCiphers, cipher);
// Encode the content, character by character, using subsequent hash bytes and the cipher. // Encode the content, character by character, using subsequent seed bytes and the cipher.
assert([keyHash length] >= [cipher length] + 1); assert([seed length] >= [cipher length] + 1);
NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]]; NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]];
for (NSUInteger c = 0; c < [cipher length]; ++c) { 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 *cipherClass = [cipher substringWithRange:NSMakeRange(c, 1)];
NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass]; NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass];
NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange(keyByte % [cipherClassCharacters length], 1)]; NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange(keyByte % [cipherClassCharacters length], 1)];

View File

@ -29,7 +29,6 @@
@synthesize passwordWindow; @synthesize passwordWindow;
@synthesize key; @synthesize key;
@synthesize keyHash;
@synthesize keyID; @synthesize keyID;
#pragma GCC diagnostic ignored "-Wfour-char-constants" #pragma GCC diagnostic ignored "-Wfour-char-constants"

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1171" systemVersion="11E53" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic"> <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1171" systemVersion="11E53" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES"> <entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
<attribute name="keyID" attributeType="String" indexed="YES" syncable="YES" isSyncIdentityProperty="YES"/> <attribute name="keyID" attributeType="Binary" indexed="YES" syncable="YES" isSyncIdentityProperty="YES"/>
<attribute name="lastUsed" attributeType="Date" syncable="YES"/> <attribute name="lastUsed" attributeType="Date" syncable="YES"/>
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES" isSyncIdentityProperty="YES"/> <attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES" isSyncIdentityProperty="YES"/>
<attribute name="type" attributeType="Integer 16" defaultValueString="16" syncable="YES"/> <attribute name="type" attributeType="Integer 16" defaultValueString="16" syncable="YES"/>