2 * Name : test RSA public key encryption
3 * Author : Chris Koeritz
5 * Exercises the RSA encryption functions from the crypto library.
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 *
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>
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;
45 //#define DEBUG_RSA_CRYPTO
46 // uncomment for noisy run.
48 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print)
50 const int KEY_SIZE = 1024;
51 // the size of the RSA key that we'll create.
53 const int MAX_STRING = 4000;
54 // the largest chunk that we'll try to encrypt.
56 const int THREAD_COUNT = 5; // number of threads testing rsa at once.
58 const int ITERATIONS = 6; // number of test runs in our testing threads.
62 class test_rsa; // forward.
64 class rsa_thread : public ethread
67 rsa_thread(test_rsa &parent) : ethread(), _parent(parent) {}
69 void perform_activity(void *ptr);
70 // try out random rsa keys on randomly chosen chunks of the fodder.
78 class test_rsa : public virtual unit_base, virtual public application_shell
82 : _fodder(string_manipulation::make_random_name(MAX_STRING + 1, MAX_STRING + 1)) {}
83 virtual ~test_rsa() {}
84 DEFINE_CLASS_NAME("test_rsa");
86 const astring &fodder() const { return _fodder; }
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.
97 int test_rsa::execute()
100 int left = THREAD_COUNT;
102 _threads.add_thread(new rsa_thread(*this), true, NULL_POINTER);
105 while (_threads.threads()) {
106 #ifdef DEBUG_RSA_CRYPTO
107 LOG(astring("cleaning debris."));
109 _threads.clean_debris();
110 #ifdef DEBUG_RSA_CRYPTO
111 LOG(astring("after cleaning debris."));
113 time_control::sleep_ms(1000);
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))));
124 return final_report();
129 #undef UNIT_BASE_THIS_OBJECT
130 #define UNIT_BASE_THIS_OBJECT (*dynamic_cast<unit_base *>(application_shell::single_instance()))
132 void rsa_thread::perform_activity(void *)
134 FUNCDEF("perform_activity");
135 int left = ITERATIONS;
139 rsa_crypto rc_private_here(KEY_SIZE);
140 int key_durat = int(time_stamp().value() - start.value());
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.
147 //RSA_print_fp(stdout, private_key, 0);
148 //RSA_print_fp(stdout, public_key, 0);
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);
156 // the first phase tests the outsiders sending back data that only we,
157 // with our private key, can decrypt.
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");
166 rsa_crypto rc_priv(private_key);
167 byte_array recovered;
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");
173 astring teddro = (char *)recovered.observe();
175 ASSERT_EQUAL(teddro, ranstring, "should not fail to get back the data");
177 // the second phase tests us using our private key to encrypt data which
178 // anyone with the public key can decode.
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");
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");
191 teddro = (char *)recovered.observe();
193 ASSERT_EQUAL(teddro, ranstring, "should not fail to get back the data here either");
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 "
200 string_end - string_start + 1, priv_enc_durat, pub_dec_durat));
203 time_control::sleep_ms(0); // take a rest.
207 HOOPLE_MAIN(test_rsa, )