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