bcrypt

bcrypt with a threadpool
git clone https://noulin.net/git/bcrypt.git
Log | Files | Refs | README | LICENSE

wrapper.c (11373B)


      1 /*
      2  * Written by Solar Designer <solar at openwall.com> in 2000-2014.
      3  * No copyright is claimed, and the software is hereby placed in the public
      4  * domain.  In case this attempt to disclaim copyright and place the software
      5  * in the public domain is deemed null and void, then the software is
      6  * Copyright (c) 2000-2014 Solar Designer and it is hereby released to the
      7  * general public under the following terms:
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted.
     11  *
     12  * There's ABSOLUTELY NO WARRANTY, express or implied.
     13  *
     14  * See crypt_blowfish.c for more information.
     15  */
     16 
     17 #include <stdlib.h>
     18 #include <string.h>
     19 
     20 #include <errno.h>
     21 #ifndef __set_errno
     22 #define __set_errno(val) errno = (val)
     23 #endif
     24 
     25 #ifdef TEST
     26 #include <stdio.h>
     27 #include <unistd.h>
     28 #include <signal.h>
     29 #include <time.h>
     30 #include <sys/time.h>
     31 #include <sys/times.h>
     32 #ifdef TEST_THREADS
     33 #include <pthread.h>
     34 #endif
     35 #endif
     36 
     37 #define CRYPT_OUTPUT_SIZE		(7 + 22 + 31 + 1)
     38 #define CRYPT_GENSALT_OUTPUT_SIZE	(7 + 22 + 1)
     39 
     40 #include "crypt_blowfish.h"
     41 
     42 static int _crypt_data_alloc(void **data, int *size, int need)
     43 {
     44 	void *updated;
     45 
     46 	if (*data && *size >= need) return 0;
     47 
     48 	updated = realloc(*data, need);
     49 
     50 	if (!updated) {
     51 		return -1;
     52 	}
     53 
     54 	*data = updated;
     55 	*size = need;
     56 
     57 	return 0;
     58 }
     59 
     60 static char *_crypt_retval_magic(char *retval, const char *setting,
     61 	char *output, int size)
     62 {
     63 	if (retval)
     64 		return retval;
     65 
     66 	if (_crypt_output_magic(setting, output, size))
     67 		return NULL; /* shouldn't happen */
     68 
     69 	return output;
     70 }
     71 
     72 char *crypt_rn(const char *key, const char *setting, void *data, int size)
     73 {
     74 	return _crypt_blowfish_rn(key, setting, (char *)data, size);
     75 }
     76 
     77 char *crypt_ra(const char *key, const char *setting,
     78 	void **data, int *size)
     79 {
     80 	if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
     81 		return NULL;
     82 	return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
     83 }
     84 
     85 char *crypt_r(const char *key, const char *setting, void *data)
     86 {
     87 	return _crypt_retval_magic(
     88 		crypt_rn(key, setting, data, CRYPT_OUTPUT_SIZE),
     89 		setting, (char *)data, CRYPT_OUTPUT_SIZE);
     90 }
     91 
     92 char *crypt(const char *key, const char *setting)
     93 {
     94 	static char output[CRYPT_OUTPUT_SIZE];
     95 
     96 	return _crypt_retval_magic(
     97 		crypt_rn(key, setting, output, sizeof(output)),
     98 		setting, output, sizeof(output));
     99 }
    100 
    101 char *crypt_gensalt_rn(const char *prefix, unsigned long count,
    102 	const char *input, int size, char *output, int output_size)
    103 {
    104 	char *(*use)(const char *_prefix, unsigned long _count,
    105 		const char *_input, int _size,
    106 		char *_output, int _output_size);
    107 
    108 	/* This may be supported on some platforms in the future */
    109 	if (!input) {
    110 		__set_errno(EINVAL);
    111 		return NULL;
    112 	}
    113 
    114 	if (!strncmp(prefix, "$2a$", 4) || !strncmp(prefix, "$2b$", 4) ||
    115 	    !strncmp(prefix, "$2y$", 4))
    116 		use = _crypt_gensalt_blowfish_rn;
    117 	else {
    118 		__set_errno(EINVAL);
    119 		return NULL;
    120 	}
    121 
    122 	return use(prefix, count, input, size, output, output_size);
    123 }
    124 
    125 char *crypt_gensalt_ra(const char *prefix, unsigned long count,
    126 	const char *input, int size)
    127 {
    128 	char output[CRYPT_GENSALT_OUTPUT_SIZE];
    129 	char *retval;
    130 
    131 	retval = crypt_gensalt_rn(prefix, count,
    132 		input, size, output, sizeof(output));
    133 
    134 	if (retval) {
    135 		retval = strdup(retval);
    136 	}
    137 
    138 	return retval;
    139 }
    140 
    141 char *crypt_gensalt(const char *prefix, unsigned long count,
    142 	const char *input, int size)
    143 {
    144 	static char output[CRYPT_GENSALT_OUTPUT_SIZE];
    145 
    146 	return crypt_gensalt_rn(prefix, count,
    147 		input, size, output, sizeof(output));
    148 }
    149 
    150 #ifdef TEST
    151 static const char *tests[][3] = {
    152 	{"$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW",
    153 		"U*U"},
    154 	{"$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK",
    155 		"U*U*"},
    156 	{"$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a",
    157 		"U*U*U"},
    158 	{"$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui",
    159 		"0123456789abcdefghijklmnopqrstuvwxyz"
    160 		"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    161 		"chars after 72 are ignored"},
    162 	{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
    163 		"\xa3"},
    164 	{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
    165 		"\xff\xff\xa3"},
    166 	{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
    167 		"\xff\xff\xa3"},
    168 	{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nqd1wy.pTMdcvrRWxyiGL2eMz.2a85.",
    169 		"\xff\xff\xa3"},
    170 	{"$2b$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
    171 		"\xff\xff\xa3"},
    172 	{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
    173 		"\xa3"},
    174 	{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
    175 		"\xa3"},
    176 	{"$2b$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
    177 		"\xa3"},
    178 	{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
    179 		"1\xa3" "345"},
    180 	{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
    181 		"\xff\xa3" "345"},
    182 	{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
    183 		"\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
    184 	{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
    185 		"\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
    186 	{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.ZC1JEJ8Z4gPfpe1JOr/oyPXTWl9EFd.",
    187 		"\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
    188 	{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
    189 		"\xff\xa3" "345"},
    190 	{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
    191 		"\xff\xa3" "345"},
    192 	{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
    193 		"\xa3" "ab"},
    194 	{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
    195 		"\xa3" "ab"},
    196 	{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
    197 		"\xa3" "ab"},
    198 	{"$2x$05$6bNw2HLQYeqHYyBfLMsv/OiwqTymGIGzFsA4hOTWebfehXHNprcAS",
    199 		"\xd1\x91"},
    200 	{"$2x$05$6bNw2HLQYeqHYyBfLMsv/O9LIGgn8OMzuDoHfof8AQimSGfcSWxnS",
    201 		"\xd0\xc1\xd2\xcf\xcc\xd8"},
    202 	{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6",
    203 		"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    204 		"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    205 		"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    206 		"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    207 		"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    208 		"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
    209 		"chars after 72 are ignored as usual"},
    210 	{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy",
    211 		"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
    212 		"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
    213 		"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
    214 		"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
    215 		"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
    216 		"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"},
    217 	{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe",
    218 		"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
    219 		"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
    220 		"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
    221 		"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
    222 		"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
    223 		"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"},
    224 	{"$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy",
    225 		""},
    226 	{"*0", "", "$2a$03$CCCCCCCCCCCCCCCCCCCCC."},
    227 	{"*0", "", "$2a$32$CCCCCCCCCCCCCCCCCCCCC."},
    228 	{"*0", "", "$2c$05$CCCCCCCCCCCCCCCCCCCCC."},
    229 	{"*0", "", "$2z$05$CCCCCCCCCCCCCCCCCCCCC."},
    230 	{"*0", "", "$2`$05$CCCCCCCCCCCCCCCCCCCCC."},
    231 	{"*0", "", "$2{$05$CCCCCCCCCCCCCCCCCCCCC."},
    232 	{"*1", "", "*0"},
    233 	{NULL}
    234 };
    235 
    236 #define which				tests[0]
    237 
    238 static volatile sig_atomic_t running;
    239 
    240 static void handle_timer(int signum)
    241 {
    242 	(void) signum;
    243 	running = 0;
    244 }
    245 
    246 static void *run(void *arg)
    247 {
    248 	unsigned long count = 0;
    249 	int i = 0;
    250 	void *data = NULL;
    251 	int size = 0x12345678;
    252 
    253 	do {
    254 		const char *hash = tests[i][0];
    255 		const char *key = tests[i][1];
    256 		const char *setting = tests[i][2];
    257 
    258 		if (!tests[++i][0])
    259 			i = 0;
    260 
    261 		if (setting && strlen(hash) < 30) /* not for benchmark */
    262 			continue;
    263 
    264 		if (strcmp(crypt_ra(key, hash, &data, &size), hash)) {
    265 			printf("%d: FAILED (crypt_ra/%d/%lu)\n",
    266 				(int)((char *)arg - (char *)0), i, count);
    267 			free(data);
    268 			return NULL;
    269 		}
    270 		count++;
    271 	} while (running);
    272 
    273 	free(data);
    274 	return count + (char *)0;
    275 }
    276 
    277 int main(void)
    278 {
    279 	struct itimerval it;
    280 	struct tms buf;
    281 	clock_t clk_tck, start_real, start_virtual, end_real, end_virtual;
    282 	unsigned long count;
    283 	void *data;
    284 	int size;
    285 	char *setting1, *setting2;
    286 	int i;
    287 #ifdef TEST_THREADS
    288 	pthread_t t[TEST_THREADS];
    289 	void *t_retval;
    290 #endif
    291 
    292 	data = NULL;
    293 	size = 0x12345678;
    294 
    295 	for (i = 0; tests[i][0]; i++) {
    296 		const char *hash = tests[i][0];
    297 		const char *key = tests[i][1];
    298 		const char *setting = tests[i][2];
    299 		const char *p;
    300 		int ok = !setting || strlen(hash) >= 30;
    301 		int o_size;
    302 		char s_buf[30], o_buf[61];
    303 		if (!setting) {
    304 			memcpy(s_buf, hash, sizeof(s_buf) - 1);
    305 			s_buf[sizeof(s_buf) - 1] = 0;
    306 			setting = s_buf;
    307 		}
    308 
    309 		__set_errno(0);
    310 		p = crypt(key, setting);
    311 		if ((!ok && !errno) || strcmp(p, hash)) {
    312 			printf("FAILED (crypt/%d)\n", i);
    313 			return 1;
    314 		}
    315 
    316 		if (ok && strcmp(crypt(key, hash), hash)) {
    317 			printf("FAILED (crypt/%d)\n", i);
    318 			return 1;
    319 		}
    320 
    321 		for (o_size = -1; o_size <= (int)sizeof(o_buf); o_size++) {
    322 			int ok_n = ok && o_size == (int)sizeof(o_buf);
    323 			const char *x = "abc";
    324 			strcpy(o_buf, x);
    325 			if (o_size >= 3) {
    326 				x = "*0";
    327 				if (setting[0] == '*' && setting[1] == '0')
    328 					x = "*1";
    329 			}
    330 			__set_errno(0);
    331 			p = crypt_rn(key, setting, o_buf, o_size);
    332 			if ((ok_n && (!p || strcmp(p, hash))) ||
    333 			    (!ok_n && (!errno || p || strcmp(o_buf, x)))) {
    334 				printf("FAILED (crypt_rn/%d)\n", i);
    335 				return 1;
    336 			}
    337 		}
    338 
    339 		__set_errno(0);
    340 		p = crypt_ra(key, setting, &data, &size);
    341 		if ((ok && (!p || strcmp(p, hash))) ||
    342 		    (!ok && (!errno || p || strcmp((char *)data, hash)))) {
    343 			printf("FAILED (crypt_ra/%d)\n", i);
    344 			return 1;
    345 		}
    346 	}
    347 
    348 	setting1 = crypt_gensalt(which[0], 12, data, size);
    349 	if (!setting1 || strncmp(setting1, "$2a$12$", 7)) {
    350 		puts("FAILED (crypt_gensalt)\n");
    351 		return 1;
    352 	}
    353 
    354 	setting2 = crypt_gensalt_ra(setting1, 12, data, size);
    355 	if (strcmp(setting1, setting2)) {
    356 		puts("FAILED (crypt_gensalt_ra/1)\n");
    357 		return 1;
    358 	}
    359 
    360 	(*(char *)data)++;
    361 	setting1 = crypt_gensalt_ra(setting2, 12, data, size);
    362 	if (!strcmp(setting1, setting2)) {
    363 		puts("FAILED (crypt_gensalt_ra/2)\n");
    364 		return 1;
    365 	}
    366 
    367 	free(setting1);
    368 	free(setting2);
    369 	free(data);
    370 
    371 #if defined(_SC_CLK_TCK) || !defined(CLK_TCK)
    372 	clk_tck = sysconf(_SC_CLK_TCK);
    373 #else
    374 	clk_tck = CLK_TCK;
    375 #endif
    376 
    377 	running = 1;
    378 	signal(SIGALRM, handle_timer);
    379 
    380 	memset(&it, 0, sizeof(it));
    381 	it.it_value.tv_sec = 5;
    382 	setitimer(ITIMER_REAL, &it, NULL);
    383 
    384 	start_real = times(&buf);
    385 	start_virtual = buf.tms_utime + buf.tms_stime;
    386 
    387 	count = (char *)run((char *)0) - (char *)0;
    388 
    389 	end_real = times(&buf);
    390 	end_virtual = buf.tms_utime + buf.tms_stime;
    391 	if (end_virtual == start_virtual) end_virtual++;
    392 
    393 	printf("%.1f c/s real, %.1f c/s virtual\n",
    394 		(float)count * clk_tck / (end_real - start_real),
    395 		(float)count * clk_tck / (end_virtual - start_virtual));
    396 
    397 #ifdef TEST_THREADS
    398 	running = 1;
    399 	it.it_value.tv_sec = 60;
    400 	setitimer(ITIMER_REAL, &it, NULL);
    401 	start_real = times(&buf);
    402 
    403 	for (i = 0; i < TEST_THREADS; i++)
    404 	if (pthread_create(&t[i], NULL, run, i + (char *)0)) {
    405 		perror("pthread_create");
    406 		return 1;
    407 	}
    408 
    409 	for (i = 0; i < TEST_THREADS; i++) {
    410 		if (pthread_join(t[i], &t_retval)) {
    411 			perror("pthread_join");
    412 			continue;
    413 		}
    414 		if (!t_retval) continue;
    415 		count = (char *)t_retval - (char *)0;
    416 		end_real = times(&buf);
    417 		printf("%d: %.1f c/s real\n", i,
    418 			(float)count * clk_tck / (end_real - start_real));
    419 	}
    420 #endif
    421 
    422 	return 0;
    423 }
    424 #endif