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;
}
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

View File

@ -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;

View File

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

View File

@ -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"];

View File

@ -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;

View File

@ -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);

View File

@ -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)];

View File

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

View File

@ -1,7 +1,7 @@
<?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">
<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="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES" isSyncIdentityProperty="YES"/>
<attribute name="type" attributeType="Integer 16" defaultValueString="16" syncable="YES"/>