Multi-threaded Password Hasher
A long time ago I read about a programming challenge that involved writing a multi-threaded password hasher, but I never got around to it and had almost forgotten about it. That is, until now, so I present here my effort after combining POSIX Message Queues and POSIX Threads. This particular implementation uses MD5 but that can be easily changed by editing the pwgen_hash() function.
Here's the code, link with -lpthread -lrt -lcrypto:
#include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <mqueue.h> #include <errno.h> #include <unistd.h> #include <openssl/md5.h> #define QUEUE_NAME "/pwgen_print" #define QUEUE_MAX_SIZE 10 #define QUEUE_MSG_SIZE 128 #define PW_LEN_MAX 32 static char set[256] = {0}; static int set_size = 1; static int min_len = 1; static int max_len = 1; typedef struct pwgen_arg_s { int my_index; int pw_len; } pwgen_arg_t; static inline void pwgen_hash(char *pw, mqd_t mq) { char buffer[QUEUE_MSG_SIZE]; unsigned char md[MD5_DIGEST_LENGTH]; MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, pw, strlen(pw)); MD5_Final(md, &ctx); snprintf(buffer, QUEUE_MSG_SIZE, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x %s", md[0], md[1], md[2], md[3], md[4], md[5], md[6], md[7], md[8], md[9], md[10], md[11], md[12], md[13], md[14], md[15], pw); mq_send(mq, buffer, strlen(buffer), 0); } static void pwgen_traverse(uint8_t pw[], int pw_len, int index, int lowest_index, mqd_t mq) { if (index == lowest_index) { do { /* Hashing is done here */ char buffer[pw_len + 1]; for (index = 0; index < pw_len; index++) { buffer[index] = set[pw[index]]; } buffer[pw_len] = '\0'; pwgen_hash(buffer, mq); pw[lowest_index]++; } while (pw[lowest_index] < set_size); } else if (index > lowest_index) { do { pwgen_traverse(pw, pw_len, index - 1, lowest_index, mq); pw[index - 1] = 0; pw[index]++; } while (pw[index] < set_size); } } static void *pwgen_thread(void *arg) { uint8_t pw[((pwgen_arg_t *)arg)->pw_len]; mqd_t mq; memset(pw, 0, ((pwgen_arg_t *)arg)->pw_len); pw[0] = ((pwgen_arg_t *)arg)->my_index; mq = mq_open(QUEUE_NAME, O_WRONLY); if (mq == (mqd_t)-1) { fprintf(stderr, "%s: mq_open(): %s\n", __func__, strerror(errno)); return ((void *)-1); } pwgen_traverse(pw, ((pwgen_arg_t *)arg)->pw_len, ((pwgen_arg_t *)arg)->pw_len - 1, 1, mq); mq_close(mq); return ((void *)0); } static void *pwgen_print_thread(void *arg) { (void)arg; mqd_t mq; ssize_t read; struct mq_attr attr; char buffer[QUEUE_MSG_SIZE]; int i; memset(&attr, 0, sizeof(struct mq_attr)); attr.mq_maxmsg = QUEUE_MAX_SIZE; attr.mq_msgsize = QUEUE_MSG_SIZE; mq = mq_open(QUEUE_NAME, O_CREAT | O_RDONLY, 0644, &attr); if (mq == (mqd_t)-1) { fprintf(stderr, "%s: mq_open(): %s\n", __func__, strerror(errno)); return ((void *)-1); } while (1) { read = mq_receive(mq, buffer, QUEUE_MSG_SIZE, NULL); if (read > 0) { for (i = 0; i < read; i++) { fputc(buffer[i], stdout); } fputc('\n', stdout); } else { if (read == -1) { fprintf(stderr, "%s: mq_receive(): %s\n", __func__, strerror(errno)); } break; } } mq_close(mq); mq_unlink(QUEUE_NAME); return ((void *)0); } static void pwgen_start(void) { pthread_t pwgen_tid[set_size]; pwgen_arg_t arg[set_size]; int len, index; for (len = min_len; len <= max_len; len++) { /* Start all threads. */ for (index = 0; index < set_size; index++) { arg[index].my_index = index; arg[index].pw_len = len; pthread_create(&pwgen_tid[index], NULL, pwgen_thread, &arg[index]); } /* Wait for all threads. */ for (index = 0; index < set_size; index++) { pthread_join(pwgen_tid[index], NULL); } } } static int pwgen(void) { pthread_t print_tid; mqd_t mq; /* Create print thread and wait for it to create its message queue. */ pthread_create(&print_tid, NULL, pwgen_print_thread, NULL); while (1) { mq = mq_open(QUEUE_NAME, O_WRONLY); if (mq == (mqd_t)-1) { if (errno == ENOENT) { usleep(1000); } else { fprintf(stderr, "%s: mq_open(): %s\n", __func__, strerror(errno)); return -1; } } else { break; } } /* Start password generating threads. */ pwgen_start(); /* Send dummy message of 0 bytes to terminate print thread. */ char dummy[1]; mq_send(mq, dummy, 0, 0); pthread_join(print_tid, NULL); return 0; } static void display_help(char *progname) { fprintf(stderr, "Usage: %s <options>\n", progname); fprintf(stderr, "Options:\n" " -h Display this help and exit.\n" " -s SET Character SET to use. (E.g. \"abcde\")\n" " -n MIN Minimum length of generated password. (2 - %d)\n" " -x MAX Maximum length of generated password. (2 - %d)\n" "\n", PW_LEN_MAX, PW_LEN_MAX); } int main(int argc, char *argv[]) { int c; while ((c = getopt(argc, argv, "hs:n:x:")) != -1) { switch (c) { case 'h': display_help(argv[0]); break; case 's': strncpy(set, optarg, sizeof(set)); set_size = strlen(set); break; case 'n': min_len = atoi(optarg); break; case 'x': max_len = atoi(optarg); break; case '?': default: display_help(argv[0]); return EXIT_FAILURE; } } if (set_size <= 1) { fprintf(stderr, "Please specify a character set!\n"); display_help(argv[0]); return EXIT_FAILURE; } if (min_len < 2 || min_len > PW_LEN_MAX) { fprintf(stderr, "Please specify minimum length within range!\n"); display_help(argv[0]); return EXIT_FAILURE; } if (max_len < 2 || max_len > PW_LEN_MAX) { fprintf(stderr, "Please specify minimum length within range!\n"); display_help(argv[0]); return EXIT_FAILURE; } pwgen(); return EXIT_SUCCESS; }