basebuilder_pel7ppc64bebuilder0
7 years ago
1 changed files with 400 additions and 0 deletions
@ -0,0 +1,400 @@
@@ -0,0 +1,400 @@
|
||||
/* Test program to verify that RSA signing is thread-safe in OpenSSL. */ |
||||
|
||||
#include <assert.h> |
||||
#include <errno.h> |
||||
#include <fcntl.h> |
||||
#include <limits.h> |
||||
#include <pthread.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
|
||||
#include <openssl/crypto.h> |
||||
#include <openssl/err.h> |
||||
#include <openssl/objects.h> |
||||
#include <openssl/rand.h> |
||||
#include <openssl/rsa.h> |
||||
#include <openssl/md5.h> |
||||
#include <openssl/ssl.h> |
||||
|
||||
/* Just assume we want to do engine stuff if we're using 0.9.6b or |
||||
* higher. This assumption is only valid for versions bundled with RHL. */ |
||||
#if OPENSSL_VERSION_NUMBER >= 0x0090602fL |
||||
#include <openssl/engine.h> |
||||
#define USE_ENGINE |
||||
#endif |
||||
|
||||
#define MAX_THREAD_COUNT 10000 |
||||
#define ITERATION_COUNT 10 |
||||
#define MAIN_COUNT 100 |
||||
|
||||
/* OpenSSL requires us to provide thread ID and locking primitives. */ |
||||
pthread_mutex_t *mutex_locks = NULL; |
||||
static unsigned long |
||||
thread_id_cb(void) |
||||
{ |
||||
return (unsigned long) pthread_self(); |
||||
} |
||||
static void |
||||
lock_cb(int mode, int n, const char *file, int line) |
||||
{ |
||||
if (mode & CRYPTO_LOCK) { |
||||
pthread_mutex_lock(&mutex_locks[n]); |
||||
} else { |
||||
pthread_mutex_unlock(&mutex_locks[n]); |
||||
} |
||||
} |
||||
|
||||
struct thread_args { |
||||
RSA *rsa; |
||||
int digest_type; |
||||
unsigned char *digest; |
||||
unsigned int digest_len; |
||||
unsigned char *signature; |
||||
unsigned int signature_len; |
||||
pthread_t main_thread; |
||||
}; |
||||
|
||||
static int print = 0; |
||||
|
||||
pthread_mutex_t sign_lock = PTHREAD_MUTEX_INITIALIZER; |
||||
static int locked_sign = 0; |
||||
static void SIGN_LOCK() {if (locked_sign) pthread_mutex_lock(&sign_lock);} |
||||
static void SIGN_UNLOCK() {if (locked_sign) pthread_mutex_unlock(&sign_lock);} |
||||
|
||||
pthread_mutex_t verify_lock = PTHREAD_MUTEX_INITIALIZER; |
||||
static int locked_verify = 0; |
||||
static void VERIFY_LOCK() {if (locked_verify) pthread_mutex_lock(&verify_lock);} |
||||
static void VERIFY_UNLOCK() {if (locked_verify) pthread_mutex_unlock(&verify_lock);} |
||||
|
||||
pthread_mutex_t failure_count_lock = PTHREAD_MUTEX_INITIALIZER; |
||||
long failure_count = 0; |
||||
static void |
||||
failure() |
||||
{ |
||||
pthread_mutex_lock(&failure_count_lock); |
||||
failure_count++; |
||||
pthread_mutex_unlock(&failure_count_lock); |
||||
} |
||||
|
||||
static void * |
||||
thread_main(void *argp) |
||||
{ |
||||
struct thread_args *args = argp; |
||||
unsigned char *signature; |
||||
unsigned int signature_len, signature_alloc_len; |
||||
int ret, i; |
||||
|
||||
signature_alloc_len = args->signature_len; |
||||
if (RSA_size(args->rsa) > signature_alloc_len) { |
||||
signature_alloc_len = RSA_size(args->rsa); |
||||
} |
||||
signature = malloc(signature_alloc_len); |
||||
if (signature == NULL) { |
||||
fprintf(stderr, "Skipping checks in thread %lu -- %s.\n", |
||||
(unsigned long) pthread_self(), strerror(errno)); |
||||
pthread_exit(0); |
||||
return NULL; |
||||
} |
||||
for (i = 0; i < ITERATION_COUNT; i++) { |
||||
signature_len = signature_alloc_len; |
||||
SIGN_LOCK(); |
||||
ret = RSA_check_key(args->rsa); |
||||
ERR_print_errors_fp(stdout); |
||||
if (ret != 1) { |
||||
failure(); |
||||
break; |
||||
} |
||||
ret = RSA_sign(args->digest_type, |
||||
args->digest, |
||||
args->digest_len, |
||||
signature, &signature_len, |
||||
args->rsa); |
||||
SIGN_UNLOCK(); |
||||
ERR_print_errors_fp(stdout); |
||||
if (ret != 1) { |
||||
failure(); |
||||
break; |
||||
} |
||||
|
||||
VERIFY_LOCK(); |
||||
ret = RSA_verify(args->digest_type, |
||||
args->digest, |
||||
args->digest_len, |
||||
signature, signature_len, |
||||
args->rsa); |
||||
VERIFY_UNLOCK(); |
||||
if (ret != 1) { |
||||
fprintf(stderr, |
||||
"Signature from thread %lu(%d) fails " |
||||
"verification (passed in thread #%lu)!\n", |
||||
(long) pthread_self(), i, |
||||
(long) args->main_thread); |
||||
ERR_print_errors_fp(stdout); |
||||
failure(); |
||||
continue; |
||||
} |
||||
if (print) { |
||||
fprintf(stderr, ">%d\n", i); |
||||
} |
||||
} |
||||
free(signature); |
||||
|
||||
pthread_exit(0); |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
unsigned char * |
||||
xmemdup(unsigned char *s, size_t len) |
||||
{ |
||||
unsigned char *r; |
||||
r = malloc(len); |
||||
if (r == NULL) { |
||||
fprintf(stderr, "Out of memory.\n"); |
||||
ERR_print_errors_fp(stdout); |
||||
assert(r != NULL); |
||||
} |
||||
memcpy(r, s, len); |
||||
return r; |
||||
} |
||||
|
||||
int |
||||
main(int argc, char **argv) |
||||
{ |
||||
RSA *rsa; |
||||
MD5_CTX md5; |
||||
int fd, i; |
||||
pthread_t threads[MAX_THREAD_COUNT]; |
||||
int thread_count = 1000; |
||||
unsigned char *message, *digest; |
||||
unsigned int message_len, digest_len; |
||||
unsigned char *correct_signature; |
||||
unsigned int correct_siglen, ret; |
||||
struct thread_args master_args, *args; |
||||
int sync = 0, seed = 0; |
||||
int again = 1; |
||||
#ifdef USE_ENGINE |
||||
char *engine = NULL; |
||||
ENGINE *e = NULL; |
||||
#endif |
||||
|
||||
pthread_mutex_init(&failure_count_lock, NULL); |
||||
|
||||
for (i = 1; i < argc; i++) { |
||||
if (strcmp(argv[i], "--seed") == 0) { |
||||
printf("Seeding PRNG.\n"); |
||||
seed++; |
||||
} else |
||||
if (strcmp(argv[i], "--sync") == 0) { |
||||
printf("Running synchronized.\n"); |
||||
sync++; |
||||
} else |
||||
if ((strcmp(argv[i], "--threads") == 0) && (i < argc - 1)) { |
||||
i++; |
||||
thread_count = atol(argv[i]); |
||||
if (thread_count > MAX_THREAD_COUNT) { |
||||
thread_count = MAX_THREAD_COUNT; |
||||
} |
||||
printf("Starting %d threads.\n", thread_count); |
||||
sync++; |
||||
} else |
||||
if (strcmp(argv[i], "--sign") == 0) { |
||||
printf("Locking signing.\n"); |
||||
locked_sign++; |
||||
} else |
||||
if (strcmp(argv[i], "--verify") == 0) { |
||||
printf("Locking verifies.\n"); |
||||
locked_verify++; |
||||
} else |
||||
if (strcmp(argv[i], "--print") == 0) { |
||||
printf("Tracing.\n"); |
||||
print++; |
||||
#ifdef USE_ENGINE |
||||
} else |
||||
if ((strcmp(argv[i], "--engine") == 0) && (i < argc - 1)) { |
||||
printf("Using engine \"%s\".\n", argv[i + 1]); |
||||
engine = argv[i + 1]; |
||||
i++; |
||||
#endif |
||||
} else { |
||||
printf("Bad argument: %s\n", argv[i]); |
||||
return 1; |
||||
} |
||||
} |
||||
|
||||
/* Get some random data to sign. */ |
||||
fd = open("/dev/urandom", O_RDONLY); |
||||
if (fd == -1) { |
||||
fprintf(stderr, "Error opening /dev/urandom: %s\n", |
||||
strerror(errno)); |
||||
} |
||||
|
||||
if (print) { |
||||
fprintf(stderr, "Reading random data.\n"); |
||||
} |
||||
message = malloc(message_len = 9371); |
||||
read(fd, message, message_len); |
||||
close(fd); |
||||
|
||||
/* Initialize the SSL library and set up thread-safe locking. */ |
||||
ERR_load_crypto_strings(); |
||||
SSL_library_init(); |
||||
mutex_locks = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks()); |
||||
for (i = 0; i < CRYPTO_num_locks(); i++) { |
||||
pthread_mutex_init(&mutex_locks[i], NULL); |
||||
} |
||||
CRYPTO_set_id_callback(thread_id_cb); |
||||
CRYPTO_set_locking_callback(lock_cb); |
||||
ERR_print_errors_fp(stdout); |
||||
|
||||
/* Seed the PRNG if we were asked to do so. */ |
||||
if (seed) { |
||||
if (print) { |
||||
fprintf(stderr, "Seeding PRNG.\n"); |
||||
} |
||||
RAND_add(message, message_len, message_len); |
||||
ERR_print_errors_fp(stdout); |
||||
} |
||||
|
||||
/* Turn on a hardware crypto device if asked to do so. */ |
||||
#ifdef USE_ENGINE |
||||
if (engine) { |
||||
#if OPENSSL_VERSION_NUMBER >= 0x0090700fL |
||||
ENGINE_load_builtin_engines(); |
||||
#endif |
||||
if (print) { |
||||
fprintf(stderr, "Initializing \"%s\" engine.\n", |
||||
engine); |
||||
} |
||||
e = ENGINE_by_id(engine); |
||||
ERR_print_errors_fp(stdout); |
||||
if (e) { |
||||
i = ENGINE_init(e); |
||||
ERR_print_errors_fp(stdout); |
||||
i = ENGINE_set_default_RSA(e); |
||||
ERR_print_errors_fp(stdout); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
/* Compute the digest for the signature. */ |
||||
if (print) { |
||||
fprintf(stderr, "Computing digest.\n"); |
||||
} |
||||
digest = malloc(digest_len = MD5_DIGEST_LENGTH); |
||||
MD5_Init(&md5); |
||||
MD5_Update(&md5, message, message_len); |
||||
MD5_Final(digest, &md5); |
||||
|
||||
/* Generate a signing key. */ |
||||
if (print) { |
||||
fprintf(stderr, "Generating key.\n"); |
||||
} |
||||
rsa = RSA_generate_key(4096, 3, NULL, NULL); |
||||
ERR_print_errors_fp(stdout); |
||||
if (rsa == NULL) { |
||||
_exit(1); |
||||
} |
||||
|
||||
/* Sign the data. */ |
||||
correct_siglen = RSA_size(rsa); |
||||
correct_signature = malloc(correct_siglen); |
||||
for (i = 0; i < MAIN_COUNT; i++) { |
||||
if (print) { |
||||
fprintf(stderr, "Signing data (%d).\n", i); |
||||
} |
||||
ret = RSA_check_key(rsa); |
||||
ERR_print_errors_fp(stdout); |
||||
if (ret != 1) { |
||||
failure(); |
||||
} |
||||
correct_siglen = RSA_size(rsa); |
||||
ret = RSA_sign(NID_md5, digest, digest_len, |
||||
correct_signature, &correct_siglen, |
||||
rsa); |
||||
ERR_print_errors_fp(stdout); |
||||
if (ret != 1) { |
||||
_exit(2); |
||||
} |
||||
if (print) { |
||||
fprintf(stderr, "Verifying data (%d).\n", i); |
||||
} |
||||
ret = RSA_verify(NID_md5, digest, digest_len, |
||||
correct_signature, correct_siglen, |
||||
rsa); |
||||
if (ret != 1) { |
||||
_exit(2); |
||||
} |
||||
} |
||||
|
||||
/* Collect up the inforamtion which other threads will need for |
||||
* comparing their signature results with ours. */ |
||||
master_args.rsa = rsa; |
||||
master_args.digest_type = NID_md5; |
||||
master_args.digest = digest; |
||||
master_args.digest_len = digest_len; |
||||
master_args.signature = correct_signature; |
||||
master_args.signature_len = correct_siglen; |
||||
master_args.main_thread = pthread_self(); |
||||
|
||||
fprintf(stdout, "Performing %d signatures in each of %d threads " |
||||
"(%d, %d).\n", ITERATION_COUNT, thread_count, |
||||
digest_len, correct_siglen); |
||||
fflush(NULL); |
||||
|
||||
/* Start up all of the threads. */ |
||||
for (i = 0; i < thread_count; i++) { |
||||
args = malloc(sizeof(struct thread_args)); |
||||
args->rsa = RSAPrivateKey_dup(master_args.rsa); |
||||
args->digest_type = master_args.digest_type; |
||||
args->digest_len = master_args.digest_len; |
||||
args->digest = xmemdup(master_args.digest, args->digest_len); |
||||
args->signature_len = master_args.signature_len; |
||||
args->signature = xmemdup(master_args.signature, |
||||
args->signature_len); |
||||
args->main_thread = pthread_self(); |
||||
ret = pthread_create(&threads[i], NULL, thread_main, args); |
||||
while ((ret != 0) && (errno == EAGAIN)) { |
||||
ret = pthread_create(&threads[i], NULL, |
||||
thread_main, &args); |
||||
fprintf(stderr, "Thread limit hit at %d.\n", i); |
||||
} |
||||
if (ret != 0) { |
||||
fprintf(stderr, "Unable to create thread %d: %s.\n", |
||||
i, strerror(errno)); |
||||
threads[i] = -1; |
||||
} else { |
||||
if (sync) { |
||||
ret = pthread_join(threads[i], NULL); |
||||
assert(ret == 0); |
||||
} |
||||
if (print) { |
||||
fprintf(stderr, "%d\n", i); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* Wait for all threads to complete. So long as we can find an |
||||
* unjoined thread, keep joining threads. */ |
||||
do { |
||||
again = 0; |
||||
for (i = 0; i < thread_count; i++) { |
||||
/* If we have an unterminated thread, join it. */ |
||||
if (threads[i] != -1) { |
||||
again = 1; |
||||
if (print) { |
||||
fprintf(stderr, "Joining thread %d.\n", |
||||
i); |
||||
} |
||||
pthread_join(threads[i], NULL); |
||||
threads[i] = -1; |
||||
break; |
||||
} |
||||
} |
||||
} while (again == 1); |
||||
|
||||
fprintf(stderr, "%ld failures\n", failure_count); |
||||
|
||||
return (failure_count != 0); |
||||
} |
Loading…
Reference in new issue