blowfish_crypto.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : blowfish encryption                                               *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *******************************************************************************
00007 * Copyright (c) 2005-$now By Author.  This program is free software; you can  *
00008 * redistribute it and/or modify it under the terms of the GNU General Public  *
00009 * License as published by the Free Software Foundation; either version 2 of   *
00010 * the License or (at your option) any later version.  This is online at:      *
00011 *     http://www.fsf.org/copyleft/gpl.html                                    *
00012 * Please send any updates to: fred@gruntose.com                               *
00013 \*****************************************************************************/
00014 
00015 #include "blowfish_crypto.h"
00016 #include "ssl_init.h"
00017 
00018 #include <basis/astring.h>
00019 #include <basis/functions.h>
00020 #include <basis/mutex.h>
00021 #include <loggers/critical_events.h>
00022 #include <loggers/program_wide_logger.h>
00023 #include <mathematics/chaos.h>
00024 #include <structures/static_memory_gremlin.h>
00025 
00026 #include <openssl/blowfish.h>
00027 #include <openssl/evp.h>
00028 
00029 using namespace basis;
00030 using namespace loggers;
00031 using namespace mathematics;
00032 using namespace structures;
00033 
00034 namespace crypto {
00035 
00036 const int FUDGE = 128;
00037   // extra space for the cipher's block size.  blowfish is only 8 bytes for
00038   // the cipher block size, but we ensure there will definitely be no
00039   // problems.
00040 
00041 #undef set_key
00042   // get rid of a macro we don't want.
00043 
00044 #undef LOG
00045 #define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t)
00046 
00047 //#define DEBUG_BLOWFISH
00048   // uncomment for noisier version.
00049 
00050 #ifdef DEBUG_BLOWFISH
00051   // this macro checks on the validity of the key sizes (in bits).
00052   #define DISCUSS_KEY_SIZE(key_size) \
00053     if (key_size < minimum_key_size()) { \
00054       continuable_error(static_class_name(), func, \
00055           a_sprintf("key size (%d bits) is less than minimum key size %d.", \
00056               key_size, minimum_key_size())); \
00057       } \
00058   if (key_size > maximum_key_size()) { \
00059       continuable_error(static_class_name(), func, \
00060           a_sprintf("key size (%d bits) is greater than maximum key size %d.", \
00061               key_size, maximum_key_size())); \
00062     }
00063   
00064   // this macro checks that the key in the byte array has enough bytes for
00065   // the key size bits.
00066   #define DISCUSS_PROVIDED_KEY(key_size, key) \
00067     if (key.length() * BITS_PER_BYTE < key_size) { \
00068       continuable_error(static_class_name(), func, \
00069           a_sprintf("key array length (%d) is less than required by key size " \
00070               "(%d bits).", key.length(), key_size)); \
00071     }
00072 #else
00073   #define DISCUSS_PROVIDED_KEY(key_size, key) 
00074   #define DISCUSS_KEY_SIZE(key_size) 
00075 #endif
00076 
00077 blowfish_crypto::blowfish_crypto(int key_size)
00078 : _key_size(key_size),
00079   _key(new byte_array)
00080 {
00081 //  FUNCDEF("constructor [int]");
00082   static_ssl_initializer();
00083   DISCUSS_KEY_SIZE(key_size);
00084   if (key_size < minimum_key_size())
00085     _key_size = minimum_key_size();
00086   if (key_size > maximum_key_size())
00087     _key_size = maximum_key_size();
00088   generate_key(_key_size, *_key);
00089 }
00090 
00091 blowfish_crypto::blowfish_crypto(const byte_array &key, int key_size)
00092 : _key_size(key_size),
00093   _key(new byte_array(key))
00094 {
00095 //  FUNCDEF("constructor [byte_array/int]");
00096   // any problems with the key provided are horrid.  they will yield a
00097   // non-working blowfish object.
00098   DISCUSS_KEY_SIZE(key_size);
00099   DISCUSS_PROVIDED_KEY(key_size, key);
00100   static_ssl_initializer();
00101 }
00102 
00103 blowfish_crypto::blowfish_crypto(const blowfish_crypto &to_copy)
00104 : root_object(),
00105   _key_size(to_copy._key_size),
00106   _key(new byte_array(*to_copy._key))
00107 { static_ssl_initializer(); }
00108 
00109 blowfish_crypto::~blowfish_crypto()
00110 {
00111   WHACK(_key);
00112 }
00113 
00114 int blowfish_crypto::key_size() const { return _key_size; }
00115 
00116 const byte_array &blowfish_crypto::get_key() const { return *_key; }
00117 
00118 int blowfish_crypto::minimum_key_size() { return 64; }
00119 
00120 int blowfish_crypto::maximum_key_size() { return 448; }
00121 
00122 blowfish_crypto &blowfish_crypto::operator = (const blowfish_crypto &to_copy)
00123 {
00124   if (this == &to_copy) return *this;
00125   _key_size = to_copy._key_size;
00126   *_key = *to_copy._key;
00127   return *this;
00128 }
00129 
00130 bool blowfish_crypto::set_key(const byte_array &new_key, int key_size)
00131 {
00132 //  FUNCDEF("set_key");
00133   if (!new_key.length()) return false;
00134   DISCUSS_KEY_SIZE(key_size);
00135   DISCUSS_PROVIDED_KEY(key_size, new_key);
00136   if ( (key_size < minimum_key_size()) || (key_size > maximum_key_size()) )
00137     return false;
00138   if (new_key.length() * BITS_PER_BYTE < key_size) return false;
00139   _key_size = key_size;
00140   *_key = new_key;
00141   return true;
00142 }
00143 
00144 void blowfish_crypto::generate_key(int size, byte_array &new_key)
00145 {
00146 //  FUNCDEF("generate_key");
00147   DISCUSS_KEY_SIZE(size);
00148   if (size < minimum_key_size())
00149     size = minimum_key_size();
00150   else if (size > maximum_key_size())
00151     size = maximum_key_size();
00152   int bytes = size / BITS_PER_BYTE;  // calculate the number of bytes needed.
00153   if (size % BITS_PER_BYTE) bytes++;  // add one for non-integral portion.
00154   new_key.reset(bytes);
00155   for (int i = 0; i < bytes; i++)
00156     new_key[i] = static_ssl_initializer().randomizer().inclusive(0, 255);
00157 }
00158 
00159 SAFE_STATIC(mutex, __vector_init_lock, )
00160 
00161 const byte_array &blowfish_crypto::init_vector()
00162 {
00163   auto_synchronizer locking(__vector_init_lock());
00164   static byte_array to_return(EVP_MAX_IV_LENGTH);
00165   static bool initted = false;
00166   if (!initted) {
00167     for (int i = 0; i < EVP_MAX_IV_LENGTH; i++)
00168       to_return[i] = 214 - i;
00169     initted = true;
00170   }
00171   return to_return;
00172 }
00173 
00174 bool blowfish_crypto::encrypt(const byte_array &source,
00175     byte_array &target) const
00176 {
00177   FUNCDEF("encrypt");
00178   target.reset();
00179   if (!_key->length() || !source.length()) return false;
00180   bool to_return = true;
00181 
00182   // initialize an encoding session.
00183   EVP_CIPHER_CTX session;
00184   EVP_CIPHER_CTX_init(&session);
00185   EVP_EncryptInit_ex(&session, EVP_bf_cbc(), NIL, _key->observe(),
00186       init_vector().observe());
00187   EVP_CIPHER_CTX_set_key_length(&session, _key_size);
00188 
00189   // allocate temporary space for encrypted data.
00190   byte_array encoded(source.length() + FUDGE);
00191 
00192   // encrypt the entire source buffer.
00193   int encoded_len = 0;
00194   int enc_ret = EVP_EncryptUpdate(&session, encoded.access(), &encoded_len,
00195       source.observe(), source.length());
00196   if (enc_ret != 1) {
00197     continuable_error(class_name(), func, a_sprintf("encryption failed, "
00198         "result=%d.", enc_ret));
00199     to_return = false;
00200   } else {
00201     // chop any extra space off.
00202 //    LOG(a_sprintf("encrypting bytes %d to %d.\n", i, edge));
00203     encoded.zap(encoded_len, encoded.last());
00204     target = encoded;
00205   }
00206 
00207   // only add padding if we succeeded with the encryption.
00208   if (enc_ret == 1) {
00209     // finalize the encryption.
00210     encoded.reset(FUDGE);  // reinflate for padding.
00211     int pad_len = 0;
00212     enc_ret = EVP_EncryptFinal_ex(&session, encoded.access(), &pad_len);
00213     if (enc_ret != 1) {
00214       continuable_error(class_name(), func, a_sprintf("finalizing encryption "
00215           "failed, result=%d.", enc_ret));
00216       to_return = false;
00217     } else {
00218 //      LOG(a_sprintf("padding added %d bytes.\n", pad_len));
00219       encoded.zap(pad_len, encoded.last());
00220       target += encoded;
00221     }
00222   }
00223 
00224   EVP_CIPHER_CTX_cleanup(&session);
00225   return to_return;
00226 }
00227 
00228 bool blowfish_crypto::decrypt(const byte_array &source,
00229     byte_array &target) const
00230 {
00231   FUNCDEF("decrypt");
00232   target.reset();
00233   if (!_key->length() || !source.length()) return false;
00234   bool to_return = true;
00235   EVP_CIPHER_CTX session;
00236   EVP_CIPHER_CTX_init(&session);
00237 //LOG(a_sprintf("key size %d bits.\n", BITS_PER_BYTE * _key->length()));
00238   EVP_DecryptInit_ex(&session, EVP_bf_cbc(), NIL, _key->observe(),
00239       init_vector().observe());
00240   EVP_CIPHER_CTX_set_key_length(&session, _key_size);
00241 
00242   // allocate enough space for decoded bytes.
00243   byte_array decoded(source.length() + FUDGE);
00244 
00245   int decoded_len = 0;
00246   int dec_ret = EVP_DecryptUpdate(&session, decoded.access(), &decoded_len,
00247       source.observe(), source.length());
00248   if (dec_ret != 1) {
00249     continuable_error(class_name(), func, "decryption failed.");
00250     to_return = false;
00251   } else {
00252 //    LOG(a_sprintf("  decrypted size in bytes is %d.\n", decoded_len));
00253     decoded.zap(decoded_len, decoded.last());
00254     target = decoded;
00255   }
00256 
00257   // only process padding if the first part of decryption succeeded.
00258   if (dec_ret == 1) {
00259     decoded.reset(FUDGE);  // reinflate for padding.
00260     int pad_len = 0;
00261     dec_ret = EVP_DecryptFinal_ex(&session, decoded.access(), &pad_len);
00262 //    LOG(a_sprintf("padding added %d bytes.\n", pad_len));
00263     if (dec_ret != 1) {
00264       continuable_error(class_name(), func, a_sprintf("finalizing decryption "
00265           "failed, result=%d, padlen=%d, target had %d bytes.", dec_ret,
00266           pad_len, target.length()));
00267       to_return = false;
00268     } else {
00269       int dec_size = pad_len;
00270       decoded.zap(dec_size, decoded.last());
00271       target += decoded;
00272     }
00273   }
00274 
00275   EVP_CIPHER_CTX_cleanup(&session);
00276   return to_return;
00277 }
00278 
00279 } //namespace.
00280 
00281 
Generated on Sat Jan 28 04:22:18 2012 for hoople2 project by  doxygen 1.6.3