feisty meow concerns codebase  2.140
blowfish_crypto.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : blowfish encryption *
4 * Author : Chris Koeritz *
5 * *
6 *******************************************************************************
7 * Copyright (c) 2005-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
14 
15 #include "blowfish_crypto.h"
16 #include "ssl_init.h"
17 
18 #include <basis/astring.h>
19 #include <basis/functions.h>
20 #include <basis/mutex.h>
23 #include <mathematics/chaos.h>
25 
26 #include <openssl/blowfish.h>
27 #include <openssl/evp.h>
28 
29 using namespace basis;
30 using namespace loggers;
31 using namespace mathematics;
32 using namespace structures;
33 
34 namespace crypto {
35 
36 const int FUDGE = 128;
37  // extra space for the cipher's block size. blowfish is only 8 bytes for
38  // the cipher block size, but we ensure there will definitely be no
39  // problems.
40 
41 //#undef set_key
42  // get rid of a macro we don't want.
43 
44 //#define DEBUG_BLOWFISH
45  // uncomment for noisier version.
46 
47 #ifdef DEBUG_BLOWFISH
48  #undef LOG
49  #define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t)
50 #else
51  #undef LOG
52  #define LOG(t)
53 #endif
54 
55 #ifdef DEBUG_BLOWFISH
56  // this macro checks on the validity of the key sizes (in bits).
57  #define DISCUSS_KEY_SIZE(key_size) \
58  if (key_size < minimum_key_size()) { \
59  continuable_error(static_class_name(), func, \
60  a_sprintf("key size (%d bits) is less than minimum key size %d.", \
61  key_size, minimum_key_size())); \
62  } \
63  if (key_size > maximum_key_size()) { \
64  continuable_error(static_class_name(), func, \
65  a_sprintf("key size (%d bits) is greater than maximum key size %d.", \
66  key_size, maximum_key_size())); \
67  }
68 
69  // this macro checks that the key in the byte array has enough bytes for
70  // the key size bits.
71  #define DISCUSS_PROVIDED_KEY(key_size, key) \
72  if (key.length() * BITS_PER_BYTE < key_size) { \
73  continuable_error(static_class_name(), func, \
74  a_sprintf("key array length (%d) is less than required by key size " \
75  "(%d bits).", key.length(), key_size)); \
76  }
77 #else
78  #define DISCUSS_PROVIDED_KEY(key_size, key)
79  #define DISCUSS_KEY_SIZE(key_size)
80 #endif
81 
82 blowfish_crypto::blowfish_crypto(int key_size)
83 : _key_size(key_size),
84  _key(new byte_array)
85 {
86  FUNCDEF("ctor(int)");
88  LOG("prior to key size discuss");
90  if (key_size < minimum_key_size())
91  _key_size = minimum_key_size();
92  if (key_size > maximum_key_size())
93  _key_size = maximum_key_size();
94  LOG("prior to generate key");
95  generate_key(_key_size, *_key);
96  LOG("after generate key");
97 }
98 
100 : _key_size(key_size),
101  _key(new byte_array(key))
102 {
103  FUNCDEF("ctor(byte_array,int)");
104  // any problems with the key provided are horrid. they will yield a
105  // non-working blowfish object.
106  LOG("prior to key size discuss");
108  LOG("prior to provided key discuss");
110  LOG("prior to ssl static init");
112  LOG("after ssl static init");
113 }
114 
116 : root_object(),
117  _key_size(to_copy._key_size),
118  _key(new byte_array(*to_copy._key))
119 {
120  FUNCDEF("copy ctor");
122  LOG("after ssl static init");
123 }
124 
126 {
127  FUNCDEF("destructor");
128  LOG("prior to key whack");
129  WHACK(_key);
130  LOG("after key whack");
131 }
132 
133 int blowfish_crypto::key_size() const { return _key_size; }
134 
135 const byte_array &blowfish_crypto::get_key() const { return *_key; }
136 
138 
140 
142 {
143  if (this == &to_copy) return *this;
144  _key_size = to_copy._key_size;
145  *_key = *to_copy._key;
146  return *this;
147 }
148 
149 bool blowfish_crypto::set_key(const byte_array &new_key, int key_size)
150 {
151  FUNCDEF("set_key");
152  if (!new_key.length()) return false;
154  DISCUSS_PROVIDED_KEY(key_size, new_key);
155  if ( (key_size < minimum_key_size()) || (key_size > maximum_key_size()) )
156  return false;
157  if (new_key.length() * BITS_PER_BYTE < key_size) return false;
158  _key_size = key_size;
159  *_key = new_key;
160  return true;
161 }
162 
164 {
165  FUNCDEF("generate_key");
166  DISCUSS_KEY_SIZE(size);
167  if (size < minimum_key_size())
168  size = minimum_key_size();
169  else if (size > maximum_key_size())
170  size = maximum_key_size();
171  int bytes = size / BITS_PER_BYTE; // calculate the number of bytes needed.
172  if (size % BITS_PER_BYTE) bytes++; // add one for non-integral portion.
173  new_key.reset(bytes);
174  for (int i = 0; i < bytes; i++)
175  new_key[i] = static_ssl_initializer().randomizer().inclusive(0, 255);
176 }
177 
178 SAFE_STATIC(mutex, __vector_init_lock, )
179 
180 const byte_array &blowfish_crypto::init_vector()
181 {
182  FUNCDEF("init_vector");
183  auto_synchronizer locking(__vector_init_lock());
184  static byte_array to_return(EVP_MAX_IV_LENGTH);
185  static bool initted = false;
186  LOG("prior to initted check");
187  if (!initted) {
188  LOG("actually doing init");
189  for (int i = 0; i < EVP_MAX_IV_LENGTH; i++)
190  to_return[i] = 214 - i;
191  initted = true;
192  }
193  LOG("leaving init check");
194  return to_return;
195 }
196 
198  byte_array &target) const
199 {
200  FUNCDEF("encrypt");
201  target.reset();
202  if (!_key->length() || !source.length()) return false;
203  bool to_return = true;
204 
205  // initialize an encoding session.
206  EVP_CIPHER_CTX *session = EVP_CIPHER_CTX_new();
207 
208  EVP_CIPHER_CTX_init(session);
209  EVP_EncryptInit_ex(session, EVP_bf_cbc(), NULL_POINTER, _key->observe(),
210  init_vector().observe());
211  EVP_CIPHER_CTX_set_key_length(session, _key_size);
212 
213  // allocate temporary space for encrypted data.
214  byte_array encoded(source.length() + FUDGE);
215 
216  // encrypt the entire source buffer.
217  int encoded_len = 0;
218  int enc_ret = EVP_EncryptUpdate(session, encoded.access(), &encoded_len,
219  source.observe(), source.length());
220  if (enc_ret != 1) {
221  continuable_error(class_name(), func, a_sprintf("encryption failed, "
222  "result=%d.", enc_ret));
223  to_return = false;
224  } else {
225  // chop any extra space off.
226  LOG(a_sprintf("chopping bytes %d to %d.\n", encoded_len, encoded.last()));
227  encoded.zap(encoded_len, encoded.last());
228  target = encoded;
229  }
230 
231  // only add padding if we succeeded with the encryption.
232  if (enc_ret == 1) {
233  // finalize the encryption.
234  encoded.reset(FUDGE); // reinflate for padding.
235  int pad_len = 0;
236  enc_ret = EVP_EncryptFinal_ex(session, encoded.access(), &pad_len);
237  if (enc_ret != 1) {
238  continuable_error(class_name(), func, a_sprintf("finalizing encryption "
239  "failed, result=%d.", enc_ret));
240  to_return = false;
241  } else {
242  LOG(a_sprintf("padding added %d bytes.\n", pad_len));
243  encoded.zap(pad_len, encoded.last());
244  target += encoded;
245  }
246  }
247 
248  EVP_CIPHER_CTX_cleanup(session);
249  EVP_CIPHER_CTX_free(session);
250  return to_return;
251 }
252 
254  byte_array &target) const
255 {
256  FUNCDEF("decrypt");
257  target.reset();
258  if (!_key->length() || !source.length()) return false;
259  bool to_return = true;
260  EVP_CIPHER_CTX *session = EVP_CIPHER_CTX_new();
261  EVP_CIPHER_CTX_init(session);
262  LOG(a_sprintf("key size %d bits.\n", BITS_PER_BYTE * _key->length()));
263  EVP_DecryptInit_ex(session, EVP_bf_cbc(), NULL_POINTER, _key->observe(),
264  init_vector().observe());
265  EVP_CIPHER_CTX_set_key_length(session, _key_size);
266 
267  // allocate enough space for decoded bytes.
268  byte_array decoded(source.length() + FUDGE);
269 
270  int decoded_len = 0;
271  int dec_ret = EVP_DecryptUpdate(session, decoded.access(), &decoded_len,
272  source.observe(), source.length());
273  if (dec_ret != 1) {
274  continuable_error(class_name(), func, "decryption failed.");
275  to_return = false;
276  } else {
277  LOG(a_sprintf(" decrypted size in bytes is %d.\n", decoded_len));
278  decoded.zap(decoded_len, decoded.last());
279  target = decoded;
280  }
281 
282  // only process padding if the first part of decryption succeeded.
283  if (dec_ret == 1) {
284  decoded.reset(FUDGE); // reinflate for padding.
285  int pad_len = 0;
286  dec_ret = EVP_DecryptFinal_ex(session, decoded.access(), &pad_len);
287  LOG(a_sprintf("padding added %d bytes.\n", pad_len));
288  if (dec_ret != 1) {
289  continuable_error(class_name(), func, a_sprintf("finalizing decryption "
290  "failed, result=%d, padlen=%d, target had %d bytes.", dec_ret,
291  pad_len, target.length()));
292  to_return = false;
293  } else {
294  int dec_size = pad_len;
295  decoded.zap(dec_size, decoded.last());
296  target += decoded;
297  }
298  }
299 
300  EVP_CIPHER_CTX_cleanup(session);
301  EVP_CIPHER_CTX_free(session);
302  return to_return;
303 }
304 
305 } //namespace.
306 
307 
#define DISCUSS_KEY_SIZE(key_size)
#define DISCUSS_PROVIDED_KEY(key_size, key)
#define LOG(t)
a_sprintf is a specialization of astring that provides printf style support.
Definition: astring.h:440
void reset(int number=0, const contents *initial_contents=NULL_POINTER)
Resizes this array and sets the contents from an array of contents.
Definition: array.h:349
const contents * observe() const
Returns a pointer to the underlying C array of data.
Definition: array.h:172
contents * access()
A non-constant access of the underlying C-array. BE REALLY CAREFUL.
Definition: array.h:175
int length() const
Returns the current reported length of the allocated C array.
Definition: array.h:115
outcome zap(int start, int end)
Deletes from "this" the objects inclusively between "start" and "end".
Definition: array.h:769
int last() const
Returns the last valid element in the array.
Definition: array.h:118
auto_synchronizer simplifies concurrent code by automatically unlocking.
Definition: mutex.h:113
A very common template for a dynamic array of bytes.
Definition: byte_array.h:36
Provides BlowFish encryption on byte_arrays using the OpenSSL package.
blowfish_crypto(int key_size)
this will create a new random key of the "key_size", in bits.
static int minimum_key_size()
returns the minimum key size (in bits) supported here.
bool decrypt(const basis::byte_array &source, basis::byte_array &target) const
decrypts the "target" array from the encrypted "source" array.
static const basis::byte_array & init_vector()
returns the initialization vector that is used by this class.
bool encrypt(const basis::byte_array &source, basis::byte_array &target) const
encrypts the "source" array into the "target" array.
static int maximum_key_size()
returns the maximum key size (in bits) supported here.
blowfish_crypto & operator=(const blowfish_crypto &to_copy)
static void generate_key(int size, basis::byte_array &new_key)
creates a "new_key" of the "size" (in bits) specified.
bool set_key(const basis::byte_array &new_key, int key_size)
sets the encryption key to "new_key".
const basis::byte_array & get_key() const
returns our current key.
const mathematics::chaos & randomizer() const
provides a random number generator for any encryption routines.
Definition: ssl_init.cpp:85
int inclusive(int low, int high) const
< Returns a pseudo-random number r, such that "low" <= r <= "high".
Definition: chaos.h:88
#define continuable_error(c, f, i)
#define BITS_PER_BYTE
A fundamental constant measuring the number of bits in a byte.
Definition: definitions.h:38
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
const int FUDGE
const ssl_init & static_ssl_initializer()
the main method for accessing the SSL initialization support.
A logger that sends to the console screen using the standard output device.
An extension to floating point primitives providing approximate equality.
Definition: averager.h:21
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#define SAFE_STATIC(type, func_name, parms)
Statically defines a singleton object whose scope is the program's lifetime.