feisty meow concerns codebase 2.140
cryptical_envelopment.cpp
Go to the documentation of this file.
1/*****************************************************************************\
2* *
3* Name : cryptical_envelopment *
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
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
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 = 1024;
38 /* extra space for the cipher's block size. blowfish, e.g., is only 8 bytes for
39 the cipher block size, so we'd only ever need possibly 8 bytes padding?
40 */
41//hmmm: guarantee the fudge space is enough for other algorithms!
42
43//#undef set_key
44 // get rid of a macro we don't want.
45
46//#define DEBUG_CRYPTICAL_ENVELOPMENT
47 // uncomment for noisier version.
48
49// our logging via LOG is disabled unless the debugging flag above is turned on.
50// but the ALWAYS_LOG macro is unfazed and will log regardless of the flag.
51#undef ALWAYS_LOG
52#define ALWAYS_LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t)
53#ifdef DEBUG_CRYPTICAL_ENVELOPMENT
54 #undef LOG
55 #define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t)
56#else
57 #undef LOG
58 #define LOG(t)
59#endif
60
61// helpful macro for the error string of last failure.
62#define GET_SSL_ERROR() \
63 ERR_error_string(ERR_get_error(), NULL_POINTER)
64
65#ifdef DEBUG_CRYPTICAL_ENVELOPMENT
66 // only cause a program stop if we're in debugging mode.
67 #define ERROR_BAILOUT(a, b, c) deadly_error(a, b, c)
68#else
69 #define ERROR_BAILOUT(a, b, c) continuable_error(a, b, c)
70#endif
71
72// this macro checks on the validity of the key sizes (in bits).
73#define DISCUSS_KEY_SIZE(key_size) \
74 if (key_size < minimum_key_size_in_bits()) { \
75 ERROR_BAILOUT(class_name(), func, \
76 a_sprintf("key size (%d bits) is less than minimum key size %d.", \
77 key_size, minimum_key_size_in_bits())); \
78 return false; \
79 } \
80 if (key_size > maximum_key_size_in_bits()) { \
81 ERROR_BAILOUT(class_name(), func, \
82 a_sprintf("key size (%d bits) is greater than maximum key size %d.", \
83 key_size, maximum_key_size_in_bits())); \
84 return false; \
85 }
86
87// this macro checks that the key in the byte array has enough bytes for
88// the key size bits.
89#define DISCUSS_PROVIDED_KEY(key_size, key) \
90 if (key.length() * BITS_PER_BYTE < key_size) { \
91 ERROR_BAILOUT(class_name(), func, \
92 a_sprintf("key array length (%d) is less than required by key size " \
93 "(%d bits).", key.length(), key_size)); \
94 return false; \
95 }
96
98: _key_size(0),
99 _key(new byte_array()),
100 _cipher_type(cipher_type)
101{
102 FUNCDEF("ctor(int)");
104}
105
107: root_object(),
108 _key_size(to_copy._key_size),
109 _key(new byte_array(*to_copy._key))
110{
111 FUNCDEF("copy ctor");
113 LOG("after ssl static init");
114}
115
117{
118 FUNCDEF("destructor");
119 LOG("prior to key whack");
120 WHACK(_key);
121 LOG("after key whack");
122}
123
125{
126 FUNCDEF("set_key(int)");
128 LOG("prior to generate key");
129 _key_size = key_size;
130 bool to_return = generate_key(_key_size, *_key);
131 LOG("after generate key");
132 return to_return;
133}
134
135int cryptical_envelopment::key_size() const { return _key_size; }
136
137const byte_array &cryptical_envelopment::get_key() const { return *_key; }
138
140{
141 if (this == &to_copy) return *this;
142 _key_size = to_copy._key_size;
143 *_key = *to_copy._key;
144 _cipher_type = to_copy._cipher_type;
145 return *this;
146}
147
148bool cryptical_envelopment::set_key(const byte_array &new_key, int key_size)
149{
150 FUNCDEF("set_key(byte_array,int)");
153 _key_size = key_size;
154 *_key = new_key;
155 return true;
156}
157
158//hmmm: move someplace more useful.
159#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
160#define BYTE_TO_BINARY(byte) \
161 ((byte) & 0x80 ? '1' : '0'), \
162 ((byte) & 0x40 ? '1' : '0'), \
163 ((byte) & 0x20 ? '1' : '0'), \
164 ((byte) & 0x10 ? '1' : '0'), \
165 ((byte) & 0x08 ? '1' : '0'), \
166 ((byte) & 0x04 ? '1' : '0'), \
167 ((byte) & 0x02 ? '1' : '0'), \
168 ((byte) & 0x01 ? '1' : '0')
169
171{
172 FUNCDEF("generate_key");
174 DISCUSS_KEY_SIZE(size);
175 int bytes = size / BITS_PER_BYTE; // calculate the number of bytes needed.
176 if (size % BITS_PER_BYTE) bytes++; // add one for non-integral portion.
177 new_key.reset(bytes);
178 for (int i = 0; i < bytes; i++)
179 new_key[i] = static_ssl_initializer().randomizer().inclusive(0, 255);
180
181//code that we thought might be necessary but which doesn't help blowfish not suck.
182//#define WIPE_UNUSED_BITS
183#ifdef WIPE_UNUSED_BITS
184 // clear the bits that cannot be non-zero for a key whose bits are not evenly divisible by 8.
185 int bits_to_wipe = BITS_PER_BYTE - (size % BITS_PER_BYTE);
186ALWAYS_LOG(a_sprintf("saying we need to zap %d bits from result.", bits_to_wipe));
187 abyte mask = 0xFF;
188//hmmm: if we leave non-zero stuff in the last byte, that's not quite right!
189// also a question of endian-ness of where to zap those bits. argh!
190
191 if (bits_to_wipe) {
192 // rotate our mask to cover the number of bits we want to actually use.
193 mask <<= bits_to_wipe;
195 new_key[bytes - 1] &= mask;
196ALWAYS_LOG(a_sprintf("last byte now: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(new_key[bytes - 1])));
197 }
198#endif //wipe.
199
200 return true;
201}
202
203SAFE_STATIC(mutex, __vector_init_lock, )
204
205/*
206hmmm: this seems like a bad security situation, because we are using a single algorithm for
207 creating init vectors, so it's super easy to guess these (by reading the code, for
208 example). is this still secure, given that the keys are not known to an attacker?
209*/
211{
212 FUNCDEF("init_vector");
214 auto_synchronizer locking(__vector_init_lock());
215 static byte_array to_return(EVP_MAX_IV_LENGTH);
216 static bool initted = false;
217 if (!initted) {
218 LOG(">> actually creating init_vector >>");
219//hmmm: are we okay with the wrap-around on the byte type (going negative) if the init vector length is longer than 214?
220 for (int i = 0; i < to_return.length(); i++)
221 to_return[i] = abyte(214 - i);
222 initted = true;
223 LOG("<< finished init_vector creation <<");
224 }
225 return to_return;
226}
227
229 byte_array &target) const
230{
231 FUNCDEF("encrypt");
232ALWAYS_LOG(">>encrypt>>");
233 target.reset();
234 if (!_key->length() || !source.length()) return false;
235 bool to_return = true;
236
237LOG(a_sprintf(" encrypting %d bytes", source.length()));
238
239 // initialize an encoding session.
240 EVP_CIPHER_CTX *session = EVP_CIPHER_CTX_new();
241 EVP_CIPHER_CTX_init(session);
242
243 //new rules!
244 // EVP_EncryptInit to set the cipher, but leave key and IV null and unset
245 // EVP_CIPHER_CTX_set_key_length and EVP_CTRL_AEAD_SET_IVLEN
246 // EVP_EncryptInit again. This time leave cipher null, because you've already set it, and set the key and IV.
247
248 int initret = EVP_EncryptInit_ex(session, _cipher_type, NULL_POINTER, NULL_POINTER, NULL_POINTER);
249 if (!initret) {
250 // zero means a failure of the initialization.
251 ERROR_BAILOUT(class_name(), func, a_sprintf("failure in calling EVP_EncryptInit_ex, with error %s", GET_SSL_ERROR()));
252 }
253 LOG(a_sprintf(" calling set key len with key size of %d", _key_size));
254 // new fancy footwork needed to keep openssl from blowing up and claiming we didn't set the key.
255//hmmm: check returns on these setters?
256 EVP_CIPHER_CTX_set_key_length(session, _key_size);
257 EVP_CIPHER_CTX_ctrl(session, EVP_CTRL_AEAD_SET_IVLEN, init_vector().length(), NULL);
258 // and round and round we go...
259 initret = EVP_EncryptInit_ex(session, NULL_POINTER, NULL_POINTER, _key->observe(), init_vector().observe());
260 if (!initret) {
261 // zero means a failure of the initialization.
262 ERROR_BAILOUT(class_name(), func, a_sprintf("second phase failure in calling EVP_EncryptInit_ex, with error %s", GET_SSL_ERROR()));
263 }
264
265 // allocate temporary space for encrypted data.
266 byte_array encoded(source.length() + FUDGE);
267
268 // encrypt the entire source buffer.
269 int encoded_len = 0;
270 int enc_ret = EVP_EncryptUpdate(session, encoded.access(), &encoded_len,
271 source.observe(), source.length());
272 if (enc_ret != 1) {
273 ERROR_BAILOUT(class_name(), func, a_sprintf("encryption failed, "
274 "result=%d with error=%s.", enc_ret, GET_SSL_ERROR()));
275 to_return = false;
276 } else {
277 // chop any extra space off.
278 LOG(a_sprintf(" chopping extra bytes %d to %d.", encoded_len, encoded.last()));
279 encoded.zap(encoded_len, encoded.last());
280 target = encoded;
281 }
282
283 // only add padding if we succeeded with the encryption.
284 if (enc_ret == 1) {
285 // finalize the encryption.
286 encoded.reset(FUDGE); // reinflate for padding.
287 int pad_len = 0;
288 enc_ret = EVP_EncryptFinal_ex(session, encoded.access(), &pad_len);
289 if (enc_ret != 1) {
290 ERROR_BAILOUT(class_name(), func, a_sprintf("finalizing encryption "
291 "failed, result=%d with error=%s.", enc_ret, GET_SSL_ERROR()));
292 to_return = false;
293 } else {
294 LOG(a_sprintf(" encryption padding added %d bytes.", pad_len));
295 encoded.zap(pad_len, encoded.last());
296 target += encoded;
297 }
298 }
299
300 EVP_CIPHER_CTX_cleanup(session);
301 EVP_CIPHER_CTX_free(session);
302ALWAYS_LOG("<<encrypt<<");
303 return to_return;
304}
305
307 byte_array &target) const
308{
309 FUNCDEF("decrypt");
310ALWAYS_LOG(">>decrypt>>");
311 target.reset();
312 if (!_key->length() || !source.length()) return false;
313 bool to_return = true;
314 EVP_CIPHER_CTX *session = EVP_CIPHER_CTX_new();
315 EVP_CIPHER_CTX_init(session);
316 LOG(a_sprintf(" using key size with %d bits.", _key_size));
317 int initret = EVP_DecryptInit_ex(session, _cipher_type, NULL_POINTER, NULL_POINTER, NULL_POINTER);
318 if (!initret) {
319 // zero means a failure of the initialization.
320 ERROR_BAILOUT(class_name(), func, a_sprintf("failure in calling EVP_DecryptInit_ex, with error %s", GET_SSL_ERROR()));
321 }
322 // more fancy fupwork.
323//hmmm: check returns on these setters?
324 EVP_CIPHER_CTX_set_key_length(session, _key_size);
325 EVP_CIPHER_CTX_ctrl(session, EVP_CTRL_AEAD_SET_IVLEN, init_vector().length(), NULL);
326 initret = EVP_DecryptInit_ex(session, NULL_POINTER, NULL_POINTER, _key->observe(), init_vector().observe());
327 if (!initret) {
328 // zero means a failure of the initialization.
329 ERROR_BAILOUT(class_name(), func, a_sprintf("second phase failure in calling EVP_DecryptInit_ex, with error %s", GET_SSL_ERROR()));
330 }
331
332 // allocate enough space for decoded bytes.
333 byte_array decoded(source.length() + FUDGE);
334
335 int decoded_len = 0;
336 int dec_ret = EVP_DecryptUpdate(session, decoded.access(), &decoded_len,
337 source.observe(), source.length());
338 if (dec_ret != 1) {
339 ERROR_BAILOUT(class_name(), func, a_sprintf("decryption failed with error=%s", GET_SSL_ERROR()));
340 to_return = false;
341 } else {
342 LOG(a_sprintf(" first part decrypted size in bytes is %d.", decoded_len));
343 decoded.zap(decoded_len, decoded.last());
344 target = decoded;
345 }
346
347 // only process padding if the first part of decryption succeeded.
348 if (dec_ret == 1) {
349 decoded.reset(FUDGE); // reinflate for padding.
350 int pad_len = 0;
351 dec_ret = EVP_DecryptFinal_ex(session, decoded.access(), &pad_len);
352 if (dec_ret != 1) {
353 ERROR_BAILOUT(class_name(), func, a_sprintf("finalizing decryption "
354 "failed, result=%d, padlen=%d, target had %d bytes, error=%s.", dec_ret,
355 pad_len, target.length(), GET_SSL_ERROR()));
356 to_return = false;
357 } else {
358 LOG(a_sprintf(" decryption final had %d bytes padding.", pad_len));
359 decoded.zap(pad_len, decoded.last());
360 target += decoded;
361 }
362 }
363
364 EVP_CIPHER_CTX_cleanup(session);
365 EVP_CIPHER_CTX_free(session);
366ALWAYS_LOG("<<decrypt<<");
367 return to_return;
368}
369
370} //namespace.
371
372
#define LOG(s)
#define ALWAYS_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
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
virtual const char * class_name() const =0
Returns the bare name of this class as a constant character pointer.
bool set_key(int key_size)
this will create a new random key of the "key_size", in bits.
bool encrypt(const basis::byte_array &source, basis::byte_array &target) const
encrypts the "source" array into the "target" array.
bool decrypt(const basis::byte_array &source, basis::byte_array &target) const
decrypts the "target" array from the encrypted "source" array.
bool generate_key(int size, basis::byte_array &new_key)
creates a "new_key" of the "size" (in bits) specified.
cryptical_envelopment(const EVP_CIPHER *cipher_type)
constructor requires the type of encryption to use.
cryptical_envelopment & operator=(const cryptical_envelopment &to_copy)
static const basis::byte_array & init_vector()
returns the initialization vector that is used by this class.
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 DISCUSS_KEY_SIZE(key_size)
#define ERROR_BAILOUT(a, b, c)
#define DISCUSS_PROVIDED_KEY(key_size, key)
#define BYTE_TO_BINARY(byte)
#define BYTE_TO_BINARY_PATTERN
#define GET_SSL_ERROR()
struct evp_cipher_st EVP_CIPHER
#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 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.