deep mods
[feisty_meow.git] / nucleus / 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, NULL_POINTER);
103   }
104
105   while (_threads.threads()) {
106 #ifdef DEBUG_RSA_CRYPTO
107     LOG(astring("cleaning debris."));
108 #endif
109     _threads.clean_debris();
110 #ifdef DEBUG_RSA_CRYPTO
111     LOG(astring("after cleaning debris."));
112 #endif
113     time_control::sleep_ms(1000);
114   }
115
116 #ifdef DEBUG_RSA_CRYPTO
117   int duration = int(time_stamp().value() - _program_start.value());
118   LOG(a_sprintf("duration for %d keys and encrypt/decrypt=%d ms,",
119       ITERATIONS * THREAD_COUNT, duration));
120   LOG(a_sprintf("that comes to %d ms per cycle.", int(double(duration
121       / ITERATIONS / THREAD_COUNT))));
122 #endif
123
124   return final_report();
125 }
126
127 //////////////
128
129 #undef UNIT_BASE_THIS_OBJECT 
130 #define UNIT_BASE_THIS_OBJECT (*dynamic_cast<unit_base *>(application_shell::single_instance()))
131
132 void rsa_thread::perform_activity(void *)
133 {
134   FUNCDEF("perform_activity");
135   int left = ITERATIONS;
136   while (left--) {
137     time_stamp start;
138
139     rsa_crypto rc_private_here(KEY_SIZE);
140     int key_durat = int(time_stamp().value() - start.value());
141
142     byte_array public_key;
143     rc_private_here.public_key(public_key);  // get our public portion.
144     byte_array private_key;
145     rc_private_here.private_key(private_key);  // get our private portion.
146
147 //RSA_print_fp(stdout, private_key, 0);
148 //RSA_print_fp(stdout, public_key, 0);
149
150     int string_start = _parent.randomizer().inclusive(0, MAX_STRING);
151     int string_end = _parent.randomizer().inclusive(0, MAX_STRING);
152     flip_increasing(string_start, string_end);
153     astring ranstring = _parent.fodder().substring(string_start, string_end);
154     byte_array target;
155
156     // the first phase tests the outsiders sending back data that only we,
157     // with our private key, can decrypt.
158
159     start.reset();
160     rsa_crypto rc_pub(public_key);
161     bool worked = rc_pub.public_encrypt(byte_array(ranstring.length() + 1,
162         (abyte*)ranstring.s()), target);
163     int pub_enc_durat = int(time_stamp().value() - start.value());
164     ASSERT_TRUE(worked, "phase 1 shouldn't fail to encrypt the string");
165
166     rsa_crypto rc_priv(private_key);
167     byte_array recovered;
168     start.reset();
169     worked = rc_priv.private_decrypt(target, recovered);
170     int priv_dec_durat = int(time_stamp().value() - start.value());
171     ASSERT_TRUE(worked, "phase 1 should not fail to decrypt the string");
172
173     astring teddro = (char *)recovered.observe();
174
175     ASSERT_EQUAL(teddro, ranstring, "should not fail to get back the data");
176
177     // the second phase tests us using our private key to encrypt data which
178     // anyone with the public key can decode.
179
180     start.reset();
181     worked = rc_priv.private_encrypt(byte_array(ranstring.length() + 1,
182         (abyte*)ranstring.s()), target);
183     int priv_enc_durat = int(time_stamp().value() - start.value());
184     ASSERT_TRUE(worked, "phase 2 should not fail to encrypt the string");
185
186     start.reset();
187     worked = rc_pub.public_decrypt(target, recovered);
188     int pub_dec_durat = int(time_stamp().value() - start.value());
189     ASSERT_TRUE(worked, "phase 2 should not fail to decrypt the string");
190
191     teddro = (char *)recovered.observe();
192
193     ASSERT_EQUAL(teddro, ranstring, "should not fail to get back the data here either");
194
195 #ifdef DEBUG_RSA_CRYPTO
196     LOG(a_sprintf("key generation: %d ms, public encrypt: %d ms, private "
197         "decrypt: %d ms", key_durat, pub_enc_durat, priv_dec_durat));
198     LOG(a_sprintf("data size: %d bytes, private encrypt: %d ms, public "
199         "decrypt: %d ms",
200         string_end - string_start + 1, priv_enc_durat, pub_dec_durat));
201 #endif
202
203     time_control::sleep_ms(0);  // take a rest.
204   }
205 }
206
207 HOOPLE_MAIN(test_rsa, )
208