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, NIL);
105 while (_threads.threads()) {
106 #ifdef DEBUG_RSA_CRYPTO
107 LOG(astring("cleaning debris."));
109 _threads.clean_debris();
110 time_control::sleep_ms(1000);
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))));
121 return final_report();
126 #undef UNIT_BASE_THIS_OBJECT
127 #define UNIT_BASE_THIS_OBJECT (*dynamic_cast<unit_base *>(application_shell::single_instance()))
129 void rsa_thread::perform_activity(void *)
131 FUNCDEF("perform_activity");
132 int left = ITERATIONS;
136 rsa_crypto rc_private_here(KEY_SIZE);
137 int key_durat = int(time_stamp().value() - start.value());
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.
144 //RSA_print_fp(stdout, private_key, 0);
145 //RSA_print_fp(stdout, public_key, 0);
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);
153 // the first phase tests the outsiders sending back data that only we,
154 // with our private key, can decrypt.
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");
163 rsa_crypto rc_priv(private_key);
164 byte_array recovered;
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");
170 astring teddro = (char *)recovered.observe();
172 ASSERT_EQUAL(teddro, ranstring, "should not fail to get back the data");
174 // the second phase tests us using our private key to encrypt data which
175 // anyone with the public key can decode.
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");
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");
188 teddro = (char *)recovered.observe();
190 ASSERT_EQUAL(teddro, ranstring, "should not fail to get back the data here either");
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 "
197 string_end - string_start + 1, priv_enc_durat, pub_dec_durat));
200 time_control::sleep_ms(0); // take a rest.
204 HOOPLE_MAIN(test_rsa, )