-1

I would like to seek for another way to save the credentials, which is used to verify the user trying to login, into my database.

I came up with this idea. Since I believe that even knowing a plaintext, and a corresponding ciphertext derived with the AES-256 cipher should not lead to the revealing of the key, when the key is properly long and random enough, may I take the password as the encryption key here?

Consider an example, suppose user 'test' should use 'apple' as his password. In the registration, the program works like this:

  1. Choose a random string of 32 bytes. Denoted by R;
  2. Construct the plaintext P, by P = Whirlpool(R:'test':'apple'); Here Whirlpool is the hash algorithm under same name.
  3. I would like to make the key more longer, so derive K as the key used for AES-256: K=Whirlpool('apple');
  4. Encrypt P by AES-256, thus get the ciphertext E=AES256Encrypt(K, P);
  5. So join R and E together, we get the credential AUTH that should be saved to database: AUTH=R:E.

And when we have later got one who enters the username, and password, we may verify that by doing:

  1. Look up for username, and fetch AUTH;
  2. Separate this AUTH and we got R and E;
  3. Use the given username and password, we could calculate P', which is based on R from database, the username and the password from input.
  4. And we'll able to derive K', a hash in Whirlpool algorithm with this password as input.
  5. So we use K' to try to decrypt E, and when successfully decrypted, compare the decrypted with P'.

In short: may I use my password as the key in AES-256, and by comparing a known plaintext and the decryption result of stored ciphertext, to verify users, and to avoid leaking users' passwords after the database is somehow leaked?

In my view only knowing the username(with does little here) and the AUTH should only leak the hints of how to construct a possible decrypt key. The attacker is able to know both a quite randomized plaintext(and he have no choice of customize) and a fixed ciphertext. And if here is AES-256, there should be no possibility to reveal the key(which is not the real password) basing on only this two factors.

And the randomness of this plaintext(derived from a conjunction of R, username, password) is here also not important. But seems the randomness of the key may tell.

The last aspect of consideration may be the speed. To me, the speed is not a really problem, at least now. And although above process have used(either in registration or in verification) 2 times of hashing and one time of AES, we may however reduce the Whirlpool to only once. When I compare this to HMAC with whirlpool(two times of Whirlpool is used here), I see here is only one time of Whirlpool and one time of AES. Should AES faster than a hash algorithm like Whirlpool, I think there may be an advantage. But I'd appreciate anyone who would refer to this aspect.

Lucifer Orichalcum
  • 715
  • 1
  • 5
  • 11

2 Answers2

2

What you are proposing here is nothing else than building your own hash function out of a block cipher, by inputting the data to hash as the key for the block cipher, and encrypting a "known value". This is, in fact, a well-known and well-studied way of building hash functions, known as Merkle-Damgård. MD5, SHA-1, SHA-256, SHA-512... are designed that way. So is, indeed, Whirlpool.

This latter case is interesting: Whirlpool is indirectly based on AES; however, the designers of Whirlpool found it fit to "modify" the base block cipher into a new one, called "W". They wanted to change two things: they wanted bigger blocks (AES uses 128-bit blocks; its superset Rijndael goes to 256 bits; but they wanted a "512-bit hash function") and, more importantly, they needed a stronger key schedule.

There is a rather esoteric weakness of block ciphers known as related-key attacks which analyses what happens when several encryption keys are used with specific differences between the keys. Related-key attacks are not a problem for block ciphers when they are used for what they were designed for, which is encryption. Thus, we are not overly worried with AES which has been shown to be suboptimal with regards to related-keys (its "key schedule", the part which derives the key into the 11 to 15 subkeys needed for encryption, does not protect well against related-keys). However, if you use the block cipher as the building block in a MD hash function, then related-key attacks translate quite immediately to collision attacks. This is the reason why the designers of Whirlpool found it fit to alter the key schedule of W into something which is more expensive (needs more CPU) but also stronger against related-keys than the base Rijndael key schedule.

So what you suggest is, basically, to build a hash function in a way which the Whirlpool designers also considered, but rejected as "too weak". This does not bode well. The usual warning against homemade crypto is this: you cannot know if your construction is secure until many cryptographers have studied it for quite some time (years), and found nothing wrong in it. Here, some cryptographers have studied the suggested construction, and did find something wrong with it.


All of that being said, even if turning AES-256 into a hash function with the MD structure was secure, it would do only part of the job. Indeed, password hashing has some extra requirements that normal hashing does not fulfil. Passwords are weak in the following sense: brute force works against them. Password hashing functions must cope with the low entropy of passwords in two ways:

  • You must not use one hash function, but a big family of hash functions, so that each instance of password hashing uses its own hash function. That's the concept behind salts. This prevents parallel attacks, in particular a specific class of parallel attacks known as "precomputed tables" (including rainbow tables). Your random "R" might play the role of a salt (but it is hard to tell until some in-depth analysis has been performed on how the salt is injected in the process).

  • The hash function must be slow. This is normally done with an iterated process, where the iteration count is in the thousands or even millions. Otherwise, enumerating potential passwords and trying them out is too efficient. The iteration count must also be regularly raised, to account for technology improvements (computers get faster over time, but not human brains). Your proposal is way too fast for comfort.

See this answer for a longer discussion and pointers.

Thomas Pornin
  • 320,799
  • 57
  • 780
  • 949
1

Firstly, it is a horrible horrible idea to roll your own cryptography scheme. Don't do it. Use bcrypt, scrypt or pbkdf2.

The encryption step in your scheme is pointless. It's just a replacing a quick compare of the hash output with a slightly less efficient encryption step. Your scheme essentially boils down to two hash iterations, one with R and the username acting as a salt, and one time without any salts whatsoever. This doesn't fix the problem of an attacker dumping your password database and running it through a password cracker which is precisely what slow password hashing schemes like bcrypt, scrypt, or pbkdf2 are trying to avoid.

  • I have searched for this site and saw a plenty of such advices. Thanks, but I would have not wrote my own site if I don't want to try something new, because all other functions of other programs as a site is already there. And to this topic, the two Whirlpool here are only trying to improve the randomness. You may just use the plain user-inputed password and a randomized string as the key and the plaintext of AES. So, It is not just a compare. And running crackers is also pointless. Should a cracker be able to solve, so will AES be cracked. – Lucifer Orichalcum Oct 20 '13 at 13:25
  • @LuciferOrichalcum You derive the key for AES encryption by hashing the password **ONCE**. This is trivial to bruteforce and has no bearing on the strength of AES as an encryption algorithm. It's fine for most things to want to try something new, but cryptography is **not** one of them. Stick with well tested schemes instead.... –  Oct 20 '13 at 13:29
  • You meant 'compare'. But in this scheme what you've get is my AES-output, and its plaintext. It is not enough like cracking a hash, just running and searching a key and hash it, because AES(I've use AES-CBC) produces different results even using same key and plaintext(there's random paddings there). You MUST use this key to DECRYPT given ciphertext to verify. – Lucifer Orichalcum Oct 20 '13 at 13:30
  • @LuciferOrichalcum I dump your database. I have the username, `R` and `E`. I randomly try deriving `P` and `K` through a bruteforce or dictionary attack by guessing the password until I find a value where `E = encrypt(k,P)`. Your scheme does not protect against this at all. –  Oct 20 '13 at 13:35
  • when you have picked(even the right) P and K and calculated E'=encrypt(k,p), you may not find `E'==E` because here should be using AES-256-CBC. But your idea looks right, just by decrypting `E` using your `K` and there will be `P'`, possibly equals to `P`. So we're really talking about the space of user's password(which is not a really large one)? – Lucifer Orichalcum Oct 20 '13 at 13:43