blowfish_crypto.cpp

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

Generated on Sat Aug 30 04:31:43 2008 for HOOPLE Libraries by  doxygen 1.5.1