Algorithms

EVP_BytesToKey

The key and IV behind openssl enc without -pbkdf2 and behind CryptoJS with a passphrase. Old and weak and still everywhere
IDevp-bytestokey27 / 27OpenSSL · variable
Hash / Password

EVP_BytesToKey

Key and IV from a passphrase, the openssl enc way.

Digest
keyLength you pick
HMAC
no key mode
Security
Weak: one fast hash per block
Family
1 in OpenSSL
6 options, 0 required

Sample

hex
e1f19169a46a95b1519b3756c9ba356808e453a65231e9a00335d06d17c2332d893e6e36d41eeecbaea88a4de98e82d0
base64
4fGRaaRqlbFRmzdWybo1aAjkU6ZSMemgAzXQbRfCMy2JPm421B7uy66oik3pjoLQ

Options

encodingstring
default hex
Output encoding: hex, base64, base64url, binary
saltstring
optional
Salt in hex, 8 bytes; none when omitted
digeststring
default md5
Hash per block: md5, sha1, sha256. openssl enc uses sha256 since 1.1.0
iterationsnumber
default 1
Hash passes per block
keyLengthnumber
default 32
Key bytes, the first part of the digest
ivLengthnumber
default 16
IV bytes after the key, 0 for none

Access

Createcreate("evp-bytestokey")
CLIhashes evp-bytestokey 'hello world' --salt 0102030405060708
Tryplayground with the sample above

Ever seen base64 that starts with U2FsdGVkX1? That's Salted__, and somebody ran openssl enc or CryptoJS.AES.encrypt(message, passphrase). Both get the key and the IV from the passphrase with EVP_BytesToKey. CryptoJS calls it EvpKDF. Same recipe, two names.

And the recipe is short. Hash the password and the salt. Hash that block again with the password and the salt behind it. Repeat until the key and the IV fit. Set iterations and each block goes through the hash that many times before the next one starts.

ts
create("evp-bytestokey").hash("password", { salt: "0102030405060708" }).digest;
// "e7b0971e52ca5cc8d0539fb3412f6316f7ba2e6ee293d9f3457b99436b51ce028d450e2ed75a84a923d4eac9fe49226b"

That's openssl enc -aes-256-cbc -P -md md5 -pass pass:password -S 0102030405060708, byte for byte. The first 32 bytes are the key and the last 16 the IV. Where's the split? keyLength and ivLength set it, and the result names both.

digest is md5 by default. CryptoJS uses it, and so did OpenSSL before 1.1.0. Newer openssl enc takes sha256, and sha1 is there too. The salt is the 8 bytes after Salted__, in hex. Leave it out and there's no salt, like -nosalt. Not a random one, because the format keeps its salt next to the ciphertext anyway. CryptoJS lets a page ask for any key size, so keyLength goes way past 32. A cipher without an IV takes ivLength 0.

Encrypt something new with it? Please don't. One MD5 per block is nothing for a GPU. It's here to open what somebody already locked. Your own keys want scrypt.