0

I'm developing an app that requires multiple passwords to access varying data areas. For example, a group of people could set up a chat that requires password authentication to view.

Here's how I'm considering doing it:

I have my keyword, let's say hypothetically:

Banana

When the user enters their password, I use RNCryptor to encrypt Banana using their entered key, and store that encrypted string to the server.

Later, when someone tries to enter a password, I take the hashed value from the server and try to decrypt it using the password they entered as a key. If the decrypted value equals Banana I know they entered the correct password.

I'm new to security, so I'm not sure if this would be an appropriate solution. All help is appreciated.

Update

After making some alterations suggested by @Greg and the aptly named @Anti-weakpasswords, here's what I have:

- (NSDictionary *) getPasswordDictionaryForPassword:(NSString *)password {

    NSData * salt = [self generateSalt256];
    NSData * key = [RNCryptor keyForPassword:password salt:salt settings:mySettings];

    NSMutableDictionary * passwordDictionary = [NSMutableDictionary new];

    NSString * saltString = stringFromData(salt);
    NSString * keyString = stringFromData(key);

    passwordDictionary[@"key"] = keyString;
    passwordDictionary[@"salt"] = saltString;
    passwordDictionary[@"version"] = @"1.0.0";
    passwordDictionary[@"iterationCount"] = @"10000";

    return passwordDictionary;
}

static const RNCryptorKeyDerivationSettings mySettings = {
    .keySize = kCCKeySizeAES256,
    .saltSize = 32,
    .PBKDFAlgorithm = kCCPBKDF2,
    .PRF = kCCPRFHmacAlgSHA1,
    .rounds = 10000
};

- (NSData *)generateSalt256 {
    unsigned char salt[32];
    for (int i=0; i<32; i++) {
        salt[i] = (unsigned char)arc4random();
    }
    NSData * dataSalt = [NSData dataWithBytes:salt length:sizeof(salt)];
    return dataSalt;
}
Logan
  • 52,262
  • 20
  • 99
  • 128
  • you solution looks good, but IMHO better way is store hash on server and send every guess password from client to check – sage444 Mar 21 '14 at 16:11

2 Answers2

5
  • Do not use a single pass of any hashing function to store passwords.
  • Do not fail to use a random salt in the 8-16 byte range.
  • Do not use reversible encryption to store passwords.
  • Do not use the password precisely as entered as your encryption key.

Instead, when the user is selecting a keyword/passphrase

  • Generate a cryptographically random 8-16 byte salt
  • Use PBKDF2, BCrypt, or SCrypt with said salt and as large an iteration count/work factor as your processors can handle to create a password hash
    • If you use PBKDF2 in specific, do not request a larger output than the native hash size (SHA-1 = 20 bytes, SHA-256 is 32 bytes, SHA-384 is 48 bytes, and SHA-512 is 64 bytes), or you increase the comparative advantage an attacker has over you, the defender.

Then in your database, you store that user's particular:

  • Salt in the clear
  • Iteration count/work factor
    • So you can easily change/upgrade it later
  • Resulting password hash
  • Version of authentication protocol - this would be 2, probably, or 1.
    • So you can easily change/upgrade it later if you move from this method to NewWellKnownMethod later

When the user wants to authenticate to your system, you:

  • Retrieve their version, salt, iteration count/work factor, and resulting hash from the database
  • Hash whatever keyword/password they just entered with the salt and iteration count/work factor from the database.
  • Compare the result you just got with what was in the database; if they're the same, let them in.
    • Advanced: use a constant time compare, so it doesn't just quit trying if the first byte is different, to reduce the vulnerability to timing attacks.

Please read How to securely hash passwords?, of which Thomas Porrin's answer is currently the most commonly referred to Stackexchange treatise on password hashing, and certainly the best I've seen so far.

Community
  • 1
  • 1
Anti-weakpasswords
  • 2,604
  • 20
  • 25
  • Thanks so much for the answer, would you mind doing a brief check of how I reimplemented it? Should salt be 8 - 16 as opposed to 32 in my example? – Logan Mar 23 '14 at 04:05
  • A longer salt is not a problem; it's just not a benefit. Try some benchmarking, and select however many iterations take the amount of time you're willing to spend (though 10000 is a reasonable hard minimum for SHA-1). Assuming you're on a 64-bit system, changing to kCCPRFHmacAlgSHA384 or kCCPRFHmacAlgSHA512 will decrease the relative advantage 2014 vintage GPU's have over your CPU due to 64-bit operations that perform poorly on the current generation of GPU's. You may also want to check on arc4random(); FreeBSD, at least, once had entropy issues just after boot with it (SA-08.11). – Anti-weakpasswords Mar 23 '14 at 04:24
  • Note that you could get a much more thorough general review over on the [Code Review Stackexchange](https://codereview.stackexchange.com/) site. – Anti-weakpasswords Mar 23 '14 at 04:27
  • I'll bump up the iterations as high as I can. I'm not sure how to replace arc4random(), but I'll see what I find. Thanks for explaining some of this stuff! – Logan Mar 23 '14 at 04:28
  • Excellent on the iteration increase! I wouldn't say replace arc4random(), but I would say check and see if it's got any major flaws in your particular system. – Anti-weakpasswords Mar 23 '14 at 04:35
1

It's not good way of doing that. You should use one way hash algorithm to hash the password (you won't be able to decrypt it). After you hash the password you save it to database after the user provide password you have to hash it and compare it (the hash value) with the hash you store in database. If it matches it means it's the same if not the authentication failed.

In that way even if someone gain access to the database the data will be secured, he won't do anything with that, it's not password stored there.

Most of the authentication is made that way.

//Extended

You should use some hash algorithm made for this kind of job.

Check out SHA or MD5

Community
  • 1
  • 1
Greg
  • 25,317
  • 6
  • 53
  • 62
  • I've noticed with RNCryptor I'll occasionally get different hashes for the same value. Wouldn't this cause complications if it doesn't generate the same hash? – Logan Mar 21 '14 at 16:12
  • 1
    @Greg SHA or MD5 is not really encryption in our time – sage444 Mar 21 '14 at 16:16
  • @sage444 sorry, what do you mean? SHA1 or MD5 are one way hashing function and are widely use for store passwords. – Greg Mar 21 '14 at 16:22
  • I mean that is old algorithms, and there is a lot of examples where it was cracked. But you are right that hashing is definitely right way to save passwords – sage444 Mar 21 '14 at 16:26
  • Greg & @Sage444 - Even though my current method involves no saving of the user's actual password anywhere, this would still be a preferred solution? – Logan Mar 21 '14 at 16:27
  • Yes. Your example - banana keyword is password so you shouldn't save it as a pure string in database, it should be a hash instead. You don't have to save user password, just hash it and compare with hash from database. – Greg Mar 21 '14 at 16:30
  • @Logan, yes, is rigth – sage444 Mar 21 '14 at 16:31
  • @Greg I think MD5 should be removed from above. Really not a good idea recommending it. – CtrlDot Mar 21 '14 at 21:38