2a6d8282d9868c3ec658ab99f8967330822e4c7d
[feisty_meow.git] / nucleus / library / crypto / blowfish_crypto.cpp
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>
21 #include <loggers/critical_events.h>
22 #include <loggers/program_wide_logger.h>
23 #include <mathematics/chaos.h>
24 #include <structures/static_memory_gremlin.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)");
87   static_ssl_initializer();
88   LOG("prior to key size discuss");
89   DISCUSS_KEY_SIZE(key_size);
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
99 blowfish_crypto::blowfish_crypto(const byte_array &key, int key_size)
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");
107   DISCUSS_KEY_SIZE(key_size);
108   LOG("prior to provided key discuss");
109   DISCUSS_PROVIDED_KEY(key_size, key);
110   LOG("prior to ssl static init");
111   static_ssl_initializer();
112   LOG("after ssl static init");
113 }
114
115 blowfish_crypto::blowfish_crypto(const blowfish_crypto &to_copy)
116 : root_object(),
117   _key_size(to_copy._key_size),
118   _key(new byte_array(*to_copy._key))
119 {
120   FUNCDEF("copy ctor");
121   static_ssl_initializer(); 
122   LOG("after ssl static init");
123 }
124
125 blowfish_crypto::~blowfish_crypto()
126 {
127   FUNCDEF("dtor");
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
137 int blowfish_crypto::minimum_key_size() { return 64; }
138
139 int blowfish_crypto::maximum_key_size() { return 448; }
140
141 blowfish_crypto &blowfish_crypto::operator = (const blowfish_crypto &to_copy)
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;
153   DISCUSS_KEY_SIZE(key_size);
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
163 void blowfish_crypto::generate_key(int size, byte_array &new_key)
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
197 bool blowfish_crypto::encrypt(const byte_array &source,
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;
207   EVP_CIPHER_CTX_init(&session);
208   EVP_EncryptInit_ex(&session, EVP_bf_cbc(), NIL, _key->observe(),
209       init_vector().observe());
210   EVP_CIPHER_CTX_set_key_length(&session, _key_size);
211
212   // allocate temporary space for encrypted data.
213   byte_array encoded(source.length() + FUDGE);
214
215   // encrypt the entire source buffer.
216   int encoded_len = 0;
217   int enc_ret = EVP_EncryptUpdate(&session, encoded.access(), &encoded_len,
218       source.observe(), source.length());
219   if (enc_ret != 1) {
220     continuable_error(class_name(), func, a_sprintf("encryption failed, "
221         "result=%d.", enc_ret));
222     to_return = false;
223   } else {
224     // chop any extra space off.
225     LOG(a_sprintf("chopping bytes %d to %d.\n", encoded_len, encoded.last()));
226     encoded.zap(encoded_len, encoded.last());
227     target = encoded;
228   }
229
230   // only add padding if we succeeded with the encryption.
231   if (enc_ret == 1) {
232     // finalize the encryption.
233     encoded.reset(FUDGE);  // reinflate for padding.
234     int pad_len = 0;
235     enc_ret = EVP_EncryptFinal_ex(&session, encoded.access(), &pad_len);
236     if (enc_ret != 1) {
237       continuable_error(class_name(), func, a_sprintf("finalizing encryption "
238           "failed, result=%d.", enc_ret));
239       to_return = false;
240     } else {
241       LOG(a_sprintf("padding added %d bytes.\n", pad_len));
242       encoded.zap(pad_len, encoded.last());
243       target += encoded;
244     }
245   }
246
247   EVP_CIPHER_CTX_cleanup(&session);
248   return to_return;
249 }
250
251 bool blowfish_crypto::decrypt(const byte_array &source,
252     byte_array &target) const
253 {
254   FUNCDEF("decrypt");
255   target.reset();
256   if (!_key->length() || !source.length()) return false;
257   bool to_return = true;
258   EVP_CIPHER_CTX session;
259   EVP_CIPHER_CTX_init(&session);
260   LOG(a_sprintf("key size %d bits.\n", BITS_PER_BYTE * _key->length()));
261   EVP_DecryptInit_ex(&session, EVP_bf_cbc(), NIL, _key->observe(),
262       init_vector().observe());
263   EVP_CIPHER_CTX_set_key_length(&session, _key_size);
264
265   // allocate enough space for decoded bytes.
266   byte_array decoded(source.length() + FUDGE);
267
268   int decoded_len = 0;
269   int dec_ret = EVP_DecryptUpdate(&session, decoded.access(), &decoded_len,
270       source.observe(), source.length());
271   if (dec_ret != 1) {
272     continuable_error(class_name(), func, "decryption failed.");
273     to_return = false;
274   } else {
275     LOG(a_sprintf("  decrypted size in bytes is %d.\n", decoded_len));
276     decoded.zap(decoded_len, decoded.last());
277     target = decoded;
278   }
279
280   // only process padding if the first part of decryption succeeded.
281   if (dec_ret == 1) {
282     decoded.reset(FUDGE);  // reinflate for padding.
283     int pad_len = 0;
284     dec_ret = EVP_DecryptFinal_ex(&session, decoded.access(), &pad_len);
285     LOG(a_sprintf("padding added %d bytes.\n", pad_len));
286     if (dec_ret != 1) {
287       continuable_error(class_name(), func, a_sprintf("finalizing decryption "
288           "failed, result=%d, padlen=%d, target had %d bytes.", dec_ret,
289           pad_len, target.length()));
290       to_return = false;
291     } else {
292       int dec_size = pad_len;
293       decoded.zap(dec_size, decoded.last());
294       target += decoded;
295     }
296   }
297
298   EVP_CIPHER_CTX_cleanup(&session);
299   return to_return;
300 }
301
302 } //namespace.
303
304