Date: Mon, 11 Jun 2012 20:56:51 +0300 From: Gleb Kurtsou <gleb.kurtsou@gmail.com> To: "Simon L. B. Nielsen" <simon@FreeBSD.org> Cc: Dag-Erling =?utf-8?B?U23DuHJncmF2?= <des@des.no>, freebsd-security@freebsd.org Subject: Re: Default password hash Message-ID: <20120611175651.GA27216@reks> In-Reply-To: <CAC8HS2HFe87JU_x7vfbXP-_=WXJKv2kmG4sPBXxGgJ-k7-3poQ@mail.gmail.com> References: <86r4tqotjo.fsf@ds4.des.no> <6E26E03B-8D1D-44D3-B94E-0552BE5CA894@FreeBSD.org> <20120610145351.GA1098@reks> <CAC8HS2HFe87JU_x7vfbXP-_=WXJKv2kmG4sPBXxGgJ-k7-3poQ@mail.gmail.com>
next in thread | previous in thread | raw e-mail | index | archive | help
--ew6BAiZeqk4r7MaW Content-Type: text/plain; charset=utf-8 Content-Disposition: inline On (11/06/2012 12:43), Simon L. B. Nielsen wrote: > On Sun, Jun 10, 2012 at 3:53 PM, Gleb Kurtsou <gleb.kurtsou@gmail.com> wrote: [...] > > Do you mean pkcs5v2_calculate from geli? It seems to have a drawback > > Correct. > > > that results produced depend on actual CPU load. > > That's not the drawback, but the whole point (well, at least a point). > While it can of course produce fewer rounds on different systems, > especially on fast systems you will likely end up with a lot more > rounds than whatever the default is. I meant that pkcs5v2_calculate() should be constant or nearly constant for a given system. It shouldn't depend on CPU load at the moment user decides to change his/her password -- function will return fewer number of rounds than it normally would. GELI use case is ok, normally one won't shuffle encrypted hard disks between machines, which is often the case with password database (the same password database accessed by all machines in network/cluster, fast and dog slow). > > % ./pkcs5v-test [*] > > 541491 > > 539568 > > 542352 > > 540376 > > 388285 -- start several instances of pkcs5v-test in parallel > > 303071 > > 284793 > > 281110 > > > > It would be awesome to provide user with options to configure minimal > > and maximal iteration count and randomly choose iteration count within > > the range for each new password. Such trivial change should considerably > > complicate mass password bruteforce cracking. > > That's also an option. I'm not sure how well that would work in practice. > > > Variable number of rounds for a password would also require changing > > crypt() interface. > > Exactly, so probably not actually a working solution for normal case, > and possibly just not worth the trouble at all due to compatibility. It turned out to be fairly easy to implement without nasty hacks. Please find the patch attached. I believe patch to be correct. There is no standard way to extract and pass salt from hashed password due to differences in formats, so universal workaround for crypt() interface limitations is the following: check password: strcmp(crypt(hashed_pass, realpw), realpw) == 0 set new password: crypt_set_format("hash type") hashed_new_pass = crypt(new_pass, random_salt); Thanks, Gleb. --ew6BAiZeqk4r7MaW Content-Type: text/plain; charset=utf-8 Content-Disposition: attachment; filename="crypt_sha2.incrandom.patch.txt" commit d8b1a55a18365b805b3025496a91899df9def8f0 Author: Gleb Kurtsou <gleb.kurtsou@gmail.com> Date: Mon Jun 11 20:55:23 2012 +0300 libcrypt: Use random number of rounds in crypt_sha* for new passwords By default randomly increment number of rounds by at most 10%. diff --git a/lib/libcrypt/crypt-sha256.c b/lib/libcrypt/crypt-sha256.c index cab7405..1e91648 100644 --- a/lib/libcrypt/crypt-sha256.c +++ b/lib/libcrypt/crypt-sha256.c @@ -58,6 +58,8 @@ static const char sha256_rounds_prefix[] = "rounds="; #define ROUNDS_MIN 1000 /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 +/* Random number of rounds increment, set 0 to disable. */ +#define ROUNDS_INCRANDOM (ROUNDS_DEFAULT / 10) static char * crypt_sha256_r(const char *key, const char *salt, char *buffer, int buflen) @@ -69,7 +71,7 @@ crypt_sha256_r(const char *key, const char *salt, char *buffer, int buflen) size_t salt_len, key_len, cnt, rounds; char *cp, *copied_key, *copied_salt, *p_bytes, *s_bytes, *endp; const char *num; - bool rounds_custom; + bool rounds_custom, has_salt_prefix; copied_key = NULL; copied_salt = NULL; @@ -77,12 +79,16 @@ crypt_sha256_r(const char *key, const char *salt, char *buffer, int buflen) /* Default number of rounds. */ rounds = ROUNDS_DEFAULT; rounds_custom = false; + has_salt_prefix = false; /* Find beginning of salt string. The prefix should normally always * be present. Just in case it is not. */ - if (strncmp(sha256_salt_prefix, salt, sizeof(sha256_salt_prefix) - 1) == 0) + if (strncmp(sha256_salt_prefix, salt, sizeof(sha256_salt_prefix) - 1) + == 0) { /* Skip salt prefix. */ salt += sizeof(sha256_salt_prefix) - 1; + has_salt_prefix = true; + } if (strncmp(salt, sha256_rounds_prefix, sizeof(sha256_rounds_prefix) - 1) == 0) { @@ -94,6 +100,9 @@ crypt_sha256_r(const char *key, const char *salt, char *buffer, int buflen) rounds = MAX(ROUNDS_MIN, MIN(srounds, ROUNDS_MAX)); rounds_custom = true; } + } else if (!has_salt_prefix && ROUNDS_INCRANDOM != 0) { + rounds += arc4random() % ROUNDS_INCRANDOM; + rounds_custom = true; } salt_len = MIN(strcspn(salt, "$"), SALT_LEN_MAX); diff --git a/lib/libcrypt/crypt-sha512.c b/lib/libcrypt/crypt-sha512.c index 8e0054f..8cc710b 100644 --- a/lib/libcrypt/crypt-sha512.c +++ b/lib/libcrypt/crypt-sha512.c @@ -58,6 +58,8 @@ static const char sha512_rounds_prefix[] = "rounds="; #define ROUNDS_MIN 1000 /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 +/* Random number of rounds increment, set 0 to disable. */ +#define ROUNDS_INCRANDOM (ROUNDS_DEFAULT / 10) static char * crypt_sha512_r(const char *key, const char *salt, char *buffer, int buflen) @@ -69,7 +71,7 @@ crypt_sha512_r(const char *key, const char *salt, char *buffer, int buflen) size_t salt_len, key_len, cnt, rounds; char *cp, *copied_key, *copied_salt, *p_bytes, *s_bytes, *endp; const char *num; - bool rounds_custom; + bool rounds_custom, has_salt_prefix; copied_key = NULL; copied_salt = NULL; @@ -80,9 +82,12 @@ crypt_sha512_r(const char *key, const char *salt, char *buffer, int buflen) /* Find beginning of salt string. The prefix should normally always * be present. Just in case it is not. */ - if (strncmp(sha512_salt_prefix, salt, sizeof(sha512_salt_prefix) - 1) == 0) + if (strncmp(sha512_salt_prefix, salt, sizeof(sha512_salt_prefix) - 1) + == 0) { /* Skip salt prefix. */ salt += sizeof(sha512_salt_prefix) - 1; + has_salt_prefix = true; + } if (strncmp(salt, sha512_rounds_prefix, sizeof(sha512_rounds_prefix) - 1) == 0) { @@ -94,6 +99,9 @@ crypt_sha512_r(const char *key, const char *salt, char *buffer, int buflen) rounds = MAX(ROUNDS_MIN, MIN(srounds, ROUNDS_MAX)); rounds_custom = true; } + } else if (!has_salt_prefix && ROUNDS_INCRANDOM != 0) { + rounds += arc4random() % ROUNDS_INCRANDOM; + rounds_custom = true; } salt_len = MIN(strcspn(salt, "$"), SALT_LEN_MAX); --ew6BAiZeqk4r7MaW--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120611175651.GA27216>