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/err.h>
28#include <openssl/evp.h>
29
30using namespace basis;
31using namespace loggers;
32using namespace mathematics;
33using namespace structures;
34
35namespace crypto {
36
37const int FUDGE = 128;
38 // extra space for the cipher's block size. blowfish is only 8 bytes for
39 // the cipher block size, but we ensure there will definitely be no
40 // problems.
41
42//#undef set_key
43 // get rid of a macro we don't want.
44
45#define DEBUG_BLOWFISH
46 // uncomment for noisier version.
47
48#undef ALWAYS_LOG
49#define ALWAYS_LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t)
50#ifdef DEBUG_BLOWFISH
51 #undef LOG
52 #define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t)
53#else
54 #undef LOG
55 #define LOG(t)
56#endif
57
58// helpful macro for the error string of last failure.
59#define GET_SSL_ERROR() \
60 ERR_error_string(ERR_get_error(), NULL_POINTER)
61
62#ifdef DEBUG_BLOWFISH
63 // this macro checks on the validity of the key sizes (in bits).
64 #define DISCUSS_KEY_SIZE(key_size) \
65 if (key_size < minimum_key_size()) { \
66 deadly_error(static_class_name(), func, \
67 a_sprintf("key size (%d bits) is less than minimum key size %d.", \
68 key_size, minimum_key_size())); \
69 } \
70 if (key_size > maximum_key_size()) { \
71 deadly_error(static_class_name(), func, \
72 a_sprintf("key size (%d bits) is greater than maximum key size %d.", \
73 key_size, maximum_key_size())); \
74 }
75
76 // this macro checks that the key in the byte array has enough bytes for
77 // the key size bits.
78 #define DISCUSS_PROVIDED_KEY(key_size, key) \
79 if (key.length() * BITS_PER_BYTE < key_size) { \
80 deadly_error(static_class_name(), func, \
81 a_sprintf("key array length (%d) is less than required by key size " \
82 "(%d bits).", key.length(), key_size)); \
83 }
84#else
85 #define DISCUSS_PROVIDED_KEY(key_size, key)
86 #define DISCUSS_KEY_SIZE(key_size)
87#endif
88
90: _key_size(key_size),
91 _key(new byte_array)
92{
93 FUNCDEF("ctor(int)");
95 LOG("prior to key size discuss");
98 _key_size = minimum_key_size();
100 _key_size = maximum_key_size();
101 LOG("prior to generate key");
102 generate_key(_key_size, *_key);
103 LOG("after generate key");
104}
105
107: _key_size(key_size),
108 _key(new byte_array(key))
109{
110 FUNCDEF("ctor(byte_array,int)");
112 // any problems with the key provided are horrid. they will yield a
113 // non-working blowfish object.
114 LOG("prior to key size discuss");
116 LOG("prior to provided key discuss");
118 LOG("prior to ssl static init");
119 LOG("after ssl static init");
120}
121
123: root_object(),
124 _key_size(to_copy._key_size),
125 _key(new byte_array(*to_copy._key))
126{
127 FUNCDEF("copy ctor");
129 LOG("after ssl static init");
130}
131
133{
134 FUNCDEF("destructor");
135 LOG("prior to key whack");
136 WHACK(_key);
137 LOG("after key whack");
138}
139
140int blowfish_crypto::key_size() const { return _key_size; }
141
142const byte_array &blowfish_crypto::get_key() const { return *_key; }
143
145
147
149{
150 if (this == &to_copy) return *this;
151 _key_size = to_copy._key_size;
152 *_key = *to_copy._key;
153 return *this;
154}
155
156bool blowfish_crypto::set_key(const byte_array &new_key, int key_size)
157{
158 FUNCDEF("set_key");
159 if (!new_key.length()) return false;
163 return false;
164 if (new_key.length() * BITS_PER_BYTE < key_size) return false;
165 _key_size = key_size;
166 *_key = new_key;
167 return true;
168}
169
171{
172 FUNCDEF("generate_key");
174 DISCUSS_KEY_SIZE(size);
175 if (size < minimum_key_size())
176 size = minimum_key_size();
177 else if (size > maximum_key_size())
178 size = maximum_key_size();
179 int bytes = size / BITS_PER_BYTE; // calculate the number of bytes needed.
180 if (size % BITS_PER_BYTE) bytes++; // add one for non-integral portion.
181 new_key.reset(bytes);
182 for (int i = 0; i < bytes; i++)
183 new_key[i] = static_ssl_initializer().randomizer().inclusive(0, 255);
184}
185
186SAFE_STATIC(mutex, __vector_init_lock, )
187
188const byte_array &blowfish_crypto::init_vector()
189{
190 FUNCDEF("init_vector");
192 auto_synchronizer locking(__vector_init_lock());
193 static byte_array to_return(EVP_MAX_IV_LENGTH);
194 static bool initted = false;
195 if (!initted) {
196 LOG("actually doing init");
197 for (int i = 0; i < to_return.length(); i++)
198 to_return[i] = abyte(214 - i);
199 initted = true;
200 LOG("finished init process");
201 }
202 return to_return;
203}
204
206 byte_array &target) const
207{
208 FUNCDEF("encrypt");
209ALWAYS_LOG(">>encrypt>>");
210 target.reset();
211 if (!_key->length() || !source.length()) return false;
212 bool to_return = true;
213
214LOG(a_sprintf(" encrypting %d bytes", source.length()));
215
216 // initialize an encoding session.
217 EVP_CIPHER_CTX *session = EVP_CIPHER_CTX_new();
218 EVP_CIPHER_CTX_init(session);
219
220//new rules!
221//EVP_EncryptInit to set the cipher, but leave key and IV null and unset
222//EVP_CIPHER_CTX_set_key_length and EVP_CTRL_AEAD_SET_IVLEN
223//EVP_EncryptInit again. This time leave cipher null, because you've already set it, and set the key and IV.
224
225 int initret = EVP_EncryptInit_ex(session, EVP_bf_cbc(), NULL_POINTER, NULL_POINTER, NULL_POINTER);
226 if (!initret) {
227 // zero means a failure of the initialization.
228 ALWAYS_LOG(a_sprintf("failure in calling EVP_EncryptInit_ex, with error %s", GET_SSL_ERROR()));
229 exit(1);
230 }
231 LOG(a_sprintf(" calling set key len with key size of %d", _key_size));
232 // new fancy footwork needed to keep openssl from blowing up and claiming we didn't set the key.
233//hmmm: check returns on these setters?
234 EVP_CIPHER_CTX_set_key_length(session, _key_size);
235 EVP_CIPHER_CTX_ctrl(session, EVP_CTRL_AEAD_SET_IVLEN, init_vector().length(), NULL);
236 // and round and round we go...
237 initret = EVP_EncryptInit_ex(session, NULL_POINTER, NULL_POINTER, _key->observe(), init_vector().observe());
238 if (!initret) {
239 // zero means a failure of the initialization.
240 ALWAYS_LOG(a_sprintf("second phase failure in calling EVP_EncryptInit_ex, with error %s", GET_SSL_ERROR()));
241 exit(1);
242 }
243
244 // allocate temporary space for encrypted data.
245 byte_array encoded(source.length() + FUDGE);
246
247 // encrypt the entire source buffer.
248 int encoded_len = 0;
249 int enc_ret = EVP_EncryptUpdate(session, encoded.access(), &encoded_len,
250 source.observe(), source.length());
251 if (enc_ret != 1) {
252 deadly_error(class_name(), func, a_sprintf("encryption failed, "
253 "result=%d with error=%s.", enc_ret, GET_SSL_ERROR()));
254 to_return = false;
255 } else {
256 // chop any extra space off.
257 LOG(a_sprintf(" chopping extra bytes %d to %d.", encoded_len, encoded.last()));
258 encoded.zap(encoded_len, encoded.last());
259 target = encoded;
260 }
261
262 // only add padding if we succeeded with the encryption.
263 if (enc_ret == 1) {
264 // finalize the encryption.
265 encoded.reset(FUDGE); // reinflate for padding.
266 int pad_len = 0;
267 enc_ret = EVP_EncryptFinal_ex(session, encoded.access(), &pad_len);
268 if (enc_ret != 1) {
269 deadly_error(class_name(), func, a_sprintf("finalizing encryption "
270 "failed, result=%d with error=%s.", enc_ret, GET_SSL_ERROR()));
271 to_return = false;
272 } else {
273 LOG(a_sprintf(" padding added %d bytes.", pad_len));
274 encoded.zap(pad_len, encoded.last());
275 target += encoded;
276 }
277 }
278
279 EVP_CIPHER_CTX_cleanup(session);
280 EVP_CIPHER_CTX_free(session);
281ALWAYS_LOG("<<encrypt<<");
282 return to_return;
283}
284
286 byte_array &target) const
287{
288 FUNCDEF("decrypt");
289ALWAYS_LOG(">>decrypt>>");
290 target.reset();
291 if (!_key->length() || !source.length()) return false;
292 bool to_return = true;
293 EVP_CIPHER_CTX *session = EVP_CIPHER_CTX_new();
294 EVP_CIPHER_CTX_init(session);
295 LOG(a_sprintf(" using key size with %d bits.", _key_size));
297 int initret = EVP_DecryptInit_ex(session, EVP_bf_cbc(), NULL_POINTER, NULL_POINTER, NULL_POINTER);
298 if (!initret) {
299 // zero means a failure of the initialization.
300//hmmm: below approach is called a deadly error. use that instead, throughout.
301 ALWAYS_LOG(a_sprintf("failure in calling EVP_DecryptInit_ex, with error %s", GET_SSL_ERROR()));
302 exit(1);
303 }
304 // more fancy fupwork.
305//hmmm: check returns on these setters?
306 EVP_CIPHER_CTX_set_key_length(session, _key_size);
307 EVP_CIPHER_CTX_ctrl(session, EVP_CTRL_AEAD_SET_IVLEN, init_vector().length(), NULL);
308 initret = EVP_DecryptInit_ex(session, NULL_POINTER, NULL_POINTER, _key->observe(), init_vector().observe());
309 if (!initret) {
310 // zero means a failure of the initialization.
311 ALWAYS_LOG(a_sprintf("second phase failure in calling EVP_DecryptInit_ex, with error %s", GET_SSL_ERROR()));
312 exit(1);
313 }
314
315 // allocate enough space for decoded bytes.
316 byte_array decoded(source.length() + FUDGE);
317
318 int decoded_len = 0;
319 int dec_ret = EVP_DecryptUpdate(session, decoded.access(), &decoded_len,
320 source.observe(), source.length());
321 if (dec_ret != 1) {
322 deadly_error(class_name(), func, a_sprintf("decryption failed with error=%s", GET_SSL_ERROR()));
323 to_return = false;
324 } else {
325 LOG(a_sprintf(" first part decrypted size in bytes is %d.", decoded_len));
326 decoded.zap(decoded_len, decoded.last());
327 target = decoded;
328 }
329
330 // only process padding if the first part of decryption succeeded.
331 if (dec_ret == 1) {
332 decoded.reset(FUDGE); // reinflate for padding.
333 int pad_len = 0;
334 dec_ret = EVP_DecryptFinal_ex(session, decoded.access(), &pad_len);
335 if (dec_ret != 1) {
336 deadly_error(class_name(), func, a_sprintf("finalizing decryption "
337 "failed, result=%d, padlen=%d, target had %d bytes, error=%s.", dec_ret,
338 pad_len, target.length(), GET_SSL_ERROR()));
339 to_return = false;
340 } else {
341 LOG(a_sprintf(" padding added %d bytes.", pad_len));
342 decoded.zap(pad_len, decoded.last());
343 target += decoded;
344 }
345 }
346
347 EVP_CIPHER_CTX_cleanup(session);
348 EVP_CIPHER_CTX_free(session);
349ALWAYS_LOG("<<decrypt<<");
350 return to_return;
351}
352
353} //namespace.
354
355
#define DISCUSS_KEY_SIZE(key_size)
#define ALWAYS_LOG(t)
#define DISCUSS_PROVIDED_KEY(key_size, key)
#define GET_SSL_ERROR()
#define LOG(s)
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
contents * access()
A non-constant access of the underlying C-array. BE REALLY CAREFUL.
Definition array.h:175
const contents * observe() const
Returns a pointer to the underlying C array of data.
Definition array.h:172
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:94
int inclusive(int low, int high) const
< Returns a pseudo-random number r, such that "low" <= r <= "high".
Definition chaos.h:88
#define deadly_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:54
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
unsigned char abyte
A fairly important unit which is seldom defined...
Definition definitions.h:51
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.