/* See LICENSE file for copyright and license details. */ #include "common.h" #ifdef __linux__ #include #endif #include #ifndef CLOCK_MONOTONIC_RAW # define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC #endif #define SALT_ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" #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); } 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; } 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 0 #define TIME_HASH(PARAMS) time_hash(PARAMS, #PARAMS, __LINE__) TIME_HASH(libar2simplified_recommendation(0)); TIME_HASH(libar2simplified_recommendation(1)); #endif return 0; }