Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/lib/libcrypt crypt(3): Adapt default Argon2 parameters to sy...



details:   https://anonhg.NetBSD.org/src/rev/eb4f2afb377d
branches:  trunk
changeset: 989925:eb4f2afb377d
user:      nia <nia%NetBSD.org@localhost>
date:      Wed Oct 20 13:03:29 2021 +0000

description:
crypt(3): Adapt default Argon2 parameters to system performance

If the parameters are unspecified:

- Set the default memory consumption based on the amount of memory
available to userspace.

The algorithm actually slows down incredibly quickly as the "memory"
parameter is increased. We want to avoid running out of memory on low
memory systems, but increase the difficulty of bruteforcing passwords
from systems with a lot of memory. At the same time, we want to avoid
problems when concurrent logins are happening.

- Run a hashing loop for one second with steadily increasing "time"
until we settle on a value for "time". We want to use as much CPU time
as reasonable for computing the password hash without making logins
inconvenient.

diffstat:

 lib/libcrypt/crypt-argon2.c |  110 ++++++++++++++++++++++++++++++++++++++++++++
 lib/libcrypt/pw_gensalt.c   |   53 +++++++++++++--------
 2 files changed, 143 insertions(+), 20 deletions(-)

diffs (274 lines):

diff -r 32cd7ca82da9 -r eb4f2afb377d lib/libcrypt/crypt-argon2.c
--- a/lib/libcrypt/crypt-argon2.c       Wed Oct 20 08:10:26 2021 +0000
+++ b/lib/libcrypt/crypt-argon2.c       Wed Oct 20 13:03:29 2021 +0000
@@ -24,6 +24,11 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <sys/resource.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/syslimits.h>
+
 #include <stdlib.h>
 #include <stdio.h> 
 #include <unistd.h>
@@ -37,6 +42,10 @@
 #include <err.h>
 #include "crypt.h"
 
+crypt_private int
+estimate_argon2_params(argon2_type, uint32_t *,
+    uint32_t *, uint32_t *);
+
 /* defaults pulled from run.c */
 #define HASHLEN                32
 #define T_COST_DEF     3 
@@ -57,6 +66,10 @@
 #define ARGON2_ARGON2D_STR     "argon2d"
 #define ARGON2_ARGON2ID_STR    "argon2id"
 
+/*
+ * Unpadded Base64 calculations are taken from the Apache2/CC-0
+ * licensed libargon2 for compatibility
+ */
 
 /*
  * Some macros for constant-time comparisons. These work over values in
@@ -123,6 +136,103 @@
        return src;
 }
 
+/*
+ * Used to find default parameters that perform well on the host
+ * machine.  Inputs should dereference to either 0 (to estimate),
+ * or desired value.
+ */
+crypt_private int
+estimate_argon2_params(argon2_type atype, uint32_t *etime,
+    uint32_t *ememory, uint32_t *ethreads)
+{
+       const int mib[] = { CTL_HW, HW_USERMEM64 };
+       struct timespec tp1, tp2, delta;
+       char tmp_salt[16];
+       char tmp_pwd[16];
+       uint32_t tmp_hash[32];
+       char tmp_encoded[256];
+       struct rlimit rlim;
+       uint64_t max_mem;
+       size_t max_mem_sz = sizeof(max_mem);
+       /* low values from argon2 test suite... */
+       uint32_t memory = 256;
+       uint32_t time = 2;
+       uint32_t threads = 1;
+
+       if (*ememory < ARGON2_MIN_MEMORY) {
+               /*
+                * attempt to find a reasonble bound for memory use
+                */
+               if (sysctl(mib, __arraycount(mib),
+                   &max_mem, &max_mem_sz, NULL, 0) < 0) {
+                       goto error;
+               }
+               if (getrlimit(RLIMIT_AS, &rlim) < 0)
+                       goto error;
+               if (max_mem > rlim.rlim_cur && rlim.rlim_cur != RLIM_INFINITY)
+                       max_mem = rlim.rlim_cur;
+
+               /*
+                * Note that adding memory also greatly slows the algorithm.
+                * Do we need to be concerned about memory usage during
+                * concurrent connections?
+                */
+               max_mem /= 1000000;
+               if (max_mem > 30000) {
+                       memory = 8192;
+               } else if (max_mem > 7000) {
+                       memory = 4096;
+               } else if (max_mem > 24) {
+                       memory = 256;
+               } else {
+                       memory = ARGON2_MIN_MEMORY;
+               }
+       } else {
+               memory = *ememory;
+       }
+
+       if (*etime < ARGON2_MIN_TIME) {
+               /*
+                * just fill these with random stuff since we'll immediately
+                * discard them after calculating hashes for 1 second
+                */
+               arc4random_buf(tmp_pwd, sizeof(tmp_pwd));
+               arc4random_buf(tmp_salt, sizeof(tmp_salt));
+
+               if (clock_gettime(CLOCK_MONOTONIC, &tp1) == -1)
+                       goto error;
+               for (; delta.tv_sec < 1 && time < ARGON2_MAX_TIME; ++time) {
+                       if (argon2_hash(time, memory, threads,
+                           tmp_pwd, sizeof(tmp_pwd), 
+                           tmp_salt, sizeof(tmp_salt), 
+                           tmp_hash, sizeof(tmp_hash), 
+                           tmp_encoded, sizeof(tmp_encoded), 
+                           atype, ARGON2_VERSION_NUMBER) != ARGON2_OK) {
+                               goto reset;
+                       }
+                       if (clock_gettime(CLOCK_MONOTONIC, &tp2) == -1)
+                               break;
+                       if (timespeccmp(&tp1, &tp2, >))
+                               break; /* broken system... */
+                       timespecsub(&tp2, &tp1, &delta);
+               }
+       } else {
+               time = *etime;
+       }
+
+error:
+       *etime = time;
+       *ememory = memory;
+       *ethreads = threads;
+       return 0;
+reset:
+       time = 2;
+       memory = 256;
+       threads = 1;
+       goto error;
+}
+
+
 /* process params to argon2 */
 /* we don't force param order as input, */
 /* but we do provide the expected order to argon2 api */
diff -r 32cd7ca82da9 -r eb4f2afb377d lib/libcrypt/pw_gensalt.c
--- a/lib/libcrypt/pw_gensalt.c Wed Oct 20 08:10:26 2021 +0000
+++ b/lib/libcrypt/pw_gensalt.c Wed Oct 20 13:03:29 2021 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: pw_gensalt.c,v 1.12 2021/10/16 10:53:33 nia Exp $      */
+/*     $NetBSD: pw_gensalt.c,v 1.13 2021/10/20 13:03:29 nia Exp $      */
 
 /*
  * Copyright 1997 Niels Provos <provos%physnet.uni-hamburg.de@localhost>
@@ -34,7 +34,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: pw_gensalt.c,v 1.12 2021/10/16 10:53:33 nia Exp $");
+__RCSID("$NetBSD: pw_gensalt.c,v 1.13 2021/10/20 13:03:29 nia Exp $");
 #endif /* not lint */
 
 #include <sys/syslimits.h>
@@ -59,6 +59,9 @@
 #define ARGON2_ARGON2I_STR      "argon2i"
 #define ARGON2_ARGON2D_STR      "argon2d"
 #define ARGON2_ARGON2ID_STR     "argon2id"
+
+crypt_private int
+estimate_argon2_params(argon2_type, uint32_t *, uint32_t *, uint32_t *);
 #endif /* HAVE_ARGON2 */
 
 static const struct pw_salt {
@@ -163,15 +166,16 @@
 
 #ifdef HAVE_ARGON2
 static int
-__gensalt_argon2_decode_option(char *dst, size_t dlen, const char *option)
+__gensalt_argon2_decode_option(char *dst, size_t dlen,
+    const char *option, argon2_type atype)
 {
-
-       char * in = 0;
-       char * a = 0;
+       char *in = 0;
+       char *a = 0;
        size_t tmp = 0;
        int error = 0;
-       /* ob buffer: m_cost, t_cost, threads */
-       uint32_t ob[3] = {4096, 3, 1}; 
+       uint32_t memory = 0;
+       uint32_t time = 0;
+       uint32_t threads = 0;
 
        memset(dst, 0, dlen);
 
@@ -179,37 +183,33 @@
                goto done;
        }
 
-       in = (char *)strdup(option);
+       in = strdup(option);
 
        while ((a = strsep(&in, ",")) != NULL) {
-               switch(*a) {
-
+               switch (*a) {
                        case 'm':
                                a += strlen("m=");
                                if ((getnum(a, &tmp)) == -1) {
                                        --error;
                                } else {
-                                       ob[0] = tmp;
+                                       memory = tmp;
                                }
-
                                break;
                        case 't':
                                a += strlen("t=");
                                if ((getnum(a, &tmp)) == -1) {
                                        --error;
                                } else {
-                                       ob[1] = tmp;
+                                       time = tmp;
                                }
-
                                break;
                        case 'p':
                                a += strlen("p=");
                                if ((getnum(a, &tmp)) == -1) {
                                        --error;
                                } else {
-                                       ob[2] = tmp;
+                                       threads = tmp;
                                }
-
                                break;
                        default:
                                --error;
@@ -217,22 +217,35 @@
        }
 
        free(in);
+
 done:
-       snprintf(dst, dlen, "m=%d,t=%d,p=%d", ob[0], ob[1], ob[2]);
+       /*
+        * If parameters are unspecified, calculate some reasonable
+        * ones based on system time.
+        */
+       if (memory < ARGON2_MIN_MEMORY ||
+           time < ARGON2_MIN_TIME ||
+           threads < ARGON2_MIN_THREADS) {
+               estimate_argon2_params(atype, &time, &memory, &threads);
+       }
+
+       snprintf(dst, dlen, "m=%d,t=%d,p=%d", memory, time, threads);
 
        return error;
 }
 
 
 static int
-__gensalt_argon2(char *salt, size_t saltsiz, const char *option,argon2_type atype)
+__gensalt_argon2(char *salt, size_t saltsiz,
+    const char *option, argon2_type atype)
 {
        int rc;
        int n;
        char buf[64];
 
        /* get param, enforcing order and applying defaults */
-       if ((rc = __gensalt_argon2_decode_option(buf, sizeof(buf), option)) < 0) {
+       if ((rc = __gensalt_argon2_decode_option(buf,
+           sizeof(buf), option, atype)) < 0) {
                return 0;
        }
 



Home | Main Index | Thread Index | Old Index