speak, it ain't illegal yet

November 15, 2006

Adding AES Encryption To Erlang Chat

Filed under: aes,chat,cryptography,dh,diffie-hellman,echat,encryption,erlang,irc — Jordan Wilberding @ 5:01 am

I have been working on adding encryption to an IRC clone that I am working on with Tristan Sloughter called EChat. Just like IRC, in normal operation EChat sends all communications to the server, then the server figures out who needs to get what. So, for my initial stab at adding encryption to EChat, I decided to encrypt the transmissions between clients and the server, instead of end to end.

Client1 Server Client2

So basically, the server does all the key management. Since AES requires a symmetric 128-bit key, we need a method of having each client the server generate a key. That is where Diffie-Hellman(DH) comes in!

DH is a quick and easy way to create symmetric keys between two people without ever having to reveal what the key actually is in plain text.

DH can be explained alot better here, than I can do, so I won’t spend the time going over it. However I will talk about a few specifics of my implementation in Erlang.

In the algorithm, a and b both need to be a certain size in order to guarantee there are 128 bits in the final key. Therefore, to create them I simply did the following:

gen_DHa() -> gen_DHb().
gen_DHb() -> crypto:rand_uniform(170141183460469231731687303715884105729,
340282366920938463463374607431768211455).

For the curious, 2^127+1 = 170141183460469231731687303715884105729,
and 2^128-1 = 340282366920938463463374607431768211455. Generating a number in this range assures us we get a number that is at least 128 bits.

Also, to generate g and h, I just did the following:

gen_DHp() -> gen_DHg().
gen_DHg() -> make_prime(50).

On the client side, we calculate A as follows:

DHg = ecrypt:gen_DHg(),
DHp = ecrypt:gen_DHp(),
DHa = ecrypt:gen_DHa(),
A = crypto:mod_exp(DHg, DHa, DHp).

On the server side we calculate B as follows:

DHb = ecrypt:gen_DHb(),
B = crypto:mod_exp(DHg, DHb, DHp).

Now the client and server exchange A and B, and each can calculate the key
appropriately:

Client: Key = crypto:mod_exp(B, DHa, DHp).
Server: Key = crypto:mod_exp(A, DHb, DHp).

Now we have a key that is at least 128 bits. We want to make sure it is exactly 128 bits though, so I wrote the following function that ensures this. All it does is generate exactly 16 bytes from the stream of bits:

integerlist_to_key([], Key, _) -> lists:concat(lists:sublist(Key, 16));
integerlist_to_key([Head | Tail], Key, Tmp) ->
Test = list_to_integer(lists:reverse([Head | Tmp])),
if
Test > 255 -> integerlist_to_key(Tail, [list_to_atom([list_to_integer(Tmp)])| Key], [Head]);
true -> integerlist_to_key(Tail, Key, lists:reverse([Head | Tmp]))
end.

Now that we have a 128-bit symmetric key for both the client and server, we can start using AES encryption!

To find out about AES, read here. Basically AES requires an initialization vector(IV) for each message, as well as a symmetric key, and whatever text you want to encode.

To create the Encrypt and Decrypt functions, I decided just to append the IV to the front of each message sent. The IV does not need to be kept secret, and is only used to deter statistical attacks on the encrypted message. Thus, the following code shows how I did the encrypt and decrypt:

encrypt(Message, Key) ->
IV = crypto:rand_bytes(16),
list_to_binary([IV] ++ [crypto:aes_cfb_128_encrypt(Key, IV, Message)]).

decrypt(Message, Key) ->
{IV, Crypt} = lists:split(16, binary_to_list(Message)),
binary_to_list(crypto:aes_cfb_128_decrypt(Key, list_to_binary(IV),
list_to_binary(Crypt))).

That’s all there is to it! Now you can add AES encryption to your application, whether it is for messaging or file storage. If you would like to find out more about the Erlang AES implementation, go here.