first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / core / library / tests_crypto / test_rsa_crypto.cpp
1 /*
2 *  Name   : test RSA public key encryption
3 *  Author : Chris Koeritz
4 *  Purpose:
5 *    Exercises the RSA encryption functions from the crypto library.
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 <application/hoople_main.h>
16 #include <basis/byte_array.h>
17 #include <basis/astring.h>
18 #include <crypto/rsa_crypto.h>
19 #include <mathematics/chaos.h>
20 #include <processes/ethread.h>
21 #include <processes/thread_cabinet.h>
22 #include <structures/static_memory_gremlin.h>
23 #include <structures/unique_id.h>
24 #include <textual/byte_formatter.h>
25 #include <textual/string_manipulation.h>
26 #include <timely/time_control.h>
27 #include <timely/time_stamp.h>
28 #include <unit_test/unit_base.h>
29
30 #include <stdio.h>
31 #include <string.h>
32
33 using namespace application;
34 using namespace basis;
35 using namespace crypto;
36 using namespace filesystem;
37 using namespace loggers;
38 using namespace mathematics;
39 using namespace processes;
40 using namespace structures;
41 using namespace textual;
42 using namespace timely;
43 using namespace unit_test;
44
45 //#define DEBUG_RSA_CRYPTO
46   // uncomment for noisy run.
47
48 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print)
49
50 const int KEY_SIZE = 1024;
51   // the size of the RSA key that we'll create.
52
53 const int MAX_STRING = 4000;
54   // the largest chunk that we'll try to encrypt.
55
56 const int THREAD_COUNT = 5;  // number of threads testing rsa at once.
57
58 const int ITERATIONS = 6;  // number of test runs in our testing threads.
59
60 //////////////
61
62 class test_rsa;  // forward.
63
64 class rsa_thread : public ethread
65 {
66 public:
67   rsa_thread(test_rsa &parent) : ethread(), _parent(parent) {}
68
69   void perform_activity(void *ptr);
70     // try out random rsa keys on randomly chosen chunks of the fodder.
71
72 private:
73   test_rsa &_parent;
74 };
75
76 //////////////
77
78 class test_rsa : public virtual unit_base, virtual public application_shell
79 {
80 public:
81   test_rsa()
82       : _fodder(string_manipulation::make_random_name(MAX_STRING + 1, MAX_STRING + 1)) {}
83   virtual ~test_rsa() {}
84   DEFINE_CLASS_NAME("test_rsa");
85
86   const astring &fodder() const { return _fodder; }
87
88   int execute();
89
90 private:
91   astring _fodder;  // chunks taken from this are encrypted and decrypted.
92   time_stamp _program_start;  // the time at which we started executing.
93   thread_cabinet _threads;  // manages our testing threads.
94   friend class rsa_thread;  // bad practice, but saves time in test app.
95 };
96
97 int test_rsa::execute()
98 {
99   FUNCDEF("execute");
100   int left = THREAD_COUNT;
101   while (left--) {
102     _threads.add_thread(new rsa_thread(*this), true, NIL);
103   }
104
105   while (_threads.threads()) {
106 #ifdef DEBUG_RSA_CRYPTO
107     LOG(astring("cleaning debris."));
108 #endif
109     _threads.clean_debris();
110     time_control::sleep_ms(1000);
111   }
112
113 #ifdef DEBUG_RSA_CRYPTO
114   int duration = int(time_stamp().value() - _program_start.value());
115   LOG(a_sprintf("duration for %d keys and encrypt/decrypt=%d ms,",
116       ITERATIONS * THREAD_COUNT, duration));
117   LOG(a_sprintf("that comes to %d ms per cycle.", int(double(duration
118       / ITERATIONS / THREAD_COUNT))));
119 #endif
120
121   return final_report();
122 }
123
124 //////////////
125
126 #undef UNIT_BASE_THIS_OBJECT 
127 #define UNIT_BASE_THIS_OBJECT (*dynamic_cast<unit_base *>(application_shell::single_instance()))
128
129 void rsa_thread::perform_activity(void *)
130 {
131   FUNCDEF("perform_activity");
132   int left = ITERATIONS;
133   while (left--) {
134     time_stamp start;
135
136     rsa_crypto rc_private_here(KEY_SIZE);
137     int key_durat = int(time_stamp().value() - start.value());
138
139     byte_array public_key;
140     rc_private_here.public_key(public_key);  // get our public portion.
141     byte_array private_key;
142     rc_private_here.private_key(private_key);  // get our private portion.
143
144 //RSA_print_fp(stdout, private_key, 0);
145 //RSA_print_fp(stdout, public_key, 0);
146
147     int string_start = _parent.randomizer().inclusive(0, MAX_STRING);
148     int string_end = _parent.randomizer().inclusive(0, MAX_STRING);
149     flip_increasing(string_start, string_end);
150     astring ranstring = _parent.fodder().substring(string_start, string_end);
151     byte_array target;
152
153     // the first phase tests the outsiders sending back data that only we,
154     // with our private key, can decrypt.
155
156     start.reset();
157     rsa_crypto rc_pub(public_key);
158     bool worked = rc_pub.public_encrypt(byte_array(ranstring.length() + 1,
159         (abyte*)ranstring.s()), target);
160     int pub_enc_durat = int(time_stamp().value() - start.value());
161     ASSERT_TRUE(worked, "phase 1 shouldn't fail to encrypt the string");
162
163     rsa_crypto rc_priv(private_key);
164     byte_array recovered;
165     start.reset();
166     worked = rc_priv.private_decrypt(target, recovered);
167     int priv_dec_durat = int(time_stamp().value() - start.value());
168     ASSERT_TRUE(worked, "phase 1 should not fail to decrypt the string");
169
170     astring teddro = (char *)recovered.observe();
171
172     ASSERT_EQUAL(teddro, ranstring, "should not fail to get back the data");
173
174     // the second phase tests us using our private key to encrypt data which
175     // anyone with the public key can decode.
176
177     start.reset();
178     worked = rc_priv.private_encrypt(byte_array(ranstring.length() + 1,
179         (abyte*)ranstring.s()), target);
180     int priv_enc_durat = int(time_stamp().value() - start.value());
181     ASSERT_TRUE(worked, "phase 2 should not fail to encrypt the string");
182
183     start.reset();
184     worked = rc_pub.public_decrypt(target, recovered);
185     int pub_dec_durat = int(time_stamp().value() - start.value());
186     ASSERT_TRUE(worked, "phase 2 should not fail to decrypt the string");
187
188     teddro = (char *)recovered.observe();
189
190     ASSERT_EQUAL(teddro, ranstring, "should not fail to get back the data here either");
191
192 #ifdef DEBUG_RSA_CRYPTO
193     LOG(a_sprintf("key generation: %d ms, public encrypt: %d ms, private "
194         "decrypt: %d ms", key_durat, pub_enc_durat, priv_dec_durat));
195     LOG(a_sprintf("data size: %d bytes, private encrypt: %d ms, public "
196         "decrypt: %d ms",
197         string_end - string_start + 1, priv_enc_durat, pub_dec_durat));
198 #endif
199
200     time_control::sleep_ms(0);  // take a rest.
201   }
202 }
203
204 HOOPLE_MAIN(test_rsa, )
205