2f2e07783baac1e8b9319441e2808cdb8e32bdd7
[feisty_meow.git] / nucleus / library / tests_crypto / test_blowfish_crypto.cpp
1 /*
2 *  Name   : test blowfish encryption
3 *  Author : Chris Koeritz
4 *  Purpose: Exercises the BlowFish encryption methods in the crypto library.
5 **
6 * Copyright (c) 2005-$now By Author.  This program is free software; you can  *
7 * redistribute it and/or modify it under the terms of the GNU General Public  *
8 * License as published by the Free Software Foundation; either version 2 of   *
9 * the License or (at your option) any later version.  This is online at:      *
10 *     http://www.fsf.org/copyleft/gpl.html                                    *
11 * Please send any updates to: fred@gruntose.com                               *
12 */
13
14 #include <application/hoople_main.h>
15 #include <basis/byte_array.h>
16 #include <basis/astring.h>
17 #include <crypto/blowfish_crypto.h>
18 #include <mathematics/chaos.h>
19 #include <processes/ethread.h>
20 #include <processes/thread_cabinet.h>
21 #include <structures/static_memory_gremlin.h>
22 #include <structures/unique_id.h>
23 #include <textual/byte_formatter.h>
24 #include <textual/string_manipulation.h>
25 #include <timely/time_control.h>
26 #include <timely/time_stamp.h>
27 #include <unit_test/unit_base.h>
28
29 #include <stdio.h>
30 #include <string.h>
31
32 using namespace application;
33 using namespace basis;
34 using namespace crypto;
35 using namespace filesystem;
36 using namespace loggers;
37 using namespace mathematics;
38 using namespace processes;
39 using namespace structures;
40 using namespace textual;
41 using namespace timely;
42 using namespace unit_test;
43
44 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print)
45
46 //#define DEBUG_BLOWFISH
47   // uncomment for noisier run.
48
49 const int TEST_RUNS_PER_KEY = 5;  // encryption test cycles done on each key.
50
51 const int THREAD_COUNT = 10;  // number of threads testing blowfish at once.
52
53 const int ITERATIONS = 4;  // number of test runs in our testing threads.
54
55 const int MAX_STRING = 20000;  // largest chunk that we'll try to encrypt.
56
57 //////////////
58
59 class test_blowfish;  // forward.
60
61 class blowfish_thread : public ethread
62 {
63 public:
64   blowfish_thread(test_blowfish &parent) : ethread(), _parent(parent) {}
65
66   void perform_activity(void *ptr);
67     // try out random blowfish keys on randomly chosen chunks of the fodder.
68
69 private:
70   test_blowfish &_parent;
71 };
72
73 //////////////
74
75 class test_blowfish : virtual public unit_base, virtual public application_shell
76 {
77 public:
78   test_blowfish()
79     : _fodder(string_manipulation::make_random_name(MAX_STRING + 1, MAX_STRING + 1)) {}
80   DEFINE_CLASS_NAME("test_blowfish");
81
82   int execute();
83
84 private:
85   astring _fodder;  // chunks taken from this are encrypted and decrypted.
86   time_stamp _program_start;  // the time at which we started executing.
87   thread_cabinet _threads;  // manages our testing threads.
88   friend class blowfish_thread;  // bad practice, but saves time in test app.
89 };
90
91 int test_blowfish::execute()
92 {
93   FUNCDEF("execute");
94 #ifdef DEBUG_BLOWFISH
95   LOG(astring("starting blowfish test..."));
96 #endif
97   int left = THREAD_COUNT;
98   while (left--) {
99 #ifdef DEBUG_BLOWFISH
100   LOG(a_sprintf("blowfish thread %d starting...", left));
101 #endif
102     _threads.add_thread(new blowfish_thread(*this), true, NULL_POINTER);
103   }
104
105 #ifdef DEBUG_BLOWFISH
106   LOG(astring("started all threads..."));
107 #endif
108
109   while (_threads.threads()) {
110 #ifdef DEBUG_BLOWFISH
111     LOG(astring("cleaning debris."));
112 #endif
113     _threads.clean_debris();
114     time_control::sleep_ms(1000);
115   }
116
117 #ifdef DEBUG_BLOWFISH
118   int duration = int(time_stamp().value() - _program_start.value());
119   LOG(a_sprintf("duration for %d keys and encrypt/decrypt=%d ms,",
120       ITERATIONS * TEST_RUNS_PER_KEY * THREAD_COUNT, duration));
121   LOG(a_sprintf("that comes to %d ms per cycle.\n", int(double(duration
122       / TEST_RUNS_PER_KEY / ITERATIONS / THREAD_COUNT))));
123 #endif
124
125   return final_report();
126 }
127
128 //////////////
129
130 #undef UNIT_BASE_THIS_OBJECT 
131 #define UNIT_BASE_THIS_OBJECT (*dynamic_cast<unit_base *>(application_shell::single_instance()))
132
133 void blowfish_thread::perform_activity(void *)
134 {
135   FUNCDEF("perform_activity");
136   int left = ITERATIONS;
137   while (left--) {
138     time_stamp key_start;
139     blowfish_crypto bc(_parent.randomizer().inclusive
140         (blowfish_crypto::minimum_key_size(),
141          blowfish_crypto::maximum_key_size()));
142 #ifdef DEBUG_BLOWFISH
143     LOG(a_sprintf("%d bit key has:", bc.key_size()));
144     astring dumped_key = byte_formatter::text_dump(bc.get_key());
145     LOG(a_sprintf("%s", dumped_key.s()));
146 #endif
147     int key_dur = int(time_stamp().value() - key_start.value());
148
149 #ifdef DEBUG_BLOWFISH
150     LOG(a_sprintf("  key generation took %d ms", key_dur));
151 #endif
152
153     for (int i = 0; i < TEST_RUNS_PER_KEY; i++) {
154       byte_array key;
155       byte_array iv;
156
157       int string_start = _parent.randomizer().inclusive(0, MAX_STRING - 1);
158       int string_end = _parent.randomizer().inclusive(0, MAX_STRING - 1);
159       flip_increasing(string_start, string_end);
160       astring ranstring = _parent._fodder.substring(string_start, string_end);
161 //LOG(a_sprintf("encoding %s\n", ranstring.s());
162 //LOG(a_sprintf("string length encoded: %d\n", ranstring.length());
163
164       byte_array target;
165
166       time_stamp test_start;
167       bool worked = bc.encrypt(byte_array(ranstring.length() + 1,
168           (abyte*)ranstring.s()), target);
169       int enc_durat = int(time_stamp().value() - test_start.value());
170       ASSERT_TRUE(worked, "phase 1 should not fail to encrypt the string");
171
172       byte_array recovered;
173       test_start.reset();
174       worked = bc.decrypt(target, recovered);
175       int dec_durat = int(time_stamp().value() - test_start.value());
176       ASSERT_TRUE(worked, "phase 1 should not fail to decrypt the string");
177 //    LOG(a_sprintf("original has %d chars, recovered has %d chars\n",
178 //        ranstring.length(), recovered.length() - 1));
179
180       astring teddro = (char *)recovered.observe();
181 //LOG(a_sprintf("decoded %s\n", teddro.s()));
182
183 #ifdef DEBUG_BLOWFISH
184       if (teddro != ranstring) {
185         LOG(a_sprintf("error!\toriginal has %d chars, recovered has %d chars\n",
186             ranstring.length(), recovered.length() - 1));
187         LOG(a_sprintf("\tencoded %s\n", ranstring.s()));
188         LOG(a_sprintf("\tdecoded %s\n", teddro.s()));
189       }
190 #endif
191       ASSERT_EQUAL(teddro, ranstring, "should not fail to regenerate the original string");
192
193 #ifdef DEBUG_BLOWFISH
194       LOG(a_sprintf("  encrypt %d ms, decrypt %d ms, data %d bytes\n",
195            enc_durat, dec_durat, string_end - string_start + 1));
196 #endif
197       time_control::sleep_ms(0);  // take a rest.
198     }
199     time_control::sleep_ms(0);  // take a rest.
200   }
201 }
202
203 HOOPLE_MAIN(test_blowfish, )
204