1 |
/* Test program to verify that RSA signing is thread-safe in OpenSSL. */ |
2 |
|
3 |
#include <assert.h> |
4 |
#include <errno.h> |
5 |
#include <fcntl.h> |
6 |
#include <limits.h> |
7 |
#include <pthread.h> |
8 |
#include <stdio.h> |
9 |
#include <string.h> |
10 |
#include <unistd.h> |
11 |
|
12 |
#include <openssl/crypto.h> |
13 |
#include <openssl/err.h> |
14 |
#include <openssl/objects.h> |
15 |
#include <openssl/rand.h> |
16 |
#include <openssl/rsa.h> |
17 |
#include <openssl/md5.h> |
18 |
#include <openssl/ssl.h> |
19 |
|
20 |
/* Just assume we want to do engine stuff if we're using 0.9.6b or |
21 |
* higher. This assumption is only valid for versions bundled with RHL. */ |
22 |
#if OPENSSL_VERSION_NUMBER >= 0x0090602fL |
23 |
#include <openssl/engine.h> |
24 |
#define USE_ENGINE |
25 |
#endif |
26 |
|
27 |
#define MAX_THREAD_COUNT 10000 |
28 |
#define ITERATION_COUNT 10 |
29 |
#define MAIN_COUNT 100 |
30 |
|
31 |
/* OpenSSL requires us to provide thread ID and locking primitives. */ |
32 |
pthread_mutex_t *mutex_locks = NULL; |
33 |
static unsigned long |
34 |
thread_id_cb(void) |
35 |
{ |
36 |
return (unsigned long) pthread_self(); |
37 |
} |
38 |
static void |
39 |
lock_cb(int mode, int n, const char *file, int line) |
40 |
{ |
41 |
if (mode & CRYPTO_LOCK) { |
42 |
pthread_mutex_lock(&mutex_locks[n]); |
43 |
} else { |
44 |
pthread_mutex_unlock(&mutex_locks[n]); |
45 |
} |
46 |
} |
47 |
|
48 |
struct thread_args { |
49 |
RSA *rsa; |
50 |
int digest_type; |
51 |
unsigned char *digest; |
52 |
unsigned int digest_len; |
53 |
unsigned char *signature; |
54 |
unsigned int signature_len; |
55 |
pthread_t main_thread; |
56 |
}; |
57 |
|
58 |
static int print = 0; |
59 |
|
60 |
pthread_mutex_t sign_lock = PTHREAD_MUTEX_INITIALIZER; |
61 |
static int locked_sign = 0; |
62 |
static void SIGN_LOCK() {if (locked_sign) pthread_mutex_lock(&sign_lock);} |
63 |
static void SIGN_UNLOCK() {if (locked_sign) pthread_mutex_unlock(&sign_lock);} |
64 |
|
65 |
pthread_mutex_t verify_lock = PTHREAD_MUTEX_INITIALIZER; |
66 |
static int locked_verify = 0; |
67 |
static void VERIFY_LOCK() {if (locked_verify) pthread_mutex_lock(&verify_lock);} |
68 |
static void VERIFY_UNLOCK() {if (locked_verify) pthread_mutex_unlock(&verify_lock);} |
69 |
|
70 |
pthread_mutex_t failure_count_lock = PTHREAD_MUTEX_INITIALIZER; |
71 |
long failure_count = 0; |
72 |
static void |
73 |
failure() |
74 |
{ |
75 |
pthread_mutex_lock(&failure_count_lock); |
76 |
failure_count++; |
77 |
pthread_mutex_unlock(&failure_count_lock); |
78 |
} |
79 |
|
80 |
static void * |
81 |
thread_main(void *argp) |
82 |
{ |
83 |
struct thread_args *args = argp; |
84 |
unsigned char *signature; |
85 |
unsigned int signature_len, signature_alloc_len; |
86 |
int ret, i; |
87 |
|
88 |
signature_alloc_len = args->signature_len; |
89 |
if (RSA_size(args->rsa) > signature_alloc_len) { |
90 |
signature_alloc_len = RSA_size(args->rsa); |
91 |
} |
92 |
signature = malloc(signature_alloc_len); |
93 |
if (signature == NULL) { |
94 |
fprintf(stderr, "Skipping checks in thread %lu -- %s.\n", |
95 |
(unsigned long) pthread_self(), strerror(errno)); |
96 |
pthread_exit(0); |
97 |
return NULL; |
98 |
} |
99 |
for (i = 0; i < ITERATION_COUNT; i++) { |
100 |
signature_len = signature_alloc_len; |
101 |
SIGN_LOCK(); |
102 |
ret = RSA_check_key(args->rsa); |
103 |
ERR_print_errors_fp(stdout); |
104 |
if (ret != 1) { |
105 |
failure(); |
106 |
break; |
107 |
} |
108 |
ret = RSA_sign(args->digest_type, |
109 |
args->digest, |
110 |
args->digest_len, |
111 |
signature, &signature_len, |
112 |
args->rsa); |
113 |
SIGN_UNLOCK(); |
114 |
ERR_print_errors_fp(stdout); |
115 |
if (ret != 1) { |
116 |
failure(); |
117 |
break; |
118 |
} |
119 |
|
120 |
VERIFY_LOCK(); |
121 |
ret = RSA_verify(args->digest_type, |
122 |
args->digest, |
123 |
args->digest_len, |
124 |
signature, signature_len, |
125 |
args->rsa); |
126 |
VERIFY_UNLOCK(); |
127 |
if (ret != 1) { |
128 |
fprintf(stderr, |
129 |
"Signature from thread %lu(%d) fails " |
130 |
"verification (passed in thread #%lu)!\n", |
131 |
(long) pthread_self(), i, |
132 |
(long) args->main_thread); |
133 |
ERR_print_errors_fp(stdout); |
134 |
failure(); |
135 |
continue; |
136 |
} |
137 |
if (print) { |
138 |
fprintf(stderr, ">%d\n", i); |
139 |
} |
140 |
} |
141 |
free(signature); |
142 |
|
143 |
pthread_exit(0); |
144 |
|
145 |
return NULL; |
146 |
} |
147 |
|
148 |
unsigned char * |
149 |
xmemdup(unsigned char *s, size_t len) |
150 |
{ |
151 |
unsigned char *r; |
152 |
r = malloc(len); |
153 |
if (r == NULL) { |
154 |
fprintf(stderr, "Out of memory.\n"); |
155 |
ERR_print_errors_fp(stdout); |
156 |
assert(r != NULL); |
157 |
} |
158 |
memcpy(r, s, len); |
159 |
return r; |
160 |
} |
161 |
|
162 |
int |
163 |
main(int argc, char **argv) |
164 |
{ |
165 |
RSA *rsa; |
166 |
MD5_CTX md5; |
167 |
int fd, i; |
168 |
pthread_t threads[MAX_THREAD_COUNT]; |
169 |
int thread_count = 1000; |
170 |
unsigned char *message, *digest; |
171 |
unsigned int message_len, digest_len; |
172 |
unsigned char *correct_signature; |
173 |
unsigned int correct_siglen, ret; |
174 |
struct thread_args master_args, *args; |
175 |
int sync = 0, seed = 0; |
176 |
int again = 1; |
177 |
#ifdef USE_ENGINE |
178 |
char *engine = NULL; |
179 |
ENGINE *e = NULL; |
180 |
#endif |
181 |
|
182 |
pthread_mutex_init(&failure_count_lock, NULL); |
183 |
|
184 |
for (i = 1; i < argc; i++) { |
185 |
if (strcmp(argv[i], "--seed") == 0) { |
186 |
printf("Seeding PRNG.\n"); |
187 |
seed++; |
188 |
} else |
189 |
if (strcmp(argv[i], "--sync") == 0) { |
190 |
printf("Running synchronized.\n"); |
191 |
sync++; |
192 |
} else |
193 |
if ((strcmp(argv[i], "--threads") == 0) && (i < argc - 1)) { |
194 |
i++; |
195 |
thread_count = atol(argv[i]); |
196 |
if (thread_count > MAX_THREAD_COUNT) { |
197 |
thread_count = MAX_THREAD_COUNT; |
198 |
} |
199 |
printf("Starting %d threads.\n", thread_count); |
200 |
sync++; |
201 |
} else |
202 |
if (strcmp(argv[i], "--sign") == 0) { |
203 |
printf("Locking signing.\n"); |
204 |
locked_sign++; |
205 |
} else |
206 |
if (strcmp(argv[i], "--verify") == 0) { |
207 |
printf("Locking verifies.\n"); |
208 |
locked_verify++; |
209 |
} else |
210 |
if (strcmp(argv[i], "--print") == 0) { |
211 |
printf("Tracing.\n"); |
212 |
print++; |
213 |
#ifdef USE_ENGINE |
214 |
} else |
215 |
if ((strcmp(argv[i], "--engine") == 0) && (i < argc - 1)) { |
216 |
printf("Using engine \"%s\".\n", argv[i + 1]); |
217 |
engine = argv[i + 1]; |
218 |
i++; |
219 |
#endif |
220 |
} else { |
221 |
printf("Bad argument: %s\n", argv[i]); |
222 |
return 1; |
223 |
} |
224 |
} |
225 |
|
226 |
/* Get some random data to sign. */ |
227 |
fd = open("/dev/urandom", O_RDONLY); |
228 |
if (fd == -1) { |
229 |
fprintf(stderr, "Error opening /dev/urandom: %s\n", |
230 |
strerror(errno)); |
231 |
} |
232 |
|
233 |
if (print) { |
234 |
fprintf(stderr, "Reading random data.\n"); |
235 |
} |
236 |
message = malloc(message_len = 9371); |
237 |
read(fd, message, message_len); |
238 |
close(fd); |
239 |
|
240 |
/* Initialize the SSL library and set up thread-safe locking. */ |
241 |
ERR_load_crypto_strings(); |
242 |
SSL_library_init(); |
243 |
mutex_locks = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks()); |
244 |
for (i = 0; i < CRYPTO_num_locks(); i++) { |
245 |
pthread_mutex_init(&mutex_locks[i], NULL); |
246 |
} |
247 |
CRYPTO_set_id_callback(thread_id_cb); |
248 |
CRYPTO_set_locking_callback(lock_cb); |
249 |
ERR_print_errors_fp(stdout); |
250 |
|
251 |
/* Seed the PRNG if we were asked to do so. */ |
252 |
if (seed) { |
253 |
if (print) { |
254 |
fprintf(stderr, "Seeding PRNG.\n"); |
255 |
} |
256 |
RAND_add(message, message_len, message_len); |
257 |
ERR_print_errors_fp(stdout); |
258 |
} |
259 |
|
260 |
/* Turn on a hardware crypto device if asked to do so. */ |
261 |
#ifdef USE_ENGINE |
262 |
if (engine) { |
263 |
#if OPENSSL_VERSION_NUMBER >= 0x0090700fL |
264 |
ENGINE_load_builtin_engines(); |
265 |
#endif |
266 |
if (print) { |
267 |
fprintf(stderr, "Initializing \"%s\" engine.\n", |
268 |
engine); |
269 |
} |
270 |
e = ENGINE_by_id(engine); |
271 |
ERR_print_errors_fp(stdout); |
272 |
if (e) { |
273 |
i = ENGINE_init(e); |
274 |
ERR_print_errors_fp(stdout); |
275 |
i = ENGINE_set_default_RSA(e); |
276 |
ERR_print_errors_fp(stdout); |
277 |
} |
278 |
} |
279 |
#endif |
280 |
|
281 |
/* Compute the digest for the signature. */ |
282 |
if (print) { |
283 |
fprintf(stderr, "Computing digest.\n"); |
284 |
} |
285 |
digest = malloc(digest_len = MD5_DIGEST_LENGTH); |
286 |
MD5_Init(&md5); |
287 |
MD5_Update(&md5, message, message_len); |
288 |
MD5_Final(digest, &md5); |
289 |
|
290 |
/* Generate a signing key. */ |
291 |
if (print) { |
292 |
fprintf(stderr, "Generating key.\n"); |
293 |
} |
294 |
rsa = RSA_generate_key(4096, 3, NULL, NULL); |
295 |
ERR_print_errors_fp(stdout); |
296 |
if (rsa == NULL) { |
297 |
_exit(1); |
298 |
} |
299 |
|
300 |
/* Sign the data. */ |
301 |
correct_siglen = RSA_size(rsa); |
302 |
correct_signature = malloc(correct_siglen); |
303 |
for (i = 0; i < MAIN_COUNT; i++) { |
304 |
if (print) { |
305 |
fprintf(stderr, "Signing data (%d).\n", i); |
306 |
} |
307 |
ret = RSA_check_key(rsa); |
308 |
ERR_print_errors_fp(stdout); |
309 |
if (ret != 1) { |
310 |
failure(); |
311 |
} |
312 |
correct_siglen = RSA_size(rsa); |
313 |
ret = RSA_sign(NID_md5, digest, digest_len, |
314 |
correct_signature, &correct_siglen, |
315 |
rsa); |
316 |
ERR_print_errors_fp(stdout); |
317 |
if (ret != 1) { |
318 |
_exit(2); |
319 |
} |
320 |
if (print) { |
321 |
fprintf(stderr, "Verifying data (%d).\n", i); |
322 |
} |
323 |
ret = RSA_verify(NID_md5, digest, digest_len, |
324 |
correct_signature, correct_siglen, |
325 |
rsa); |
326 |
if (ret != 1) { |
327 |
_exit(2); |
328 |
} |
329 |
} |
330 |
|
331 |
/* Collect up the inforamtion which other threads will need for |
332 |
* comparing their signature results with ours. */ |
333 |
master_args.rsa = rsa; |
334 |
master_args.digest_type = NID_md5; |
335 |
master_args.digest = digest; |
336 |
master_args.digest_len = digest_len; |
337 |
master_args.signature = correct_signature; |
338 |
master_args.signature_len = correct_siglen; |
339 |
master_args.main_thread = pthread_self(); |
340 |
|
341 |
fprintf(stdout, "Performing %d signatures in each of %d threads " |
342 |
"(%d, %d).\n", ITERATION_COUNT, thread_count, |
343 |
digest_len, correct_siglen); |
344 |
fflush(NULL); |
345 |
|
346 |
/* Start up all of the threads. */ |
347 |
for (i = 0; i < thread_count; i++) { |
348 |
args = malloc(sizeof(struct thread_args)); |
349 |
args->rsa = RSAPrivateKey_dup(master_args.rsa); |
350 |
args->digest_type = master_args.digest_type; |
351 |
args->digest_len = master_args.digest_len; |
352 |
args->digest = xmemdup(master_args.digest, args->digest_len); |
353 |
args->signature_len = master_args.signature_len; |
354 |
args->signature = xmemdup(master_args.signature, |
355 |
args->signature_len); |
356 |
args->main_thread = pthread_self(); |
357 |
ret = pthread_create(&threads[i], NULL, thread_main, args); |
358 |
while ((ret != 0) && (errno == EAGAIN)) { |
359 |
ret = pthread_create(&threads[i], NULL, |
360 |
thread_main, &args); |
361 |
fprintf(stderr, "Thread limit hit at %d.\n", i); |
362 |
} |
363 |
if (ret != 0) { |
364 |
fprintf(stderr, "Unable to create thread %d: %s.\n", |
365 |
i, strerror(errno)); |
366 |
threads[i] = -1; |
367 |
} else { |
368 |
if (sync) { |
369 |
ret = pthread_join(threads[i], NULL); |
370 |
assert(ret == 0); |
371 |
} |
372 |
if (print) { |
373 |
fprintf(stderr, "%d\n", i); |
374 |
} |
375 |
} |
376 |
} |
377 |
|
378 |
/* Wait for all threads to complete. So long as we can find an |
379 |
* unjoined thread, keep joining threads. */ |
380 |
do { |
381 |
again = 0; |
382 |
for (i = 0; i < thread_count; i++) { |
383 |
/* If we have an unterminated thread, join it. */ |
384 |
if (threads[i] != -1) { |
385 |
again = 1; |
386 |
if (print) { |
387 |
fprintf(stderr, "Joining thread %d.\n", |
388 |
i); |
389 |
} |
390 |
pthread_join(threads[i], NULL); |
391 |
threads[i] = -1; |
392 |
break; |
393 |
} |
394 |
} |
395 |
} while (again == 1); |
396 |
|
397 |
fprintf(stderr, "%ld failures\n", failure_count); |
398 |
|
399 |
return (failure_count != 0); |
400 |
} |