I had the discussion with some friends about symmetric encryption, and about the problem of security that it supposes that the encryption resulting of a block of data, for the same password, will be always the same. I write it here the explanations I tell them and what I do, so I can just send this link instead of explaining the same every time. :)
I am using different kinds of encryption for my new 2014’s Messenger C-Client.
The main advantage of symmetric encryption for me, is that is cheap in terms of CPU usage.
When I send files thought my messenger all the data blocks have to be encrypted, and so, if I send the Ubuntu 14.10 ISO image, this is a lot of data to encrypt, and in order to get a good throughput and performance I needed to find an outstanding optimal way to do it, that is at the same time secure.
Many encryption techniques and algorithms can be used, and all at the same time over the same packets to make them more secure. In this article I will describe only an improvement to the symmetric encryption to make it no predictable and very fast, so cheap to use.
So I introduce the concept here of noise, or entropy, in symmetric data packets.
Imagine that you want to send a chunk of data that is:
SGVsbG8sIEkgc2VlIHRoYXQgeW91IHRyeSB0aGluZ3MuIDopIENvbmdyYXRz
For clarity, only base64 are used in this sample, no binary data.
The problem is that every time that you send this chunk of data, you’ll get the same encrypted string. This is predictable and can lead to someone to reverse engineer your password.
So taking in consideration this sample Java 1.7 source code (note that variables use MT notation) that just encrypts for a given key:
import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import javax.xml.bind.DatatypeConverter; import java.util.UUID; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; /** * @author Carles Mateo */ public abstract class Security { public static String encrypt(String s_key, String s_str) { try { /** encryption cypher */ Cipher o_ecipher; SecureRandom seed = SecureRandom.getInstance("SHA1PRNG"); seed.setSeed(s_key.getBytes()); KeyGenerator keygen = KeyGenerator.getInstance("DES"); keygen.init(seed); SecretKey key = keygen.generateKey(); o_ecipher = Cipher.getInstance("DES"); o_ecipher.init(Cipher.ENCRYPT_MODE, key); // Encode the string into bytes using utf-8 byte[] utf8 = s_str.getBytes("UTF8"); // Encrypt byte[] enc = o_ecipher.doFinal(utf8); // Encode bytes to base64 to get a string String s_encrypted_encoded = encodeBase64(enc); return s_encrypted_encoded; } catch (javax.crypto.BadPaddingException e) { } catch (NoSuchAlgorithmException e) { } catch (NoSuchPaddingException e) { } catch (IllegalBlockSizeException e) { } catch (java.io.IOException e) { } catch (Exception e) { System.out.println(e.getMessage()); } return null; } }
Then here is the cheap way that consist in making the original data always be different, that I came with.
Instead of sending just the data, add a bit of entropy or noise, like an UUID, so our packet would be:
f47ac10b-58cc-4372-a567-0e02b2c3d479
|SGVsbG8sIEkgc2VlIHRoYXQgeW91IHRyeSB0aGluZ3MuIDopIENvbmdyYXRz
If you’re reading this article you already know, but just in case, the f47ac10b-58cc-4372-a567-0e02b2c3d479 is an UUID.
The pipe | is for clarity, as the UUID has a length fix it is not really needed to manipulate the string.
Having this UUID in here is also cool, as it will allow us to do things like using this UUID as the password for the next packet. So if our messenger starts with the user’s password, from there the next packets could use this UUID as password (and the next one, the previously random generated UUID and so on…) so every packet has a new password, that must be known from the previous history in order to get all the packets description.
So if a man-in-the-middle is capturing all the data sent, hoping to being able to break the encryption in a near future, it will have to be sure to record all the packets to decode the sequence easily, otherwise will have to fight to break every single packet.
So UUID is random, but is not noise at all. It is useful entropy.
Another thing that we can do to is set a timestamp on the packet, that way, even if a man in the middle is able to clone and later send the initial packet to the Server, it will be discarded.
For example:
f47ac10b-58cc-4372-a567-0e02b2c3d479
|1428002755|SGVsbG8sIEkgc2VlIHRoYXQgeW91IHRyeSB0aGluZ3MuIDopIENvbmdyYXRz
The second field is the unix timestamp, so our Server that has tracked the time or the Client’s computer and knows its rid, knows that if he gets a timestamp in the packet that has more than 2 seconds of difference, it will discard the packet and end the connection. So no one in-the-middle will be able to success cloning a login packet, and injecting it to the Server.
So having a basic encapsulation, that is cheap, for transport, the data inside can be also in addition be encrypted with asymmetric and other mechanisms that guarantee that only Client1 and Client2 can decrypt it, and not the Server, while the Server ensures packets are compliant and no noise/attacks are sent though the Server to Clients.