Using password-based encryption as a hashing scheme Classic scheme:


Given password and hashing algorithm
Generate salt
Calculate hash=hashing(password, salt)
Separately storing hash &amp; salt


Checking password validity: comparing stored hash with calculated test_hash=hashing(test_password, salt). If hash=test_hash - password is ok, otherwise test failed.

Proposed scheme:


Given password, cipher algorithm and predefined dictionary (e.g. set of digits {0,1,2,3,4,5,6,7,8,9})
Generate salt
Generate random test_phrase from our dictionary (e.g. only random digits, like test_phrase=32759823767865236451236647894678923897)
Calculate hash=cipher(test_phrase) with key=keygen(password,salt)
Storing hash and salt (no need to store test_phrase)


Checking password validity: 


calculating test_phrase=decipher(hash) for a test_key=keygen(test_password,salt)
check if test_phrase consists only symbols/bytes of predefined dictionary (e.g. decrypted phrase are only digits, say if decrypted phrase is smth like: askjdfhasjkdfhasjkdhe-0123=jkasfhasjdfh - that means test failed, since some symbols are not digits)


Question

What do you think about pros/cons for this proposed hashing scheme?
Essentially, instead of checking against a (salted) hash of a password, you suggest using the hash (since you can choose hashing = keygen) as a key to encrypt a kind of test value. The main question is whether this adds or reduces security.


If you store the hash/key directly, the chance of a randomly chosen password hashing to the same value is $2^{-n}$, where $n$ is the length of the hash in bits.
If you use the key to encrypt a string from the dictionary in your example (10 out of 256), the chance that a random key (from a password guess) decrypts the stored value to a valid $k$-bit string is $(10/256)^{k/8} = 25.6^{-k/8} \approx 1.5^{-k}$.


To have equivalent security you need to store a longer verification string. For example, to match the security of a 256-bit hash value, you'd need to encrypt a 439-bit value.

In addition, the whole process would allow an attacker to discard wrong guesses slightly faster than the verification process takes, since most encryption algorithms encrypt in blocks and if the first block is a mismatch, the whole thing must be.

That's without considering the inherent insecurity in using a complex scheme that relies on multiple primitives for its security.



You could get back to an equal length-to-security ratio by encrypting a known string (like a string of zeros) instead. The downside of complexity would remain, without a clear upside to compensate. It could be more resistant to a weak hash, but that might be difficult to prove.