Fairy tales in password hashing with scrypt

Update: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 author of scrypt said that he's added a warning on top of scryptenc.h.

TL;DR: scrypt is a password-based key derivation function that is often used as a password hashing scheme. libscrypt is considered cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 official implementation of scrypt, but it's actually a file encryption API with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scrypt function never exposed directly. If one misuses libscrypt for password hashing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y may end up with extremely weak hashes.

Password-based key derivation functions (PBKDF) are algorithms that take a password and some non-secret data (e.g., salt), and output one or more secret keys that can be furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r used in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r crypto constructions. These functions are generally made deliberately slow so as to frustrate brute-force attack or dictionary attack on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input password.

scrypt is considered a state-of-cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365-art PBKDF, but its most common use is as a password hashing scheme, even though it wasn't originally designed for this purpose. The author of scrypt has never released a standalone library for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scrypt function; he's released only a utility, confusingly named scrypt, that uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scrypt function to implement an encryption API.

This makes a lot of senses, as scrypt was originally built for Tarsnap, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 online file backup service built by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same person. It's also, however, a crypto disaster waiting to happen: if one uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file encryption API as a password hashing scheme cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y may end up with extremely weak hashes. Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 introduction of scrypt I've seen a few of such misuses, all of which were made by ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise competent programmers.

The scrypt encryption API is declared as follows:

/**
 * scryptenc_buf(inbuf, inbuflen, outbuf, passwd, passwdlen,
 *     maxmem, maxmemfrac, maxtime):
 * Encrypt inbuflen bytes from inbuf, writing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resulting inbuflen + 128
 * bytes to outbuf.
 */
int scryptenc_buf(const uint8_t *, size_t, uint8_t *,
    const uint8_t *, size_t, size_t, double, double);

The intended usage of this function is to derive a key from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 password using scrypt (with a randomly generated salt,) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 derived key to encrypt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input buffer. I've found that many people that want to use scrypt as a password hashing function end up using scryptenc_buf.

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 programmer used scryptenc_buf to encrypt an empty string using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input password as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 key, and saved cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 password hash. There's no badness: although cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hash isn't compatible with scrypt anymore, it still has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same strength. It went downhill very fast from here though.

The second programmer used cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same function, but she encrypted cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input password with a static key. Since scryptenc_buf generates a random salt for each invocation, each password is probably encrypted with a unique key (derived from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 random salt and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 static key,) but it's still pretty bad: if anyone obtains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 static key cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can recover all passwords from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir hashes. The developer knew that encrypting password is a bad idea, but she was confused by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 API. After all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hashes looked random. As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 saying goes, bad crypto is usually indistinguishable from good crypto.

The third programmer, for some reason that I've forgotten, wanted to use a single salt for all users, so he modified scryptenc_buf as follows:

scryptenc_buf(const uint8_t * inbuf, size_t inbuflen, uint8_t * outbuf, const uint8_t * passwd,
    size_t passwdlen, const uint8_t * salt, size_t saltlen,
    size_t maxmem, double maxmemfrac, double maxtime);

Like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second programmer, he encrypted cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input password with a static key. With a static salt, all passwords are encrypted under one single key. The encryption algorithm used in scryptenc_buf is AES in counter mode with a zero IV. Did you spot cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerability? It's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 classic keystream reuse: knowing a single password leads to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recovery of all passwords from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir hashes. If one can register as a user cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can recover all passwords. How comes hashing password introduces such a deadly vulnerability? I run out of people to blame.

The last case was a group of Java programmers. One of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m wrote a JNI wrapper on top of libscrypt. The wrapper accepted a memcost parameter, which should be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same as \(N\) in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original scrypt paper, but somehow its author wanted it to be \(\log{N}\). When anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r programmer called this function he passed, however, \(N\), so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual memcost parameter became super big. This mismatch should be caught easily, as libscrypt would return an error code if it couldn't allocate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 required memory. Checking return value for errors seems not to be, however, a popular pattern in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Java world.

As a result no password was hashed, and all stored hashes were a series of zero bytes. Anyone could sign in to anybody else's accounts using any passwords. Fortunately, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y brought my consulting team in very early in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 development process, and I found cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y had any real users. Moral of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 story? Always look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output of your crypto.

If I develop a crypto library, I'll conduct user studies like how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y do it in usability research. Give developers cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 library and ask cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m to conduct a specific task. Rinse and repeat until nobody would misuse it.

Comments

Anonymous said…
I don't get your last example. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker doesn't know cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 static key cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n how can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack work?

- http://security.stackexchange.com/questions/5355/compute-cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365-aes-encryption-key-given-cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365-plaintext-and-its-ciphertext
Anonymous said…
Ah, I see now what you're talking about: https://en.wikipedia.org/wiki/Stream_cipher_attack