2
0

Improved algorithm security.

[UPDATED]   Algorithm updated to reflect advice from randombit.net
            cryptography list:
                - Add in a salt (user name) to defeat rainbow tables.
                - Add in a fixed string to scope the algorithm and avoid
                  colliding with someone else's similar or identical
                  algorithm (also helps protect against precalculated
                  rainbow tables).
                - Use HMAC instead of plain SHA to avoid SHA weaknesses.
                  The old implementation wasn't vulnerable to extension
                  attacks or other known weaknesses, but HMAC is a safer
                  choice and will bring up less suspicion.
                - Prefix strings by length as an extra precautionary
                  measure against possible bugs in hash functions.
This commit is contained in:
Maarten Billemont 2012-06-07 00:12:38 +02:00
parent b472c85c9d
commit 679990dc4b
5 changed files with 30 additions and 25 deletions

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit 3a61ba22afefb23c2f87a3836e8b996d2b5a96fb
Subproject commit e55ef6876ee26f61a7cd2c075fc1e7a942016de0

View File

@ -62,7 +62,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
if (![tryPassword length])
return NO;
NSData *tryKey = keyForPassword(tryPassword);
NSData *tryKey = keyForPassword(tryPassword, user.name);
NSData *tryKeyID = keyIDForKey(tryKey);
inf(@"Key ID was known? %@.", user.keyID? @"YES": @"NO");
if (user.keyID) {

View File

@ -269,7 +269,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
user = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] lastObject];
}
if ([key isEqualToString:@"Key ID"]) {
if (![(keyIDHex = value) isEqualToString:[keyIDForPassword(password) encodeHex]])
if (![(keyIDHex = value) isEqualToString:[keyIDForPassword(password, userName) encodeHex]])
return MPImportResultInvalidPassword;
}

View File

@ -78,8 +78,8 @@ typedef enum {
#define MPNotificationKeyForgotten @"MPNotificationKeyForgotten"
#define MPNotificationElementUsed @"MPNotificationElementUsed"
NSData *keyForPassword(NSString *password);
NSData *keyIDForPassword(NSString *password);
NSData *keyForPassword(NSString *password, NSString *username);
NSData *keyIDForPassword(NSString *password, NSString *username);
NSData *keyIDForKey(NSData *key);
NSString *NSStringFromMPElementType(MPElementType type);
NSString *ClassNameFromMPElementType(MPElementType type);

View File

@ -11,24 +11,28 @@
#import "MPElementStoredEntity.h"
#define MP_salt nil
#define MP_N 131072
#define MP_r 8
#define MP_p 1
#define MP_dkLen 64
#define MP_hash PearlDigestSHA256
#define MP_hash PearlHashSHA256
NSData *keyForPassword(NSString *password) {
NSData *keyForPassword(NSString *password, NSString *username) {
uint32_t nusernameLength = htonl(username.length);
NSData *key = [PearlSCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding]
usingSalt:MP_salt N:MP_N r:MP_r p:MP_p];
usingSalt:[NSData dataByConcatenatingDatas:
[@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding],
[NSData dataWithBytes:&nusernameLength length:sizeof(nusernameLength)],
[username dataUsingEncoding:NSUTF8StringEncoding],
nil] N:MP_N r:MP_r p:MP_p];
trc(@"Password: %@ derives to key ID: %@", password, [keyIDForKey(key) encodeHex]);
trc(@"User: %@, password: %@ derives to key ID: %@", username, password, [keyIDForKey(key) encodeHex]);
return key;
}
NSData *keyIDForPassword(NSString *password) {
NSData *keyIDForPassword(NSString *password, NSString *username) {
return keyIDForKey(keyForPassword(password));
return keyIDForKey(keyForPassword(password, username));
}
NSData *keyIDForKey(NSData *key) {
@ -116,32 +120,33 @@ NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *key, ui
err(@"Incorrect type (is not MPElementTypeClassGenerated): %d, for: %@", type, name);
return nil;
}
if (!name) {
if (!name.length) {
err(@"Missing name.");
return nil;
}
if (!key) {
err(@"Key not set.");
if (!key.length) {
err(@"Missing key.");
return nil;
}
uint32_t salt = counter;
if (!counter)
// Counter unset, go into OTP mode.
// Get the UNIX timestamp of the start of the interval of 5 minutes that the current time is in.
salt = ((uint32_t)([[NSDate date] timeIntervalSince1970] / 300)) * 300;
counter = ((uint32_t)([[NSDate date] timeIntervalSince1970] / 300)) * 300;
if (MPTypes_ciphers == nil)
MPTypes_ciphers = [NSDictionary dictionaryWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"ciphers"
withExtension:@"plist"]];
// Determine the seed whose bytes will be used for calculating a password: sha1(name . '\0' . key . '\0' . salt)
uint32_t nsalt = htonl(salt);
trc(@"seed from: sha1(%@, %@, %u)", name, key, nsalt);
NSData *seed = [[NSData dataByConcatenatingWithDelimitor:'\0' datas:
// Determine the seed whose bytes will be used for calculating a password
trc(@"seed from: hmac-sha256(key, 'com.lyndir.masterpassword' | %u | %@ | %u)", key, name.length, name, counter);
uint32_t ncounter = htonl(counter), nnameLength = htonl(name.length);
NSData *seed = [[NSData dataByConcatenatingDatas:
[@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding],
[NSData dataWithBytes:&nnameLength length:sizeof(nnameLength)],
[name dataUsingEncoding:NSUTF8StringEncoding],
key,
[NSData dataWithBytes:&nsalt length:sizeof(nsalt)],
nil] hashWith:PearlDigestSHA256];
[NSData dataWithBytes:&ncounter length:sizeof(ncounter)],
nil]
hmacWith:PearlHashSHA256 key:key];
trc(@"seed is: %@", seed);
const char *seedBytes = seed.bytes;