Fawkes API  Fawkes Development Version
encrypt.cpp
00001 
00002 /***************************************************************************
00003  *  encrypt.cpp - WorldInfo encryptio routine
00004  *
00005  *  Created: Thu May 03 15:21:00 2007
00006  *  Copyright  2006-2007  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <core/exceptions/software.h>
00025 #include <netcomm/worldinfo/encrypt.h>
00026 
00027 #ifdef HAVE_LIBCRYPTO
00028 #  include <openssl/evp.h>
00029 #else
00030 #  include <cstring>
00031 #endif
00032 
00033 namespace fawkes {
00034 
00035 /** @class MessageEncryptionException <netcomm/worldinfo/encrypt.h>
00036  * Message encryption failed.
00037  * This exception shall be thrown if there was a problem encrypting a
00038  * world info message.
00039  * @ingroup NetComm
00040  */
00041 
00042 /** Constructor.
00043  * @param msg message
00044  */
00045 MessageEncryptionException::MessageEncryptionException(const char *msg)
00046   : Exception(msg)
00047 {
00048 }
00049 
00050 
00051 /** @class WorldInfoMessageEncryptor <netcomm/worldinfo/encrypt.h>
00052  * WorldInfo message encryptor.
00053  * This class is used to encrypt world info message before they are sent
00054  * over the network.
00055  *
00056  * The used encryption is AES (128 bit) with a supplied key and initialisation
00057  * vector that both sides have to agree on.
00058  * The encryption is used in the less safe Electronic Code Book (ECB) mode. It
00059  * is prefered over Cipher Block Chaining (CBC) mode since we expect a very
00060  * unreliable transport medium (wifi in a totally crowded and signal-hostile
00061  * environment) where we could have severe packet loss. In CBC mode if you loose
00062  * a single packet you can not only not decrypt this packet that you didn't get,
00063  * but also not the directly following packages. In this case it can already
00064  * cause severe problems if about half of the packes are lost.
00065  *
00066  * We are merely interested in some kind of child-proof blinds that is just used
00067  * to make cheating too much work to be interesting. We actually don't care if
00068  * someone can decrypt our traffic with enough time, we just don't want other
00069  * teams to be able to decrypt our traffic during the game. Otherwise teams
00070  * could cheat and just read the network messages to know where the opponents
00071  * are instead of really detecting them using sensors.
00072  *
00073  * This implementation uses OpenSSL for the AES encryption (in fact it uses the
00074  * accompanying libcrypto that comes with OpenSSL, not libopenssl itself). It is
00075  * almost everywhere available and easy to use.
00076  * 
00077  * @ingroup NetComm
00078  * @author Tim Niemueller
00079  */
00080 
00081 
00082 /** Constructor.
00083  * @param key encryption key
00084  * @param iv initialisation vector
00085  */
00086 WorldInfoMessageEncryptor::WorldInfoMessageEncryptor(const unsigned char *key, const unsigned char *iv)
00087 {
00088   plain_buffer = NULL;
00089   plain_buffer_length = 0;
00090   crypt_buffer = NULL;
00091   crypt_buffer_length = 0;
00092 
00093   this->key = key;
00094   this->iv  = iv;
00095 }
00096 
00097 
00098 /** Empty destructor. */
00099 WorldInfoMessageEncryptor::~WorldInfoMessageEncryptor()
00100 {
00101 }
00102 
00103 
00104 
00105 
00106 /** Set plain buffer.
00107  * This set the source buffer that is encrypted.
00108  * @param buffer plain buffer
00109  * @param buffer_length plain buffer length
00110  */
00111 void
00112 WorldInfoMessageEncryptor::set_plain_buffer(void *buffer, size_t buffer_length)
00113 {
00114   plain_buffer        = buffer;
00115   plain_buffer_length = buffer_length;
00116 }
00117 
00118 
00119 /** Get recommended crypted buffer size.
00120  * The cryto text is in most cases longer than the plain text. This is because
00121  * we use a block cipher. This block cipher encrypts block of certain sizes (in case
00122  * of AES128 a block has a size of 16 bytes). If our data does not align to this block
00123  * size padding at the end is required to fill up the last block to the requested
00124  * size. Since this padding depends on the used cipher this convenience method
00125  * is provided to get the recommended minimum size depending on the plain text
00126  * buffer (that you have to set before you call this method.
00127  * @return recommended minimum size of the crypted buffer
00128  * @exception MissingParameterException thrown, if set_plain_buffer() has not
00129  * been called or if the supplied buffer had zero size.
00130  */
00131 size_t
00132 WorldInfoMessageEncryptor::recommended_crypt_buffer_size()
00133 {
00134   if ( plain_buffer_length == 0 ) {
00135     throw MissingParameterException("plain buffer must be set and plain buffer size > 0");
00136   }
00137 
00138 #ifdef HAVE_LIBCRYPTO
00139   EVP_CIPHER_CTX ctx;
00140   EVP_EncryptInit(&ctx, EVP_aes_128_ecb(), key, iv);
00141   size_t rv = plain_buffer_length + EVP_CIPHER_CTX_block_size(&ctx);
00142   EVP_CIPHER_CTX_cleanup(&ctx);
00143   return rv;
00144 #else
00145   return plain_buffer_length;
00146 #endif
00147 }
00148 
00149 
00150 /** Set crypted buffer.
00151  * This set the destination buffer to which the encrypted message is written.
00152  * @param buffer crypted buffer
00153  * @param buffer_length crypted buffer length
00154  */
00155 void
00156 WorldInfoMessageEncryptor::set_crypt_buffer(void *buffer, size_t buffer_length)
00157 {
00158   crypt_buffer        = buffer;
00159   crypt_buffer_length = buffer_length;
00160 }
00161 
00162 
00163 /** Encrypt.
00164  * Do the encryption.
00165  * @return size of the crypted message in bytes
00166  */
00167 size_t
00168 WorldInfoMessageEncryptor::encrypt()
00169 {
00170   if ( (plain_buffer == NULL) || (plain_buffer_length == 0) ||
00171        (crypt_buffer == NULL) || (crypt_buffer_length == 0) ) {
00172     throw MissingParameterException("Buffer(s) not set for encryption");
00173   }
00174 
00175 #ifdef HAVE_LIBCRYPTO
00176   EVP_CIPHER_CTX ctx;
00177   if ( ! EVP_EncryptInit(&ctx, EVP_aes_128_ecb(), key, iv) ) {
00178     throw MessageEncryptionException("Could not initialize cipher context");
00179   }
00180 
00181 
00182   int outl = crypt_buffer_length;
00183   if ( ! EVP_EncryptUpdate(&ctx,
00184                            (unsigned char *)crypt_buffer, &outl,
00185                            (unsigned char *)plain_buffer, plain_buffer_length) ) {
00186     throw MessageEncryptionException("EncryptUpdate failed");
00187   }
00188 
00189   int plen = 0;
00190   if ( ! EVP_EncryptFinal_ex(&ctx, (unsigned char *)crypt_buffer + outl, &plen) ) {
00191     throw MessageEncryptionException("EncryptFinal failed");
00192   }
00193   outl += plen;
00194  
00195   return outl;
00196 #else
00197   /* Plain text copy-through for debugging */
00198   memcpy(crypt_buffer, plain_buffer, plain_buffer_length);
00199   return plain_buffer_length;
00200 #endif
00201 }
00202 
00203 } // end namespace fawkes