How We Generate Encryption Keys From Pass Phrases

Humans like words, and computers like binary, so we need to find ways where we can generate encryption keys that we can remember. One…

How We Generate Encryption Keys From Pass Phrases

Humans like words, and computers like binary, so we need to find ways where we can generate encryption keys that we can remember. One method is to use a Key Derivation Function (KDF).

One method is HKDF, and which is a HMAC method for key derivation. Initially HKDF creates a pseudorandom key (PRK) using a pass phrase and a salt value (and any other random functions which are relevant), in order to produce an HMAC hash function (such as HMAC-SHA256), and along with a salt value. Next the PRK output is used to produce a key of the required length. If we generate a 16-byte output (32 hex characters), we have a 128-bit key, and a 32-byte output (64 hex characters) will generate a 256-bit key. HKDF is used in TLS 1.3 for generating encryption keys.

A sample run is [here]:

Message:	hello123
Salt: 8e94ef805b93e683ff18
===================
PRK: e229a4a30ea99b3bac27d233cef0d1feb4be4dcf6531f86d9ce521f9b5af19324fdeb74622bbb52353563cbd37a552c615daf696541f461428aaa39481c60559
Key: e786fca9472ab083e5bb84c55fe6b581

The output of the first stage is the pseudorandom key. In this case we have a key of “e786fca9472ab083e5bb84c55fe6b581” which is 32 hex characters, and is thus 128-bits (16 bytes).

An outline of the code is:

import sys
from binascii import unhexlify,hexlify,b2a_base64
import hashlib
import hmac
def hkdf_extract(salt, input_key_material, hash=hashlib.sha512):
	hash_len = hash().digest_size
if salt == None or len(salt) == 0:
salt = chr(0) * hash_len
return hmac.new(salt, input_key_material, hash).digest()
def hkdf_expand(pseudo_random_key, info="", length=32, hash=hashlib.sha512):
	hash_len = hash().digest_size
length = int(length)
blocks_needed = length / hash_len + (0 if length % hash_len == 0 else 1)
okm = ""
output_block = ""
for counter in range(blocks_needed):
output_block = hmac.new(pseudo_random_key, output_block + info + chr(counter + 1), hash).digest()
okm += output_block
return okm[:length]
password='hello'
salt="8e94ef805b93e683ff18"
prk = hkdf_extract(unhexlify(salt), password)
key = hkdf_expand(prk, b"", 16)
print "Message:\t",password
print "Salt:\t\t",salt
print "==================="
print "Key (Hex):\t\t",hexlify(key)
print "Key (Base-64):\t\t",b2a_base64(key)

In this case we are using hash=hashlib.sha512, and which generates a 512-bit key. For the hashing function we could also use md5() [128-bit key], sha1() [160-bit key], sha224() [224-bit key], sha256() [256-bit key], or sha384() [384-bit key].

Increasingly we are using pass phrases instead of passwords, and where we can select words which make a phrase. Many cryptocurrency wallet methods now use this method in order to generate the protection on the wallet. An example is shown on the left-hand side provides 12 words in a sequence. These words can then be built into a phrase which can then be used to generate the encryption key which protects the wallet.

The method was created by Hugo Krawczyk and is standardised with RFC 5869. A important focus is that it will always produce the same key for the same input phrase. It was also published as a paper here.