I need some advice about the best way to encrypt data for transmission across sockets using PHP.

I am currently developing a distributed application wherein a centralized computer (call it a Manager) must communicate via socket with dozens of client computers. I am required to make sure these socket communications are encrypted for security. I am required to use sockets for latency reasons.

I know that PHP has the mcrypt libraries. This page has some sample code:

<?php

// Designate string to be encrypted
$string = "Applied Cryptography, by Bruce Schneier, is 
a wonderful cryptography reference.";

// Encryption/decryption key
$key = "Four score and twenty years ago";

// Encryption Algorithm
$cipher_alg = MCRYPT_RIJNDAEL_128;

// Create the initialization vector for added security.
$iv = mcrypt_create_iv(mcrypt_get_iv_size($cipher_alg, 
MCRYPT_MODE_ECB), MCRYPT_RAND);

// Output original string
print "Original string: $string <p>";

// Encrypt $string
$encrypted_string = mcrypt_encrypt($cipher_alg, $key, 
$string, MCRYPT_MODE_CBC, $iv);

// Convert to hexadecimal and output to browser
print "Encrypted string: ".bin2hex($encrypted_string)."<p>";

$decrypted_string = mcrypt_decrypt($cipher_alg, $key, 
$encrypted_string, MCRYPT_MODE_CBC, $iv);

print "Decrypted string: $decrypted_string";

?>

The problem with using mcrypt is that both Manager and client must know the $key and the $iv. I have no idea what an initialization vector is, BTW. Obviously, I can't just communicate these values over the socket because then security would be totally compromised. Communicating them ahead of time in a config file also seems like a bad idea. It would seem like I need an SSL-like connection protocol.

Rather than reinventing the wheel, I was imagining that each client, when initializing a connection, might request some $key and $iv value from the central Manager over an encrypted SSL connection using cURL. I could then rely on cURL to negotiate a secure connection and then share some randomly generated $key safely.

Questions:
1) Is this process secure (i.e., request $key via SSL, use mCrypt and $key to encrypt/decrypt all communications) ?
2) Is mCrypt the best way to go in terms of speed performance?
3) Might there be some easy way to implement a handshake for exchange of public keys?
4) Does some code library already exist to accomplish secure connections via socket?

Any help would be much appreciated.

    sneakyimp wrote:

    Obviously, I can't just communicate these values over the socket because then security would be totally compromised.

    You can send the IV in the clear over the socket, but you must never use the same IV more than once with the same key and encryption algorithm.

    sneakyimp wrote:

    Rather than reinventing the wheel, I was imagining that each client, when initializing a connection, might request some $key and $iv value from the central Manager over an encrypted SSL connection using cURL.

    I believe that with SSL, it is the two parties that generate the key and IV. The central manager probably should perform the role of the certificate authority instead. It is probably wiser to implement SSL/TLS instead of inventing your own protocol - you already have many ways to go wrong in the implementation.

      laserlight;10901975 wrote:

      You can send the IV in the clear over the socket, but you must never use the same IV more than once with the same key and encryption algorithm.

      Thanks for the clarification. According to what I've now read you can apparently send the IV right along with the ciphertext without any loss in security. The IV is merely intended to insure that the same message, sent twice, will be encrypted differently each time. If that is the case, I'm deducing that it would be good practice to try and make your IV look as similar to your ciphertext as possible. I.e., if your ciphertext consists entirely of uppercase hexadecimal chars (0-9 and A-F) then so should your IV.

      There is some debate on the [man]mcrypt_create_iv[/man] function in the PHP documentation comments with many folks reporting that it is slow. Since performance is very important for this application, I'm tempted to use the alternative IV generator offered in the comments there:

      function alt_mcrypt_create_iv ($size) {
          $iv = '';
          for($i = 0; $i < $size; $i++) {
              $iv .= chr(rand(0,255));
          }
          return $iv;
      }
      

      I'm not really sure how long a given IV should be. Wikipedia says "The size of the IV depends on the encryption algorithm and on the cryptographic protocol in use and is normally as large as the block size of the cipher or as large as the encryption key." The word cipher (which can refer to either the algorithm or the ciphertext) as used here refers to the length of the ciphertext if I'm not mistaken. On the other hand, if you need the IV to encrypt the message, how does one know how long the encrypted message is? It's a bit confusing.

      questions:
      What do we think of that alt_mcrypt_create_iv function? Does it appear less secure than the mcrypt_create_iv function?
      How long should the IV be? I'm guessing that it is more secure if the IV is longer but that exceeding the length of the plaintext message doesn't offer any benefit. The onlamp example I previously posted simply sets IV length to the length of the constant string MCRYPT_RIJNDAEL_128 and strlen('rijndael-128') == 12.
      * Am I right in understanding what the technical/philosophical reason for the IV is, namely that it prevents a given plaintext string from being encrypted the same way twice?

      laserlight;10901975 wrote:

      I believe that with SSL, it is the two parties that generate the key and IV. The central manager probably should perform the role of the certificate authority instead. It is probably wiser to implement SSL/TLS instead of inventing your own protocol - you already have many ways to go wrong in the implementation.

      It is my understanding (but I could be wrong) that the public and private keys belonging to each party are basically very large prime numbers and that the establishment of a communication channel involves some pretty heavy lifting mathematically. Given that it's not practical to do this heavy lifting for each message to be sent, a 'session key' is established for a given SSL communication session. Seems to me I might use HTTPS to create some kind of session key for my socket communications without any appreciable loss in security, right?

      I'm not sure what you mean by the Manager acting as a CA. My plan is basically this.

      1) as each client process is launched, it will make an HTTPS connection to the Manger to request a session key. I rely on cURL and Apache to make sure the connection is secure and simply generate a random string of some decent length to use as an encryption key. I might also negotiate other things such as an agreed-upon IV length for the communication session. Or I could just default IV length to strlen($key).

      2) The Manager will respond to each HTTPS request with the necessary information and will keep a record of it as being connected to the particular connecting client.

      3) The HTTPS connection will be closed

      4) a socket connection to the Manager (which is listening for connections) will be initiated by the client. All information transmitted across the socket will be encrypted using mcrypt functions, the agreed-upon key and an IV of the agreed-upon length.

      Any feedback would be most appreciated. This approach seems pretty secure to me. It's important that it be both secure and fast.

        sneakyimp wrote:

        If that is the case, I'm deducing that it would be good practice to try and make your IV look as similar to your ciphertext as possible. I.e., if your ciphertext consists entirely of uppercase hexadecimal chars (0-9 and A-F) then so should your IV.

        Well, a good assumption to make is that the attacker knows the algorithm (read up on "Kerckhoffs' principle"), so "make your IV look as similar to your ciphertext as possible" would just be security by obscurity. On the other hand, a practical concern is that whatever method you use to format your ciphertext for transmission should also be used for the IV.

        sneakyimp wrote:

        The word cipher (which can refer to either the algorithm or the ciphertext) as used here refers to the length of the ciphertext if I'm not mistaken.

        In this case (and in most cases) cipher refers to the encryption algorithm.

        sneakyimp wrote:

        What do we think of that alt_mcrypt_create_iv function? Does it appear less secure than the mcrypt_create_iv function?

        The randomness of the IV matters much less than the fact that it is not reused with the same encryption algorithm and key.

        sneakyimp wrote:

        How long should the IV be? I'm guessing that it is more secure if the IV is longer but that exceeding the length of the plaintext message doesn't offer any benefit.

        I think Wikipedia already gave you the answer: it depends. For example, with the CBC mode the IV is usually as long as the cipher's block size. So, the plain text message is divided into blocks, and each block is XORed with the output of encrypting the previous block before being encrypted. The IV then serves as the "previous block" for the first block.

        sneakyimp wrote:

        The onlamp example I previously posted simply sets IV length to the length of the constant string MCRYPT_RIJNDAEL_128 and strlen('rijndael-128') == 12.

        No, it gets the IV length from mcrypt_get_iv_size(). However, the author made a mistake by using MCRYPT_MODE_ECB instead of MCRYPT_MODE_CBC, so there is a chance that mcrypt_get_iv_size() returns 0 since ECB mode does not need an IV. The PHP manual recommends [man]mcrypt_enc_get_iv_size/man instead.

        sneakyimp wrote:

        Am I right in understanding what the technical/philosophical reason for the IV is, namely that it prevents a given plaintext string from being encrypted the same way twice?

        That is one reason, yes.

        sneakyimp wrote:

        Seems to me I might use HTTPS to create some kind of session key for my socket communications without any appreciable loss in security, right?

        Yes, but then you could use it for the entire communication too. To help understand the requirements better: is this an academic exercise or a real world project?

        sneakyimp wrote:

        I'm not sure what you mean by the Manager acting as a CA.

        Sorry, that was from an incorrect understanding on what you were trying to do: I thought you wanted to get the client computers to communicate to each over.

          Yes, I'm familiar with the whole 'enemy knows the system' concept. I was going to mention that but figured my post was long enough already.

          And yes, I sort of didn't read the wikipedia article closely enough. It says the IV is as large as the 'block size' of the cipher, NOT the length of the ciphertext. Sorry about that.

          Thanks for the clarifications about IV length. I'm not really following the EBC/CBC stuff. What does these acronyms mean?

            sneakyimp wrote:

            I'm not really following the EBC/CBC stuff. What does these acronyms mean?

            They are abbreviations for the names of two encryption modes, Electronic Code Book and Cipher Block Chaining, respectively.

              Write a Reply...