aboutsummaryrefslogblamecommitdiffstats
path: root/test.c
blob: cd22e4560081e425f414b0357617bc9ed07e93ab (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                         


                       



                                            

                                                                                        
 

                              




































                                                                                                                          
                                                                                                             















                                                                                  
                                                                                                             











                                                                                  

                                                                                 

                                                
                                                                                      




                             







                                                                                                  
 



                                                                         
                      



                                                              
 




                                                                          

         



                        
                
                                     
                              
                          
































                                                                         
                           


























                                                                                                                     
 
                        
































                                                                                    
      

 











                                                                                


          
     
                         
                                                        






























                                                                                                                                                                                                  
                                                                                                                             

                                                                                                                     














                                                                                                                  

                                     



                                                                                                       
                        




                                                              

                 
/* See LICENSE file for copyright and license details. */
#include "common.h"
#ifdef __linux__
#include <sys/random.h>
#endif
#include <time.h>
#ifndef CLOCK_MONOTONIC_RAW
# define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
#endif

#define SALT_ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

#define TIME_RECOMMENDATIONS 0


#define MEM(S) S, sizeof(S) - 1


#define assert(TRUTH) assert_(TRUTH, #TRUTH, __LINE__)
#define assert_streq(RESULT, EXPECT) assert_streq_(RESULT, EXPECT, #RESULT, __LINE__)
#define assert_zueq(RESULT, EXPECT) assert_zueq_(RESULT, EXPECT, #RESULT, __LINE__)

static int from_lineno = 0;


static int
nulstrcmp(const char *a, const char *b)
{
	return !a ? -!!b : !b ? +1 : strcmp(a, b);
}


static void
assert_(int truth, const char *truthstr, int lineno)
{
	if (!truth) {
		if (from_lineno)
			fprintf(stderr, "Assertion at line %i, from line %i failed: %s\n", lineno, from_lineno, truthstr);
		else
			fprintf(stderr, "Assertion at line %i failed: %s\n", lineno, truthstr);
		fprintf(stderr, "\terrno: %i (%s)\n", errno, strerror(errno));
		exit(1);
	}
}


static void
assert_streq_(const char *result, const char *expect, const char *code, int lineno)
{
	if (nulstrcmp(result, expect)) {
		if (from_lineno)
			fprintf(stderr, "Assertion at line %i, from line %i failed:\n", lineno, from_lineno);
		else
			fprintf(stderr, "Assertion at line %i failed:\n", lineno);
		fprintf(stderr, "\tcode:     %s\n", code);
		fprintf(stderr, "\tresult:   %s\n", result);
		fprintf(stderr, "\texpected: %s\n", expect);
		fprintf(stderr, "\terrno:    %i (%s)\n", errno, strerror(errno));
		exit(1);
	}
}


static void
assert_zueq_(size_t result, size_t expect, const char *code, int lineno)
{
	if (result != expect) {
		if (from_lineno)
			fprintf(stderr, "Assertion at line %i, from line %i failed:\n", lineno, from_lineno);
		else
			fprintf(stderr, "Assertion at line %i failed:\n", lineno);
		fprintf(stderr, "\tcode:     %s\n", code);
		fprintf(stderr, "\tresult:   %zu\n", result);
		fprintf(stderr, "\texpected: %zu\n", expect);
		fprintf(stderr, "\terrno:    %i (%s)\n", errno, strerror(errno));
		exit(1);
	}
}


static void
check_hash(const char *pwd, size_t pwdlen, const char *input, const char *output,
	   int (*saltgenerator)(char *out, size_t n), int lineno)
{
	struct libar2_argon2_parameters *params;
	char tag_buf[512], pwd_buf[512], *input_tag, *tag_got, *paramstr, *output_got;
	size_t taglen;

	from_lineno = lineno;
	errno = 0;

	assert(!!(params = libar2simplified_decode(input, &input_tag, NULL, saltgenerator)));
	if (input_tag) {
		assert_zueq(libar2_decode_base64(input_tag, tag_buf, &taglen), strlen(input_tag));
		assert_zueq(taglen, params->hashlen);
		assert(!!(paramstr = libar2simplified_encode(params, tag_buf)));
		assert_streq(paramstr, output);
		free(paramstr);
	}

	strcpy(pwd_buf, pwd);
	assert(!libar2simplified_hash(tag_buf, pwd_buf, pwdlen, params));
	tag_got = libar2simplified_encode_hash(params, tag_buf);
	assert_streq(tag_got, &strrchr(output, '$')[1]);
	free(tag_got);
	output_got = libar2simplified_encode(params, tag_buf);
	assert_streq(output_got, output);
	free(output_got);
	free(params);

	if (strlen(pwd) == pwdlen && !saltgenerator) {
		strcpy(pwd_buf, pwd);
		output_got = libar2simplified_crypt(pwd_buf, input, NULL);
		assert_streq(output_got, output);
		free(output_got);
	}

	from_lineno = 0;
}


#ifdef __linux__
static ssize_t getrandom_return = -1;
static char getrandom_random0;
static int getrandom_real;
#endif

ssize_t
getrandom(void *buf, size_t buflen, unsigned int flags)
{
	size_t i;
	assert(flags == GRND_NONBLOCK);
	if (getrandom_return < 0)
		return getrandom_return;
	for (i = 0; i < buflen && i < (size_t)getrandom_return; i++)
		((char *)buf)[i] = (char)((size_t)getrandom_random0 + i);
	return (ssize_t)i;
}

static void
check_random_salt_generate(void)
{
	struct libar2_argon2_parameters *params[8];
	size_t i, num_equal_first;

#ifdef __linux__
	char expected_salt[8];
	const char *expected_salts[] = {
		"AAAAAAAAAAA",
		"BCBCBCBCBCB",
		"CDECDECDECD",
		"DEFGDEFGDEF",
		"EFGHIEFGHIE",
		"FGHIJKFGHIJ",
		"GHIJKLMGHIJ",
		"HIJKLMNOHIJ"
	};

	getrandom_real = 0;
	for (i = 0; i < sizeof(params) / sizeof(*params); i++) {
		getrandom_return = (ssize_t)(i + 1);
		getrandom_random0 = (char)i;
		assert(!!(params[i] = libar2simplified_decode("$argon2d$v=16$m=8,t=1,p=1$*8$*8", NULL, NULL, NULL)));
		assert_zueq(params[i]->saltlen, sizeof(expected_salt));
		libar2_decode_base64(expected_salts[i], expected_salt, &(size_t){0});
		assert(!memcmp(params[i]->salt, expected_salt, sizeof(expected_salt)));
		free(params[i]);
	}

	getrandom_return = -1;
	getrandom_random0 = 0;
#endif

	num_equal_first = 0;
	for (i = 0; i < sizeof(params) / sizeof(*params); i++) {
		assert(!!(params[i] = libar2simplified_decode("$argon2d$v=16$m=8,t=1,p=1$*8$*8", NULL, NULL, NULL)));
	}
	for (i = 1; i < sizeof(params) / sizeof(*params); i++) {
		assert_zueq(params[i]->saltlen, params[0]->saltlen);
		num_equal_first += !memcmp(params[i]->salt, params[0]->salt, params[0]->saltlen);
		free(params[i]);
	}
	free(params[0]);
	assert(num_equal_first <= (sizeof(params) / sizeof(*params) - 1) / 4);
}


#if TIME_RECOMMENDATIONS
static void
time_hash(const char *params_str, const char *params_name, int lineno)
{
	struct libar2_argon2_parameters *params;
	unsigned char hash[1024];
	struct timespec start, end;
	uintmax_t ms, sub_ms;
	int r;

	from_lineno = lineno;
	errno = 0;

	assert(!!(params = libar2simplified_decode(params_str, NULL, NULL, NULL)));
	assert(params->hashlen <= sizeof(hash));
	clock_gettime(CLOCK_MONOTONIC_RAW, &start);
	r = libar2simplified_hash(hash, NULL, 0, params);
	clock_gettime(CLOCK_MONOTONIC_RAW, &end);
	assert(!r);
	free(params);
	end.tv_sec -= start.tv_sec;
	end.tv_nsec -= start.tv_nsec;
	if (end.tv_nsec < 0) {
		end.tv_sec -= 1;
		end.tv_nsec += 1000000000L;
	}
	ms = (uintmax_t)end.tv_sec;
	ms *= 1000;
	ms += (uintmax_t)(end.tv_nsec / 1000000L);
	sub_ms = (uintmax_t)(end.tv_nsec % 1000000L);
	fprintf(stderr, "Hash time for %s: %ju.%06jums\n", params_name, ms, sub_ms);

	from_lineno = 0;
}
#endif


static int
gensalt_ICAgICAgICA(char *out, size_t n)
{
	const char *salt = "ICAgICAgICA";
	size_t i;
	assert_zueq(n, strlen(salt));
	for (i = 0; i < n; i++)
		out[i] = (char)(strchr(SALT_ALPHABET, salt[i]) - SALT_ALPHABET);
	return 0;
}


int
main(void)
{
#if 1
#define CHECK(PWD, HASH)\
	check_hash(MEM(PWD), HASH, HASH, NULL, __LINE__)

	CHECK("\x00", "$argon2d$v=16$m=8,t=1,p=1$ICAgICAgICA$Eyx1BxGazSuPQoy7osaQuo20Dw9VI97dYUOgcC3cMgw");
	CHECK("test", "$argon2i$v=19$m=4096,t=3,p=1$fn5/f35+f38$9tqKA4WMEsSAOEUwatjxvJLSqL1j0GQkgbsfnpresDw");
	CHECK("\x00", "$argon2id$v=16$m=8,t=1,p=1$ICAgICAgICA$fXq1aUbp9yhbn+EQc4AzUUE6AKnHAkvzIXsN6J4ukvE");
	CHECK("", "$argon2d$v=16$m=8,t=1,p=1$ICAgICAgICA$X54KZYxUSfMUihzebb70sKbheabHilo8gsUldrVU4IU");
	CHECK("", "$argon2d$v=16$m=8,t=1,p=1$ICAgICAgICA$NjODMrWrS7zeivNNpHsuxD9c6uDmUQ6YqPRhb8H5DSNw9n683FUCJZ3tyxgfJpYYANI+01WT/S5zp1UVs+qNRwnkdEyLKZMg+DIOXVc9z1po9ZlZG8+Gp4g5brqfza3lvkR9vw");
	CHECK("", "$argon2ds$v=16$m=8,t=1,p=1$ICAgICAgICA$zgdykk9ZjN5VyrW0LxGw8LmrJ1Z6fqSC+3jPQtn4n0s");

	CHECK("password", "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ");
	CHECK("password", "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY");
	CHECK("password", "$argon2i$m=65536,t=1,p=1$c29tZXNhbHQ$gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI");
	CHECK("password", "$argon2i$m=65536,t=4,p=1$c29tZXNhbHQ$8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs");
	CHECK("differentpassword", "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ$6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM");
	CHECK("password", "$argon2i$m=65536,t=2,p=1$ZGlmZnNhbHQ$eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc");

	CHECK("password", "$argon2i$v=16$m=256,t=2,p=1$c29tZXNhbHQ$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY");

	CHECK("password", "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ$iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8");
	CHECK("password", "$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQ$0WgHXE2YXhPr6uVgz4uUw7XYoWxRkWtvSsLaOsEbvs8");
	CHECK("differentpassword", "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$FK6NoBr+qHAMI1jc73xTWNkCEoK9iGY6RWL1n7dNIu4");

	CHECK("password", "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4");
	CHECK("password", "$argon2id$v=19$m=65536,t=1,p=1$c29tZXNhbHQ$9qWtwbpyPd3vm1rB1GThgPzZ3/ydHL92zKL+15XZypg");
	CHECK("password", "$argon2id$v=19$m=65536,t=4,p=1$c29tZXNhbHQ$kCXUjmjvc5XMqQedpMTsOv+zyJEf5PhtGiUghW9jFyw");
	CHECK("differentpassword", "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$C4TWUs9rDEvq7w3+J4umqA32aWKB1+DSiRuBfYxFj94");

	CHECK("password", "$argon2i$m=256,t=2,p=2$c29tZXNhbHQ$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs");
	CHECK("password", "$argon2i$v=16$m=256,t=2,p=2$c29tZXNhbHQ$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs");
	CHECK("password", "$argon2i$v=19$m=256,t=2,p=2$c29tZXNhbHQ$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E");
	CHECK("password", "$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ$bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc");

	/* This hash is not well-known. It is used to test thread-support and was calculated with multi-threading disabled */
	CHECK("password", "$argon2id$v=19$m=2048,t=16,p=16$c29tZXNhbHQ$FRWpYzcrsos+DHNInvfsl0g8mZBdPqUdarIYh/Pnc1g");

#undef CHECK
#define CHECK(PWD, INPUT, SALTGEN, OUTPUT)\
	check_hash(MEM(PWD), INPUT, OUTPUT, SALTGEN, __LINE__)

	CHECK("", "$argon2d$v=16$m=8,t=1,p=1$*8$*32", gensalt_ICAgICAgICA,
	      "$argon2d$v=16$m=8,t=1,p=1$ICAgICAgICA$X54KZYxUSfMUihzebb70sKbheabHilo8gsUldrVU4IU");
	CHECK("", "$argon2d$v=16$m=8,t=1,p=1$ICAgICAgICA$*32", NULL,
	      "$argon2d$v=16$m=8,t=1,p=1$ICAgICAgICA$X54KZYxUSfMUihzebb70sKbheabHilo8gsUldrVU4IU");
	CHECK("", "$argon2d$v=16$m=8,t=1,p=1$*8$X54KZYxUSfMUihzebb70sKbheabHilo8gsUldrVU4IU", gensalt_ICAgICAgICA,
	      "$argon2d$v=16$m=8,t=1,p=1$ICAgICAgICA$X54KZYxUSfMUihzebb70sKbheabHilo8gsUldrVU4IU");
	CHECK("", "$argon2d$v=16$m=8,t=1,p=1$*8$*100", gensalt_ICAgICAgICA,
	      "$argon2d$v=16$m=8,t=1,p=1$ICAgICAgICA$"
	      "NjODMrWrS7zeivNNpHsuxD9c6uDmUQ6YqPRhb8H5DSNw9n683FUCJZ3tyxgfJpYYANI"
	      "+01WT/S5zp1UVs+qNRwnkdEyLKZMg+DIOXVc9z1po9ZlZG8+Gp4g5brqfza3lvkR9vw");

	check_random_salt_generate();

	assert_streq(libar2simplified_recommendation(0), RECOMMENDATION_SIDE_CHANNEL_ENVIRONMENT);
	assert_streq(libar2simplified_recommendation(1), RECOMMENDATION_SIDE_CHANNEL_FREE_ENVIRONMENT);
#endif

#if TIME_RECOMMENDATIONS
#define TIME_HASH(PARAMS) time_hash(PARAMS, #PARAMS, __LINE__)
	TIME_HASH(libar2simplified_recommendation(0));
	TIME_HASH(libar2simplified_recommendation(1));
#endif

	return 0;
}