first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / core / 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 #undef LOG
45 #define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t)
46
47 //#define DEBUG_BLOWFISH
48   // uncomment for noisier version.
49
50 #ifdef DEBUG_BLOWFISH
51   // this macro checks on the validity of the key sizes (in bits).
52   #define DISCUSS_KEY_SIZE(key_size) \
53     if (key_size < minimum_key_size()) { \
54       continuable_error(static_class_name(), func, \
55           a_sprintf("key size (%d bits) is less than minimum key size %d.", \
56               key_size, minimum_key_size())); \
57       } \
58   if (key_size > maximum_key_size()) { \
59       continuable_error(static_class_name(), func, \
60           a_sprintf("key size (%d bits) is greater than maximum key size %d.", \
61               key_size, maximum_key_size())); \
62     }
63   
64   // this macro checks that the key in the byte array has enough bytes for
65   // the key size bits.
66   #define DISCUSS_PROVIDED_KEY(key_size, key) \
67     if (key.length() * BITS_PER_BYTE < key_size) { \
68       continuable_error(static_class_name(), func, \
69           a_sprintf("key array length (%d) is less than required by key size " \
70               "(%d bits).", key.length(), key_size)); \
71     }
72 #else
73   #define DISCUSS_PROVIDED_KEY(key_size, key) 
74   #define DISCUSS_KEY_SIZE(key_size) 
75 #endif
76
77 blowfish_crypto::blowfish_crypto(int key_size)
78 : _key_size(key_size),
79   _key(new byte_array)
80 {
81 //  FUNCDEF("constructor [int]");
82   static_ssl_initializer();
83   DISCUSS_KEY_SIZE(key_size);
84   if (key_size < minimum_key_size())
85     _key_size = minimum_key_size();
86   if (key_size > maximum_key_size())
87     _key_size = maximum_key_size();
88   generate_key(_key_size, *_key);
89 }
90
91 blowfish_crypto::blowfish_crypto(const byte_array &key, int key_size)
92 : _key_size(key_size),
93   _key(new byte_array(key))
94 {
95 //  FUNCDEF("constructor [byte_array/int]");
96   // any problems with the key provided are horrid.  they will yield a
97   // non-working blowfish object.
98   DISCUSS_KEY_SIZE(key_size);
99   DISCUSS_PROVIDED_KEY(key_size, key);
100   static_ssl_initializer();
101 }
102
103 blowfish_crypto::blowfish_crypto(const blowfish_crypto &to_copy)
104 : root_object(),
105   _key_size(to_copy._key_size),
106   _key(new byte_array(*to_copy._key))
107 { static_ssl_initializer(); }
108
109 blowfish_crypto::~blowfish_crypto()
110 {
111   WHACK(_key);
112 }
113
114 int blowfish_crypto::key_size() const { return _key_size; }
115
116 const byte_array &blowfish_crypto::get_key() const { return *_key; }
117
118 int blowfish_crypto::minimum_key_size() { return 64; }
119
120 int blowfish_crypto::maximum_key_size() { return 448; }
121
122 blowfish_crypto &blowfish_crypto::operator = (const blowfish_crypto &to_copy)
123 {
124   if (this == &to_copy) return *this;
125   _key_size = to_copy._key_size;
126   *_key = *to_copy._key;
127   return *this;
128 }
129
130 bool blowfish_crypto::set_key(const byte_array &new_key, int key_size)
131 {
132 //  FUNCDEF("set_key");
133   if (!new_key.length()) return false;
134   DISCUSS_KEY_SIZE(key_size);
135   DISCUSS_PROVIDED_KEY(key_size, new_key);
136   if ( (key_size < minimum_key_size()) || (key_size > maximum_key_size()) )
137     return false;
138   if (new_key.length() * BITS_PER_BYTE < key_size) return false;
139   _key_size = key_size;
140   *_key = new_key;
141   return true;
142 }
143
144 void blowfish_crypto::generate_key(int size, byte_array &new_key)
145 {
146 //  FUNCDEF("generate_key");
147   DISCUSS_KEY_SIZE(size);
148   if (size < minimum_key_size())
149     size = minimum_key_size();
150   else if (size > maximum_key_size())
151     size = maximum_key_size();
152   int bytes = size / BITS_PER_BYTE;  // calculate the number of bytes needed.
153   if (size % BITS_PER_BYTE) bytes++;  // add one for non-integral portion.
154   new_key.reset(bytes);
155   for (int i = 0; i < bytes; i++)
156     new_key[i] = static_ssl_initializer().randomizer().inclusive(0, 255);
157 }
158
159 SAFE_STATIC(mutex, __vector_init_lock, )
160
161 const byte_array &blowfish_crypto::init_vector()
162 {
163   auto_synchronizer locking(__vector_init_lock());
164   static byte_array to_return(EVP_MAX_IV_LENGTH);
165   static bool initted = false;
166   if (!initted) {
167     for (int i = 0; i < EVP_MAX_IV_LENGTH; i++)
168       to_return[i] = 214 - i;
169     initted = true;
170   }
171   return to_return;
172 }
173
174 bool blowfish_crypto::encrypt(const byte_array &source,
175     byte_array &target) const
176 {
177   FUNCDEF("encrypt");
178   target.reset();
179   if (!_key->length() || !source.length()) return false;
180   bool to_return = true;
181
182   // initialize an encoding session.
183   EVP_CIPHER_CTX session;
184   EVP_CIPHER_CTX_init(&session);
185   EVP_EncryptInit_ex(&session, EVP_bf_cbc(), NIL, _key->observe(),
186       init_vector().observe());
187   EVP_CIPHER_CTX_set_key_length(&session, _key_size);
188
189   // allocate temporary space for encrypted data.
190   byte_array encoded(source.length() + FUDGE);
191
192   // encrypt the entire source buffer.
193   int encoded_len = 0;
194   int enc_ret = EVP_EncryptUpdate(&session, encoded.access(), &encoded_len,
195       source.observe(), source.length());
196   if (enc_ret != 1) {
197     continuable_error(class_name(), func, a_sprintf("encryption failed, "
198         "result=%d.", enc_ret));
199     to_return = false;
200   } else {
201     // chop any extra space off.
202 //    LOG(a_sprintf("encrypting bytes %d to %d.\n", i, edge));
203     encoded.zap(encoded_len, encoded.last());
204     target = encoded;
205   }
206
207   // only add padding if we succeeded with the encryption.
208   if (enc_ret == 1) {
209     // finalize the encryption.
210     encoded.reset(FUDGE);  // reinflate for padding.
211     int pad_len = 0;
212     enc_ret = EVP_EncryptFinal_ex(&session, encoded.access(), &pad_len);
213     if (enc_ret != 1) {
214       continuable_error(class_name(), func, a_sprintf("finalizing encryption "
215           "failed, result=%d.", enc_ret));
216       to_return = false;
217     } else {
218 //      LOG(a_sprintf("padding added %d bytes.\n", pad_len));
219       encoded.zap(pad_len, encoded.last());
220       target += encoded;
221     }
222   }
223
224   EVP_CIPHER_CTX_cleanup(&session);
225   return to_return;
226 }
227
228 bool blowfish_crypto::decrypt(const byte_array &source,
229     byte_array &target) const
230 {
231   FUNCDEF("decrypt");
232   target.reset();
233   if (!_key->length() || !source.length()) return false;
234   bool to_return = true;
235   EVP_CIPHER_CTX session;
236   EVP_CIPHER_CTX_init(&session);
237 //LOG(a_sprintf("key size %d bits.\n", BITS_PER_BYTE * _key->length()));
238   EVP_DecryptInit_ex(&session, EVP_bf_cbc(), NIL, _key->observe(),
239       init_vector().observe());
240   EVP_CIPHER_CTX_set_key_length(&session, _key_size);
241
242   // allocate enough space for decoded bytes.
243   byte_array decoded(source.length() + FUDGE);
244
245   int decoded_len = 0;
246   int dec_ret = EVP_DecryptUpdate(&session, decoded.access(), &decoded_len,
247       source.observe(), source.length());
248   if (dec_ret != 1) {
249     continuable_error(class_name(), func, "decryption failed.");
250     to_return = false;
251   } else {
252 //    LOG(a_sprintf("  decrypted size in bytes is %d.\n", decoded_len));
253     decoded.zap(decoded_len, decoded.last());
254     target = decoded;
255   }
256
257   // only process padding if the first part of decryption succeeded.
258   if (dec_ret == 1) {
259     decoded.reset(FUDGE);  // reinflate for padding.
260     int pad_len = 0;
261     dec_ret = EVP_DecryptFinal_ex(&session, decoded.access(), &pad_len);
262 //    LOG(a_sprintf("padding added %d bytes.\n", pad_len));
263     if (dec_ret != 1) {
264       continuable_error(class_name(), func, a_sprintf("finalizing decryption "
265           "failed, result=%d, padlen=%d, target had %d bytes.", dec_ret,
266           pad_len, target.length()));
267       to_return = false;
268     } else {
269       int dec_size = pad_len;
270       decoded.zap(dec_size, decoded.last());
271       target += decoded;
272     }
273   }
274
275   EVP_CIPHER_CTX_cleanup(&session);
276   return to_return;
277 }
278
279 } //namespace.
280
281